| // Copyright (c) 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 "ios/net/chunked_data_stream_uploader.h" | 
 |  | 
 | #include <array> | 
 | #include <memory> | 
 |  | 
 | #include "net/base/io_buffer.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "testing/platform_test.h" | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 | const int kDefaultIOBufferSize = 1024; | 
 | } | 
 |  | 
 | // Mock delegate to provide data from its internal buffer. | 
 | class MockChunkedDataStreamUploaderDelegate | 
 |     : public ChunkedDataStreamUploader::Delegate { | 
 |  public: | 
 |   MockChunkedDataStreamUploaderDelegate() : data_length_(0) {} | 
 |   ~MockChunkedDataStreamUploaderDelegate() override {} | 
 |  | 
 |   int OnRead(char* buffer, int buffer_length) override { | 
 |     int bytes_read = 0; | 
 |     if (data_length_ > 0) { | 
 |       CHECK_GE(buffer_length, data_length_); | 
 |       memcpy(buffer, data_, data_length_); | 
 |       bytes_read = data_length_; | 
 |       data_length_ = 0; | 
 |     } | 
 |     return bytes_read; | 
 |   } | 
 |  | 
 |   void SetReadData(const char* data, int data_length) { | 
 |     CHECK_GE(sizeof(data_), static_cast<size_t>(data_length)); | 
 |     memcpy(data_, data, data_length); | 
 |     data_length_ = data_length; | 
 |     CHECK(!memcmp(data_, data, data_length)); | 
 |   } | 
 |  | 
 |  private: | 
 |   char data_[kDefaultIOBufferSize]; | 
 |   int data_length_; | 
 | }; | 
 |  | 
 | class ChunkedDataStreamUploaderTest : public PlatformTest { | 
 |  public: | 
 |   ChunkedDataStreamUploaderTest() : callback_count(0) { | 
 |     delegate_ = std::make_unique<MockChunkedDataStreamUploaderDelegate>(); | 
 |     uploader_owner_ = | 
 |         std::make_unique<ChunkedDataStreamUploader>(delegate_.get()); | 
 |     uploader_ = uploader_owner_->GetWeakPtr(); | 
 |  | 
 |     uploader_owner_->Init(base::BindRepeating([](int) {}), | 
 |                           net::NetLogWithSource()); | 
 |   } | 
 |  | 
 |   void CompletionCallback(int result) { ++callback_count; } | 
 |  | 
 |  protected: | 
 |   std::unique_ptr<MockChunkedDataStreamUploaderDelegate> delegate_; | 
 |  | 
 |   std::unique_ptr<ChunkedDataStreamUploader> uploader_owner_; | 
 |   base::WeakPtr<ChunkedDataStreamUploader> uploader_; | 
 |  | 
 |   // Completion callback counter for each case. | 
 |   int callback_count; | 
 | }; | 
 |  | 
 | // Tests that data from the application layer become ready before the network | 
 | // layer callback. | 
 | TEST_F(ChunkedDataStreamUploaderTest, ExternalDataReadyFirst) { | 
 |   // Application layer data is ready. | 
 |   const char kTestData[] = "Hello world!"; | 
 |   delegate_->SetReadData(kTestData, sizeof(kTestData)); | 
 |   uploader_->UploadWhenReady(false); | 
 |  | 
 |   // Network layer callback is called next, and the application data is expected | 
 |   // to be read to the |buffer|. | 
 |   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kDefaultIOBufferSize); | 
 |   int bytes_read = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |  | 
 |   EXPECT_EQ(sizeof(kTestData), static_cast<size_t>(bytes_read)); | 
 |   EXPECT_FALSE( | 
 |       memcmp(kTestData, buffer->data(), static_cast<size_t>(bytes_read))); | 
 |  | 
 |   // Application finishes data upload. Called after all data has been uploaded. | 
 |   delegate_->SetReadData("", 0); | 
 |   uploader_->UploadWhenReady(true); | 
 |   bytes_read = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(0, bytes_read); | 
 |   EXPECT_TRUE(uploader_->IsEOF()); | 
 |  | 
 |   // No completion callback is called because Read() call should return | 
 |   // directly. | 
 |   EXPECT_EQ(0, callback_count); | 
 | } | 
 |  | 
 | // Tests that callback from the network layer is called before the application | 
 | // layer data available. | 
 | TEST_F(ChunkedDataStreamUploaderTest, InternalReadReadyFirst) { | 
 |   // Network layer callback is called and the request is pending. | 
 |   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kDefaultIOBufferSize); | 
 |   int ret = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(ERR_IO_PENDING, ret); | 
 |  | 
 |   // The data is writen into |buffer| once the application layer data is ready. | 
 |   const char kTestData[] = "Hello world!"; | 
 |   delegate_->SetReadData(kTestData, sizeof(kTestData)); | 
 |   uploader_->UploadWhenReady(false); | 
 |   EXPECT_FALSE(memcmp(kTestData, buffer->data(), sizeof(kTestData))); | 
 |  | 
 |   // Callback is called again after a successful read. | 
 |   ret = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(ERR_IO_PENDING, ret); | 
 |  | 
 |   // No more data is available, and the upload will be finished. | 
 |   delegate_->SetReadData("", 0); | 
 |   uploader_->UploadWhenReady(true); | 
 |   EXPECT_TRUE(uploader_->IsEOF()); | 
 |  | 
 |   EXPECT_EQ(2, callback_count); | 
 | } | 
 |  | 
 | // Tests that null data is correctly handled when the callback comes first. | 
 | TEST_F(ChunkedDataStreamUploaderTest, NullContentWithReadFirst) { | 
 |   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kDefaultIOBufferSize); | 
 |   int ret = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(ERR_IO_PENDING, ret); | 
 |  | 
 |   delegate_->SetReadData("", 0); | 
 |   uploader_->UploadWhenReady(true); | 
 |   EXPECT_TRUE(uploader_->IsEOF()); | 
 |  | 
 |   EXPECT_EQ(1, callback_count); | 
 | } | 
 |  | 
 | // Tests that null data is correctly handled when data is available first. | 
 | TEST_F(ChunkedDataStreamUploaderTest, NullContentWithDataFirst) { | 
 |   delegate_->SetReadData("", 0); | 
 |   uploader_->UploadWhenReady(true); | 
 |  | 
 |   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kDefaultIOBufferSize); | 
 |   int bytes_read = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(0, bytes_read); | 
 |   EXPECT_TRUE(uploader_->IsEOF()); | 
 |  | 
 |   EXPECT_EQ(0, callback_count); | 
 | } | 
 |  | 
 | // A complex test case that the application layer data and network layer | 
 | // callback becomes ready first reciprocally. | 
 | TEST_F(ChunkedDataStreamUploaderTest, MixedScenarioTest) { | 
 |   // Data comes first. | 
 |   const char kTestData[] = "Hello world!"; | 
 |   delegate_->SetReadData(kTestData, sizeof(kTestData)); | 
 |   uploader_->UploadWhenReady(false); | 
 |  | 
 |   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kDefaultIOBufferSize); | 
 |   int bytes_read = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(sizeof(kTestData), static_cast<size_t>(bytes_read)); | 
 |   EXPECT_FALSE( | 
 |       memcmp(kTestData, buffer->data(), static_cast<size_t>(bytes_read))); | 
 |  | 
 |   // Callback comes first. | 
 |   int ret = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(ERR_IO_PENDING, ret); | 
 |  | 
 |   char test_data_long[kDefaultIOBufferSize]; | 
 |   for (int i = 0; i < static_cast<int>(sizeof(test_data_long)); ++i) { | 
 |     test_data_long[i] = static_cast<char>(i & 0xFF); | 
 |   } | 
 |   delegate_->SetReadData(test_data_long, sizeof(test_data_long)); | 
 |   uploader_->UploadWhenReady(false); | 
 |   EXPECT_FALSE(memcmp(test_data_long, buffer->data(), sizeof(test_data_long))); | 
 |  | 
 |   // Callback comes first again. | 
 |   ret = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(ERR_IO_PENDING, ret); | 
 |   delegate_->SetReadData(kTestData, sizeof(kTestData)); | 
 |   uploader_->UploadWhenReady(false); | 
 |   EXPECT_FALSE(memcmp(kTestData, buffer->data(), sizeof(kTestData))); | 
 |  | 
 |   // Finish and data comes first. | 
 |   delegate_->SetReadData("", 0); | 
 |   uploader_->UploadWhenReady(true); | 
 |   bytes_read = uploader_->Read( | 
 |       buffer.get(), kDefaultIOBufferSize, | 
 |       base::BindRepeating(&ChunkedDataStreamUploaderTest::CompletionCallback, | 
 |                           base::Unretained(this))); | 
 |   EXPECT_EQ(0, bytes_read); | 
 |   EXPECT_TRUE(uploader_->IsEOF()); | 
 |  | 
 |   // Completion callback is called only after Read() returns ERR_IO_PENDING; | 
 |   EXPECT_EQ(2, callback_count); | 
 | } | 
 |  | 
 | }  // namespace net |