Chromium Code Reviews| Index: net/socket/unix_domain_client_socket_posix_unittest.cc |
| diff --git a/net/socket/unix_domain_client_socket_posix_unittest.cc b/net/socket/unix_domain_client_socket_posix_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..432c6dbc018e1ab326185303825660062250697e |
| --- /dev/null |
| +++ b/net/socket/unix_domain_client_socket_posix_unittest.cc |
| @@ -0,0 +1,397 @@ |
| +// Copyright 2014 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 "net/socket/unix_domain_client_socket_posix.h" |
| + |
| +#include <unistd.h> |
| + |
| +#include "base/bind.h" |
| +#include "base/files/file_path.h" |
| +#include "base/files/scoped_temp_dir.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "net/base/io_buffer.h" |
| +#include "net/base/net_errors.h" |
| +#include "net/base/test_completion_callback.h" |
| +#include "net/socket/unix_domain_server_socket_posix.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace net { |
| + |
| +namespace { |
| + |
| +const char kSocketFilename[] = "unix_domain_socket_for_testing"; |
| + |
| +bool UserCanConnectCallback(bool allow_user, uid_t uid, gid_t gid) { |
| + // Here peers are running in same process. Check if they are expected. |
| + EXPECT_EQ(getuid(), uid); |
| + EXPECT_EQ(getgid(), gid); |
| + return allow_user; |
| +} |
| + |
| +UnixDomainServerSocket::AuthCallback CreateAuthCallback(bool allow_user) { |
| + return base::Bind(&UserCanConnectCallback, allow_user); |
| +} |
| + |
| +// Connects socket synchronously. |
| +int ConnectSynchronously(StreamSocket* socket) { |
| + TestCompletionCallback connect_callback; |
| + int rv = socket->Connect(connect_callback.callback()); |
| + if (rv == ERR_IO_PENDING) |
| + rv = connect_callback.WaitForResult(); |
| + return rv; |
| +} |
| + |
| +// Reads data from |socket| until it fills |buf| at least up to |min_data_len|. |
| +// Returns length of data read, or a net error. |
| +int ReadSynchronously(StreamSocket* socket, |
| + IOBuffer* buf, |
| + int buf_len, |
| + int min_data_len) { |
| + scoped_refptr<DrainableIOBuffer> read_buf( |
| + new DrainableIOBuffer(buf, buf_len)); |
| + TestCompletionCallback read_callback; |
| + // Iterate reading several times (but not infinite) until it reads buf |
| + // at least up to |min_data_len|. |
| + for (int retry_count = 10; |
| + retry_count > 0 && (read_buf->BytesConsumed() < min_data_len || |
| + // Try at least once when min_data_len == 0. |
| + min_data_len == 0); |
| + --retry_count) { |
| + int rv = socket->Read(read_buf, read_buf->BytesRemaining(), |
| + read_callback.callback()); |
| + EXPECT_GE(read_buf->BytesRemaining(), rv); |
| + if (rv == ERR_IO_PENDING) { |
| + // If |min_data_len| is 0, returns ERR_IO_PENDING to distinguish the case |
| + // when some data has been read. |
| + if (min_data_len == 0) { |
| + // No data has been read because of for-loop condition. |
| + DCHECK_EQ(0, read_buf->BytesConsumed()); |
| + return ERR_IO_PENDING; |
| + } |
| + rv = read_callback.WaitForResult(); |
| + } |
| + EXPECT_NE(ERR_IO_PENDING, rv); |
| + if (rv < 0) |
| + return rv; |
| + read_buf->DidConsume(rv); |
| + } |
| + EXPECT_LE(0, read_buf->BytesRemaining()); |
| + return read_buf->BytesConsumed(); |
| +} |
| + |
| +// Writes data to |socket| until it completes writing |buf| up to |buf_len|. |
| +// Returns length of data written, or a net error. |
| +int WriteSynchronously(StreamSocket* socket, |
| + IOBuffer* buf, |
| + int buf_len) { |
| + scoped_refptr<DrainableIOBuffer> write_buf( |
| + new DrainableIOBuffer(buf, buf_len)); |
| + TestCompletionCallback write_callback; |
| + // Iterate writing several times (but not infinite) until it writes buf fully. |
| + for (int retry_count = 10; |
| + retry_count > 0 && write_buf->BytesRemaining() > 0; |
| + --retry_count) { |
| + int rv = socket->Write(write_buf, write_buf->BytesRemaining(), |
| + write_callback.callback()); |
| + EXPECT_GE(write_buf->BytesRemaining(), rv); |
| + if (rv == ERR_IO_PENDING) |
| + rv = write_callback.WaitForResult(); |
| + EXPECT_NE(ERR_IO_PENDING, rv); |
| + if (rv < 0) |
| + return rv; |
| + write_buf->DidConsume(rv); |
| + } |
| + EXPECT_LE(0, write_buf->BytesRemaining()); |
| + return write_buf->BytesConsumed(); |
| +} |
| + |
| +} // namespace |
| + |
| +class UnixDomainClientSocketTest : public testing::Test { |
| + protected: |
| + UnixDomainClientSocketTest() { |
| + EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| + socket_path_ = temp_dir_.path().Append(kSocketFilename).value(); |
| + } |
| + |
| + base::ScopedTempDir temp_dir_; |
| + std::string socket_path_; |
| +}; |
| + |
| +TEST_F(UnixDomainClientSocketTest, Connect) { |
| + const bool kUseAbstractNamespace = false; |
| + |
| + UnixDomainServerSocket server_socket(CreateAuthCallback(true), |
| + kUseAbstractNamespace); |
| + EXPECT_EQ(OK, server_socket.ListenWithAddressAndPort(socket_path_, 0, 1)); |
| + |
| + scoped_ptr<StreamSocket> accepted_socket; |
| + TestCompletionCallback accept_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + server_socket.Accept(&accepted_socket, accept_callback.callback())); |
| + EXPECT_FALSE(accepted_socket); |
| + |
| + UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| + |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
| + EXPECT_TRUE(client_socket.IsConnected()); |
| + // Server has not yet been notified of the connection. |
| + EXPECT_FALSE(accepted_socket); |
| + |
| + EXPECT_EQ(OK, accept_callback.WaitForResult()); |
| + EXPECT_TRUE(accepted_socket); |
| + EXPECT_TRUE(accepted_socket->IsConnected()); |
| +} |
| + |
| +TEST_F(UnixDomainClientSocketTest, ConnectWithAbstractNamespace) { |
| + const bool kUseAbstractNamespace = true; |
| + |
| + UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| + |
| +#if defined(OS_ANDROID) || defined(OS_LINUX) |
| + UnixDomainServerSocket server_socket(CreateAuthCallback(true), |
| + kUseAbstractNamespace); |
| + EXPECT_EQ(OK, server_socket.ListenWithAddressAndPort(socket_path_, 0, 1)); |
| + |
| + scoped_ptr<StreamSocket> accepted_socket; |
| + TestCompletionCallback accept_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + server_socket.Accept(&accepted_socket, accept_callback.callback())); |
| + EXPECT_FALSE(accepted_socket); |
| + |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
| + EXPECT_TRUE(client_socket.IsConnected()); |
| + // Server has not yet beend notified of the connection. |
| + EXPECT_FALSE(accepted_socket); |
| + |
| + EXPECT_EQ(OK, accept_callback.WaitForResult()); |
| + EXPECT_TRUE(accepted_socket); |
| + EXPECT_TRUE(accepted_socket->IsConnected()); |
| +#else |
| + EXPECT_EQ(ERR_ADDRESS_INVALID, ConnectSynchronously(&client_socket)); |
| +#endif |
| +} |
| + |
| +TEST_F(UnixDomainClientSocketTest, ConnectToNonExistentSocket) { |
| + const bool kUseAbstractNamespace = false; |
| + |
| + UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| + EXPECT_EQ(ERR_FILE_NOT_FOUND, ConnectSynchronously(&client_socket)); |
| +} |
| + |
| +TEST_F(UnixDomainClientSocketTest, |
| + ConnectToNonExistentSocketWithAbstractNamespace) { |
| + const bool kUseAbstractNamespace = true; |
| + |
| + UnixDomainClientSocket client_socket(socket_path_, kUseAbstractNamespace); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| + |
| + TestCompletionCallback connect_callback; |
| +#if defined(OS_ANDROID) || defined(OS_LINUX) |
| + EXPECT_EQ(ERR_CONNECTION_REFUSED, |
| + client_socket.Connect(connect_callback.callback())); |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
|
mmenke
2014/07/16 18:30:35
This is a bug, right?
byungchul
2014/07/16 22:02:30
Oops, sorry. Done.
|
| +#else |
| + EXPECT_EQ(ERR_ADDRESS_INVALID, |
| + client_socket.Connect(connect_callback.callback())); |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
| +#endif |
| +} |
| + |
| +TEST_F(UnixDomainClientSocketTest, DisconnectFromClient) { |
| + UnixDomainServerSocket server_socket(CreateAuthCallback(true), false); |
| + EXPECT_EQ(OK, server_socket.ListenWithAddressAndPort(socket_path_, 0, 1)); |
| + scoped_ptr<StreamSocket> accepted_socket; |
| + TestCompletionCallback accept_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + server_socket.Accept(&accepted_socket, accept_callback.callback())); |
| + UnixDomainClientSocket client_socket(socket_path_, false); |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
| + |
| + EXPECT_EQ(OK, accept_callback.WaitForResult()); |
| + EXPECT_TRUE(accepted_socket->IsConnected()); |
| + EXPECT_TRUE(client_socket.IsConnected()); |
| + |
| + // Wait for read event. |
|
mmenke
2014/07/16 18:30:35
nit: This isn't accurate - we're not waiting, we'
byungchul
2014/07/16 22:02:30
Done.
|
| + const int kWriteDataSize = 10; |
|
mmenke
2014/07/16 18:30:35
nit: kReadDataSize?
byungchul
2014/07/16 22:02:30
Done.
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kWriteDataSize)); |
| + TestCompletionCallback read_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + accepted_socket->Read(read_buffer, kWriteDataSize, |
| + read_callback.callback())); |
| + |
| + // Disconnect from client side. |
| + client_socket.Disconnect(); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| + EXPECT_FALSE(accepted_socket->IsConnected()); |
| + |
| + // Connection closed by peer. |
| + EXPECT_EQ(0 /* EOF */, read_callback.WaitForResult()); |
| + // Note that read callback won't be called when the connection is closed |
| + // by local. SocketLibevent just clears callbacks. |
|
mmenke
2014/07/16 18:30:35
"by local" -> "locally" or maybe "locally, before
byungchul
2014/07/16 22:02:30
Done.
|
| +} |
| + |
| +TEST_F(UnixDomainClientSocketTest, DisconnectFromServer) { |
| + UnixDomainServerSocket server_socket(CreateAuthCallback(true), false); |
| + EXPECT_EQ(OK, server_socket.ListenWithAddressAndPort(socket_path_, 0, 1)); |
| + scoped_ptr<StreamSocket> accepted_socket; |
| + TestCompletionCallback accept_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + server_socket.Accept(&accepted_socket, accept_callback.callback())); |
| + UnixDomainClientSocket client_socket(socket_path_, false); |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
| + |
| + EXPECT_EQ(OK, accept_callback.WaitForResult()); |
| + EXPECT_TRUE(accepted_socket->IsConnected()); |
| + EXPECT_TRUE(client_socket.IsConnected()); |
| + |
| + // Wait for read event. |
| + const int kWriteDataSize = 10; |
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kWriteDataSize)); |
| + TestCompletionCallback read_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + client_socket.Read(read_buffer, kWriteDataSize, |
| + read_callback.callback())); |
| + |
| + // Disconnect from server side. |
| + accepted_socket->Disconnect(); |
| + EXPECT_FALSE(accepted_socket->IsConnected()); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| + |
| + // Connection closed by peer. |
| + EXPECT_EQ(0 /* EOF */, read_callback.WaitForResult()); |
| + // Note that read callback won't be called when the connection is closed |
| + // by local. SocketLibevent just clears callbacks. |
| +} |
| + |
| +TEST_F(UnixDomainClientSocketTest, ReadAfterWrite) { |
| + UnixDomainServerSocket server_socket(CreateAuthCallback(true), false); |
| + EXPECT_EQ(OK, server_socket.ListenWithAddressAndPort(socket_path_, 0, 1)); |
| + scoped_ptr<StreamSocket> accepted_socket; |
| + TestCompletionCallback accept_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + server_socket.Accept(&accepted_socket, accept_callback.callback())); |
| + UnixDomainClientSocket client_socket(socket_path_, false); |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
| + |
| + EXPECT_EQ(OK, accept_callback.WaitForResult()); |
| + EXPECT_TRUE(accepted_socket->IsConnected()); |
| + EXPECT_TRUE(client_socket.IsConnected()); |
| + |
| + // Send data from client to server. |
| + const int kWriteDataSize = 10; |
| + scoped_refptr<IOBuffer> write_buffer( |
| + new StringIOBuffer(std::string(kWriteDataSize, 'd'))); |
| + EXPECT_EQ(kWriteDataSize, |
| + WriteSynchronously(&client_socket, |
| + write_buffer, |
| + kWriteDataSize)); |
| + |
| + // The buffer is bigger than write data size. |
| + const int kReadBufferSize = kWriteDataSize * 2; |
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); |
| + EXPECT_EQ(kWriteDataSize, |
| + ReadSynchronously(accepted_socket.get(), |
| + read_buffer, |
| + kReadBufferSize, |
| + kWriteDataSize)); |
| + EXPECT_EQ(std::string(write_buffer->data(), kWriteDataSize), |
| + std::string(read_buffer->data(), kWriteDataSize)); |
| + |
| + // Send data from server and client. |
| + EXPECT_EQ(kWriteDataSize, |
| + WriteSynchronously(accepted_socket.get(), |
| + write_buffer, |
| + kWriteDataSize)); |
| + |
| + // Read multiple times. |
| + const int kSmallReadBufferSize = kWriteDataSize / 3; |
| + EXPECT_EQ(kSmallReadBufferSize, |
| + ReadSynchronously(&client_socket, |
| + read_buffer, |
| + kSmallReadBufferSize, |
| + kSmallReadBufferSize)); |
| + EXPECT_EQ(std::string(write_buffer->data(), kSmallReadBufferSize), |
| + std::string(read_buffer->data(), kSmallReadBufferSize)); |
| + |
| + EXPECT_EQ(kWriteDataSize - kSmallReadBufferSize, |
| + ReadSynchronously(&client_socket, |
| + read_buffer, |
| + kReadBufferSize, |
| + kWriteDataSize - kSmallReadBufferSize)); |
| + EXPECT_EQ(std::string(write_buffer->data() + kSmallReadBufferSize, |
| + kWriteDataSize - kSmallReadBufferSize), |
| + std::string(read_buffer->data(), |
| + kWriteDataSize - kSmallReadBufferSize)); |
| + |
| + // No more data. |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + ReadSynchronously(&client_socket, |
| + read_buffer, |
| + kReadBufferSize, |
| + 0)); |
| + |
| + // Disconnect from server side after read-write. |
| + accepted_socket->Disconnect(); |
| + EXPECT_FALSE(accepted_socket->IsConnected()); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| +} |
| + |
| +TEST_F(UnixDomainClientSocketTest, ReadBeforeWrite) { |
| + UnixDomainServerSocket server_socket(CreateAuthCallback(true), false); |
| + EXPECT_EQ(OK, server_socket.ListenWithAddressAndPort(socket_path_, 0, 1)); |
| + scoped_ptr<StreamSocket> accepted_socket; |
| + TestCompletionCallback accept_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + server_socket.Accept(&accepted_socket, accept_callback.callback())); |
| + UnixDomainClientSocket client_socket(socket_path_, false); |
| + EXPECT_EQ(OK, ConnectSynchronously(&client_socket)); |
| + |
| + EXPECT_EQ(OK, accept_callback.WaitForResult()); |
| + EXPECT_TRUE(accepted_socket->IsConnected()); |
| + EXPECT_TRUE(client_socket.IsConnected()); |
| + |
| + // Wait for data from client. |
| + const int kWriteDataSize = 10; |
| + const int kReadBufferSize = kWriteDataSize * 2; |
| + const int kSmallReadBufferSize = kWriteDataSize / 3; |
| + // Read smaller than write data size first. |
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize)); |
| + TestCompletionCallback read_callback; |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + accepted_socket->Read(read_buffer, kSmallReadBufferSize, |
| + read_callback.callback())); |
| + |
| + scoped_refptr<IOBuffer> write_buffer( |
| + new StringIOBuffer(std::string(kWriteDataSize, 'd'))); |
| + EXPECT_EQ(kWriteDataSize, |
| + WriteSynchronously(&client_socket, |
| + write_buffer, |
| + kWriteDataSize)); |
| + |
| + // First read completed. |
| + EXPECT_EQ(kSmallReadBufferSize, read_callback.WaitForResult()); |
| + // Read remaining data. |
| + EXPECT_EQ(kWriteDataSize - kSmallReadBufferSize, |
| + ReadSynchronously(accepted_socket.get(), |
| + read_buffer, |
| + kReadBufferSize, |
| + kWriteDataSize - kSmallReadBufferSize)); |
| + // No more data. |
| + EXPECT_EQ(ERR_IO_PENDING, |
| + ReadSynchronously(accepted_socket.get(), |
| + read_buffer, |
| + kReadBufferSize, |
| + 0)); |
| + |
| + // Disconnect from server side after read-write. |
| + accepted_socket->Disconnect(); |
| + EXPECT_FALSE(accepted_socket->IsConnected()); |
| + EXPECT_FALSE(client_socket.IsConnected()); |
| +} |
| + |
| +} // namespace net |