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::Ge; 25 using ::testing::Gt; 26 using ::testing::InSequence; 27 using ::testing::NotNull; 28 using ::testing::Pointee; 29 using ::testing::Return; 30 using ::testing::SetArgPointee; 31 using ::testing::SetErrnoAndReturn; 32 using ::testing::StrEq; 33 34 class NetHandleTest : public ::testing::Test 35 { 36 protected: 37 NetHandleTest() : handler(&blobMock, &progMock, host, port, &sysMock) 38 { 39 sa.sin6_family = AF_INET6; 40 sa.sin6_port = htons(622); 41 sa.sin6_flowinfo = 0; 42 sa.sin6_addr = in6addr_loopback; // ::1 43 sa.sin6_scope_id = 0; 44 45 addr.ai_family = AF_INET6; 46 addr.ai_socktype = SOCK_STREAM; 47 addr.ai_addr = reinterpret_cast<struct sockaddr*>(&sa); 48 addr.ai_addrlen = sizeof(sa); 49 addr.ai_protocol = 0; 50 addr.ai_next = nullptr; 51 } 52 53 void expectOpenFile() 54 { 55 EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) 56 .WillOnce(Return(inFd)); 57 EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0)); 58 EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))) 59 .WillOnce(Return(fakeFileSize)); 60 } 61 62 void expectAddrInfo() 63 { 64 EXPECT_CALL( 65 sysMock, 66 getaddrinfo(StrEq(host), StrEq(port), 67 AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), 68 Field(&addrinfo::ai_family, AF_UNSPEC), 69 Field(&addrinfo::ai_socktype, SOCK_STREAM)), 70 NotNull())) 71 .WillOnce(DoAll(SetArgPointee<3>(&addr), Return(0))); 72 EXPECT_CALL(sysMock, freeaddrinfo(&addr)); 73 } 74 75 void expectConnection() 76 { 77 EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) 78 .WillOnce(Return(connFd)); 79 EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); 80 EXPECT_CALL(sysMock, 81 connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), 82 sizeof(sa))) 83 .WillOnce(Return(0)); 84 } 85 86 internal::InternalSysMock sysMock; 87 ipmiblob::BlobInterfaceMock blobMock; 88 ProgressMock progMock; 89 90 const std::string host = "::1"s; 91 const std::string port = "622"s; 92 93 struct sockaddr_in6 sa; 94 struct addrinfo addr; 95 96 static constexpr std::uint16_t session = 0xbeef; 97 const std::string filePath = "/asdf"s; 98 static constexpr int inFd = 5; 99 static constexpr int connFd = 7; 100 static constexpr size_t fakeFileSize = 128; 101 static constexpr size_t chunkSize = 16; 102 103 NetDataHandler handler; 104 }; 105 106 TEST_F(NetHandleTest, openFileFail) 107 { 108 EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) 109 .WillOnce(SetErrnoAndReturn(EACCES, -1)); 110 111 EXPECT_FALSE(handler.sendContents(filePath, session)); 112 } 113 114 TEST_F(NetHandleTest, getaddrinfoFail) 115 { 116 expectOpenFile(); 117 118 EXPECT_CALL(sysMock, 119 getaddrinfo(StrEq(host), StrEq(port), 120 AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), 121 Field(&addrinfo::ai_family, AF_UNSPEC), 122 Field(&addrinfo::ai_socktype, SOCK_STREAM)), 123 NotNull())) 124 .WillOnce(Return(EAI_ADDRFAMILY)); 125 126 EXPECT_FALSE(handler.sendContents(filePath, session)); 127 } 128 129 TEST_F(NetHandleTest, connectFail) 130 { 131 expectOpenFile(); 132 expectAddrInfo(); 133 134 EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) 135 .WillOnce(Return(connFd)); 136 EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); 137 EXPECT_CALL( 138 sysMock, 139 connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa))) 140 .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1)); 141 142 EXPECT_FALSE(handler.sendContents(filePath, session)); 143 } 144 145 TEST_F(NetHandleTest, sendfileFail) 146 { 147 expectOpenFile(); 148 expectAddrInfo(); 149 expectConnection(); 150 151 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _)) 152 .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1)); 153 154 EXPECT_FALSE(handler.sendContents(filePath, session)); 155 } 156 157 TEST_F(NetHandleTest, successOneChunk) 158 { 159 expectOpenFile(); 160 expectAddrInfo(); 161 expectConnection(); 162 163 { 164 InSequence seq; 165 166 EXPECT_CALL(sysMock, 167 sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize))) 168 .WillOnce( 169 DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize))); 170 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), 171 Gt(fakeFileSize))) 172 .WillOnce(Return(0)); 173 } 174 175 struct ipmi_flash::ExtChunkHdr chunk; 176 chunk.length = fakeFileSize; 177 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 178 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 179 EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes))); 180 181 EXPECT_CALL(progMock, updateProgress(fakeFileSize)); 182 183 EXPECT_TRUE(handler.sendContents(filePath, session)); 184 } 185 186 TEST_F(NetHandleTest, successMultiChunk) 187 { 188 expectOpenFile(); 189 expectAddrInfo(); 190 expectConnection(); 191 192 struct ipmi_flash::ExtChunkHdr chunk; 193 chunk.length = chunkSize; 194 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 195 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 196 197 { 198 InSequence seq; 199 200 for (std::uint32_t offset = 0; offset < fakeFileSize; 201 offset += chunkSize) 202 { 203 EXPECT_CALL(sysMock, 204 sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize))) 205 .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize), 206 Return(chunkSize))); 207 208 EXPECT_CALL(blobMock, 209 writeBytes(session, offset, ContainerEq(chunkBytes))); 210 EXPECT_CALL(progMock, updateProgress(chunkSize)); 211 } 212 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), 213 Gt(chunkSize))) 214 .WillOnce(Return(0)); 215 } 216 217 EXPECT_TRUE(handler.sendContents(filePath, session)); 218 } 219 220 TEST_F(NetHandleTest, successFallback) 221 { 222 expectOpenFile(); 223 expectAddrInfo(); 224 expectConnection(); 225 226 struct ipmi_flash::ExtChunkHdr chunk; 227 chunk.length = chunkSize; 228 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 229 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 230 231 { 232 InSequence seq; 233 EXPECT_CALL(sysMock, sendfile(connFd, inFd, _, _)) 234 .WillOnce([](int, int, off_t*, size_t) { 235 errno = EINVAL; 236 return -1; 237 }); 238 239 std::vector<uint8_t> chunk(chunkSize); 240 for (std::uint32_t offset = 0; offset < fakeFileSize; 241 offset += chunkSize) 242 { 243 for (size_t i = 0; i < chunkSize; ++i) 244 { 245 chunk[i] = i + offset; 246 } 247 EXPECT_CALL(sysMock, read(inFd, _, Ge(chunkSize))) 248 .WillOnce([chunk](int, void* buf, size_t) { 249 memcpy(buf, chunk.data(), chunkSize); 250 return chunkSize; 251 }); 252 EXPECT_CALL(sysMock, send(connFd, _, chunkSize, 0)) 253 .WillOnce([chunk](int, const void* data, size_t len, int) { 254 std::vector<uint8_t> dcopy(len); 255 memcpy(dcopy.data(), data, len); 256 EXPECT_THAT(dcopy, ContainerEq(chunk)); 257 return chunkSize; 258 }); 259 EXPECT_CALL(blobMock, 260 writeBytes(session, offset, ContainerEq(chunkBytes))); 261 EXPECT_CALL(progMock, updateProgress(chunkSize)); 262 } 263 EXPECT_CALL(sysMock, read(inFd, _, Ge(chunkSize))).WillOnce(Return(0)); 264 EXPECT_CALL(sysMock, send(connFd, _, 0, 0)).WillOnce(Return(0)); 265 } 266 267 EXPECT_TRUE(handler.sendContents(filePath, session)); 268 } 269 270 } // namespace 271 } // namespace host_tool 272