1 | #include <IO/WriteBufferAIO.h> |
2 | #include <Core/Defines.h> |
3 | |
4 | #include <filesystem> |
5 | #include <iostream> |
6 | #include <fstream> |
7 | #include <streambuf> |
8 | #include <cstdlib> |
9 | #include <functional> |
10 | |
11 | namespace |
12 | { |
13 | |
14 | namespace fs = std::filesystem; |
15 | |
16 | void run(); |
17 | [[noreturn]] void die(const std::string & msg); |
18 | void runTest(unsigned int num, const std::function<bool()> & func); |
19 | std::string createTmpFile(); |
20 | std::string generateString(size_t n); |
21 | |
22 | bool test1(); |
23 | bool test2(); |
24 | bool test3(); |
25 | bool test4(); |
26 | bool test5(); |
27 | bool test6(); |
28 | bool test7(); |
29 | bool test8(); |
30 | bool test9(); |
31 | bool test10(); |
32 | |
33 | void run() |
34 | { |
35 | const std::vector<std::function<bool()>> tests = |
36 | { |
37 | test1, |
38 | test2, |
39 | test3, |
40 | test4, |
41 | test5, |
42 | test6, |
43 | test7, |
44 | test8, |
45 | test9, |
46 | test10 |
47 | }; |
48 | |
49 | unsigned int num = 0; |
50 | for (const auto & test : tests) |
51 | { |
52 | ++num; |
53 | runTest(num, test); |
54 | } |
55 | } |
56 | |
57 | void die(const std::string & msg) |
58 | { |
59 | std::cout << msg; |
60 | ::exit(EXIT_FAILURE); |
61 | } |
62 | |
63 | void runTest(unsigned int num, const std::function<bool()> & func) |
64 | { |
65 | bool ok; |
66 | |
67 | try |
68 | { |
69 | ok = func(); |
70 | } |
71 | catch (const DB::Exception & ex) |
72 | { |
73 | ok = false; |
74 | std::cout << "Caught exception " << ex.displayText() << "\n" ; |
75 | } |
76 | catch (const std::exception & ex) |
77 | { |
78 | ok = false; |
79 | std::cout << "Caught exception " << ex.what() << "\n" ; |
80 | } |
81 | |
82 | if (ok) |
83 | std::cout << "Test " << num << " passed\n" ; |
84 | else |
85 | std::cout << "Test " << num << " failed\n" ; |
86 | } |
87 | |
88 | std::string createTmpFile() |
89 | { |
90 | char pattern[] = "/tmp/fileXXXXXX" ; |
91 | char * dir = ::mkdtemp(pattern); |
92 | if (dir == nullptr) |
93 | die("Could not create directory" ); |
94 | |
95 | return std::string(dir) + "/foo" ; |
96 | } |
97 | |
98 | std::string generateString(size_t n) |
99 | { |
100 | static const std::string symbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ; |
101 | |
102 | std::string buf; |
103 | buf.reserve(n); |
104 | |
105 | for (size_t i = 0; i < n; ++i) |
106 | buf += symbols[i % symbols.length()]; |
107 | |
108 | return buf; |
109 | } |
110 | |
111 | bool test1() |
112 | { |
113 | std::string filename = createTmpFile(); |
114 | |
115 | size_t n = 10 * DEFAULT_AIO_FILE_BLOCK_SIZE; |
116 | |
117 | std::string buf = generateString(n); |
118 | |
119 | { |
120 | DB::WriteBufferAIO out(filename, 3 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
121 | |
122 | if (out.getFileName() != filename) |
123 | return false; |
124 | if (out.getFD() == -1) |
125 | return false; |
126 | |
127 | out.write(buf.data(), buf.length()); |
128 | } |
129 | |
130 | std::ifstream in(filename.c_str()); |
131 | if (!in.is_open()) |
132 | die("Could not open file" ); |
133 | |
134 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
135 | |
136 | in.close(); |
137 | fs::remove_all(fs::path(filename).parent_path().string()); |
138 | |
139 | return (received == buf); |
140 | } |
141 | |
142 | bool test2() |
143 | { |
144 | std::string filename = createTmpFile(); |
145 | |
146 | size_t n = 10 * DEFAULT_AIO_FILE_BLOCK_SIZE; |
147 | |
148 | std::string buf = generateString(n); |
149 | |
150 | { |
151 | DB::WriteBufferAIO out(filename, 3 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
152 | |
153 | if (out.getFileName() != filename) |
154 | return false; |
155 | if (out.getFD() == -1) |
156 | return false; |
157 | |
158 | out.write(buf.data(), buf.length() / 2); |
159 | out.seek(DEFAULT_AIO_FILE_BLOCK_SIZE, SEEK_CUR); |
160 | out.write(&buf[buf.length() / 2], buf.length() / 2); |
161 | } |
162 | |
163 | std::ifstream in(filename.c_str()); |
164 | if (!in.is_open()) |
165 | die("Could not open file" ); |
166 | |
167 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
168 | |
169 | in.close(); |
170 | fs::remove_all(fs::path(filename).parent_path().string()); |
171 | |
172 | if (received.substr(0, buf.length() / 2) != buf.substr(0, buf.length() / 2)) |
173 | return false; |
174 | if (received.substr(buf.length() / 2, DEFAULT_AIO_FILE_BLOCK_SIZE) != std::string(DEFAULT_AIO_FILE_BLOCK_SIZE, '\0')) |
175 | return false; |
176 | if (received.substr(buf.length() / 2 + DEFAULT_AIO_FILE_BLOCK_SIZE) != buf.substr(buf.length() / 2)) |
177 | return false; |
178 | |
179 | return true; |
180 | } |
181 | |
182 | bool test3() |
183 | { |
184 | std::string filename = createTmpFile(); |
185 | |
186 | size_t n = 10 * DEFAULT_AIO_FILE_BLOCK_SIZE; |
187 | |
188 | std::string buf = generateString(n); |
189 | |
190 | { |
191 | DB::WriteBufferAIO out(filename, 3 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
192 | |
193 | if (out.getFileName() != filename) |
194 | return false; |
195 | if (out.getFD() == -1) |
196 | return false; |
197 | |
198 | out.write(buf.data(), buf.length()); |
199 | |
200 | off_t pos1 = out.getPositionInFile(); |
201 | |
202 | out.truncate(buf.length() / 2); |
203 | |
204 | off_t pos2 = out.getPositionInFile(); |
205 | |
206 | if (pos1 != pos2) |
207 | return false; |
208 | } |
209 | |
210 | std::ifstream in(filename.c_str()); |
211 | if (!in.is_open()) |
212 | die("Could not open file" ); |
213 | |
214 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
215 | |
216 | in.close(); |
217 | fs::remove_all(fs::path(filename).parent_path().string()); |
218 | |
219 | return (received == buf.substr(0, buf.length() / 2)); |
220 | } |
221 | |
222 | bool test4() |
223 | { |
224 | std::string filename = createTmpFile(); |
225 | |
226 | size_t n = 10 * DEFAULT_AIO_FILE_BLOCK_SIZE; |
227 | |
228 | std::string buf = generateString(n); |
229 | |
230 | { |
231 | DB::WriteBufferAIO out(filename, 3 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
232 | |
233 | if (out.getFileName() != filename) |
234 | return false; |
235 | if (out.getFD() == -1) |
236 | return false; |
237 | |
238 | out.write(buf.data(), buf.length()); |
239 | |
240 | off_t pos1 = out.getPositionInFile(); |
241 | |
242 | out.truncate(3 * buf.length() / 2); |
243 | |
244 | off_t pos2 = out.getPositionInFile(); |
245 | |
246 | if (pos1 != pos2) |
247 | return false; |
248 | } |
249 | |
250 | std::ifstream in(filename.c_str()); |
251 | if (!in.is_open()) |
252 | die("Could not open file" ); |
253 | |
254 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
255 | |
256 | in.close(); |
257 | fs::remove_all(fs::path(filename).parent_path().string()); |
258 | |
259 | if (received.substr(0, buf.length()) != buf) |
260 | return false; |
261 | |
262 | if (received.substr(buf.length()) != std::string(buf.length() / 2, '\0')) |
263 | return false; |
264 | |
265 | return true; |
266 | } |
267 | |
268 | bool test5() |
269 | { |
270 | std::string filename = createTmpFile(); |
271 | |
272 | size_t n = 10 * DEFAULT_AIO_FILE_BLOCK_SIZE; |
273 | |
274 | std::string buf = generateString(n); |
275 | |
276 | { |
277 | DB::WriteBufferAIO out(filename, 3 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
278 | |
279 | if (out.getFileName() != filename) |
280 | return false; |
281 | if (out.getFD() == -1) |
282 | return false; |
283 | |
284 | out.seek(1, SEEK_SET); |
285 | out.write(buf.data(), buf.length()); |
286 | } |
287 | |
288 | std::ifstream in(filename.c_str()); |
289 | if (!in.is_open()) |
290 | die("Could not open file" ); |
291 | |
292 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
293 | |
294 | in.close(); |
295 | fs::remove_all(fs::path(filename).parent_path().string()); |
296 | |
297 | return received.substr(1) == buf; |
298 | } |
299 | |
300 | bool test6() |
301 | { |
302 | std::string filename = createTmpFile(); |
303 | |
304 | size_t n = 10 * DEFAULT_AIO_FILE_BLOCK_SIZE; |
305 | |
306 | std::string buf = generateString(n); |
307 | |
308 | std::string buf2 = "1111111111" ; |
309 | |
310 | { |
311 | DB::WriteBufferAIO out(filename, 3 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
312 | |
313 | if (out.getFileName() != filename) |
314 | return false; |
315 | if (out.getFD() == -1) |
316 | return false; |
317 | |
318 | out.seek(3, SEEK_SET); |
319 | out.write(buf.data(), buf.length()); |
320 | out.seek(-2 * DEFAULT_AIO_FILE_BLOCK_SIZE, SEEK_CUR); |
321 | out.write(buf2.data(), buf2.length()); |
322 | } |
323 | |
324 | std::ifstream in(filename.c_str()); |
325 | if (!in.is_open()) |
326 | die("Could not open file" ); |
327 | |
328 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
329 | |
330 | in.close(); |
331 | fs::remove_all(fs::path(filename).parent_path().string()); |
332 | |
333 | if (received.substr(3, 8 * DEFAULT_AIO_FILE_BLOCK_SIZE) != buf.substr(0, 8 * DEFAULT_AIO_FILE_BLOCK_SIZE)) |
334 | return false; |
335 | |
336 | if (received.substr(3 + 8 * DEFAULT_AIO_FILE_BLOCK_SIZE, 10) != buf2) |
337 | return false; |
338 | |
339 | if (received.substr(13 + 8 * DEFAULT_AIO_FILE_BLOCK_SIZE) != buf.substr(10 + 8 * DEFAULT_AIO_FILE_BLOCK_SIZE)) |
340 | return false; |
341 | |
342 | return true; |
343 | } |
344 | |
345 | bool test7() |
346 | { |
347 | std::string filename = createTmpFile(); |
348 | |
349 | std::string buf2 = "11111111112222222222" ; |
350 | |
351 | { |
352 | DB::WriteBufferAIO out(filename, DEFAULT_AIO_FILE_BLOCK_SIZE); |
353 | |
354 | if (out.getFileName() != filename) |
355 | return false; |
356 | if (out.getFD() == -1) |
357 | return false; |
358 | |
359 | out.seek(DEFAULT_AIO_FILE_BLOCK_SIZE - (buf2.length() / 2), SEEK_SET); |
360 | out.write(buf2.data(), buf2.length()); |
361 | } |
362 | |
363 | std::ifstream in(filename.c_str()); |
364 | if (!in.is_open()) |
365 | die("Could not open file" ); |
366 | |
367 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
368 | |
369 | if (received.length() != 4106) |
370 | return false; |
371 | if (received.substr(0, 4086) != std::string(4086, '\0')) |
372 | return false; |
373 | if (received.substr(4086, 20) != buf2) |
374 | return false; |
375 | |
376 | in.close(); |
377 | fs::remove_all(fs::path(filename).parent_path().string()); |
378 | |
379 | return true; |
380 | } |
381 | |
382 | bool test8() |
383 | { |
384 | std::string filename = createTmpFile(); |
385 | |
386 | std::string buf2 = "11111111112222222222" ; |
387 | |
388 | { |
389 | DB::WriteBufferAIO out(filename, 2 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
390 | |
391 | if (out.getFileName() != filename) |
392 | return false; |
393 | if (out.getFD() == -1) |
394 | return false; |
395 | |
396 | out.seek(2 * DEFAULT_AIO_FILE_BLOCK_SIZE - (buf2.length() / 2), SEEK_SET); |
397 | out.write(buf2.data(), buf2.length()); |
398 | } |
399 | |
400 | std::ifstream in(filename.c_str()); |
401 | if (!in.is_open()) |
402 | die("Could not open file" ); |
403 | |
404 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
405 | |
406 | if (received.length() != 8202) |
407 | return false; |
408 | if (received.substr(0, 8182) != std::string(8182, '\0')) |
409 | return false; |
410 | if (received.substr(8182, 20) != buf2) |
411 | return false; |
412 | |
413 | in.close(); |
414 | fs::remove_all(fs::path(filename).parent_path().string()); |
415 | |
416 | return true; |
417 | } |
418 | |
419 | bool test9() |
420 | { |
421 | std::string filename = createTmpFile(); |
422 | |
423 | size_t n = 3 * DEFAULT_AIO_FILE_BLOCK_SIZE; |
424 | |
425 | std::string buf = generateString(n); |
426 | |
427 | std::string buf2(DEFAULT_AIO_FILE_BLOCK_SIZE + 10, '1'); |
428 | |
429 | { |
430 | DB::WriteBufferAIO out(filename, 2 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
431 | |
432 | if (out.getFileName() != filename) |
433 | return false; |
434 | if (out.getFD() == -1) |
435 | return false; |
436 | |
437 | out.seek(3, SEEK_SET); |
438 | out.write(buf.data(), buf.length()); |
439 | out.seek(-DEFAULT_AIO_FILE_BLOCK_SIZE, SEEK_CUR); |
440 | out.write(buf2.data(), buf2.length()); |
441 | } |
442 | |
443 | std::ifstream in(filename.c_str()); |
444 | if (!in.is_open()) |
445 | die("Could not open file" ); |
446 | |
447 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
448 | |
449 | in.close(); |
450 | fs::remove_all(fs::path(filename).parent_path().string()); |
451 | |
452 | if (received.substr(3, 2 * DEFAULT_AIO_FILE_BLOCK_SIZE) != buf.substr(0, 2 * DEFAULT_AIO_FILE_BLOCK_SIZE)) |
453 | return false; |
454 | |
455 | if (received.substr(3 + 2 * DEFAULT_AIO_FILE_BLOCK_SIZE, DEFAULT_AIO_FILE_BLOCK_SIZE + 10) != buf2) |
456 | return false; |
457 | |
458 | return true; |
459 | } |
460 | |
461 | bool test10() |
462 | { |
463 | std::string filename = createTmpFile(); |
464 | |
465 | size_t n = 10 * DEFAULT_AIO_FILE_BLOCK_SIZE + 3; |
466 | |
467 | std::string buf = generateString(n); |
468 | |
469 | { |
470 | DB::WriteBufferAIO out(filename, 3 * DEFAULT_AIO_FILE_BLOCK_SIZE); |
471 | |
472 | if (out.getFileName() != filename) |
473 | return false; |
474 | if (out.getFD() == -1) |
475 | return false; |
476 | |
477 | out.write(buf.data(), buf.length()); |
478 | } |
479 | |
480 | std::ifstream in(filename.c_str()); |
481 | if (!in.is_open()) |
482 | die("Could not open file" ); |
483 | |
484 | std::string received{ std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>() }; |
485 | |
486 | in.close(); |
487 | fs::remove_all(fs::path(filename).parent_path().string()); |
488 | |
489 | return (received == buf); |
490 | } |
491 | |
492 | } |
493 | |
494 | int main() |
495 | { |
496 | run(); |
497 | return 0; |
498 | } |
499 | |