| Index: content/child/throttling_url_loader_unittest.cc | 
| diff --git a/content/child/throttling_url_loader_unittest.cc b/content/child/throttling_url_loader_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..89d9c0969ae97a79528bea569d014453adb385af | 
| --- /dev/null | 
| +++ b/content/child/throttling_url_loader_unittest.cc | 
| @@ -0,0 +1,560 @@ | 
| +// Copyright 2017 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 "content/child/throttling_url_loader.h" | 
| +#include "base/logging.h" | 
| +#include "base/macros.h" | 
| +#include "base/message_loop/message_loop.h" | 
| +#include "base/run_loop.h" | 
| +#include "content/common/url_loader.mojom.h" | 
| +#include "content/common/url_loader_factory.mojom.h" | 
| +#include "content/public/child/url_loader_throttle.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +namespace content { | 
| +namespace { | 
| + | 
| +class TestURLLoaderFactory : public mojom::URLLoaderFactory { | 
| + public: | 
| +  TestURLLoaderFactory() : binding_(this) { | 
| +    binding_.Bind(mojo::MakeRequest(&factory_ptr_)); | 
| +  } | 
| + | 
| +  mojom::URLLoaderFactoryPtr& factory_ptr() { return factory_ptr_; } | 
| +  mojom::URLLoaderClientPtr& client_ptr() { return client_ptr_; } | 
| + | 
| +  size_t create_loader_and_start_called() const { | 
| +    return create_loader_and_start_called_; | 
| +  } | 
| + | 
| +  void NotifyClientOnReceiveResponse() { | 
| +    client_ptr_->OnReceiveResponse(ResourceResponseHead(), base::nullopt, | 
| +                                   nullptr); | 
| +  } | 
| + | 
| +  void NotifyClientOnReceiveRedirect() { | 
| +    client_ptr_->OnReceiveRedirect(net::RedirectInfo(), ResourceResponseHead()); | 
| +  } | 
| + | 
| +  void NotifyClientOnComplete(int error_code) { | 
| +    ResourceRequestCompletionStatus data; | 
| +    data.error_code = error_code; | 
| +    client_ptr_->OnComplete(data); | 
| +  } | 
| + | 
| + private: | 
| +  // mojom::URLLoaderFactory implementation. | 
| +  void CreateLoaderAndStart(mojom::URLLoaderRequest request, | 
| +                            int32_t routing_id, | 
| +                            int32_t request_id, | 
| +                            uint32_t options, | 
| +                            const ResourceRequest& url_request, | 
| +                            mojom::URLLoaderClientPtr client) override { | 
| +    create_loader_and_start_called_++; | 
| + | 
| +    client_ptr_ = std::move(client); | 
| +  } | 
| + | 
| +  void SyncLoad(int32_t routing_id, | 
| +                int32_t request_id, | 
| +                const ResourceRequest& request, | 
| +                SyncLoadCallback callback) override { | 
| +    NOTREACHED(); | 
| +  } | 
| + | 
| +  size_t create_loader_and_start_called_ = 0; | 
| + | 
| +  mojo::Binding<mojom::URLLoaderFactory> binding_; | 
| +  mojom::URLLoaderFactoryPtr factory_ptr_; | 
| +  mojom::URLLoaderClientPtr client_ptr_; | 
| +  DISALLOW_COPY_AND_ASSIGN(TestURLLoaderFactory); | 
| +}; | 
| + | 
| +class TestURLLoaderClient : public mojom::URLLoaderClient { | 
| + public: | 
| +  TestURLLoaderClient() {} | 
| + | 
| +  size_t on_received_response_called() const { | 
| +    return on_received_response_called_; | 
| +  } | 
| + | 
| +  size_t on_received_redirect_called() const { | 
| +    return on_received_redirect_called_; | 
| +  } | 
| + | 
| +  size_t on_complete_called() const { return on_complete_called_; } | 
| + | 
| +  using OnCompleteCallback = base::Callback<void(int error_code)>; | 
| +  void set_on_complete_callback(const OnCompleteCallback& callback) { | 
| +    on_complete_callback_ = callback; | 
| +  } | 
| + | 
| + private: | 
| +  // mojom::URLLoaderClient implementation: | 
| +  void OnReceiveResponse( | 
| +      const ResourceResponseHead& response_head, | 
| +      const base::Optional<net::SSLInfo>& ssl_info, | 
| +      mojom::DownloadedTempFilePtr downloaded_file) override { | 
| +    on_received_response_called_++; | 
| +  } | 
| +  void OnReceiveRedirect(const net::RedirectInfo& redirect_info, | 
| +                         const ResourceResponseHead& response_head) override { | 
| +    on_received_redirect_called_++; | 
| +  } | 
| +  void OnDataDownloaded(int64_t data_len, int64_t encoded_data_len) override {} | 
| +  void OnUploadProgress(int64_t current_position, | 
| +                        int64_t total_size, | 
| +                        OnUploadProgressCallback ack_callback) override {} | 
| +  void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {} | 
| +  void OnTransferSizeUpdated(int32_t transfer_size_diff) override {} | 
| +  void OnStartLoadingResponseBody( | 
| +      mojo::ScopedDataPipeConsumerHandle body) override {} | 
| +  void OnComplete(const ResourceRequestCompletionStatus& status) override { | 
| +    on_complete_called_++; | 
| +    if (on_complete_callback_) | 
| +      on_complete_callback_.Run(status.error_code); | 
| +  } | 
| + | 
| +  size_t on_received_response_called_ = 0; | 
| +  size_t on_received_redirect_called_ = 0; | 
| +  size_t on_complete_called_ = 0; | 
| + | 
| +  OnCompleteCallback on_complete_callback_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(TestURLLoaderClient); | 
| +}; | 
| + | 
| +class TestURLLoaderThrottle : public URLLoaderThrottle { | 
| + public: | 
| +  TestURLLoaderThrottle() {} | 
| + | 
| +  using ThrottleCallback = | 
| +      base::Callback<void(URLLoaderThrottle::Delegate* delegate, bool* defer)>; | 
| + | 
| +  size_t will_start_request_called() const { | 
| +    return will_start_request_called_; | 
| +  } | 
| +  size_t will_redirect_request_called() const { | 
| +    return will_redirect_request_called_; | 
| +  } | 
| +  size_t will_process_response_called() const { | 
| +    return will_process_response_called_; | 
| +  } | 
| + | 
| +  void set_will_start_request_callback(const ThrottleCallback& callback) { | 
| +    will_start_request_callback_ = callback; | 
| +  } | 
| + | 
| +  void set_will_redirect_request_callback(const ThrottleCallback& callback) { | 
| +    will_redirect_request_callback_ = callback; | 
| +  } | 
| + | 
| +  void set_will_process_response_callback(const ThrottleCallback& callback) { | 
| +    will_process_response_callback_ = callback; | 
| +  } | 
| + | 
| +  Delegate* delegate() const { return delegate_; } | 
| + | 
| + private: | 
| +  // URLLoaderThrottle implementation. | 
| +  void WillStartRequest(const GURL& url, | 
| +                        int load_flags, | 
| +                        ResourceType resource_type, | 
| +                        bool* defer) override { | 
| +    will_start_request_called_++; | 
| +    if (will_start_request_callback_) | 
| +      will_start_request_callback_.Run(delegate_, defer); | 
| +  } | 
| + | 
| +  void WillRedirectRequest(const net::RedirectInfo& redirect_info, | 
| +                           bool* defer) override { | 
| +    will_redirect_request_called_++; | 
| +    if (will_redirect_request_callback_) | 
| +      will_redirect_request_callback_.Run(delegate_, defer); | 
| +  } | 
| + | 
| +  void WillProcessResponse(bool* defer) override { | 
| +    will_process_response_called_++; | 
| +    if (will_process_response_callback_) | 
| +      will_process_response_callback_.Run(delegate_, defer); | 
| +  } | 
| + | 
| +  size_t will_start_request_called_ = 0; | 
| +  size_t will_redirect_request_called_ = 0; | 
| +  size_t will_process_response_called_ = 0; | 
| + | 
| +  ThrottleCallback will_start_request_callback_; | 
| +  ThrottleCallback will_redirect_request_callback_; | 
| +  ThrottleCallback will_process_response_callback_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(TestURLLoaderThrottle); | 
| +}; | 
| + | 
| +class ThrottlingURLLoaderTest : public testing::Test { | 
| + public: | 
| +  ThrottlingURLLoaderTest() {} | 
| + | 
| + protected: | 
| +  // testing::Test implementation. | 
| +  void SetUp() override { | 
| +    auto throttle = base::MakeUnique<TestURLLoaderThrottle>(); | 
| +    throttle_ = throttle.get(); | 
| + | 
| +    throttles_.push_back(std::move(throttle)); | 
| +  } | 
| + | 
| +  void CreateLoaderAndStart() { | 
| +    auto request = base::MakeUnique<ResourceRequest>(); | 
| +    request->url = GURL("http://example.org"); | 
| +    loader_ = ThrottlingURLLoader::CreateLoaderAndStart( | 
| +        factory_.factory_ptr().get(), std::move(throttles_), 0, 0, 0, | 
| +        std::move(request), &client_); | 
| +    factory_.factory_ptr().FlushForTesting(); | 
| +  } | 
| + | 
| +  // Be the first member so it is destroyed last. | 
| +  base::MessageLoop message_loop_; | 
| + | 
| +  std::unique_ptr<ThrottlingURLLoader> loader_; | 
| +  std::vector<std::unique_ptr<URLLoaderThrottle>> throttles_; | 
| + | 
| +  TestURLLoaderFactory factory_; | 
| +  TestURLLoaderClient client_; | 
| + | 
| +  // Owned by |throttles_| or |loader_|. | 
| +  TestURLLoaderThrottle* throttle_ = nullptr; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(ThrottlingURLLoaderTest); | 
| +}; | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, CancelBeforeStart) { | 
| +  throttle_->set_will_start_request_callback( | 
| +      base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        delegate->CancelWithError(net::ERR_ACCESS_DENIED); | 
| +      })); | 
| + | 
| +  base::RunLoop run_loop; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::ERR_ACCESS_DENIED, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| +  run_loop.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(0u, factory_.create_loader_and_start_called()); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, DeferBeforeStart) { | 
| +  throttle_->set_will_start_request_callback( | 
| +      base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        *defer = true; | 
| +      })); | 
| + | 
| +  base::RunLoop run_loop; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::OK, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(0u, factory_.create_loader_and_start_called()); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(0u, client_.on_complete_called()); | 
| + | 
| +  throttle_->delegate()->Resume(); | 
| +  factory_.factory_ptr().FlushForTesting(); | 
| + | 
| +  EXPECT_EQ(1u, factory_.create_loader_and_start_called()); | 
| + | 
| +  factory_.NotifyClientOnReceiveResponse(); | 
| +  factory_.NotifyClientOnComplete(net::OK); | 
| + | 
| +  run_loop.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(1u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, CancelBeforeRedirect) { | 
| +  throttle_->set_will_redirect_request_callback( | 
| +      base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        delegate->CancelWithError(net::ERR_ACCESS_DENIED); | 
| +      })); | 
| + | 
| +  base::RunLoop run_loop; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::ERR_ACCESS_DENIED, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| + | 
| +  factory_.NotifyClientOnReceiveRedirect(); | 
| + | 
| +  run_loop.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, DeferBeforeRedirect) { | 
| +  base::RunLoop run_loop1; | 
| +  throttle_->set_will_redirect_request_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, | 
| +         URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        *defer = true; | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop1.QuitClosure())); | 
| + | 
| +  base::RunLoop run_loop2; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::ERR_UNEXPECTED, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop2.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| + | 
| +  factory_.NotifyClientOnReceiveRedirect(); | 
| + | 
| +  run_loop1.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_process_response_called()); | 
| + | 
| +  factory_.NotifyClientOnComplete(net::ERR_UNEXPECTED); | 
| + | 
| +  base::RunLoop run_loop3; | 
| +  run_loop3.RunUntilIdle(); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(0u, client_.on_complete_called()); | 
| + | 
| +  throttle_->delegate()->Resume(); | 
| +  run_loop2.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(1u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, CancelBeforeResponse) { | 
| +  throttle_->set_will_process_response_callback( | 
| +      base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        delegate->CancelWithError(net::ERR_ACCESS_DENIED); | 
| +      })); | 
| + | 
| +  base::RunLoop run_loop; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::ERR_ACCESS_DENIED, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| + | 
| +  factory_.NotifyClientOnReceiveResponse(); | 
| + | 
| +  run_loop.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, DeferBeforeResponse) { | 
| +  base::RunLoop run_loop1; | 
| +  throttle_->set_will_process_response_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, | 
| +         URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        *defer = true; | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop1.QuitClosure())); | 
| + | 
| +  base::RunLoop run_loop2; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::ERR_UNEXPECTED, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop2.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| + | 
| +  factory_.NotifyClientOnReceiveResponse(); | 
| + | 
| +  run_loop1.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_process_response_called()); | 
| + | 
| +  factory_.NotifyClientOnComplete(net::ERR_UNEXPECTED); | 
| + | 
| +  base::RunLoop run_loop3; | 
| +  run_loop3.RunUntilIdle(); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(0u, client_.on_complete_called()); | 
| + | 
| +  throttle_->delegate()->Resume(); | 
| +  run_loop2.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(1u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, ResumeNoOpIfNotDeferred) { | 
| +  auto resume_callback = | 
| +      base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        delegate->Resume(); | 
| +        delegate->Resume(); | 
| +      }); | 
| +  throttle_->set_will_start_request_callback(resume_callback); | 
| +  throttle_->set_will_redirect_request_callback(resume_callback); | 
| +  throttle_->set_will_process_response_callback(resume_callback); | 
| + | 
| +  base::RunLoop run_loop; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::OK, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| +  factory_.NotifyClientOnReceiveRedirect(); | 
| +  factory_.NotifyClientOnReceiveResponse(); | 
| +  factory_.NotifyClientOnComplete(net::OK); | 
| + | 
| +  run_loop.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(1u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(1u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, CancelNoOpIfAlreadyCanceled) { | 
| +  throttle_->set_will_start_request_callback( | 
| +      base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        delegate->CancelWithError(net::ERR_ACCESS_DENIED); | 
| +        delegate->CancelWithError(net::ERR_UNEXPECTED); | 
| +      })); | 
| + | 
| +  base::RunLoop run_loop; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::ERR_ACCESS_DENIED, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| +  throttle_->delegate()->CancelWithError(net::ERR_INVALID_ARGUMENT); | 
| +  run_loop.Run(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(0u, factory_.create_loader_and_start_called()); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +TEST_F(ThrottlingURLLoaderTest, ResumeNoOpIfAlreadyCanceled) { | 
| +  throttle_->set_will_process_response_callback( | 
| +      base::Bind([](URLLoaderThrottle::Delegate* delegate, bool* defer) { | 
| +        delegate->CancelWithError(net::ERR_ACCESS_DENIED); | 
| +        delegate->Resume(); | 
| +      })); | 
| + | 
| +  base::RunLoop run_loop1; | 
| +  client_.set_on_complete_callback(base::Bind( | 
| +      [](const base::Closure& quit_closure, int error) { | 
| +        EXPECT_EQ(net::ERR_ACCESS_DENIED, error); | 
| +        quit_closure.Run(); | 
| +      }, | 
| +      run_loop1.QuitClosure())); | 
| + | 
| +  CreateLoaderAndStart(); | 
| + | 
| +  factory_.NotifyClientOnReceiveResponse(); | 
| + | 
| +  run_loop1.Run(); | 
| + | 
| +  throttle_->delegate()->Resume(); | 
| + | 
| +  base::RunLoop run_loop2; | 
| +  run_loop2.RunUntilIdle(); | 
| + | 
| +  EXPECT_EQ(1u, throttle_->will_start_request_called()); | 
| +  EXPECT_EQ(0u, throttle_->will_redirect_request_called()); | 
| +  EXPECT_EQ(1u, throttle_->will_process_response_called()); | 
| + | 
| +  EXPECT_EQ(0u, client_.on_received_response_called()); | 
| +  EXPECT_EQ(0u, client_.on_received_redirect_called()); | 
| +  EXPECT_EQ(1u, client_.on_complete_called()); | 
| +} | 
| + | 
| +}  // namespace | 
| +}  // namespace content | 
|  |