root/webkit/child/multipart_response_delegate_unittest.cc

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

DEFINITIONS

This source file includes following definitions.
  1. PushOverLine
  2. ParseHeaders
  3. FindBoundary
  4. boundary
  5. data
  6. willSendRequest
  7. didSendData
  8. didReceiveResponse
  9. didReceiveData
  10. didFinishLoading
  11. didFail
  12. Reset
  13. GetResponseHeader
  14. TEST
  15. TEST
  16. TEST
  17. VariousChunkSizesTest
  18. TEST
  19. TEST
  20. TEST
  21. TEST
  22. TEST
  23. TEST
  24. TEST
  25. TEST

// Copyright (c) 2011 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 <vector>

#include "base/basictypes.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
#include "third_party/WebKit/public/platform/WebURLResponse.h"
#include "webkit/child/multipart_response_delegate.h"

using std::string;
using blink::WebString;
using blink::WebURL;
using blink::WebURLError;
using blink::WebURLLoader;
using blink::WebURLLoaderClient;
using blink::WebURLRequest;
using blink::WebURLResponse;
using webkit_glue::MultipartResponseDelegate;
using webkit_glue::MultipartResponseDelegateTester;

namespace webkit_glue {

class MultipartResponseDelegateTester {
 public:
  MultipartResponseDelegateTester(MultipartResponseDelegate* delegate)
      : delegate_(delegate) {
  }

  int PushOverLine(const std::string& data, size_t pos) {
    return delegate_->PushOverLine(data, pos);
  }

  bool ParseHeaders() { return delegate_->ParseHeaders(); }
  size_t FindBoundary() { return delegate_->FindBoundary(); }
  std::string& boundary() { return delegate_->boundary_; }
  std::string& data() { return delegate_->data_; }

 private:
  MultipartResponseDelegate* delegate_;
};

}  // namespace webkit_glue

namespace {

class MultipartResponseTest : public testing::Test {
};

class MockWebURLLoaderClient : public WebURLLoaderClient {
 public:
  MockWebURLLoaderClient() { Reset(); }

  virtual void willSendRequest(
      WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
  virtual void didSendData(
      WebURLLoader*, unsigned long long, unsigned long long) {}

  virtual void didReceiveResponse(WebURLLoader* loader,
                                  const WebURLResponse& response) {
    ++received_response_;
    response_ = response;
    data_.clear();
  }
  virtual void didReceiveData(
      blink::WebURLLoader* loader,
      const char* data,
      int data_length,
      int encoded_data_length) {
    ++received_data_;
    data_.append(data, data_length);
    total_encoded_data_length_ += encoded_data_length;
  }
  virtual void didFinishLoading(
      WebURLLoader*, double finishTime, int64_t total_encoded_data_length) {}
  virtual void didFail(WebURLLoader*, const WebURLError&) {}

  void Reset() {
    received_response_ = received_data_ = total_encoded_data_length_ = 0;
    data_.clear();
    response_.reset();
  }

  string GetResponseHeader(const char* name) const {
    return string(response_.httpHeaderField(WebString::fromUTF8(name)).utf8());
  }

  int received_response_, received_data_, total_encoded_data_length_;
  string data_;
  WebURLResponse response_;
};

// We can't put this in an anonymous function because it's a friend class for
// access to private members.
TEST(MultipartResponseTest, Functions) {
  // PushOverLine tests

  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Foo", "Bar");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");
  MultipartResponseDelegateTester delegate_tester(&delegate);

  struct {
    const char* input;
    const int position;
    const int expected;
  } line_tests[] = {
    { "Line", 0, 0 },
    { "Line", 2, 0 },
    { "Line", 10, 0 },
    { "\r\nLine", 0, 2 },
    { "\nLine", 0, 1 },
    { "\n\nLine", 0, 2 },
    { "\rLine", 0, 1 },
    { "Line\r\nLine", 4, 2 },
    { "Line\nLine", 4, 1 },
    { "Line\n\nLine", 4, 2 },
    { "Line\rLine", 4, 1 },
    { "Line\r\rLine", 4, 1 },
  };
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(line_tests); ++i) {
    EXPECT_EQ(line_tests[i].expected,
              delegate_tester.PushOverLine(line_tests[i].input,
                                           line_tests[i].position));
  }

  // ParseHeaders tests
  struct {
    const char* data;
    const bool rv;
    const int received_response_calls;
    const char* newdata;
  } header_tests[] = {
    { "This is junk", false, 0, "This is junk" },
    { "Foo: bar\nBaz:\n\nAfter:\n", true, 1, "After:\n" },
    { "Foo: bar\nBaz:\n", false, 0, "Foo: bar\nBaz:\n" },
    { "Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 1, "After:\r\n" },
    { "Foo: bar\r\nBaz:\r\n", false, 0, "Foo: bar\r\nBaz:\r\n" },
    { "Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 1, "After:\n\n" },
    { "Foo: bar\r\nBaz:\n", false, 0, "Foo: bar\r\nBaz:\n" },
    { "\r\n", true, 1, "" },
  };
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(header_tests); ++i) {
    client.Reset();
    delegate_tester.data().assign(header_tests[i].data);
    EXPECT_EQ(header_tests[i].rv,
              delegate_tester.ParseHeaders());
    EXPECT_EQ(header_tests[i].received_response_calls,
              client.received_response_);
    EXPECT_EQ(string(header_tests[i].newdata),
              delegate_tester.data());
  }
  // Test that the resource response is filled in correctly when parsing
  // headers.
  client.Reset();
  string test_header("content-type: image/png\ncontent-length: 10\n\n");
  delegate_tester.data().assign(test_header);
  EXPECT_TRUE(delegate_tester.ParseHeaders());
  EXPECT_TRUE(delegate_tester.data().length() == 0);
  EXPECT_EQ(string("image/png"), client.GetResponseHeader("Content-Type"));
  EXPECT_EQ(string("10"), client.GetResponseHeader("content-length"));
  // This header is passed from the original request.
  EXPECT_EQ(string("Bar"), client.GetResponseHeader("foo"));

  // Make sure we parse the right mime-type if a charset is provided.
  client.Reset();
  string test_header2("content-type: text/html; charset=utf-8\n\n");
  delegate_tester.data().assign(test_header2);
  EXPECT_TRUE(delegate_tester.ParseHeaders());
  EXPECT_TRUE(delegate_tester.data().length() == 0);
  EXPECT_EQ(string("text/html; charset=utf-8"),
            client.GetResponseHeader("Content-Type"));
  EXPECT_EQ(string("utf-8"),
            string(client.response_.textEncodingName().utf8()));

  // FindBoundary tests
  struct {
    const char* boundary;
    const char* data;
    const size_t position;
  } boundary_tests[] = {
    { "bound", "bound", 0 },
    { "bound", "--bound", 0 },
    { "bound", "junkbound", 4 },
    { "bound", "junk--bound", 4 },
    { "foo", "bound", string::npos },
    { "bound", "--boundbound", 0 },
  };
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(boundary_tests); ++i) {
    delegate_tester.boundary().assign(boundary_tests[i].boundary);
    delegate_tester.data().assign(boundary_tests[i].data);
    EXPECT_EQ(boundary_tests[i].position,
              delegate_tester.FindBoundary());
  }
}

TEST(MultipartResponseTest, MissingBoundaries) {
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Foo", "Bar");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");

  // No start boundary
  string no_start_boundary(
    "Content-type: text/plain\n\n"
    "This is a sample response\n"
    "--bound--"
    "ignore junk after end token --bound\n\nTest2\n");
  delegate.OnReceivedData(no_start_boundary.c_str(),
                          static_cast<int>(no_start_boundary.length()),
                          static_cast<int>(no_start_boundary.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response"), client.data_);
  EXPECT_EQ(static_cast<int>(no_start_boundary.length()),
            client.total_encoded_data_length_);

  delegate.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);

  // No end boundary
  client.Reset();
  MultipartResponseDelegate delegate2(&client, NULL, response, "bound");
  string no_end_boundary(
    "bound\nContent-type: text/plain\n\n"
    "This is a sample response\n");
  delegate2.OnReceivedData(no_end_boundary.c_str(),
                          static_cast<int>(no_end_boundary.length()),
                          static_cast<int>(no_end_boundary.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ("This is a sample response\n", client.data_);
  EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
            client.total_encoded_data_length_);

  delegate2.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response\n"), client.data_);
  EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
            client.total_encoded_data_length_);

  // Neither boundary
  client.Reset();
  MultipartResponseDelegate delegate3(&client, NULL, response, "bound");
  string no_boundaries(
    "Content-type: text/plain\n\n"
    "This is a sample response\n");
  delegate3.OnReceivedData(no_boundaries.c_str(),
                           static_cast<int>(no_boundaries.length()),
                           static_cast<int>(no_boundaries.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ("This is a sample response\n", client.data_);
  EXPECT_EQ(static_cast<int>(no_boundaries.length()),
            client.total_encoded_data_length_);

  delegate3.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response\n"), client.data_);
  EXPECT_EQ(static_cast<int>(no_boundaries.length()),
            client.total_encoded_data_length_);
}

TEST(MultipartResponseTest, MalformedBoundary) {
  // Some servers send a boundary that is prefixed by "--".  See bug 5786.

  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Foo", "Bar");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "--bound");

  string data(
    "--bound\n"
    "Content-type: text/plain\n\n"
    "This is a sample response\n"
    "--bound--"
    "ignore junk after end token --bound\n\nTest2\n");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);

  delegate.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
}


// Used in for tests that break the data in various places.
struct TestChunk {
  const int start_pos;  // offset in data
  const int end_pos;    // end offset in data
  const int expected_responses;
  const int expected_received_data;
  const char* expected_data;
  const int expected_encoded_data_length;
};

void VariousChunkSizesTest(const TestChunk chunks[], int chunks_size,
                           int responses, int received_data,
                           const char* completed_data,
                           int completed_encoded_data_length) {
  const string data(
    "--bound\n"                    // 0-7
    "Content-type: image/png\n\n"  // 8-32
    "datadatadatadatadata"         // 33-52
    "--bound\n"                    // 53-60
    "Content-type: image/jpg\n\n"  // 61-85
    "foofoofoofoofoo"              // 86-100
    "--bound--");                  // 101-109

  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");

  for (int i = 0; i < chunks_size; ++i) {
    ASSERT_TRUE(chunks[i].start_pos < chunks[i].end_pos);
    string chunk = data.substr(chunks[i].start_pos,
                               chunks[i].end_pos - chunks[i].start_pos);
    delegate.OnReceivedData(
        chunk.c_str(),
        static_cast<int>(chunk.length()),
        static_cast<int>(chunk.length()));
    EXPECT_EQ(chunks[i].expected_responses, client.received_response_);
    EXPECT_EQ(chunks[i].expected_received_data, client.received_data_);
    EXPECT_EQ(string(chunks[i].expected_data), client.data_);
    EXPECT_EQ(chunks[i].expected_encoded_data_length,
              client.total_encoded_data_length_);
  }
  // Check final state
  delegate.OnCompletedRequest();
  EXPECT_EQ(responses, client.received_response_);
  EXPECT_EQ(received_data, client.received_data_);
  string completed_data_string(completed_data);
  EXPECT_EQ(completed_data_string, client.data_);
  EXPECT_EQ(completed_encoded_data_length, client.total_encoded_data_length_);
}

TEST(MultipartResponseTest, BreakInBoundary) {
  // Break in the first boundary
  const TestChunk bound1[] = {
    { 0, 4, 0, 0, "", 0 },
    { 4, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(bound1, arraysize(bound1),
                        2, 2, "foofoofoofoofoo", 110);

  // Break in first and second
  const TestChunk bound2[] = {
    { 0, 4, 0, 0, "", 0 },
    { 4, 55, 1, 1, "datadatadatadat", 55 },
    { 55, 65, 1, 2, "datadatadatadatadata", 65 },
    { 65, 110, 2, 3, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(bound2, arraysize(bound2),
                        2, 3, "foofoofoofoofoo", 110);

  // Break in second only
  const TestChunk bound3[] = {
    { 0, 55, 1, 1, "datadatadatadat", 55 },
    { 55, 110, 2, 3, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(bound3, arraysize(bound3),
                        2, 3, "foofoofoofoofoo", 110);
}

TEST(MultipartResponseTest, BreakInHeaders) {
  // Break in first header
  const TestChunk header1[] = {
    { 0, 10, 0, 0, "", 0 },
    { 10, 35, 1, 0, "", 0 },
    { 35, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(header1, arraysize(header1),
                        2, 2, "foofoofoofoofoo", 110);

  // Break in both headers
  const TestChunk header2[] = {
    { 0, 10, 0, 0, "", 0 },
    { 10, 65, 1, 1, "datadatadatadatadata", 65 },
    { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(header2, arraysize(header2),
                        2, 2, "foofoofoofoofoo", 110);

  // Break at end of a header
  const TestChunk header3[] = {
    { 0, 33, 1, 0, "", 0 },
    { 33, 65, 1, 1, "datadatadatadatadata", 65 },
    { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(header3, arraysize(header3),
                        2, 2, "foofoofoofoofoo", 110);
}

TEST(MultipartResponseTest, BreakInData) {
  // All data as one chunk
  const TestChunk data1[] = {
    { 0, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(data1, arraysize(data1),
                        2, 2, "foofoofoofoofoo", 110);

  // breaks in data segment
  const TestChunk data2[] = {
    { 0, 35, 1, 0, "", 0 },
    { 35, 65, 1, 1, "datadatadatadatadata", 65 },
    { 65, 90, 2, 1, "", 65 },
    { 90, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(data2, arraysize(data2),
                        2, 2, "foofoofoofoofoo", 110);

  // Incomplete send
  const TestChunk data3[] = {
    { 0, 35, 1, 0, "", 0 },
    { 35, 90, 2, 1, "", 90 },
  };
  VariousChunkSizesTest(data3, arraysize(data3),
                        2, 2, "foof", 90);
}

TEST(MultipartResponseTest, SmallChunk) {
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");

  // Test chunks of size 1, 2, and 0.
  string data(
    "--boundContent-type: text/plain\n\n"
    "\n--boundContent-type: text/plain\n\n"
    "\n\n--boundContent-type: text/plain\n\n"
    "--boundContent-type: text/plain\n\n"
    "end--bound--");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(4, client.received_response_);
  EXPECT_EQ(2, client.received_data_);
  EXPECT_EQ(string("end"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);

  delegate.OnCompletedRequest();
  EXPECT_EQ(4, client.received_response_);
  EXPECT_EQ(2, client.received_data_);
}

TEST(MultipartResponseTest, MultipleBoundaries) {
  // Test multiple boundaries back to back
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");

  string data("--bound\r\n\r\n--bound\r\n\r\nfoofoo--bound--");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(2, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("foofoo"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
}

TEST(MultipartResponseTest, MultipartByteRangeParsingTest) {
  // Test multipart/byteranges based boundary parsing.
  WebURLResponse response1;
  response1.initialize();
  response1.setMIMEType("multipart/x-mixed-replace");
  response1.setHTTPHeaderField("Content-Length", "200");
  response1.setHTTPHeaderField("Content-type",
                               "multipart/byteranges; boundary=--bound--");

  std::string multipart_boundary;
  bool result = MultipartResponseDelegate::ReadMultipartBoundary(
      response1, &multipart_boundary);
  EXPECT_EQ(result, true);
  EXPECT_EQ(string("--bound--"),
            multipart_boundary);

  WebURLResponse response2;
  response2.initialize();
  response2.setMIMEType("image/png");

  response2.setHTTPHeaderField("Content-Length", "300");
  response2.setHTTPHeaderField("Last-Modified",
                               "Mon, 04 Apr 2005 20:36:01 GMT");
  response2.setHTTPHeaderField("Date", "Thu, 11 Sep 2008 18:21:42 GMT");

  multipart_boundary.clear();
  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response2, &multipart_boundary);
  EXPECT_EQ(result, false);

  WebURLResponse response3;
  response3.initialize();
  response3.setMIMEType("multipart/byteranges");

  response3.setHTTPHeaderField("Content-Length", "300");
  response3.setHTTPHeaderField("Last-Modified",
                               "Mon, 04 Apr 2005 20:36:01 GMT");
  response3.setHTTPHeaderField("Date", "Thu, 11 Sep 2008 18:21:42 GMT");
  response3.setHTTPHeaderField("Content-type", "multipart/byteranges");

  multipart_boundary.clear();
  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response3, &multipart_boundary);
  EXPECT_EQ(result, false);
  EXPECT_EQ(multipart_boundary.length(), 0U);

  WebURLResponse response4;
  response4.initialize();
  response4.setMIMEType("multipart/byteranges");
  response4.setHTTPHeaderField("Content-Length", "200");
  response4.setHTTPHeaderField("Content-type",
      "multipart/byteranges; boundary=--bound--; charSet=utf8");

  multipart_boundary.clear();

  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response4, &multipart_boundary);
  EXPECT_EQ(result, true);
  EXPECT_EQ(string("--bound--"), multipart_boundary);

  WebURLResponse response5;
  response5.initialize();
  response5.setMIMEType("multipart/byteranges");
  response5.setHTTPHeaderField("Content-Length", "200");
  response5.setHTTPHeaderField("Content-type",
      "multipart/byteranges; boundary=\"--bound--\"; charSet=utf8");

  multipart_boundary.clear();

  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response5, &multipart_boundary);
  EXPECT_EQ(result, true);
  EXPECT_EQ(string("--bound--"), multipart_boundary);
}

TEST(MultipartResponseTest, MultipartContentRangesTest) {
  WebURLResponse response1;
  response1.initialize();
  response1.setMIMEType("application/pdf");
  response1.setHTTPHeaderField("Content-Length", "200");  // Ignored!
  // Use intentionally >32bit values to check they are handled correctly.
  response1.setHTTPHeaderField("Content-Range",
                               "bytes 5000000000-5000000050/6000000000");

  int64 content_range_lower_bound = 0;
  int64 content_range_upper_bound = 0;
  int64 content_range_instance_size = 0;

  bool result = MultipartResponseDelegate::ReadContentRanges(
      response1, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);

  EXPECT_EQ(result, true);
  EXPECT_EQ(content_range_lower_bound, 5e9);
  EXPECT_EQ(content_range_upper_bound, 5e9+50);
  EXPECT_EQ(content_range_instance_size, 6e9);

  WebURLResponse response2;
  response2.initialize();
  response2.setMIMEType("application/pdf");
  response2.setHTTPHeaderField("Content-Length", "200");
  response2.setHTTPHeaderField("Content-Range", "bytes 1000/1050");

  content_range_lower_bound = 0;
  content_range_upper_bound = 0;
  content_range_instance_size = 0;

  result = MultipartResponseDelegate::ReadContentRanges(
      response2, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);

  EXPECT_EQ(result, false);

  WebURLResponse response3;
  response3.initialize();
  response3.setMIMEType("application/pdf");
  response3.setHTTPHeaderField("Content-Length", "200");
  response3.setHTTPHeaderField("Range", "bytes 1000-1050/5000");

  content_range_lower_bound = 0;
  content_range_upper_bound = 0;
  content_range_instance_size = 0;

  result = MultipartResponseDelegate::ReadContentRanges(
      response3, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);

  EXPECT_EQ(result, true);
  EXPECT_EQ(content_range_lower_bound, 1000);
  EXPECT_EQ(content_range_upper_bound, 1050);

  WebURLResponse response4;
  response4.initialize();
  response4.setMIMEType("application/pdf");
  response4.setHTTPHeaderField("Content-Length", "200");

  content_range_lower_bound = 0;
  content_range_upper_bound = 0;
  content_range_instance_size = 0;

  result = MultipartResponseDelegate::ReadContentRanges(
      response4, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);

  EXPECT_EQ(result, false);
}

TEST(MultipartResponseTest, MultipartPayloadSet) {
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");

  string data(
      "--bound\n"
      "Content-type: text/plain\n\n"
      "response data\n"
      "--bound\n");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(string("response data"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
  EXPECT_FALSE(client.response_.isMultipartPayload());

  string data2(
      "Content-type: text/plain\n\n"
      "response data2\n"
      "--bound\n");
  delegate.OnReceivedData(data2.c_str(),
                          static_cast<int>(data2.length()),
                          static_cast<int>(data2.length()));
  EXPECT_EQ(2, client.received_response_);
  EXPECT_EQ(string("response data2"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()) + static_cast<int>(data2.length()),
            client.total_encoded_data_length_);
  EXPECT_TRUE(client.response_.isMultipartPayload());
}

}  // namespace

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