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 EXPECT_CALL(progMock, start(fakeFileSize)); 61 } 62 63 void expectAddrInfo() 64 { 65 EXPECT_CALL( 66 sysMock, 67 getaddrinfo(StrEq(host), StrEq(port), 68 AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), 69 Field(&addrinfo::ai_family, AF_UNSPEC), 70 Field(&addrinfo::ai_socktype, SOCK_STREAM)), 71 NotNull())) 72 .WillOnce(DoAll(SetArgPointee<3>(&addr), Return(0))); 73 EXPECT_CALL(sysMock, freeaddrinfo(&addr)); 74 } 75 76 void expectConnection() 77 { 78 EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) 79 .WillOnce(Return(connFd)); 80 EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); 81 EXPECT_CALL(sysMock, 82 connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), 83 sizeof(sa))) 84 .WillOnce(Return(0)); 85 } 86 87 internal::InternalSysMock sysMock; 88 ipmiblob::BlobInterfaceMock blobMock; 89 ProgressMock progMock; 90 91 const std::string host = "::1"s; 92 const std::string port = "622"s; 93 94 struct sockaddr_in6 sa; 95 struct addrinfo addr; 96 97 static constexpr std::uint16_t session = 0xbeef; 98 const std::string filePath = "/asdf"s; 99 static constexpr int inFd = 5; 100 static constexpr int connFd = 7; 101 static constexpr size_t fakeFileSize = 128; 102 static constexpr size_t chunkSize = 16; 103 104 NetDataHandler handler; 105 }; 106 107 TEST_F(NetHandleTest, openFileFail) 108 { 109 EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) 110 .WillOnce(SetErrnoAndReturn(EACCES, -1)); 111 112 EXPECT_FALSE(handler.sendContents(filePath, session)); 113 } 114 115 TEST_F(NetHandleTest, getSizeFail) 116 { 117 EXPECT_CALL(sysMock, open(StrEq(filePath.c_str()), _)) 118 .WillOnce(Return(inFd)); 119 EXPECT_CALL(sysMock, close(inFd)).WillOnce(Return(0)); 120 EXPECT_CALL(sysMock, getSize(StrEq(filePath.c_str()))).WillOnce(Return(0)); 121 122 EXPECT_FALSE(handler.sendContents(filePath, session)); 123 } 124 125 TEST_F(NetHandleTest, getaddrinfoFail) 126 { 127 expectOpenFile(); 128 129 EXPECT_CALL(sysMock, 130 getaddrinfo(StrEq(host), StrEq(port), 131 AllOf(Field(&addrinfo::ai_flags, AI_NUMERICHOST), 132 Field(&addrinfo::ai_family, AF_UNSPEC), 133 Field(&addrinfo::ai_socktype, SOCK_STREAM)), 134 NotNull())) 135 .WillOnce(Return(EAI_ADDRFAMILY)); 136 137 EXPECT_FALSE(handler.sendContents(filePath, session)); 138 } 139 140 TEST_F(NetHandleTest, connectFail) 141 { 142 expectOpenFile(); 143 expectAddrInfo(); 144 145 EXPECT_CALL(sysMock, socket(AF_INET6, SOCK_STREAM, 0)) 146 .WillOnce(Return(connFd)); 147 EXPECT_CALL(sysMock, close(connFd)).WillOnce(Return(0)); 148 EXPECT_CALL( 149 sysMock, 150 connect(connFd, reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa))) 151 .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1)); 152 153 EXPECT_FALSE(handler.sendContents(filePath, session)); 154 } 155 156 TEST_F(NetHandleTest, sendfileFail) 157 { 158 expectOpenFile(); 159 expectAddrInfo(); 160 expectConnection(); 161 162 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(0), _)) 163 .WillOnce(SetErrnoAndReturn(ETIMEDOUT, -1)); 164 165 EXPECT_FALSE(handler.sendContents(filePath, session)); 166 } 167 168 TEST_F(NetHandleTest, successOneChunk) 169 { 170 expectOpenFile(); 171 expectAddrInfo(); 172 expectConnection(); 173 174 { 175 InSequence seq; 176 177 EXPECT_CALL(sysMock, 178 sendfile(connFd, inFd, Pointee(0), Gt(fakeFileSize))) 179 .WillOnce( 180 DoAll(SetArgPointee<2>(fakeFileSize), Return(fakeFileSize))); 181 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), 182 Gt(fakeFileSize))) 183 .WillOnce(Return(0)); 184 } 185 186 struct ipmi_flash::ExtChunkHdr chunk; 187 chunk.length = fakeFileSize; 188 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 189 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 190 EXPECT_CALL(blobMock, writeBytes(session, 0, ContainerEq(chunkBytes))); 191 192 EXPECT_CALL(progMock, updateProgress(fakeFileSize)); 193 194 EXPECT_TRUE(handler.sendContents(filePath, session)); 195 } 196 197 TEST_F(NetHandleTest, successMultiChunk) 198 { 199 expectOpenFile(); 200 expectAddrInfo(); 201 expectConnection(); 202 203 struct ipmi_flash::ExtChunkHdr chunk; 204 chunk.length = chunkSize; 205 std::vector<std::uint8_t> chunkBytes(sizeof(chunk)); 206 std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk)); 207 208 { 209 InSequence seq; 210 211 for (std::uint32_t offset = 0; offset < fakeFileSize; 212 offset += chunkSize) 213 { 214 EXPECT_CALL(sysMock, 215 sendfile(connFd, inFd, Pointee(offset), Gt(chunkSize))) 216 .WillOnce(DoAll(SetArgPointee<2>(offset + chunkSize), 217 Return(chunkSize))); 218 219 EXPECT_CALL(blobMock, 220 writeBytes(session, offset, ContainerEq(chunkBytes))); 221 EXPECT_CALL(progMock, updateProgress(chunkSize)); 222 } 223 EXPECT_CALL(sysMock, sendfile(connFd, inFd, Pointee(fakeFileSize), 224 Gt(chunkSize))) 225 .WillOnce(Return(0)); 226 } 227 228 EXPECT_TRUE(handler.sendContents(filePath, session)); 229 } 230 231 } // namespace 232 } // namespace host_tool 233