root/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. TEST
  2. on_completion_is_error_
  3. RunTest
  4. RunTestWithCancel
  5. RunTestWithDeferredCancel
  6. OnCompletion
  7. OnTimeout
  8. OnCancelTimer
  9. WaitUntilDone
  10. FinishTestAllowCleanup
  11. TEST
  12. TEST
  13. ImplGetPacURLFromDhcp
  14. ImplCreateDhcpQuery
  15. ImplCreateAdapterFetcher
  16. TEST
  17. fetch_delay_ms_
  18. Fetch
  19. Cancel
  20. DidFinish
  21. GetResult
  22. GetPacScript
  23. OnTimer
  24. Configure
  25. ImplGetCandidateAdapterNames
  26. worker_finished_event_
  27. PushBackAdapter
  28. ConfigureAndPushBackAdapter
  29. ImplCreateAdapterFetcher
  30. ImplCreateAdapterQuery
  31. ImplGetMaxWait
  32. ImplOnGetCandidateAdapterNamesDone
  33. ResetTestState
  34. HasPendingFetchers
  35. result_
  36. RunTest
  37. RunMessageLoopUntilComplete
  38. RunMessageLoopUntilWorkerDone
  39. OnCompletion
  40. ResetTestState
  41. GetTaskRunner
  42. TestNormalCaseURLConfiguredOneAdapter
  43. TEST
  44. TestNormalCaseURLConfiguredMultipleAdapters
  45. TEST
  46. TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout
  47. TEST
  48. TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout
  49. TEST
  50. TestFailureCaseNoURLConfigured
  51. TEST
  52. TestFailureCaseNoDhcpAdapters
  53. TEST
  54. TestShortCircuitLessPreferredAdapters
  55. TEST
  56. TestImmediateCancel
  57. TEST
  58. TEST

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/proxy/dhcp_proxy_script_fetcher_win.h"

#include <vector>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/rand_util.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/timer/elapsed_timer.h"
#include "net/base/completion_callback.h"
#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

TEST(DhcpProxyScriptFetcherWin, AdapterNamesAndPacURLFromDhcp) {
  // This tests our core Win32 implementation without any of the wrappers
  // we layer on top to achieve asynchronous and parallel operations.
  //
  // We don't make assumptions about the environment this unit test is
  // running in, so it just exercises the code to make sure there
  // is no crash and no error returned, but does not assert on the number
  // of interfaces or the information returned via DHCP.
  std::set<std::string> adapter_names;
  DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(&adapter_names);
  for (std::set<std::string>::const_iterator it = adapter_names.begin();
       it != adapter_names.end();
       ++it) {
    const std::string& adapter_name = *it;
    std::string pac_url =
        DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
    printf("Adapter '%s' has PAC URL '%s' configured in DHCP.\n",
           adapter_name.c_str(),
           pac_url.c_str());
  }
}

// Helper for RealFetch* tests below.
class RealFetchTester {
 public:
  RealFetchTester()
      : context_(new TestURLRequestContext),
        fetcher_(new DhcpProxyScriptFetcherWin(context_.get())),
        finished_(false),
        on_completion_is_error_(false) {
    // Make sure the test ends.
    timeout_.Start(FROM_HERE,
        base::TimeDelta::FromSeconds(5), this, &RealFetchTester::OnTimeout);
  }

  void RunTest() {
    int result = fetcher_->Fetch(
        &pac_text_,
        base::Bind(&RealFetchTester::OnCompletion, base::Unretained(this)));
    if (result != ERR_IO_PENDING)
      finished_ = true;
  }

  void RunTestWithCancel() {
    RunTest();
    fetcher_->Cancel();
  }

  void RunTestWithDeferredCancel() {
    // Put the cancellation into the queue before even running the
    // test to avoid the chance of one of the adapter fetcher worker
    // threads completing before cancellation.  See http://crbug.com/86756.
    cancel_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(0),
                        this, &RealFetchTester::OnCancelTimer);
    RunTest();
  }

  void OnCompletion(int result) {
    if (on_completion_is_error_) {
      FAIL() << "Received completion for test in which this is error.";
    }
    finished_ = true;
    printf("Result code %d PAC data length %d\n", result, pac_text_.size());
  }

  void OnTimeout() {
    printf("Timeout!");
    OnCompletion(0);
  }

  void OnCancelTimer() {
    fetcher_->Cancel();
    finished_ = true;
  }

  void WaitUntilDone() {
    while (!finished_) {
      base::MessageLoop::current()->RunUntilIdle();
    }
    base::MessageLoop::current()->RunUntilIdle();
  }

  // Attempts to give worker threads time to finish.  This is currently
  // very simplistic as completion (via completion callback or cancellation)
  // immediately "detaches" any worker threads, so the best we can do is give
  // them a little time.  If we start running into Valgrind leaks, we can
  // do something a bit more clever to track worker threads even when the
  // DhcpProxyScriptFetcherWin state machine has finished.
  void FinishTestAllowCleanup() {
    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30));
  }

  scoped_ptr<URLRequestContext> context_;
  scoped_ptr<DhcpProxyScriptFetcherWin> fetcher_;
  bool finished_;
  base::string16 pac_text_;
  base::OneShotTimer<RealFetchTester> timeout_;
  base::OneShotTimer<RealFetchTester> cancel_timer_;
  bool on_completion_is_error_;
};

TEST(DhcpProxyScriptFetcherWin, RealFetch) {
  // This tests a call to Fetch() with no stubbing out of dependencies.
  //
  // We don't make assumptions about the environment this unit test is
  // running in, so it just exercises the code to make sure there
  // is no crash and no unexpected error returned, but does not assert on
  // results beyond that.
  RealFetchTester fetcher;
  fetcher.RunTest();

  fetcher.WaitUntilDone();
  printf("PAC URL was %s\n",
      fetcher.fetcher_->GetPacURL().possibly_invalid_spec().c_str());

  fetcher.FinishTestAllowCleanup();
}

TEST(DhcpProxyScriptFetcherWin, RealFetchWithCancel) {
  // Does a Fetch() with an immediate cancel.  As before, just
  // exercises the code without stubbing out dependencies.
  RealFetchTester fetcher;
  fetcher.RunTestWithCancel();
  base::MessageLoop::current()->RunUntilIdle();

  // Attempt to avoid Valgrind leak reports in case worker thread is
  // still running.
  fetcher.FinishTestAllowCleanup();
}

// For RealFetchWithDeferredCancel, below.
class DelayingDhcpProxyScriptAdapterFetcher
    : public DhcpProxyScriptAdapterFetcher {
 public:
  DelayingDhcpProxyScriptAdapterFetcher(
      URLRequestContext* url_request_context,
      scoped_refptr<base::TaskRunner> task_runner)
      : DhcpProxyScriptAdapterFetcher(url_request_context, task_runner) {
  }

  class DelayingDhcpQuery : public DhcpQuery {
   public:
    explicit DelayingDhcpQuery()
        : DhcpQuery() {
    }

    std::string ImplGetPacURLFromDhcp(
        const std::string& adapter_name) OVERRIDE {
      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
      return DhcpQuery::ImplGetPacURLFromDhcp(adapter_name);
    }
  };

  DhcpQuery* ImplCreateDhcpQuery() OVERRIDE {
    return new DelayingDhcpQuery();
  }
};

// For RealFetchWithDeferredCancel, below.
class DelayingDhcpProxyScriptFetcherWin
    : public DhcpProxyScriptFetcherWin {
 public:
  explicit DelayingDhcpProxyScriptFetcherWin(
      URLRequestContext* context)
      : DhcpProxyScriptFetcherWin(context) {
  }

  DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
    return new DelayingDhcpProxyScriptAdapterFetcher(url_request_context(),
                                                     GetTaskRunner());
  }
};

TEST(DhcpProxyScriptFetcherWin, RealFetchWithDeferredCancel) {
  // Does a Fetch() with a slightly delayed cancel.  As before, just
  // exercises the code without stubbing out dependencies, but
  // introduces a guaranteed 20 ms delay on the worker threads so that
  // the cancel is called before they complete.
  RealFetchTester fetcher;
  fetcher.fetcher_.reset(
      new DelayingDhcpProxyScriptFetcherWin(fetcher.context_.get()));
  fetcher.on_completion_is_error_ = true;
  fetcher.RunTestWithDeferredCancel();
  fetcher.WaitUntilDone();
}

// The remaining tests are to exercise our state machine in various
// situations, with actual network access fully stubbed out.

class DummyDhcpProxyScriptAdapterFetcher
    : public DhcpProxyScriptAdapterFetcher {
 public:
  DummyDhcpProxyScriptAdapterFetcher(URLRequestContext* context,
                                     scoped_refptr<base::TaskRunner> runner)
      : DhcpProxyScriptAdapterFetcher(context, runner),
        did_finish_(false),
        result_(OK),
        pac_script_(L"bingo"),
        fetch_delay_ms_(1) {
  }

  void Fetch(const std::string& adapter_name,
             const CompletionCallback& callback) OVERRIDE {
    callback_ = callback;
    timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(fetch_delay_ms_),
                 this, &DummyDhcpProxyScriptAdapterFetcher::OnTimer);
  }

  void Cancel() OVERRIDE {
    timer_.Stop();
  }

  bool DidFinish() const OVERRIDE {
    return did_finish_;
  }

  int GetResult() const OVERRIDE {
    return result_;
  }

  base::string16 GetPacScript() const OVERRIDE {
    return pac_script_;
  }

  void OnTimer() {
    callback_.Run(result_);
  }

  void Configure(bool did_finish,
                 int result,
                 base::string16 pac_script,
                 int fetch_delay_ms) {
    did_finish_ = did_finish;
    result_ = result;
    pac_script_ = pac_script;
    fetch_delay_ms_ = fetch_delay_ms;
  }

 private:
  bool did_finish_;
  int result_;
  base::string16 pac_script_;
  int fetch_delay_ms_;
  CompletionCallback callback_;
  base::OneShotTimer<DummyDhcpProxyScriptAdapterFetcher> timer_;
};

class MockDhcpProxyScriptFetcherWin : public DhcpProxyScriptFetcherWin {
 public:
  class MockAdapterQuery : public AdapterQuery {
   public:
    MockAdapterQuery() {
    }

    virtual ~MockAdapterQuery() {
    }

    virtual bool ImplGetCandidateAdapterNames(
        std::set<std::string>* adapter_names) OVERRIDE {
      adapter_names->insert(
          mock_adapter_names_.begin(), mock_adapter_names_.end());
      return true;
    }

    std::vector<std::string> mock_adapter_names_;
  };

  MockDhcpProxyScriptFetcherWin(URLRequestContext* context)
      : DhcpProxyScriptFetcherWin(context),
        num_fetchers_created_(0),
        worker_finished_event_(true, false) {
    ResetTestState();
  }

  virtual ~MockDhcpProxyScriptFetcherWin() {
    ResetTestState();
  }

  using DhcpProxyScriptFetcherWin::GetTaskRunner;

  // Adds a fetcher object to the queue of fetchers used by
  // |ImplCreateAdapterFetcher()|, and its name to the list of adapters
  // returned by ImplGetCandidateAdapterNames.
  void PushBackAdapter(const std::string& adapter_name,
                       DhcpProxyScriptAdapterFetcher* fetcher) {
    adapter_query_->mock_adapter_names_.push_back(adapter_name);
    adapter_fetchers_.push_back(fetcher);
  }

  void ConfigureAndPushBackAdapter(const std::string& adapter_name,
                                   bool did_finish,
                                   int result,
                                   base::string16 pac_script,
                                   base::TimeDelta fetch_delay) {
    scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
        new DummyDhcpProxyScriptAdapterFetcher(url_request_context(),
                                               GetTaskRunner()));
    adapter_fetcher->Configure(
        did_finish, result, pac_script, fetch_delay.InMilliseconds());
    PushBackAdapter(adapter_name, adapter_fetcher.release());
  }

  DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
    ++num_fetchers_created_;
    return adapter_fetchers_[next_adapter_fetcher_index_++];
  }

  virtual AdapterQuery* ImplCreateAdapterQuery() OVERRIDE {
    DCHECK(adapter_query_);
    return adapter_query_.get();
  }

  base::TimeDelta ImplGetMaxWait() OVERRIDE {
    return max_wait_;
  }

  void ImplOnGetCandidateAdapterNamesDone() OVERRIDE {
    worker_finished_event_.Signal();
  }

  void ResetTestState() {
    // Delete any adapter fetcher objects we didn't hand out.
    std::vector<DhcpProxyScriptAdapterFetcher*>::const_iterator it
        = adapter_fetchers_.begin();
    for (; it != adapter_fetchers_.end(); ++it) {
      if (num_fetchers_created_-- <= 0) {
        delete (*it);
      }
    }

    next_adapter_fetcher_index_ = 0;
    num_fetchers_created_ = 0;
    adapter_fetchers_.clear();
    adapter_query_ = new MockAdapterQuery();
    max_wait_ = TestTimeouts::tiny_timeout();
  }

  bool HasPendingFetchers() {
    return num_pending_fetchers() > 0;
  }

  int next_adapter_fetcher_index_;

  // Ownership gets transferred to the implementation class via
  // ImplCreateAdapterFetcher, but any objects not handed out are
  // deleted on destruction.
  std::vector<DhcpProxyScriptAdapterFetcher*> adapter_fetchers_;

  scoped_refptr<MockAdapterQuery> adapter_query_;

  base::TimeDelta max_wait_;
  int num_fetchers_created_;
  base::WaitableEvent worker_finished_event_;
};

class FetcherClient {
 public:
  FetcherClient()
      : context_(new TestURLRequestContext),
        fetcher_(context_.get()),
        finished_(false),
        result_(ERR_UNEXPECTED) {
  }

  void RunTest() {
    int result = fetcher_.Fetch(
        &pac_text_,
        base::Bind(&FetcherClient::OnCompletion, base::Unretained(this)));
    ASSERT_EQ(ERR_IO_PENDING, result);
  }

  void RunMessageLoopUntilComplete() {
    while (!finished_) {
      base::MessageLoop::current()->RunUntilIdle();
    }
    base::MessageLoop::current()->RunUntilIdle();
  }

  void RunMessageLoopUntilWorkerDone() {
    DCHECK(fetcher_.adapter_query_.get());
    while (!fetcher_.worker_finished_event_.TimedWait(
        base::TimeDelta::FromMilliseconds(10))) {
      base::MessageLoop::current()->RunUntilIdle();
    }
  }

  void OnCompletion(int result) {
    finished_ = true;
    result_ = result;
  }

  void ResetTestState() {
    finished_ = false;
    result_ = ERR_UNEXPECTED;
    pac_text_ = L"";
    fetcher_.ResetTestState();
  }

  scoped_refptr<base::TaskRunner> GetTaskRunner() {
    return fetcher_.GetTaskRunner();
  }

  scoped_ptr<URLRequestContext> context_;
  MockDhcpProxyScriptFetcherWin fetcher_;
  bool finished_;
  int result_;
  base::string16 pac_text_;
};

// We separate out each test's logic so that we can easily implement
// the ReuseFetcher test at the bottom.
void TestNormalCaseURLConfiguredOneAdapter(FetcherClient* client) {
  TestURLRequestContext context;
  scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
      new DummyDhcpProxyScriptAdapterFetcher(&context,
                                             client->GetTaskRunner()));
  adapter_fetcher->Configure(true, OK, L"bingo", 1);
  client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
  client->RunTest();
  client->RunMessageLoopUntilComplete();
  ASSERT_EQ(OK, client->result_);
  ASSERT_EQ(L"bingo", client->pac_text_);
}

TEST(DhcpProxyScriptFetcherWin, NormalCaseURLConfiguredOneAdapter) {
  FetcherClient client;
  TestNormalCaseURLConfiguredOneAdapter(&client);
}

void TestNormalCaseURLConfiguredMultipleAdapters(FetcherClient* client) {
  client->fetcher_.ConfigureAndPushBackAdapter(
      "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
      base::TimeDelta::FromMilliseconds(1));
  client->fetcher_.ConfigureAndPushBackAdapter(
      "second", true, OK, L"bingo", base::TimeDelta::FromMilliseconds(50));
  client->fetcher_.ConfigureAndPushBackAdapter(
      "third", true, OK, L"rocko", base::TimeDelta::FromMilliseconds(1));
  client->RunTest();
  client->RunMessageLoopUntilComplete();
  ASSERT_EQ(OK, client->result_);
  ASSERT_EQ(L"bingo", client->pac_text_);
}

TEST(DhcpProxyScriptFetcherWin, NormalCaseURLConfiguredMultipleAdapters) {
  FetcherClient client;
  TestNormalCaseURLConfiguredMultipleAdapters(&client);
}

void TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(
    FetcherClient* client) {
  client->fetcher_.ConfigureAndPushBackAdapter(
      "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
      base::TimeDelta::FromMilliseconds(1));
  // This will time out.
  client->fetcher_.ConfigureAndPushBackAdapter(
      "second", false, ERR_IO_PENDING, L"bingo",
      TestTimeouts::action_timeout());
  client->fetcher_.ConfigureAndPushBackAdapter(
      "third", true, OK, L"rocko", base::TimeDelta::FromMilliseconds(1));
  client->RunTest();
  client->RunMessageLoopUntilComplete();
  ASSERT_EQ(OK, client->result_);
  ASSERT_EQ(L"rocko", client->pac_text_);
}

TEST(DhcpProxyScriptFetcherWin,
     NormalCaseURLConfiguredMultipleAdaptersWithTimeout) {
  FetcherClient client;
  TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
}

void TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(
    FetcherClient* client) {
  client->fetcher_.ConfigureAndPushBackAdapter(
      "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
      base::TimeDelta::FromMilliseconds(1));
  // This will time out.
  client->fetcher_.ConfigureAndPushBackAdapter(
      "second", false, ERR_IO_PENDING, L"bingo",
      TestTimeouts::action_timeout());
  // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
  // should be chosen.
  client->fetcher_.ConfigureAndPushBackAdapter(
      "third", true, ERR_PAC_STATUS_NOT_OK, L"",
      base::TimeDelta::FromMilliseconds(1));
  client->fetcher_.ConfigureAndPushBackAdapter(
      "fourth", true, ERR_NOT_IMPLEMENTED, L"",
      base::TimeDelta::FromMilliseconds(1));
  client->RunTest();
  client->RunMessageLoopUntilComplete();
  ASSERT_EQ(ERR_PAC_STATUS_NOT_OK, client->result_);
  ASSERT_EQ(L"", client->pac_text_);
}

TEST(DhcpProxyScriptFetcherWin,
     FailureCaseURLConfiguredMultipleAdaptersWithTimeout) {
  FetcherClient client;
  TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
}

void TestFailureCaseNoURLConfigured(FetcherClient* client) {
  client->fetcher_.ConfigureAndPushBackAdapter(
      "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"",
      base::TimeDelta::FromMilliseconds(1));
  // This will time out.
  client->fetcher_.ConfigureAndPushBackAdapter(
      "second", false, ERR_IO_PENDING, L"bingo",
      TestTimeouts::action_timeout());
  // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
  // should be chosen.
  client->fetcher_.ConfigureAndPushBackAdapter(
      "third", true, ERR_PAC_NOT_IN_DHCP, L"",
      base::TimeDelta::FromMilliseconds(1));
  client->RunTest();
  client->RunMessageLoopUntilComplete();
  ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, client->result_);
  ASSERT_EQ(L"", client->pac_text_);
}

TEST(DhcpProxyScriptFetcherWin, FailureCaseNoURLConfigured) {
  FetcherClient client;
  TestFailureCaseNoURLConfigured(&client);
}

void TestFailureCaseNoDhcpAdapters(FetcherClient* client) {
  client->RunTest();
  client->RunMessageLoopUntilComplete();
  ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, client->result_);
  ASSERT_EQ(L"", client->pac_text_);
  ASSERT_EQ(0, client->fetcher_.num_fetchers_created_);
}

TEST(DhcpProxyScriptFetcherWin, FailureCaseNoDhcpAdapters) {
  FetcherClient client;
  TestFailureCaseNoDhcpAdapters(&client);
}

void TestShortCircuitLessPreferredAdapters(FetcherClient* client) {
  // Here we have a bunch of adapters; the first reports no PAC in DHCP,
  // the second responds quickly with a PAC file, the rest take a long
  // time.  Verify that we complete quickly and do not wait for the slow
  // adapters, i.e. we finish before timeout.
  client->fetcher_.ConfigureAndPushBackAdapter(
      "1", true, ERR_PAC_NOT_IN_DHCP, L"",
      base::TimeDelta::FromMilliseconds(1));
  client->fetcher_.ConfigureAndPushBackAdapter(
      "2", true, OK, L"bingo",
      base::TimeDelta::FromMilliseconds(1));
  client->fetcher_.ConfigureAndPushBackAdapter(
      "3", true, OK, L"wrongo", TestTimeouts::action_max_timeout());

  // Increase the timeout to ensure the short circuit mechanism has
  // time to kick in before the timeout waiting for more adapters kicks in.
  client->fetcher_.max_wait_ = TestTimeouts::action_timeout();

  base::ElapsedTimer timer;
  client->RunTest();
  client->RunMessageLoopUntilComplete();
  ASSERT_TRUE(client->fetcher_.HasPendingFetchers());
  // Assert that the time passed is definitely less than the wait timer
  // timeout, to get a second signal that it was the shortcut mechanism
  // (in OnFetcherDone) that kicked in, and not the timeout waiting for
  // more adapters.
  ASSERT_GT(client->fetcher_.max_wait_ - (client->fetcher_.max_wait_ / 10),
            timer.Elapsed());
}

TEST(DhcpProxyScriptFetcherWin, ShortCircuitLessPreferredAdapters) {
  FetcherClient client;
  TestShortCircuitLessPreferredAdapters(&client);
}

void TestImmediateCancel(FetcherClient* client) {
  TestURLRequestContext context;
  scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
      new DummyDhcpProxyScriptAdapterFetcher(&context,
                                             client->GetTaskRunner()));
  adapter_fetcher->Configure(true, OK, L"bingo", 1);
  client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
  client->RunTest();
  client->fetcher_.Cancel();
  client->RunMessageLoopUntilWorkerDone();
  ASSERT_EQ(0, client->fetcher_.num_fetchers_created_);
}

// Regression test to check that when we cancel immediately, no
// adapter fetchers get created.
TEST(DhcpProxyScriptFetcherWin, ImmediateCancel) {
  FetcherClient client;
  TestImmediateCancel(&client);
}

TEST(DhcpProxyScriptFetcherWin, ReuseFetcher) {
  FetcherClient client;

  // The ProxyScriptFetcher interface stipulates that only a single
  // |Fetch()| may be in flight at once, but allows reuse, so test
  // that the state transitions correctly from done to start in all
  // cases we're testing.

  typedef void (*FetcherClientTestFunction)(FetcherClient*);
  typedef std::vector<FetcherClientTestFunction> TestVector;
  TestVector test_functions;
  test_functions.push_back(TestNormalCaseURLConfiguredOneAdapter);
  test_functions.push_back(TestNormalCaseURLConfiguredMultipleAdapters);
  test_functions.push_back(
      TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout);
  test_functions.push_back(
      TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout);
  test_functions.push_back(TestFailureCaseNoURLConfigured);
  test_functions.push_back(TestFailureCaseNoDhcpAdapters);
  test_functions.push_back(TestShortCircuitLessPreferredAdapters);
  test_functions.push_back(TestImmediateCancel);

  std::random_shuffle(test_functions.begin(),
                      test_functions.end(),
                      base::RandGenerator);
  for (TestVector::const_iterator it = test_functions.begin();
       it != test_functions.end();
       ++it) {
    (*it)(&client);
    client.ResetTestState();
  }

  // Re-do the first test to make sure the last test that was run did
  // not leave things in a bad state.
  (*test_functions.begin())(&client);
}

}  // namespace

}  // namespace net

/* [<][>][^][v][top][bottom][index][help] */