#include "data.hpp" #include "internal_sys_mock.hpp" #include "net.hpp" #include "progress_mock.hpp" #include #include #include namespace host_tool { namespace { using namespace std::literals; using ::testing::_; using ::testing::AllOf; using ::testing::ContainerEq; using ::testing::DoAll; using ::testing::Field; using ::testing::Ge; 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(&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)); } 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(&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, 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(&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 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 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)); } TEST_F(NetHandleTest, successFallback) { expectOpenFile(); expectAddrInfo(); expectConnection(); struct ipmi_flash::ExtChunkHdr chunk; chunk.length = chunkSize; std::vector chunkBytes(sizeof(chunk)); std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); { InSequence seq; EXPECT_CALL(sysMock, sendfile(connFd, inFd, _, _)) .WillOnce([](int, int, off_t*, size_t) { errno = EINVAL; return -1; }); std::vector chunk(chunkSize); for (std::uint32_t offset = 0; offset < fakeFileSize; offset += chunkSize) { for (size_t i = 0; i < chunkSize; ++i) { chunk[i] = i + offset; } EXPECT_CALL(sysMock, read(inFd, _, Ge(chunkSize))) .WillOnce([chunk](int, void* buf, size_t) { memcpy(buf, chunk.data(), chunkSize); return chunkSize; }); EXPECT_CALL(sysMock, send(connFd, _, chunkSize, 0)) .WillOnce([chunk](int, const void* data, size_t len, int) { std::vector dcopy(len); memcpy(dcopy.data(), data, len); EXPECT_THAT(dcopy, ContainerEq(chunk)); return chunkSize; }); EXPECT_CALL(blobMock, writeBytes(session, offset, ContainerEq(chunkBytes))); EXPECT_CALL(progMock, updateProgress(chunkSize)); } EXPECT_CALL(sysMock, read(inFd, _, Ge(chunkSize))).WillOnce(Return(0)); EXPECT_CALL(sysMock, send(connFd, _, 0, 0)).WillOnce(Return(0)); } EXPECT_TRUE(handler.sendContents(filePath, session)); } } // namespace } // namespace host_tool