1 #include "data.hpp" 2 #include "internal_sys_mock.hpp" 3 #include "net.hpp" 4 #include "progress_mock.hpp" 5 6 #include <ipmiblob/test/blob_interface_mock.hpp> 7 8 #include <cstring> 9 10 #include <gtest/gtest.h> 11 12 namespace host_tool 13 { 14 namespace 15 { 16 17 using namespace std::literals; 18 19 using ::testing::_; 20 using ::testing::AllOf; 21 using ::testing::ContainerEq; 22 using ::testing::DoAll; 23 using ::testing::Field; 24 using ::testing::Gt; 25 using ::testing::InSequence; 26 using ::testing::NotNull; 27 using ::testing::Pointee; 28 using ::testing::Return; 29 using ::testing::SetArgPointee; 30 using ::testing::SetErrnoAndReturn; 31 using ::testing::StrEq; 32 33 class NetHandleTest : public ::testing::Test 34 { 35 protected: 36 NetHandleTest() : handler(&blobMock, &progMock, host, port, &sysMock) 37 { 38 sa.sin6_family = AF_INET6; 39 sa.sin6_port = htons(622); 40 sa.sin6_flowinfo = 0; 41 sa.sin6_addr = in6addr_loopback; // ::1 42 sa.sin6_scope_id = 0; 43 44 addr.ai_family = AF_INET6; 45 addr.ai_socktype = SOCK_STREAM; 46 addr.ai_addr = reinterpret_cast<struct sockaddr*>(&sa); 47 addr.ai_addrlen = sizeof(sa); 48 addr.ai_protocol = 0; 49 addr.ai_next = nullptr; 50 } 51 52 void expectOpenFile() 53 { 54 EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) 55 .WillOnce(Return(inFd)); 56 EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0)); 57 EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))) 58 .WillOnce(Return(fakeFileSize)); 59 } 60 61 void expectAddrInfo() 62 { 63 EXPECT_CALL( 64 sysMock, 65 getaddrinfo(StrEq(host), StrEq(port), 66 AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), 67 Field(&addrinfo::ai_family, AF_UNSPEC), 68 Field(&addrinfo::ai_socktype, SOCK_STREAM)), 69 NotNull())) 70 .WillOnce(DoAll(SetArgPointee<3>(&addr), Return(0))); 71 EXPECT_CALL(sysMock, freeaddrinfo(&addr)); 72 } 73 74 void expectConnection() 75 { 76 EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) 77 .WillOnce(Return(connFd)); 78 EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); 79 EXPECT_CALL(sysMock, 80 connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), 81 sizeof(sa))) 82 .WillOnce(Return(0)); 83 } 84 85 internal::InternalSysMock sysMock; 86 ipmiblob::BlobInterfaceMock blobMock; 87 ProgressMock progMock; 88 89 const std::string host = "::1"s; 90 const std::string port = "622"s; 91 92 struct sockaddr_in6 sa; 93 struct addrinfo addr; 94 95 static constexpr std::uint16_t session = 0xbeef; 96 const std::string filePath = "/asdf"s; 97 static constexpr int inFd = 5; 98 static constexpr int connFd = 7; 99 static constexpr size_t fakeFileSize = 128; 100 static constexpr size_t chunkSize = 16; 101 102 NetDataHandler handler; 103 }; 104 105 TEST_F(NetHandleTest, openFileFail) 106 { 107 EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) 108 .WillOnce(SetErrnoAndReturn(EACCES, -1)); 109 110 EXPECT_FALSE(handler.sendContents(filePath, session)); 111 } 112 113 TEST_F(NetHandleTest, getaddrinfoFail) 114 { 115 expectOpenFile(); 116 117 EXPECT_CALL(sysMock, 118 getaddrinfo(StrEq(host), StrEq(port), 119 AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), 120 Field(&addrinfo::ai_family, AF_UNSPEC), 121 Field(&addrinfo::ai_socktype, SOCK_STREAM)), 122 NotNull())) 123 .WillOnce(Return(EAI_ADDRFAMILY)); 124 125 EXPECT_FALSE(handler.sendContents(filePath, session)); 126 } 127 128 TEST_F(NetHandleTest, connectFail) 129 { 130 expectOpenFile(); 131 expectAddrInfo(); 132 133 EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) 134 .WillOnce(Return(connFd)); 135 EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); 136 EXPECT_CALL( 137 sysMock, 138 connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa))) 139 .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1)); 140 141 EXPECT_FALSE(handler.sendContents(filePath, session)); 142 } 143 144 TEST_F(NetHandleTest, sendfileFail) 145 { 146 expectOpenFile(); 147 expectAddrInfo(); 148 expectConnection(); 149 150 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _)) 151 .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1)); 152 153 EXPECT_FALSE(handler.sendContents(filePath, session)); 154 } 155 156 TEST_F(NetHandleTest, successOneChunk) 157 { 158 expectOpenFile(); 159 expectAddrInfo(); 160 expectConnection(); 161 162 { 163 InSequence seq; 164 165 EXPECT_CALL(sysMock, 166 sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize))) 167 .WillOnce( 168 DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize))); 169 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), 170 Gt(fakeFileSize))) 171 .WillOnce(Return(0)); 172 } 173 174 struct ipmi_flash::ExtChunkHdr chunk; 175 chunk.length = fakeFileSize; 176 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 177 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 178 EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes))); 179 180 EXPECT_CALL(progMock, updateProgress(fakeFileSize)); 181 182 EXPECT_TRUE(handler.sendContents(filePath, session)); 183 } 184 185 TEST_F(NetHandleTest, successMultiChunk) 186 { 187 expectOpenFile(); 188 expectAddrInfo(); 189 expectConnection(); 190 191 struct ipmi_flash::ExtChunkHdr chunk; 192 chunk.length = chunkSize; 193 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 194 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 195 196 { 197 InSequence seq; 198 199 for (std::uint32_t offset = 0; offset < fakeFileSize; 200 offset += chunkSize) 201 { 202 EXPECT_CALL(sysMock, 203 sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize))) 204 .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize), 205 Return(chunkSize))); 206 207 EXPECT_CALL(blobMock, 208 writeBytes(session, offset, ContainerEq(chunkBytes))); 209 EXPECT_CALL(progMock, updateProgress(chunkSize)); 210 } 211 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), 212 Gt(chunkSize))) 213 .WillOnce(Return(0)); 214 } 215 216 EXPECT_TRUE(handler.sendContents(filePath, session)); 217 } 218 219 } // namespace 220 } // namespace host_tool 221