| /* |
| * Copyright 2020 The Chromium OS 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 "arc/adbd/arcvm_sock_to_usb.h" |
| |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/optional.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_file.h> |
| #include <gtest/gtest.h> |
| |
| namespace adbd { |
| namespace { |
| |
| std::unique_ptr<ArcVmSockToUsb> SetupChannel(const int sock_fd, |
| const int output_fd) { |
| std::unique_ptr<ArcVmSockToUsb> channel = |
| std::make_unique<ArcVmSockToUsb>(sock_fd, output_fd); |
| if (!channel->Start()) { |
| LOG(ERROR) << "Failed to start channel for test"; |
| return nullptr; |
| } |
| return channel; |
| } |
| |
| base::Optional<std::pair<base::ScopedFD, base::ScopedFD>> SetupSocketPair() { |
| int fds[2]; |
| if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds) == -1) { |
| PLOG(ERROR) << "Failed to create socket pair for test"; |
| return base::nullopt; |
| } |
| return base::make_optional( |
| std::make_pair(base::ScopedFD(fds[0]), base::ScopedFD(fds[1]))); |
| } |
| |
| bool SetPayloadLength(const size_t payload_len, std::vector<char>* header) { |
| if (header == nullptr) |
| return false; |
| auto sz = header->size(); |
| if (sz < kAmessageDataLenOffset + 4) |
| return false; |
| for (int i = 0; i < 4; i++) { |
| // Some parentheses in the next line are added for readability. |
| (*header)[kAmessageDataLenOffset + i] = (payload_len >> (8 * i)) & 0xff; |
| } |
| return true; |
| } |
| |
| // Writes the data in a vector to a fd and read the data back to check. |
| // Returns true if the content is identical between the write and read. |
| bool LoopCheck(int fd, const std::vector<char>& src) { |
| auto sz = src.size(); |
| if (fd < 0 || !sz) |
| return false; |
| if (!base::WriteFileDescriptor(fd, src.data(), sz)) |
| return false; |
| std::vector<char> output(sz); |
| if (!base::ReadFromFD(fd, output.data(), sz)) |
| return false; |
| return src == output; |
| } |
| |
| // Channel should send out message without waiting more data |
| // when the message has no payload (payload fields are zeros) |
| void TestHeaderOnly() { |
| auto sock_pair = SetupSocketPair(); |
| ASSERT_TRUE(sock_pair.has_value()); |
| auto channel_fd = sock_pair->second.get(); |
| |
| // A socket in the pair is bidirectional so we use the same socket |
| // for both of input and output of the channel. |
| auto channel = SetupChannel(channel_fd, channel_fd); |
| ASSERT_NE(channel, nullptr); |
| std::vector<char> header(kAmessageSize, 0x5); |
| ASSERT_TRUE(SetPayloadLength(0, &header)); |
| EXPECT_TRUE(LoopCheck(sock_pair->first.get(), header)); |
| exit(EXIT_SUCCESS); |
| } |
| |
| // TODO(crbug.com/1087440): Replace deathtest with conventional unit test |
| // APIs once we refactor the main code to test transfer only, without |
| // creating threads. |
| |
| // We use death test in gtest to run test cases in another process. |
| // By doing so exiting channel won't terminate the gtest process |
| // prematurely when we still have other cases to run with it. |
| TEST(ArcVmSockToUsbDeathTest, TestHeaderOnly) { |
| EXPECT_EXIT(TestHeaderOnly(), ::testing::ExitedWithCode(EXIT_SUCCESS), ""); |
| } |
| |
| // Tests a message with maximum payload. |
| void TestMaxPayload() { |
| auto sock_pair = SetupSocketPair(); |
| ASSERT_TRUE(sock_pair.has_value()); |
| auto channel_fd = sock_pair->second.get(); |
| |
| // A socket in the pair is bidirectional so we use the same socket |
| // for both of input and output of the channel. |
| auto channel = SetupChannel(channel_fd, channel_fd); |
| ASSERT_NE(channel, nullptr); |
| size_t message_len = kAmessageSize + kAdbPayloadMaxSize; |
| std::vector<char> message(message_len, 0x5); |
| ASSERT_TRUE(SetPayloadLength(kAdbPayloadMaxSize, &message)); |
| EXPECT_TRUE(LoopCheck(sock_pair->first.get(), message)); |
| exit(EXIT_SUCCESS); |
| } |
| |
| TEST(ArcVmSockToUsbDeathTest, TestMaxPayload) { |
| EXPECT_EXIT(TestMaxPayload(), ::testing::ExitedWithCode(EXIT_SUCCESS), ""); |
| } |
| |
| // Tests an invalid payload length. Channel should exit with error. |
| void TestInvalidPayloadLength() { |
| auto sock_pair = SetupSocketPair(); |
| ASSERT_TRUE(sock_pair.has_value()); |
| auto channel_fd = sock_pair->second.get(); |
| |
| // A socket in the pair is bidirectional so we use the same socket |
| // for both of input and output of the channel. |
| auto channel = SetupChannel(channel_fd, channel_fd); |
| ASSERT_NE(channel, nullptr); |
| size_t message_len = kAmessageSize; |
| std::vector<char> message(message_len, 0x5); |
| ASSERT_TRUE(SetPayloadLength(kAdbPayloadMaxSize + 1, &message)); |
| LoopCheck(sock_pair->first.get(), message); |
| ASSERT_TRUE(false) << "Should not reach here."; |
| } |
| |
| TEST(ArcVmSockToUsbDeathTest, TestInvalidPayloadLength) { |
| EXPECT_EXIT(TestInvalidPayloadLength(), |
| ::testing::ExitedWithCode(EXIT_FAILURE), |
| "payload length is too big"); |
| } |
| |
| } // namespace |
| } // namespace adbd |