// Copyright 2016 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 "pdf/document_loader_impl.h"

#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/callback.h"
#include "base/check.h"
#include "base/test/scoped_feature_list.h"
#include "pdf/pdf_features.h"
#include "pdf/ppapi_migration/callback.h"
#include "pdf/url_loader_wrapper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/range/range.h"

using ::testing::_;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Sequence;

namespace chrome_pdf {

namespace {

constexpr uint32_t kDefaultRequestSize =
    DocumentLoaderImpl::kDefaultRequestSize;

class TestURLLoader : public URLLoaderWrapper {
 public:
  class LoaderData {
   public:
    LoaderData() = default;
    LoaderData(const LoaderData&) = delete;
    LoaderData& operator=(const LoaderData&) = delete;
    ~LoaderData() {
      // We should call callbacks to prevent memory leaks.
      // The callbacks don't do anything, because the objects that created the
      // callbacks have been destroyed.
      if (IsWaitRead())
        CallReadCallback(-1);
      if (IsWaitOpen())
        CallOpenCallback(-1);
    }

    int content_length() const { return content_length_; }
    void set_content_length(int content_length) {
      content_length_ = content_length;
    }
    bool accept_ranges_bytes() const { return accept_ranges_bytes_; }
    void set_accept_ranges_bytes(bool accept_ranges_bytes) {
      accept_ranges_bytes_ = accept_ranges_bytes;
    }
    bool content_encoded() const { return content_encoded_; }
    void set_content_encoded(bool content_encoded) {
      content_encoded_ = content_encoded;
    }
    const std::string& content_type() const { return content_type_; }
    void set_content_type(const std::string& content_type) {
      content_type_ = content_type;
    }
    const std::string& content_disposition() const {
      return content_disposition_;
    }
    void set_content_disposition(const std::string& content_disposition) {
      content_disposition_ = content_disposition;
    }
    const std::string& multipart_boundary() const {
      return multipart_boundary_;
    }
    void set_multipart_boundary(const std::string& multipart_boundary) {
      multipart_boundary_ = multipart_boundary;
    }
    const gfx::Range& byte_range() const { return byte_range_; }
    void set_byte_range(const gfx::Range& byte_range) {
      byte_range_ = byte_range;
    }
    bool is_multipart() const { return is_multipart_; }
    void set_is_multipart(bool is_multipart) { is_multipart_ = is_multipart; }
    int status_code() const { return status_code_; }
    void set_status_code(int status_code) { status_code_ = status_code; }
    bool closed() const { return closed_; }
    void set_closed(bool closed) { closed_ = closed; }
    const gfx::Range& open_byte_range() const { return open_byte_range_; }
    void set_open_byte_range(const gfx::Range& open_byte_range) {
      open_byte_range_ = open_byte_range;
    }

    bool IsWaitRead() const { return !did_read_callback_.is_null(); }
    bool IsWaitOpen() const { return !did_open_callback_.is_null(); }
    char* buffer() const { return buffer_; }
    int buffer_size() const { return buffer_size_; }

    void SetReadCallback(ResultCallback read_callback,
                         char* buffer,
                         int buffer_size) {
      did_read_callback_ = std::move(read_callback);
      buffer_ = buffer;
      buffer_size_ = buffer_size;
    }

    void SetOpenCallback(ResultCallback open_callback,
                         gfx::Range req_byte_range) {
      did_open_callback_ = std::move(open_callback);
      set_open_byte_range(req_byte_range);
    }

    void CallOpenCallback(int result) {
      DCHECK(IsWaitOpen());
      std::move(did_open_callback_).Run(result);
    }

    void CallReadCallback(int result) {
      DCHECK(IsWaitRead());
      std::move(did_read_callback_).Run(result);
    }

   private:
    ResultCallback did_open_callback_;
    ResultCallback did_read_callback_;
    char* buffer_ = nullptr;
    int buffer_size_ = 0;

    int content_length_ = -1;
    bool accept_ranges_bytes_ = false;
    bool content_encoded_ = false;
    std::string content_type_;
    std::string content_disposition_;
    std::string multipart_boundary_;
    gfx::Range byte_range_ = gfx::Range::InvalidRange();
    bool is_multipart_ = false;
    int status_code_ = 0;
    bool closed_ = true;
    gfx::Range open_byte_range_ = gfx::Range::InvalidRange();
  };

  explicit TestURLLoader(LoaderData* data) : data_(data) {
    data_->set_closed(false);
  }
  TestURLLoader(const TestURLLoader&) = delete;
  TestURLLoader& operator=(const TestURLLoader&) = delete;
  ~TestURLLoader() override { Close(); }

  int GetContentLength() const override { return data_->content_length(); }

  bool IsAcceptRangesBytes() const override {
    return data_->accept_ranges_bytes();
  }

  bool IsContentEncoded() const override { return data_->content_encoded(); }

  std::string GetContentType() const override { return data_->content_type(); }

  std::string GetContentDisposition() const override {
    return data_->content_disposition();
  }

  int GetStatusCode() const override { return data_->status_code(); }

  bool IsMultipart() const override { return data_->is_multipart(); }

  bool GetByteRangeStart(int* start) const override {
    *start = data_->byte_range().start();
    return data_->byte_range().IsValid();
  }

  void Close() override { data_->set_closed(true); }

  void OpenRange(const std::string& url,
                 const std::string& referrer_url,
                 uint32_t position,
                 uint32_t size,
                 ResultCallback callback) override {
    data_->SetOpenCallback(std::move(callback),
                           gfx::Range(position, position + size));
  }

  void ReadResponseBody(char* buffer,
                        int buffer_size,
                        ResultCallback callback) override {
    data_->SetReadCallback(std::move(callback), buffer, buffer_size);
  }

 private:
  LoaderData* data_;
};

class TestClient : public DocumentLoader::Client {
 public:
  TestClient() { full_page_loader_data()->set_content_type("application/pdf"); }
  TestClient(const TestClient&) = delete;
  TestClient& operator=(const TestClient&) = delete;
  ~TestClient() override = default;

  // DocumentLoader::Client overrides:
  pp::Instance* GetPluginInstance() override { return nullptr; }
  std::unique_ptr<URLLoaderWrapper> CreateURLLoader() override {
    return std::unique_ptr<URLLoaderWrapper>(
        new TestURLLoader(partial_loader_data()));
  }
  void OnPendingRequestComplete() override {}
  void OnNewDataReceived() override {}
  void OnDocumentComplete() override {}
  void OnDocumentCanceled() override {}

  std::unique_ptr<URLLoaderWrapper> CreateFullPageLoader() {
    return std::unique_ptr<URLLoaderWrapper>(
        new TestURLLoader(full_page_loader_data()));
  }

  TestURLLoader::LoaderData* full_page_loader_data() {
    return &full_page_loader_data_;
  }
  TestURLLoader::LoaderData* partial_loader_data() {
    return &partial_loader_data_;
  }

  void SetCanUsePartialLoading() {
    full_page_loader_data()->set_content_length(10 * 1024 * 1024);
    full_page_loader_data()->set_content_encoded(false);
    full_page_loader_data()->set_accept_ranges_bytes(true);
  }

  void SendAllPartialData() {
    partial_loader_data_.set_byte_range(partial_loader_data_.open_byte_range());
    partial_loader_data_.CallOpenCallback(0);
    uint32_t length = partial_loader_data_.byte_range().length();
    while (length > 0) {
      constexpr uint32_t max_part_len = kDefaultRequestSize;
      const uint32_t part_len = std::min(length, max_part_len);
      partial_loader_data_.CallReadCallback(part_len);
      length -= part_len;
    }
    if (partial_loader_data_.IsWaitRead()) {
      partial_loader_data_.CallReadCallback(0);
    }
  }

 private:
  TestURLLoader::LoaderData full_page_loader_data_;
  TestURLLoader::LoaderData partial_loader_data_;
};

class MockClient : public TestClient {
 public:
  MockClient() = default;
  MockClient(const MockClient&) = delete;
  MockClient& operator=(const MockClient&) = delete;

  MOCK_METHOD(void, OnPendingRequestComplete, (), (override));
  MOCK_METHOD(void, OnNewDataReceived, (), (override));
  MOCK_METHOD(void, OnDocumentComplete, (), (override));
  MOCK_METHOD(void, OnDocumentCanceled, (), (override));
};

}  // namespace

class DocumentLoaderImplTest : public testing::Test {
 protected:
  DocumentLoaderImplTest() {
    scoped_feature_list_.InitAndEnableFeature(features::kPdfPartialLoading);
  }

  base::test::ScopedFeatureList scoped_feature_list_;
};

TEST_F(DocumentLoaderImplTest, PartialLoadingFeatureDefault) {
  scoped_feature_list_.Reset();
  scoped_feature_list_.Init();

  // Test that partial loading is disabled when feature is defaulted.
  TestClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(1000000, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingFeatureDisabled) {
  scoped_feature_list_.Reset();
  scoped_feature_list_.InitAndDisableFeature(features::kPdfPartialLoading);

  // Test that partial loading is disabled when feature is disabled.
  TestClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(1000000, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingEnabled) {
  // Test that partial loading is enabled. (Fixture enables PdfPartialLoading.)
  TestClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(1000000, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingDisabledOnSmallFiles) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 2);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(1000000, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingDisabledIfContentEncoded) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_encoded(true);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(1000000, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingDisabledNoAcceptRangeBytes) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_accept_ranges_bytes(false);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(1000000, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingReallyDisabledRequestFromBegin) {
  TestClient client;
  DocumentLoaderImpl loader(&client);
  client.SetCanUsePartialLoading();
  loader.SetPartialLoadingEnabled(false);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  // We should not start partial loading if requested data is beside full page
  // loading position.
  loader.RequestData(kDefaultRequestSize, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingReallyDisabledRequestFromMiddle) {
  TestClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.SetPartialLoadingEnabled(false);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(1000000, 1);
  EXPECT_FALSE(loader.is_partial_loader_active());
  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingSimple) {
  TestClient client;
  client.SetCanUsePartialLoading();

  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  // While we have no requests, we should not start partial loading.
  EXPECT_FALSE(loader.is_partial_loader_active());

  loader.RequestData(5000000, 1);

  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
  EXPECT_FALSE(loader.is_partial_loader_active());

  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  // Partial loader should request headers.
  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  EXPECT_TRUE(loader.is_partial_loader_active());
  // Loader should be stopped.
  EXPECT_TRUE(client.full_page_loader_data()->closed());

  EXPECT_EQ("{4980736,10485760}",
            client.partial_loader_data()->open_byte_range().ToString());
}

TEST_F(DocumentLoaderImplTest, PartialLoadingBackOrder) {
  TestClient client;
  client.SetCanUsePartialLoading();

  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  // While we have no requests, we should not start partial loading.
  EXPECT_FALSE(loader.is_partial_loader_active());

  loader.RequestData(client.full_page_loader_data()->content_length() - 1, 1);

  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
  EXPECT_FALSE(loader.is_partial_loader_active());

  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  // Partial loader should request headers.
  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  EXPECT_TRUE(loader.is_partial_loader_active());
  // Loader should be stopped.
  EXPECT_TRUE(client.full_page_loader_data()->closed());

  // Requested range should be enlarged.
  EXPECT_GT(client.partial_loader_data()->open_byte_range().length(), 1u);
  EXPECT_EQ("{9830400,10485760}",
            client.partial_loader_data()->open_byte_range().ToString());
}

TEST_F(DocumentLoaderImplTest, CompleteWithoutPartial) {
  TestClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  EXPECT_FALSE(client.full_page_loader_data()->closed());
  while (client.full_page_loader_data()->IsWaitRead()) {
    client.full_page_loader_data()->CallReadCallback(1000);
  }
  EXPECT_TRUE(loader.IsDocumentComplete());
  EXPECT_TRUE(client.full_page_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, ErrorDownloadFullDocument) {
  TestClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  EXPECT_TRUE(client.full_page_loader_data()->IsWaitRead());
  EXPECT_FALSE(client.full_page_loader_data()->closed());
  client.full_page_loader_data()->CallReadCallback(-3);
  EXPECT_TRUE(client.full_page_loader_data()->closed());
  EXPECT_FALSE(loader.IsDocumentComplete());
}

TEST_F(DocumentLoaderImplTest, CompleteNoContentLength) {
  TestClient client;
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  EXPECT_FALSE(client.full_page_loader_data()->closed());
  for (int i = 0; i < 10; ++i) {
    EXPECT_TRUE(client.full_page_loader_data()->IsWaitRead());
    client.full_page_loader_data()->CallReadCallback(1000);
  }
  EXPECT_TRUE(client.full_page_loader_data()->IsWaitRead());
  client.full_page_loader_data()->CallReadCallback(0);
  EXPECT_EQ(10000ul, loader.GetDocumentSize());
  EXPECT_TRUE(loader.IsDocumentComplete());
  EXPECT_TRUE(client.full_page_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, CompleteWithPartial) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(19 * kDefaultRequestSize, kDefaultRequestSize);
  EXPECT_FALSE(client.full_page_loader_data()->closed());
  EXPECT_FALSE(client.partial_loader_data()->IsWaitRead());
  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());

  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(client.full_page_loader_data()->closed());
  EXPECT_FALSE(client.partial_loader_data()->closed());

  client.SendAllPartialData();
  // Now we should send other document data.
  client.SendAllPartialData();
  EXPECT_TRUE(client.full_page_loader_data()->closed());
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, PartialRequestLastChunk) {
  constexpr uint32_t kLastChunkSize = 300;
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20 +
                                                     kLastChunkSize);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(20 * kDefaultRequestSize, 1);

  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  EXPECT_EQ(
      static_cast<int>(client.partial_loader_data()->open_byte_range().end()),
      client.full_page_loader_data()->content_length());
  client.partial_loader_data()->set_byte_range(
      client.partial_loader_data()->open_byte_range());
  client.partial_loader_data()->CallOpenCallback(0);
  uint32_t data_length = client.partial_loader_data()->byte_range().length();
  while (data_length > kDefaultRequestSize) {
    client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
    data_length -= kDefaultRequestSize;
  }
  EXPECT_EQ(kLastChunkSize, data_length);
  client.partial_loader_data()->CallReadCallback(kLastChunkSize);
  EXPECT_TRUE(loader.IsDataAvailable(kDefaultRequestSize * 20, kLastChunkSize));
}

TEST_F(DocumentLoaderImplTest, DocumentSize) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(123456789);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  EXPECT_EQ(static_cast<int>(loader.GetDocumentSize()),
            client.full_page_loader_data()->content_length());
}

TEST_F(DocumentLoaderImplTest, DocumentSizeNoContentLength) {
  TestClient client;
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  EXPECT_EQ(0ul, loader.GetDocumentSize());
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  client.full_page_loader_data()->CallReadCallback(1000);
  client.full_page_loader_data()->CallReadCallback(500);
  client.full_page_loader_data()->CallReadCallback(0);
  EXPECT_EQ(kDefaultRequestSize + 1000ul + 500ul, loader.GetDocumentSize());
  EXPECT_TRUE(loader.IsDocumentComplete());
}

TEST_F(DocumentLoaderImplTest, ClearPendingRequests) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 100 +
                                                     58383);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(17 * kDefaultRequestSize + 100, 10);
  loader.ClearPendingRequests();
  loader.RequestData(15 * kDefaultRequestSize + 200, 20);
  // pending requests are accumulating, and will be processed after initial data
  // load.
  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  {
    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
    constexpr gfx::Range range_requested(15 * kDefaultRequestSize,
                                         16 * kDefaultRequestSize);
    EXPECT_EQ(range_requested.start(),
              client.partial_loader_data()->open_byte_range().start());
    EXPECT_LE(range_requested.end(),
              client.partial_loader_data()->open_byte_range().end());
    client.partial_loader_data()->set_byte_range(
        client.partial_loader_data()->open_byte_range());
  }
  // clear requests before Open callback.
  loader.ClearPendingRequests();
  // Current request should continue loading.
  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->CallOpenCallback(0);
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(client.partial_loader_data()->closed());
  // Current request should continue loading, because no other request queued.

  loader.RequestData(18 * kDefaultRequestSize + 200, 20);
  // Requests queue is processed only on receiving data.
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  // New request within close distance from the one currently loading. Loading
  // isn't restarted.
  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());

  loader.ClearPendingRequests();
  // request again two.
  loader.RequestData(60 * kDefaultRequestSize + 100, 10);
  loader.RequestData(35 * kDefaultRequestSize + 200, 20);
  // Requests queue is processed only on receiving data.
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  {
    // new requset not with in close distance from current loading.
    // Loading should be restarted.
    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
    // The first requested chunk should be processed.
    constexpr gfx::Range range_requested(35 * kDefaultRequestSize,
                                         36 * kDefaultRequestSize);
    EXPECT_EQ(range_requested.start(),
              client.partial_loader_data()->open_byte_range().start());
    EXPECT_LE(range_requested.end(),
              client.partial_loader_data()->open_byte_range().end());
    client.partial_loader_data()->set_byte_range(
        client.partial_loader_data()->open_byte_range());
  }
  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->CallOpenCallback(0);
  // Override pending requests.
  loader.ClearPendingRequests();
  loader.RequestData(70 * kDefaultRequestSize + 100, 10);

  // Requests queue is processed only on receiving data.
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  {
    // New requset not with in close distance from current loading.
    // Loading should be restarted .
    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
    // The first requested chunk should be processed.
    constexpr gfx::Range range_requested(70 * kDefaultRequestSize,
                                         71 * kDefaultRequestSize);
    EXPECT_EQ(range_requested.start(),
              client.partial_loader_data()->open_byte_range().start());
    EXPECT_LE(range_requested.end(),
              client.partial_loader_data()->open_byte_range().end());
    client.partial_loader_data()->set_byte_range(
        client.partial_loader_data()->open_byte_range());
  }
  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
}

TEST_F(DocumentLoaderImplTest, GetBlock) {
  std::vector<char> buffer(kDefaultRequestSize);
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20 +
                                                     58383);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  EXPECT_FALSE(loader.GetBlock(0, 1000, buffer.data()));
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(loader.GetBlock(0, 1000, buffer.data()));
  EXPECT_FALSE(loader.GetBlock(kDefaultRequestSize, 1500, buffer.data()));
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(loader.GetBlock(kDefaultRequestSize, 1500, buffer.data()));

  EXPECT_FALSE(loader.GetBlock(17 * kDefaultRequestSize, 3000, buffer.data()));
  loader.RequestData(17 * kDefaultRequestSize + 100, 10);
  EXPECT_FALSE(loader.GetBlock(17 * kDefaultRequestSize, 3000, buffer.data()));

  // Requests queue is processed only on receiving data.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  client.SendAllPartialData();
  EXPECT_TRUE(loader.GetBlock(17 * kDefaultRequestSize, 3000, buffer.data()));
}

TEST_F(DocumentLoaderImplTest, IsDataAvailable) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20 +
                                                     58383);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  EXPECT_FALSE(loader.IsDataAvailable(0, 1000));
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(loader.IsDataAvailable(0, 1000));
  EXPECT_FALSE(loader.IsDataAvailable(kDefaultRequestSize, 1500));
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(loader.IsDataAvailable(kDefaultRequestSize, 1500));

  EXPECT_FALSE(loader.IsDataAvailable(17 * kDefaultRequestSize, 3000));
  loader.RequestData(17 * kDefaultRequestSize + 100, 10);
  EXPECT_FALSE(loader.IsDataAvailable(17 * kDefaultRequestSize, 3000));

  // Requests queue is processed only on receiving data.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  client.SendAllPartialData();
  EXPECT_TRUE(loader.IsDataAvailable(17 * kDefaultRequestSize, 3000));
}

TEST_F(DocumentLoaderImplTest, RequestData) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 100 +
                                                     58383);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(37 * kDefaultRequestSize + 200, 10);
  loader.RequestData(25 * kDefaultRequestSize + 600, 100);
  loader.RequestData(13 * kDefaultRequestSize + 900, 500);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  {
    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
    constexpr gfx::Range range_requested(13 * kDefaultRequestSize,
                                         14 * kDefaultRequestSize);
    EXPECT_EQ(range_requested.start(),
              client.partial_loader_data()->open_byte_range().start());
    EXPECT_LE(range_requested.end(),
              client.partial_loader_data()->open_byte_range().end());
    client.partial_loader_data()->set_byte_range(
        client.partial_loader_data()->open_byte_range());
  }
  client.partial_loader_data()->CallOpenCallback(0);
  // Override pending requests.
  loader.ClearPendingRequests();
  loader.RequestData(38 * kDefaultRequestSize + 200, 10);
  loader.RequestData(26 * kDefaultRequestSize + 600, 100);
  // Requests queue is processed only on receiving data.
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  {
    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
    constexpr gfx::Range range_requested(26 * kDefaultRequestSize,
                                         27 * kDefaultRequestSize);
    EXPECT_EQ(range_requested.start(),
              client.partial_loader_data()->open_byte_range().start());
    EXPECT_LE(range_requested.end(),
              client.partial_loader_data()->open_byte_range().end());
    client.partial_loader_data()->set_byte_range(
        client.partial_loader_data()->open_byte_range());
  }
  client.partial_loader_data()->CallOpenCallback(0);
  // Override pending requests.
  loader.ClearPendingRequests();
  loader.RequestData(39 * kDefaultRequestSize + 200, 10);
  // Requests queue is processed only on receiving data.
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  {
    EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
    constexpr gfx::Range range_requested(39 * kDefaultRequestSize,
                                         40 * kDefaultRequestSize);
    EXPECT_EQ(range_requested.start(),
              client.partial_loader_data()->open_byte_range().start());
    EXPECT_LE(range_requested.end(),
              client.partial_loader_data()->open_byte_range().end());
    client.partial_loader_data()->set_byte_range(
        client.partial_loader_data()->open_byte_range());
  }
  // Fill all gaps.
  while (!loader.IsDocumentComplete()) {
    client.SendAllPartialData();
  }
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, DoNotLoadAvailablePartialData) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20 +
                                                     58383);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  // Send more data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  loader.RequestData(2 * kDefaultRequestSize + 200, 10);

  // Send more data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  // Partial loading should not have started for already available data.
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, DoNotLoadDataAfterComplete) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  for (int i = 0; i < 20; ++i) {
    client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  }

  EXPECT_TRUE(loader.IsDocumentComplete());

  loader.RequestData(17 * kDefaultRequestSize + 200, 10);

  EXPECT_TRUE(client.partial_loader_data()->closed());
  EXPECT_TRUE(client.full_page_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, DoNotLoadPartialDataAboveDocumentSize) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(20 * kDefaultRequestSize + 200, 10);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, MergePendingRequests) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 50 +
                                                     58383);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  loader.RequestData(17 * kDefaultRequestSize + 200, 10);
  loader.RequestData(16 * kDefaultRequestSize + 600, 100);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  constexpr gfx::Range range_requested(16 * kDefaultRequestSize,
                                       18 * kDefaultRequestSize);
  EXPECT_EQ(range_requested.start(),
            client.partial_loader_data()->open_byte_range().start());
  EXPECT_LE(range_requested.end(),
            client.partial_loader_data()->open_byte_range().end());

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());

  // Fill all gaps.
  while (!loader.IsDocumentComplete()) {
    client.SendAllPartialData();
  }
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, PartialStopOnStatusCodeError) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(17 * kDefaultRequestSize + 200, 10);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->set_status_code(404);
  client.partial_loader_data()->CallOpenCallback(0);
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest,
       PartialAsFullDocumentLoadingRangeRequestNoRangeField) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(17 * kDefaultRequestSize + 200, 10);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->set_byte_range(gfx::Range::InvalidRange());
  client.partial_loader_data()->CallOpenCallback(0);
  EXPECT_FALSE(client.partial_loader_data()->closed());
  // Partial loader is used to load the whole page, like full page loader.
  EXPECT_FALSE(loader.is_partial_loader_active());
}

TEST_F(DocumentLoaderImplTest, PartialMultiPart) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(17 * kDefaultRequestSize + 200, 10);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->set_is_multipart(true);
  client.partial_loader_data()->CallOpenCallback(0);
  client.partial_loader_data()->set_byte_range(
      gfx::Range(17 * kDefaultRequestSize, 18 * kDefaultRequestSize));
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(
      loader.IsDataAvailable(17 * kDefaultRequestSize, kDefaultRequestSize));
}

TEST_F(DocumentLoaderImplTest, PartialMultiPartRangeError) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(17 * kDefaultRequestSize + 200, 10);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->set_is_multipart(true);
  client.partial_loader_data()->CallOpenCallback(0);
  client.partial_loader_data()->set_byte_range(gfx::Range::InvalidRange());
  client.partial_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_FALSE(
      loader.IsDataAvailable(17 * kDefaultRequestSize, kDefaultRequestSize));
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, PartialConnectionErrorOnOpen) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(17 * kDefaultRequestSize + 200, 10);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->CallOpenCallback(-3);
  EXPECT_TRUE(client.partial_loader_data()->closed());

  // Partial loading should not restart after any error.
  loader.RequestData(18 * kDefaultRequestSize + 200, 10);

  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, PartialConnectionErrorOnRead) {
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(17 * kDefaultRequestSize + 200, 10);

  // Send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  EXPECT_TRUE(client.partial_loader_data()->IsWaitOpen());
  client.partial_loader_data()->set_byte_range(
      gfx::Range(17 * kDefaultRequestSize, 18 * kDefaultRequestSize));
  client.partial_loader_data()->CallOpenCallback(0);
  EXPECT_TRUE(client.partial_loader_data()->IsWaitRead());
  client.partial_loader_data()->CallReadCallback(-3);
  EXPECT_TRUE(client.partial_loader_data()->closed());

  // Partial loading should not restart after any error.
  loader.RequestData(18 * kDefaultRequestSize + 200, 10);

  EXPECT_FALSE(client.partial_loader_data()->IsWaitOpen());
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, ClientCompleteCallbacks) {
  MockClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
  for (int i = 0; i < 19; ++i)
    client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  Mock::VerifyAndClear(&client);

  EXPECT_CALL(client, OnDocumentComplete()).Times(1);
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  Mock::VerifyAndClear(&client);
}

TEST_F(DocumentLoaderImplTest, ClientCompleteCallbacksNoContentLength) {
  MockClient client;
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  EXPECT_CALL(client, OnDocumentCanceled()).Times(0);
  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
  for (int i = 0; i < 20; ++i)
    client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  Mock::VerifyAndClear(&client);

  EXPECT_CALL(client, OnDocumentCanceled()).Times(0);
  EXPECT_CALL(client, OnDocumentComplete()).Times(1);
  client.full_page_loader_data()->CallReadCallback(0);
  Mock::VerifyAndClear(&client);
}

TEST_F(DocumentLoaderImplTest, ClientCancelCallback) {
  MockClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  EXPECT_CALL(client, OnDocumentCanceled()).Times(0);
  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
  for (int i = 0; i < 10; ++i)
    client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  Mock::VerifyAndClear(&client);

  EXPECT_CALL(client, OnDocumentComplete()).Times(0);
  EXPECT_CALL(client, OnDocumentCanceled()).Times(1);
  client.full_page_loader_data()->CallReadCallback(-3);
  Mock::VerifyAndClear(&client);
}

TEST_F(DocumentLoaderImplTest, NewDataAvailable) {
  MockClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDefaultRequestSize * 20);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  EXPECT_CALL(client, OnNewDataReceived()).Times(1);
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  Mock::VerifyAndClear(&client);

  EXPECT_CALL(client, OnNewDataReceived()).Times(1);
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize - 100);
  Mock::VerifyAndClear(&client);

  EXPECT_CALL(client, OnNewDataReceived()).Times(1);
  client.full_page_loader_data()->CallReadCallback(100);
  Mock::VerifyAndClear(&client);
}

TEST_F(DocumentLoaderImplTest, ClientPendingRequestCompleteFullLoader) {
  MockClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  loader.RequestData(1000, 4000);

  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  Mock::VerifyAndClear(&client);
}

TEST_F(DocumentLoaderImplTest, ClientPendingRequestCompletePartialLoader) {
  MockClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
  loader.RequestData(15 * kDefaultRequestSize + 4000, 4000);

  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  client.SendAllPartialData();
  Mock::VerifyAndClear(&client);
}

TEST_F(DocumentLoaderImplTest,
       ClientPendingRequestCompletePartialAndFullLoader) {
  MockClient client;
  client.SetCanUsePartialLoading();
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");

  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
  loader.RequestData(16 * kDefaultRequestSize + 4000, 4000);
  loader.RequestData(4 * kDefaultRequestSize + 4000, 4000);

  for (int i = 0; i < 5; ++i)
    client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);

  Mock::VerifyAndClear(&client);

  EXPECT_CALL(client, OnPendingRequestComplete()).Times(1);
  client.SendAllPartialData();
  Mock::VerifyAndClear(&client);
}

TEST_F(DocumentLoaderImplTest, IgnoreDataMoreThanExpectedWithPartial) {
  static constexpr uint32_t kDocSize = kDefaultRequestSize * 80 - 321;
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDocSize);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  // Request data at and.
  loader.RequestData(kDocSize - 100, 100);

  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(client.full_page_loader_data()->closed());
  EXPECT_FALSE(client.partial_loader_data()->closed());

  // Request data at middle to continue loading partial, but not all remaining
  // data.
  loader.RequestData(kDocSize / 2, 100);

  // Fill data at the end, the partial loding should be started for second
  // requested data after receive data for first request.
  client.SendAllPartialData();

  ASSERT_TRUE(client.partial_loader_data()->IsWaitOpen());
  // Process second request.
  const uint32_t expected_length =
      client.partial_loader_data()->open_byte_range().length();

  // Send data.
  client.partial_loader_data()->set_byte_range(
      client.partial_loader_data()->open_byte_range());
  client.partial_loader_data()->CallOpenCallback(0);
  uint32_t length = expected_length;
  while (length > 0) {
    constexpr uint32_t max_part_len = kDefaultRequestSize;
    const uint32_t part_len = std::min(length, max_part_len);
    client.partial_loader_data()->CallReadCallback(part_len);
    length -= part_len;
  }

  // The partial loading should be finished for current chunks sequence, if
  // expected range was received, and remaining sequence should start loading.
  EXPECT_FALSE(client.partial_loader_data()->IsWaitRead());
  ASSERT_TRUE(client.partial_loader_data()->IsWaitOpen());

  // Send other document data.
  client.SendAllPartialData();
  // The downloads should be finished.
  EXPECT_TRUE(client.full_page_loader_data()->closed());
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

TEST_F(DocumentLoaderImplTest, IgnoreDataMoreThanExpectedWithPartialAtFileEnd) {
  static constexpr uint32_t kExtraSize = 100;
  static constexpr uint32_t kRealSize = kDefaultRequestSize * 20 - 300;
  static constexpr uint32_t kDocSize = kRealSize - kExtraSize;
  TestClient client;
  client.SetCanUsePartialLoading();
  client.full_page_loader_data()->set_content_length(kDocSize);
  DocumentLoaderImpl loader(&client);
  loader.Init(client.CreateFullPageLoader(), "http://url.com");
  // Request data at middle.
  static constexpr uint32_t kFirstPartial = kDefaultRequestSize * 11;
  loader.RequestData(kFirstPartial, kDefaultRequestSize);

  // Always send initial data from FullPageLoader.
  client.full_page_loader_data()->CallReadCallback(kDefaultRequestSize);
  EXPECT_TRUE(client.full_page_loader_data()->closed());
  EXPECT_FALSE(client.partial_loader_data()->closed());

  // Send data to file end and extra non expected data.
  client.partial_loader_data()->set_byte_range(
      gfx::Range(kFirstPartial, kRealSize));
  client.partial_loader_data()->CallOpenCallback(0);
  uint32_t length = client.partial_loader_data()->byte_range().length();
  while (length > 0) {
    constexpr uint32_t max_part_len = kDefaultRequestSize;
    const uint32_t part_len = std::min(length, max_part_len);
    client.partial_loader_data()->CallReadCallback(part_len);
    length -= part_len;
  }

  // The partial loading should be finished for current chunks sequence, if
  // eof was reached, and remaining sequence should start loading.
  EXPECT_FALSE(client.partial_loader_data()->IsWaitRead());
  EXPECT_EQ(gfx::Range(kDefaultRequestSize, kFirstPartial),
            client.partial_loader_data()->open_byte_range());

  // Send other document data.
  client.SendAllPartialData();
  // The downloads should be finished.
  EXPECT_TRUE(client.full_page_loader_data()->closed());
  EXPECT_TRUE(client.partial_loader_data()->closed());
}

}  // namespace chrome_pdf