Home | History | Annotate | Download | only in proxy
      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 "net/proxy/dhcp_proxy_script_fetcher_win.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/rand_util.h"
     13 #include "base/test/test_timeouts.h"
     14 #include "base/threading/platform_thread.h"
     15 #include "base/timer/elapsed_timer.h"
     16 #include "net/base/completion_callback.h"
     17 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
     18 #include "net/url_request/url_request_test_util.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 namespace net {
     22 
     23 namespace {
     24 
     25 TEST(DhcpProxyScriptFetcherWin, AdapterNamesAndPacURLFromDhcp) {
     26   // This tests our core Win32 implementation without any of the wrappers
     27   // we layer on top to achieve asynchronous and parallel operations.
     28   //
     29   // We don't make assumptions about the environment this unit test is
     30   // running in, so it just exercises the code to make sure there
     31   // is no crash and no error returned, but does not assert on the number
     32   // of interfaces or the information returned via DHCP.
     33   std::set<std::string> adapter_names;
     34   DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(&adapter_names);
     35   for (std::set<std::string>::const_iterator it = adapter_names.begin();
     36        it != adapter_names.end();
     37        ++it) {
     38     const std::string& adapter_name = *it;
     39     std::string pac_url =
     40         DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
     41     printf("Adapter '%s' has PAC URL '%s' configured in DHCP.\n",
     42            adapter_name.c_str(),
     43            pac_url.c_str());
     44   }
     45 }
     46 
     47 // Helper for RealFetch* tests below.
     48 class RealFetchTester {
     49  public:
     50   RealFetchTester()
     51       : context_(new TestURLRequestContext),
     52         fetcher_(new DhcpProxyScriptFetcherWin(context_.get())),
     53         finished_(false),
     54         on_completion_is_error_(false) {
     55     // Make sure the test ends.
     56     timeout_.Start(FROM_HERE,
     57         base::TimeDelta::FromSeconds(5), this, &RealFetchTester::OnTimeout);
     58   }
     59 
     60   void RunTest() {
     61     int result = fetcher_->Fetch(
     62         &pac_text_,
     63         base::Bind(&RealFetchTester::OnCompletion, base::Unretained(this)));
     64     if (result != ERR_IO_PENDING)
     65       finished_ = true;
     66   }
     67 
     68   void RunTestWithCancel() {
     69     RunTest();
     70     fetcher_->Cancel();
     71   }
     72 
     73   void RunTestWithDeferredCancel() {
     74     // Put the cancellation into the queue before even running the
     75     // test to avoid the chance of one of the adapter fetcher worker
     76     // threads completing before cancellation.  See http://crbug.com/86756.
     77     cancel_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(0),
     78                         this, &RealFetchTester::OnCancelTimer);
     79     RunTest();
     80   }
     81 
     82   void OnCompletion(int result) {
     83     if (on_completion_is_error_) {
     84       FAIL() << "Received completion for test in which this is error.";
     85     }
     86     finished_ = true;
     87     printf("Result code %d PAC data length %d\n", result, pac_text_.size());
     88   }
     89 
     90   void OnTimeout() {
     91     printf("Timeout!");
     92     OnCompletion(0);
     93   }
     94 
     95   void OnCancelTimer() {
     96     fetcher_->Cancel();
     97     finished_ = true;
     98   }
     99 
    100   void WaitUntilDone() {
    101     while (!finished_) {
    102       base::MessageLoop::current()->RunUntilIdle();
    103     }
    104     base::MessageLoop::current()->RunUntilIdle();
    105   }
    106 
    107   // Attempts to give worker threads time to finish.  This is currently
    108   // very simplistic as completion (via completion callback or cancellation)
    109   // immediately "detaches" any worker threads, so the best we can do is give
    110   // them a little time.  If we start running into Valgrind leaks, we can
    111   // do something a bit more clever to track worker threads even when the
    112   // DhcpProxyScriptFetcherWin state machine has finished.
    113   void FinishTestAllowCleanup() {
    114     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30));
    115   }
    116 
    117   scoped_ptr<URLRequestContext> context_;
    118   scoped_ptr<DhcpProxyScriptFetcherWin> fetcher_;
    119   bool finished_;
    120   base::string16 pac_text_;
    121   base::OneShotTimer<RealFetchTester> timeout_;
    122   base::OneShotTimer<RealFetchTester> cancel_timer_;
    123   bool on_completion_is_error_;
    124 };
    125 
    126 TEST(DhcpProxyScriptFetcherWin, RealFetch) {
    127   // This tests a call to Fetch() with no stubbing out of dependencies.
    128   //
    129   // We don't make assumptions about the environment this unit test is
    130   // running in, so it just exercises the code to make sure there
    131   // is no crash and no unexpected error returned, but does not assert on
    132   // results beyond that.
    133   RealFetchTester fetcher;
    134   fetcher.RunTest();
    135 
    136   fetcher.WaitUntilDone();
    137   printf("PAC URL was %s\n",
    138       fetcher.fetcher_->GetPacURL().possibly_invalid_spec().c_str());
    139 
    140   fetcher.FinishTestAllowCleanup();
    141 }
    142 
    143 TEST(DhcpProxyScriptFetcherWin, RealFetchWithCancel) {
    144   // Does a Fetch() with an immediate cancel.  As before, just
    145   // exercises the code without stubbing out dependencies.
    146   RealFetchTester fetcher;
    147   fetcher.RunTestWithCancel();
    148   base::MessageLoop::current()->RunUntilIdle();
    149 
    150   // Attempt to avoid Valgrind leak reports in case worker thread is
    151   // still running.
    152   fetcher.FinishTestAllowCleanup();
    153 }
    154 
    155 // For RealFetchWithDeferredCancel, below.
    156 class DelayingDhcpProxyScriptAdapterFetcher
    157     : public DhcpProxyScriptAdapterFetcher {
    158  public:
    159   DelayingDhcpProxyScriptAdapterFetcher(
    160       URLRequestContext* url_request_context,
    161       scoped_refptr<base::TaskRunner> task_runner)
    162       : DhcpProxyScriptAdapterFetcher(url_request_context, task_runner) {
    163   }
    164 
    165   class DelayingDhcpQuery : public DhcpQuery {
    166    public:
    167     explicit DelayingDhcpQuery()
    168         : DhcpQuery() {
    169     }
    170 
    171     std::string ImplGetPacURLFromDhcp(
    172         const std::string& adapter_name) OVERRIDE {
    173       base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
    174       return DhcpQuery::ImplGetPacURLFromDhcp(adapter_name);
    175     }
    176   };
    177 
    178   DhcpQuery* ImplCreateDhcpQuery() OVERRIDE {
    179     return new DelayingDhcpQuery();
    180   }
    181 };
    182 
    183 // For RealFetchWithDeferredCancel, below.
    184 class DelayingDhcpProxyScriptFetcherWin
    185     : public DhcpProxyScriptFetcherWin {
    186  public:
    187   explicit DelayingDhcpProxyScriptFetcherWin(
    188       URLRequestContext* context)
    189       : DhcpProxyScriptFetcherWin(context) {
    190   }
    191 
    192   DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
    193     return new DelayingDhcpProxyScriptAdapterFetcher(url_request_context(),
    194                                                      GetTaskRunner());
    195   }
    196 };
    197 
    198 TEST(DhcpProxyScriptFetcherWin, RealFetchWithDeferredCancel) {
    199   // Does a Fetch() with a slightly delayed cancel.  As before, just
    200   // exercises the code without stubbing out dependencies, but
    201   // introduces a guaranteed 20 ms delay on the worker threads so that
    202   // the cancel is called before they complete.
    203   RealFetchTester fetcher;
    204   fetcher.fetcher_.reset(
    205       new DelayingDhcpProxyScriptFetcherWin(fetcher.context_.get()));
    206   fetcher.on_completion_is_error_ = true;
    207   fetcher.RunTestWithDeferredCancel();
    208   fetcher.WaitUntilDone();
    209 }
    210 
    211 // The remaining tests are to exercise our state machine in various
    212 // situations, with actual network access fully stubbed out.
    213 
    214 class DummyDhcpProxyScriptAdapterFetcher
    215     : public DhcpProxyScriptAdapterFetcher {
    216  public:
    217   DummyDhcpProxyScriptAdapterFetcher(URLRequestContext* context,
    218                                      scoped_refptr<base::TaskRunner> runner)
    219       : DhcpProxyScriptAdapterFetcher(context, runner),
    220         did_finish_(false),
    221         result_(OK),
    222         pac_script_(L"bingo"),
    223         fetch_delay_ms_(1) {
    224   }
    225 
    226   void Fetch(const std::string& adapter_name,
    227              const CompletionCallback& callback) OVERRIDE {
    228     callback_ = callback;
    229     timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(fetch_delay_ms_),
    230                  this, &DummyDhcpProxyScriptAdapterFetcher::OnTimer);
    231   }
    232 
    233   void Cancel() OVERRIDE {
    234     timer_.Stop();
    235   }
    236 
    237   bool DidFinish() const OVERRIDE {
    238     return did_finish_;
    239   }
    240 
    241   int GetResult() const OVERRIDE {
    242     return result_;
    243   }
    244 
    245   base::string16 GetPacScript() const OVERRIDE {
    246     return pac_script_;
    247   }
    248 
    249   void OnTimer() {
    250     callback_.Run(result_);
    251   }
    252 
    253   void Configure(bool did_finish,
    254                  int result,
    255                  base::string16 pac_script,
    256                  int fetch_delay_ms) {
    257     did_finish_ = did_finish;
    258     result_ = result;
    259     pac_script_ = pac_script;
    260     fetch_delay_ms_ = fetch_delay_ms;
    261   }
    262 
    263  private:
    264   bool did_finish_;
    265   int result_;
    266   base::string16 pac_script_;
    267   int fetch_delay_ms_;
    268   CompletionCallback callback_;
    269   base::OneShotTimer<DummyDhcpProxyScriptAdapterFetcher> timer_;
    270 };
    271 
    272 class MockDhcpProxyScriptFetcherWin : public DhcpProxyScriptFetcherWin {
    273  public:
    274   class MockAdapterQuery : public AdapterQuery {
    275    public:
    276     MockAdapterQuery() {
    277     }
    278 
    279     virtual ~MockAdapterQuery() {
    280     }
    281 
    282     virtual bool ImplGetCandidateAdapterNames(
    283         std::set<std::string>* adapter_names) OVERRIDE {
    284       adapter_names->insert(
    285           mock_adapter_names_.begin(), mock_adapter_names_.end());
    286       return true;
    287     }
    288 
    289     std::vector<std::string> mock_adapter_names_;
    290   };
    291 
    292   MockDhcpProxyScriptFetcherWin(URLRequestContext* context)
    293       : DhcpProxyScriptFetcherWin(context),
    294         num_fetchers_created_(0),
    295         worker_finished_event_(true, false) {
    296     ResetTestState();
    297   }
    298 
    299   virtual ~MockDhcpProxyScriptFetcherWin() {
    300     ResetTestState();
    301   }
    302 
    303   using DhcpProxyScriptFetcherWin::GetTaskRunner;
    304 
    305   // Adds a fetcher object to the queue of fetchers used by
    306   // |ImplCreateAdapterFetcher()|, and its name to the list of adapters
    307   // returned by ImplGetCandidateAdapterNames.
    308   void PushBackAdapter(const std::string& adapter_name,
    309                        DhcpProxyScriptAdapterFetcher* fetcher) {
    310     adapter_query_->mock_adapter_names_.push_back(adapter_name);
    311     adapter_fetchers_.push_back(fetcher);
    312   }
    313 
    314   void ConfigureAndPushBackAdapter(const std::string& adapter_name,
    315                                    bool did_finish,
    316                                    int result,
    317                                    base::string16 pac_script,
    318                                    base::TimeDelta fetch_delay) {
    319     scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
    320         new DummyDhcpProxyScriptAdapterFetcher(url_request_context(),
    321                                                GetTaskRunner()));
    322     adapter_fetcher->Configure(
    323         did_finish, result, pac_script, fetch_delay.InMilliseconds());
    324     PushBackAdapter(adapter_name, adapter_fetcher.release());
    325   }
    326 
    327   DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
    328     ++num_fetchers_created_;
    329     return adapter_fetchers_[next_adapter_fetcher_index_++];
    330   }
    331 
    332   virtual AdapterQuery* ImplCreateAdapterQuery() OVERRIDE {
    333     DCHECK(adapter_query_);
    334     return adapter_query_.get();
    335   }
    336 
    337   base::TimeDelta ImplGetMaxWait() OVERRIDE {
    338     return max_wait_;
    339   }
    340 
    341   void ImplOnGetCandidateAdapterNamesDone() OVERRIDE {
    342     worker_finished_event_.Signal();
    343   }
    344 
    345   void ResetTestState() {
    346     // Delete any adapter fetcher objects we didn't hand out.
    347     std::vector<DhcpProxyScriptAdapterFetcher*>::const_iterator it
    348         = adapter_fetchers_.begin();
    349     for (; it != adapter_fetchers_.end(); ++it) {
    350       if (num_fetchers_created_-- <= 0) {
    351         delete (*it);
    352       }
    353     }
    354 
    355     next_adapter_fetcher_index_ = 0;
    356     num_fetchers_created_ = 0;
    357     adapter_fetchers_.clear();
    358     adapter_query_ = new MockAdapterQuery();
    359     max_wait_ = TestTimeouts::tiny_timeout();
    360   }
    361 
    362   bool HasPendingFetchers() {
    363     return num_pending_fetchers() > 0;
    364   }
    365 
    366   int next_adapter_fetcher_index_;
    367 
    368   // Ownership gets transferred to the implementation class via
    369   // ImplCreateAdapterFetcher, but any objects not handed out are
    370   // deleted on destruction.
    371   std::vector<DhcpProxyScriptAdapterFetcher*> adapter_fetchers_;
    372 
    373   scoped_refptr<MockAdapterQuery> adapter_query_;
    374 
    375   base::TimeDelta max_wait_;
    376   int num_fetchers_created_;
    377   base::WaitableEvent worker_finished_event_;
    378 };
    379 
    380 class FetcherClient {
    381  public:
    382   FetcherClient()
    383       : context_(new TestURLRequestContext),
    384         fetcher_(context_.get()),
    385         finished_(false),
    386         result_(ERR_UNEXPECTED) {
    387   }
    388 
    389   void RunTest() {
    390     int result = fetcher_.Fetch(
    391         &pac_text_,
    392         base::Bind(&FetcherClient::OnCompletion, base::Unretained(this)));
    393     ASSERT_EQ(ERR_IO_PENDING, result);
    394   }
    395 
    396   void RunMessageLoopUntilComplete() {
    397     while (!finished_) {
    398       base::MessageLoop::current()->RunUntilIdle();
    399     }
    400     base::MessageLoop::current()->RunUntilIdle();
    401   }
    402 
    403   void RunMessageLoopUntilWorkerDone() {
    404     DCHECK(fetcher_.adapter_query_.get());
    405     while (!fetcher_.worker_finished_event_.TimedWait(
    406         base::TimeDelta::FromMilliseconds(10))) {
    407       base::MessageLoop::current()->RunUntilIdle();
    408     }
    409   }
    410 
    411   void OnCompletion(int result) {
    412     finished_ = true;
    413     result_ = result;
    414   }
    415 
    416   void ResetTestState() {
    417     finished_ = false;
    418     result_ = ERR_UNEXPECTED;
    419     pac_text_ = L"";
    420     fetcher_.ResetTestState();
    421   }
    422 
    423   scoped_refptr<base::TaskRunner> GetTaskRunner() {
    424     return fetcher_.GetTaskRunner();
    425   }
    426 
    427   scoped_ptr<URLRequestContext> context_;
    428   MockDhcpProxyScriptFetcherWin fetcher_;
    429   bool finished_;
    430   int result_;
    431   base::string16 pac_text_;
    432 };
    433 
    434 // We separate out each test's logic so that we can easily implement
    435 // the ReuseFetcher test at the bottom.
    436 void TestNormalCaseURLConfiguredOneAdapter(FetcherClient* client) {
    437   TestURLRequestContext context;
    438   scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
    439       new DummyDhcpProxyScriptAdapterFetcher(&context,
    440                                              client->GetTaskRunner()));
    441   adapter_fetcher->Configure(true, OK, L"bingo", 1);
    442   client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
    443   client->RunTest();
    444   client->RunMessageLoopUntilComplete();
    445   ASSERT_EQ(OK, client->result_);
    446   ASSERT_EQ(L"bingo", client->pac_text_);
    447 }
    448 
    449 TEST(DhcpProxyScriptFetcherWin, NormalCaseURLConfiguredOneAdapter) {
    450   FetcherClient client;
    451   TestNormalCaseURLConfiguredOneAdapter(&client);
    452 }
    453 
    454 void TestNormalCaseURLConfiguredMultipleAdapters(FetcherClient* client) {
    455   client->fetcher_.ConfigureAndPushBackAdapter(
    456       "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
    457       base::TimeDelta::FromMilliseconds(1));
    458   client->fetcher_.ConfigureAndPushBackAdapter(
    459       "second", true, OK, L"bingo", base::TimeDelta::FromMilliseconds(50));
    460   client->fetcher_.ConfigureAndPushBackAdapter(
    461       "third", true, OK, L"rocko", base::TimeDelta::FromMilliseconds(1));
    462   client->RunTest();
    463   client->RunMessageLoopUntilComplete();
    464   ASSERT_EQ(OK, client->result_);
    465   ASSERT_EQ(L"bingo", client->pac_text_);
    466 }
    467 
    468 TEST(DhcpProxyScriptFetcherWin, NormalCaseURLConfiguredMultipleAdapters) {
    469   FetcherClient client;
    470   TestNormalCaseURLConfiguredMultipleAdapters(&client);
    471 }
    472 
    473 void TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(
    474     FetcherClient* client) {
    475   client->fetcher_.ConfigureAndPushBackAdapter(
    476       "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
    477       base::TimeDelta::FromMilliseconds(1));
    478   // This will time out.
    479   client->fetcher_.ConfigureAndPushBackAdapter(
    480       "second", false, ERR_IO_PENDING, L"bingo",
    481       TestTimeouts::action_timeout());
    482   client->fetcher_.ConfigureAndPushBackAdapter(
    483       "third", true, OK, L"rocko", base::TimeDelta::FromMilliseconds(1));
    484   client->RunTest();
    485   client->RunMessageLoopUntilComplete();
    486   ASSERT_EQ(OK, client->result_);
    487   ASSERT_EQ(L"rocko", client->pac_text_);
    488 }
    489 
    490 TEST(DhcpProxyScriptFetcherWin,
    491      NormalCaseURLConfiguredMultipleAdaptersWithTimeout) {
    492   FetcherClient client;
    493   TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
    494 }
    495 
    496 void TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(
    497     FetcherClient* client) {
    498   client->fetcher_.ConfigureAndPushBackAdapter(
    499       "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
    500       base::TimeDelta::FromMilliseconds(1));
    501   // This will time out.
    502   client->fetcher_.ConfigureAndPushBackAdapter(
    503       "second", false, ERR_IO_PENDING, L"bingo",
    504       TestTimeouts::action_timeout());
    505   // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
    506   // should be chosen.
    507   client->fetcher_.ConfigureAndPushBackAdapter(
    508       "third", true, ERR_PAC_STATUS_NOT_OK, L"",
    509       base::TimeDelta::FromMilliseconds(1));
    510   client->fetcher_.ConfigureAndPushBackAdapter(
    511       "fourth", true, ERR_NOT_IMPLEMENTED, L"",
    512       base::TimeDelta::FromMilliseconds(1));
    513   client->RunTest();
    514   client->RunMessageLoopUntilComplete();
    515   ASSERT_EQ(ERR_PAC_STATUS_NOT_OK, client->result_);
    516   ASSERT_EQ(L"", client->pac_text_);
    517 }
    518 
    519 TEST(DhcpProxyScriptFetcherWin,
    520      FailureCaseURLConfiguredMultipleAdaptersWithTimeout) {
    521   FetcherClient client;
    522   TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
    523 }
    524 
    525 void TestFailureCaseNoURLConfigured(FetcherClient* client) {
    526   client->fetcher_.ConfigureAndPushBackAdapter(
    527       "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
    528       base::TimeDelta::FromMilliseconds(1));
    529   // This will time out.
    530   client->fetcher_.ConfigureAndPushBackAdapter(
    531       "second", false, ERR_IO_PENDING, L"bingo",
    532       TestTimeouts::action_timeout());
    533   // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
    534   // should be chosen.
    535   client->fetcher_.ConfigureAndPushBackAdapter(
    536       "third", true, ERR_PAC_NOT_IN_DHCP, L"",
    537       base::TimeDelta::FromMilliseconds(1));
    538   client->RunTest();
    539   client->RunMessageLoopUntilComplete();
    540   ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, client->result_);
    541   ASSERT_EQ(L"", client->pac_text_);
    542 }
    543 
    544 TEST(DhcpProxyScriptFetcherWin, FailureCaseNoURLConfigured) {
    545   FetcherClient client;
    546   TestFailureCaseNoURLConfigured(&client);
    547 }
    548 
    549 void TestFailureCaseNoDhcpAdapters(FetcherClient* client) {
    550   client->RunTest();
    551   client->RunMessageLoopUntilComplete();
    552   ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, client->result_);
    553   ASSERT_EQ(L"", client->pac_text_);
    554   ASSERT_EQ(0, client->fetcher_.num_fetchers_created_);
    555 }
    556 
    557 TEST(DhcpProxyScriptFetcherWin, FailureCaseNoDhcpAdapters) {
    558   FetcherClient client;
    559   TestFailureCaseNoDhcpAdapters(&client);
    560 }
    561 
    562 void TestShortCircuitLessPreferredAdapters(FetcherClient* client) {
    563   // Here we have a bunch of adapters; the first reports no PAC in DHCP,
    564   // the second responds quickly with a PAC file, the rest take a long
    565   // time.  Verify that we complete quickly and do not wait for the slow
    566   // adapters, i.e. we finish before timeout.
    567   client->fetcher_.ConfigureAndPushBackAdapter(
    568       "1", true, ERR_PAC_NOT_IN_DHCP, L"",
    569       base::TimeDelta::FromMilliseconds(1));
    570   client->fetcher_.ConfigureAndPushBackAdapter(
    571       "2", true, OK, L"bingo",
    572       base::TimeDelta::FromMilliseconds(1));
    573   client->fetcher_.ConfigureAndPushBackAdapter(
    574       "3", true, OK, L"wrongo", TestTimeouts::action_max_timeout());
    575 
    576   // Increase the timeout to ensure the short circuit mechanism has
    577   // time to kick in before the timeout waiting for more adapters kicks in.
    578   client->fetcher_.max_wait_ = TestTimeouts::action_timeout();
    579 
    580   base::ElapsedTimer timer;
    581   client->RunTest();
    582   client->RunMessageLoopUntilComplete();
    583   ASSERT_TRUE(client->fetcher_.HasPendingFetchers());
    584   // Assert that the time passed is definitely less than the wait timer
    585   // timeout, to get a second signal that it was the shortcut mechanism
    586   // (in OnFetcherDone) that kicked in, and not the timeout waiting for
    587   // more adapters.
    588   ASSERT_GT(client->fetcher_.max_wait_ - (client->fetcher_.max_wait_ / 10),
    589             timer.Elapsed());
    590 }
    591 
    592 TEST(DhcpProxyScriptFetcherWin, ShortCircuitLessPreferredAdapters) {
    593   FetcherClient client;
    594   TestShortCircuitLessPreferredAdapters(&client);
    595 }
    596 
    597 void TestImmediateCancel(FetcherClient* client) {
    598   TestURLRequestContext context;
    599   scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
    600       new DummyDhcpProxyScriptAdapterFetcher(&context,
    601                                              client->GetTaskRunner()));
    602   adapter_fetcher->Configure(true, OK, L"bingo", 1);
    603   client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
    604   client->RunTest();
    605   client->fetcher_.Cancel();
    606   client->RunMessageLoopUntilWorkerDone();
    607   ASSERT_EQ(0, client->fetcher_.num_fetchers_created_);
    608 }
    609 
    610 // Regression test to check that when we cancel immediately, no
    611 // adapter fetchers get created.
    612 TEST(DhcpProxyScriptFetcherWin, ImmediateCancel) {
    613   FetcherClient client;
    614   TestImmediateCancel(&client);
    615 }
    616 
    617 TEST(DhcpProxyScriptFetcherWin, ReuseFetcher) {
    618   FetcherClient client;
    619 
    620   // The ProxyScriptFetcher interface stipulates that only a single
    621   // |Fetch()| may be in flight at once, but allows reuse, so test
    622   // that the state transitions correctly from done to start in all
    623   // cases we're testing.
    624 
    625   typedef void (*FetcherClientTestFunction)(FetcherClient*);
    626   typedef std::vector<FetcherClientTestFunction> TestVector;
    627   TestVector test_functions;
    628   test_functions.push_back(TestNormalCaseURLConfiguredOneAdapter);
    629   test_functions.push_back(TestNormalCaseURLConfiguredMultipleAdapters);
    630   test_functions.push_back(
    631       TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout);
    632   test_functions.push_back(
    633       TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout);
    634   test_functions.push_back(TestFailureCaseNoURLConfigured);
    635   test_functions.push_back(TestFailureCaseNoDhcpAdapters);
    636   test_functions.push_back(TestShortCircuitLessPreferredAdapters);
    637   test_functions.push_back(TestImmediateCancel);
    638 
    639   std::random_shuffle(test_functions.begin(),
    640                       test_functions.end(),
    641                       base::RandGenerator);
    642   for (TestVector::const_iterator it = test_functions.begin();
    643        it != test_functions.end();
    644        ++it) {
    645     (*it)(&client);
    646     client.ResetTestState();
    647   }
    648 
    649   // Re-do the first test to make sure the last test that was run did
    650   // not leave things in a bad state.
    651   (*test_functions.begin())(&client);
    652 }
    653 
    654 }  // namespace
    655 
    656 }  // namespace net
    657