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:
NetHandleTest()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
expectOpenFile()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
expectAddrInfo()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
expectConnection()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
TEST_F(NetHandleTest,openFileFail)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
TEST_F(NetHandleTest,getaddrinfoFail)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
TEST_F(NetHandleTest,connectFail)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(sysMock,
138 connect(connFd, reinterpret_cast<struct sockaddr*>(&sa),
139 sizeof(sa)))
140 .WillOnce(SetErrnoAndReturn(ECONNREFUSED, -1));
141
142 EXPECT_FALSE(handler.sendContents(filePath, session));
143 }
144
TEST_F(NetHandleTest,sendfileFail)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
TEST_F(NetHandleTest,successOneChunk)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
TEST_F(NetHandleTest,successMultiChunk)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
TEST_F(NetHandleTest,successFallback)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