// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/protocol/buffered_socket_writer.h" #include <stdlib.h> #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "remoting/protocol/fake_session.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace remoting { namespace protocol { namespace { const int kTestBufferSize = 10 * 1024; // 10k; const size_t kWriteChunkSize = 1024U; } // namespace class BufferedSocketWriterTest : public testing::Test { public: BufferedSocketWriterTest() : write_error_(0) { } void OnDone() { base::MessageLoop::current()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); } void DestroyWriterAndQuit() { written_data_ = socket_->written_data(); writer_.reset(); socket_.reset(); base::MessageLoop::current()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); } void Unexpected() { EXPECT_TRUE(false); } protected: virtual void SetUp() OVERRIDE { socket_.reset(new FakeSocket()); writer_.reset(new BufferedSocketWriter()); writer_->Init(socket_.get(), base::Bind( &BufferedSocketWriterTest::OnWriteFailed, base::Unretained(this))); test_buffer_ = new net::IOBufferWithSize(kTestBufferSize); test_buffer_2_ = new net::IOBufferWithSize(kTestBufferSize); for (int i = 0; i< kTestBufferSize; ++i) { test_buffer_->data()[i] = rand() % 256; test_buffer_2_->data()[i] = rand() % 256; } } void OnWriteFailed(int error) { write_error_ = error; } void TestWrite() { writer_->Write(test_buffer_, base::Bind(&BufferedSocketWriterTest::OnDone, base::Unretained(this))); writer_->Write(test_buffer_2_, base::Bind(&BufferedSocketWriterTest::OnDone, base::Unretained(this))); message_loop_.Run(); ASSERT_EQ(static_cast<size_t>(test_buffer_->size() + test_buffer_2_->size()), socket_->written_data().size()); EXPECT_EQ(0, memcmp(test_buffer_->data(), socket_->written_data().data(), test_buffer_->size())); EXPECT_EQ(0, memcmp(test_buffer_2_->data(), socket_->written_data().data() + test_buffer_->size(), test_buffer_2_->size())); } void TestAppendInCallback() { writer_->Write(test_buffer_, base::Bind( base::IgnoreResult(&BufferedSocketWriterBase::Write), base::Unretained(writer_.get()), test_buffer_2_, base::Bind(&BufferedSocketWriterTest::OnDone, base::Unretained(this)))); message_loop_.Run(); ASSERT_EQ(static_cast<size_t>(test_buffer_->size() + test_buffer_2_->size()), socket_->written_data().size()); EXPECT_EQ(0, memcmp(test_buffer_->data(), socket_->written_data().data(), test_buffer_->size())); EXPECT_EQ(0, memcmp(test_buffer_2_->data(), socket_->written_data().data() + test_buffer_->size(), test_buffer_2_->size())); } base::MessageLoop message_loop_; scoped_ptr<FakeSocket> socket_; scoped_ptr<BufferedSocketWriter> writer_; scoped_refptr<net::IOBufferWithSize> test_buffer_; scoped_refptr<net::IOBufferWithSize> test_buffer_2_; std::string written_data_; int write_error_; }; // Test synchronous write. TEST_F(BufferedSocketWriterTest, WriteFull) { TestWrite(); } // Test synchronous write in 1k chunks. TEST_F(BufferedSocketWriterTest, WriteChunks) { socket_->set_write_limit(kWriteChunkSize); TestWrite(); } // Test asynchronous write. TEST_F(BufferedSocketWriterTest, WriteAsync) { socket_->set_async_write(true); socket_->set_write_limit(kWriteChunkSize); TestWrite(); } // Make sure we can call Write() from the done callback. TEST_F(BufferedSocketWriterTest, AppendInCallbackSync) { TestAppendInCallback(); } // Make sure we can call Write() from the done callback. TEST_F(BufferedSocketWriterTest, AppendInCallbackAsync) { socket_->set_async_write(true); socket_->set_write_limit(kWriteChunkSize); TestAppendInCallback(); } // Test that the writer can be destroyed from callback. TEST_F(BufferedSocketWriterTest, DestroyFromCallback) { socket_->set_async_write(true); writer_->Write(test_buffer_, base::Bind( &BufferedSocketWriterTest::DestroyWriterAndQuit, base::Unretained(this))); writer_->Write(test_buffer_2_, base::Bind( &BufferedSocketWriterTest::Unexpected, base::Unretained(this))); socket_->set_async_write(false); message_loop_.Run(); ASSERT_GE(written_data_.size(), static_cast<size_t>(test_buffer_->size())); EXPECT_EQ(0, memcmp(test_buffer_->data(), written_data_.data(), test_buffer_->size())); } // Verify that it stops writing after the first error. TEST_F(BufferedSocketWriterTest, TestWriteErrorSync) { socket_->set_write_limit(kWriteChunkSize); writer_->Write(test_buffer_, base::Closure()); socket_->set_async_write(true); writer_->Write(test_buffer_2_, base::Bind(&BufferedSocketWriterTest::Unexpected, base::Unretained(this))); socket_->set_next_write_error(net::ERR_FAILED); socket_->set_async_write(false); base::RunLoop().RunUntilIdle(); EXPECT_EQ(net::ERR_FAILED, write_error_); EXPECT_EQ(static_cast<size_t>(test_buffer_->size()), socket_->written_data().size()); } // Verify that it stops writing after the first error. TEST_F(BufferedSocketWriterTest, TestWriteErrorAsync) { socket_->set_write_limit(kWriteChunkSize); writer_->Write(test_buffer_, base::Closure()); socket_->set_async_write(true); writer_->Write(test_buffer_2_, base::Bind(&BufferedSocketWriterTest::Unexpected, base::Unretained(this))); socket_->set_next_write_error(net::ERR_FAILED); base::RunLoop().RunUntilIdle(); EXPECT_EQ(net::ERR_FAILED, write_error_); EXPECT_EQ(static_cast<size_t>(test_buffer_->size()), socket_->written_data().size()); } } // namespace protocol } // namespace remoting