/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "UnixSocket.h" #include <gtest/gtest.h> #include <string> #include <thread> TEST(UnixSocket, message_buffer_smoke) { struct Message { uint32_t len; uint32_t type; char data[10]; } send_msg; constexpr size_t send_data_size = 1024; std::vector<char> send_data(send_data_size); std::vector<char> read_data; for (size_t i = 0; i < send_data_size; ++i) { send_data[i] = i & 0xff; } UnixSocketMessageBuffer buffer(100); size_t per_msg_bytes = 0; size_t send_bytes = 0; while (true) { // Send data as much as possible. while (send_bytes < send_data_size) { size_t n = std::min(per_msg_bytes, send_data_size - send_bytes); per_msg_bytes = (per_msg_bytes + 1) % 10; memcpy(send_msg.data, &send_data[send_bytes], n); send_msg.len = sizeof(UnixSocketMessage) + n; send_msg.type = n; if (!buffer.StoreMessage( *reinterpret_cast<UnixSocketMessage*>(&send_msg))) { break; } send_bytes += n; } if (buffer.Empty()) { break; } // Read one message. std::vector<char> read_buf; auto read_func = [&](size_t size) { while (read_buf.size() < size) { const char* p; size_t n = buffer.PeekData(&p); n = std::min(n, size - read_buf.size()); read_buf.insert(read_buf.end(), p, p + n); buffer.CommitData(n); } }; read_func(sizeof(UnixSocketMessage)); Message* msg = reinterpret_cast<Message*>(read_buf.data()); size_t aligned_len = Align(msg->len, UnixSocketMessageAlignment); read_func(aligned_len); msg = reinterpret_cast<Message*>(read_buf.data()); ASSERT_EQ(msg->len, msg->type + sizeof(UnixSocketMessage)); read_data.insert(read_data.end(), msg->data, msg->data + msg->type); } ASSERT_EQ(send_data, read_data); } static void ClientToTestUndelayedMessage(const std::string& path, bool& client_success) { std::unique_ptr<UnixSocketConnection> client = UnixSocketConnection::Connect(path, true); ASSERT_TRUE(client != nullptr); IOEventLoop loop; // For each message received from the server, the client replies a msg // with type + 1. auto receive_message_callback = [&](const UnixSocketMessage& msg) { if (msg.len != sizeof(UnixSocketMessage)) { return false; } UnixSocketMessage reply_msg; reply_msg.len = sizeof(UnixSocketMessage); reply_msg.type = msg.type + 1; return client->SendMessage(reply_msg, true); }; auto close_connection_callback = [&]() { return loop.ExitLoop(); }; ASSERT_TRUE(client->PrepareForIO(loop, receive_message_callback, close_connection_callback)); ASSERT_TRUE(loop.RunLoop()); client_success = true; } TEST(UnixSocket, undelayed_message) { std::string path = "unix_socket_test_" + std::to_string(getpid()); std::unique_ptr<UnixSocketServer> server = UnixSocketServer::Create(path, true); ASSERT_TRUE(server != nullptr); bool client_success = false; std::thread thread( [&]() { ClientToTestUndelayedMessage(path, client_success); }); std::unique_ptr<UnixSocketConnection> conn = server->AcceptConnection(); ASSERT_TRUE(conn != nullptr); IOEventLoop loop; uint32_t need_reply_type = 1; // For each message received from the client, the server replies a msg // with type + 1, and exits when type reaches 10. auto receive_message_callback = [&](const UnixSocketMessage& msg) { if (msg.len != sizeof(UnixSocketMessage) || msg.type != need_reply_type) { return false; } if (need_reply_type >= 10) { return conn->NoMoreMessage(); } UnixSocketMessage new_msg; new_msg.len = sizeof(UnixSocketMessage); new_msg.type = msg.type + 1; need_reply_type = msg.type + 2; return conn->SendMessage(new_msg, true); }; auto close_connection_callback = [&]() { return loop.ExitLoop(); }; ASSERT_TRUE(conn->PrepareForIO(loop, receive_message_callback, close_connection_callback)); UnixSocketMessage msg; msg.len = sizeof(UnixSocketMessage); msg.type = 0; ASSERT_TRUE(conn->SendMessage(msg, true)); ASSERT_TRUE(loop.RunLoop()); thread.join(); ASSERT_TRUE(client_success); } static void ClientToTestBufferedMessage(const std::string& path, bool& client_success) { std::unique_ptr<UnixSocketConnection> client = UnixSocketConnection::Connect(path, true); ASSERT_TRUE(client != nullptr); IOEventLoop loop; // The client exits once receiving a message from the server. auto receive_message_callback = [&](const UnixSocketMessage& msg) { if (msg.len != sizeof(UnixSocketMessage) || msg.type != 0) { return false; } return client->NoMoreMessage(); }; auto close_connection_callback = [&]() { return loop.ExitLoop(); }; ASSERT_TRUE(client->PrepareForIO(loop, receive_message_callback, close_connection_callback)); // The client sends buffered messages until the send buffer is full. UnixSocketMessage msg; msg.len = sizeof(UnixSocketMessage); msg.type = 0; while (true) { msg.type++; if (!client->SendMessage(msg, false)) { break; } } ASSERT_TRUE(loop.RunLoop()); client_success = true; } TEST(UnixSocket, buffered_message) { std::string path = "unix_socket_test_" + std::to_string(getpid()); std::unique_ptr<UnixSocketServer> server = UnixSocketServer::Create(path, true); ASSERT_TRUE(server != nullptr); bool client_success = false; std::thread thread( [&]() { ClientToTestBufferedMessage(path, client_success); }); std::unique_ptr<UnixSocketConnection> conn = server->AcceptConnection(); ASSERT_TRUE(conn != nullptr); IOEventLoop loop; uint32_t need_reply_type = 1; auto receive_message_callback = [&](const UnixSocketMessage& msg) { // The server checks if the type of received message is increased by one // each time. if (msg.len != sizeof(UnixSocketMessage) || msg.type != need_reply_type) { return false; } if (need_reply_type == 1) { // Notify the client to exit. UnixSocketMessage new_msg; new_msg.len = sizeof(UnixSocketMessage); new_msg.type = 0; if (!conn->SendMessage(new_msg, true)) { return false; } } need_reply_type++; return true; }; auto close_connection_callback = [&]() { return loop.ExitLoop(); }; ASSERT_TRUE(conn->PrepareForIO(loop, receive_message_callback, close_connection_callback)); ASSERT_TRUE(loop.RunLoop()); thread.join(); ASSERT_TRUE(client_success); }