| #include "data.hpp" |
| #include "internal_sys_mock.hpp" |
| #include "net.hpp" |
| #include "progress_mock.hpp" |
| |
| #include <cstring> |
| #include <ipmiblob/test/blob_interface_mock.hpp> |
| |
| #include <gtest/gtest.h> |
| |
| namespace host_tool |
| { |
| namespace |
| { |
| |
| using namespace std::literals; |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::ContainerEq; |
| using ::testing::Field; |
| using ::testing::Gt; |
| using ::testing::InSequence; |
| using ::testing::NotNull; |
| using ::testing::Pointee; |
| using ::testing::Return; |
| using ::testing::SetArgPointee; |
| using ::testing::SetErrnoAndReturn; |
| using ::testing::StrEq; |
| |
| class NetHandleTest : public ::testing::Test |
| { |
| protected: |
| NetHandleTest() : handler(&blobMock, &progMock, host, port, &sysMock) |
| { |
| sa.sin6_family = AF_INET6; |
| sa.sin6_port = htons(622); |
| sa.sin6_flowinfo = 0; |
| sa.sin6_addr = in6addr_loopback; // ::1 |
| sa.sin6_scope_id = 0; |
| |
| addr.ai_family = AF_INET6; |
| addr.ai_socktype = SOCK_STREAM; |
| addr.ai_addr = reinterpret_cast<struct sockaddr*>(&sa); |
| addr.ai_addrlen = sizeof(sa); |
| addr.ai_protocol = 0; |
| addr.ai_next = nullptr; |
| } |
| |
| void expectOpenFile() |
| { |
| EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) |
| .WillOnce(Return(inFd)); |
| EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0)); |
| EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))) |
| .WillOnce(Return(fakeFileSize)); |
| |
| EXPECT_CALL(progMock, start(fakeFileSize)); |
| } |
| |
| void expectAddrInfo() |
| { |
| EXPECT_CALL( |
| sysMock, |
| getaddrinfo(StrEq(host), StrEq(port), |
| AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), |
| Field(&addrinfo::ai_family, AF_UNSPEC), |
| Field(&addrinfo::ai_socktype, SOCK_STREAM)), |
| NotNull())) |
| .WillOnce(DoAll(SetArgPointee<3>(&addr), Return(0))); |
| EXPECT_CALL(sysMock, freeaddrinfo(&addr)); |
| } |
| |
| void expectConnection() |
| { |
| EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) |
| .WillOnce(Return(connFd)); |
| EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); |
| EXPECT_CALL(sysMock, |
| connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), |
| sizeof(sa))) |
| .WillOnce(Return(0)); |
| } |
| |
| internal::InternalSysMock sysMock; |
| ipmiblob::BlobInterfaceMock blobMock; |
| ProgressMock progMock; |
| |
| const std::string host = "::1"s; |
| const std::string port = "622"s; |
| |
| struct sockaddr_in6 sa; |
| struct addrinfo addr; |
| |
| static constexpr std::uint16_t session = 0xbeef; |
| const std::string filePath = "/asdf"s; |
| static constexpr int inFd = 5; |
| static constexpr int connFd = 7; |
| static constexpr size_t fakeFileSize = 128; |
| static constexpr size_t chunkSize = 16; |
| |
| NetDataHandler handler; |
| }; |
| |
| TEST_F(NetHandleTest, openFileFail) |
| { |
| EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) |
| .WillOnce(SetErrnoAndReturn(EACCES, -1)); |
| |
| EXPECT_FALSE(handler.sendContents(filePath, session)); |
| } |
| |
| TEST_F(NetHandleTest, getSizeFail) |
| { |
| EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) |
| .WillOnce(Return(inFd)); |
| EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0)); |
| EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))).WillOnce(Return(0)); |
| |
| EXPECT_FALSE(handler.sendContents(filePath, session)); |
| } |
| |
| TEST_F(NetHandleTest, getaddrinfoFail) |
| { |
| expectOpenFile(); |
| |
| EXPECT_CALL(sysMock, |
| getaddrinfo(StrEq(host), StrEq(port), |
| AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), |
| Field(&addrinfo::ai_family, AF_UNSPEC), |
| Field(&addrinfo::ai_socktype, SOCK_STREAM)), |
| NotNull())) |
| .WillOnce(Return(EAI_ADDRFAMILY)); |
| |
| EXPECT_FALSE(handler.sendContents(filePath, session)); |
| } |
| |
| TEST_F(NetHandleTest, connectFail) |
| { |
| expectOpenFile(); |
| expectAddrInfo(); |
| |
| EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) |
| .WillOnce(Return(connFd)); |
| EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); |
| EXPECT_CALL( |
| sysMock, |
| connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa))) |
| .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1)); |
| |
| EXPECT_FALSE(handler.sendContents(filePath, session)); |
| } |
| |
| TEST_F(NetHandleTest, sendfileFail) |
| { |
| expectOpenFile(); |
| expectAddrInfo(); |
| expectConnection(); |
| |
| EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _)) |
| .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1)); |
| |
| EXPECT_FALSE(handler.sendContents(filePath, session)); |
| } |
| |
| TEST_F(NetHandleTest, successOneChunk) |
| { |
| expectOpenFile(); |
| expectAddrInfo(); |
| expectConnection(); |
| |
| { |
| InSequence seq; |
| |
| EXPECT_CALL(sysMock, |
| sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize))) |
| .WillOnce( |
| DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize))); |
| EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), |
| Gt(fakeFileSize))) |
| .WillOnce(Return(0)); |
| } |
| |
| struct ipmi_flash::ExtChunkHdr chunk; |
| chunk.length = fakeFileSize; |
| std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); |
| std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); |
| EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes))); |
| |
| EXPECT_CALL(progMock, updateProgress(fakeFileSize)); |
| |
| EXPECT_TRUE(handler.sendContents(filePath, session)); |
| } |
| |
| TEST_F(NetHandleTest, successMultiChunk) |
| { |
| expectOpenFile(); |
| expectAddrInfo(); |
| expectConnection(); |
| |
| struct ipmi_flash::ExtChunkHdr chunk; |
| chunk.length = chunkSize; |
| std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); |
| std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); |
| |
| { |
| InSequence seq; |
| |
| for (std::uint32_t offset = 0; offset < fakeFileSize; |
| offset += chunkSize) |
| { |
| EXPECT_CALL(sysMock, |
| sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize))) |
| .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize), |
| Return(chunkSize))); |
| |
| EXPECT_CALL(blobMock, |
| writeBytes(session, offset, ContainerEq(chunkBytes))); |
| EXPECT_CALL(progMock, updateProgress(chunkSize)); |
| } |
| EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), |
| Gt(chunkSize))) |
| .WillOnce(Return(0)); |
| } |
| |
| EXPECT_TRUE(handler.sendContents(filePath, session)); |
| } |
| |
| } // namespace |
| } // namespace host_tool |