Line data Source code
1 : /*
2 : * (C) Copyright 2010 Marek Dopiera
3 : *
4 : * This file is part of CoherentDB.
5 : *
6 : * CoherentDB is free software: you can redistribute it and/or modify it
7 : * under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation, either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * CoherentDB is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public
17 : * License along with CoherentDB. If not, see
18 : * http://www.gnu.org/licenses/.
19 : */
20 :
21 : #include <errno.h>
22 : #include <sys/fcntl.h>
23 :
24 : #include <cstring>
25 : #include <cstdlib>
26 :
27 : #include <boost/shared_array.hpp>
28 :
29 : #include <util/file.h>
30 : #include <util/aio.h>
31 : #include <util/thread.h>
32 : #include <config/config.h>
33 : #include <debug/asserts.h>
34 : #include <log/log.h>
35 :
36 : namespace coherent {
37 : namespace util {
38 : namespace unittests {
39 :
40 : using namespace std;
41 : using namespace boost;
42 : using namespace log4cxx;
43 : using namespace coherent::config;
44 : using namespace coherent::log;
45 :
46 : typedef auto_ptr<file> file_ptr;
47 :
48 : static uint32_t const file_size = 4 * 1024 * 1024 + 1; //make sure that it's unaligned
49 :
50 : struct test_pattern
51 : {
52 3 : test_pattern()
53 : {
54 12582918 : for (uint32_t i = 0; i < file_size; ++i)
55 : {
56 12582915 : pattern[i] = random();
57 : }
58 3 : }
59 :
60 : char pattern[file_size];
61 : };
62 :
63 1 : void is_pow_2_test()
64 : {
65 : LOG(INFO, "===== is_pow_2_test");
66 : r_assert(is_pow2(1), "error");
67 : r_assert(is_pow2(2), "error");
68 : r_assert(is_pow2(4), "error");
69 : r_assert(is_pow2(8), "error");
70 : r_assert(is_pow2(131072), "error");
71 :
72 : r_assert(!is_pow2(0), "error");
73 : r_assert(!is_pow2(-2), "error");
74 : r_assert(!is_pow2(3), "error");
75 : r_assert(!is_pow2(7), "error");
76 : r_assert(!is_pow2(131071), "error");
77 :
78 : r_assert(is_pow2(1U << (sizeof(unsigned) - 1)), "error");
79 : r_assert(!is_pow2(numeric_limits<int>::max()), "error");
80 1 : }
81 :
82 1 : void align_test()
83 : {
84 : LOG(INFO, "===== align_test");
85 :
86 : r_assert(align_down(0, 16) == 0, "error");
87 : r_assert(align_down(1, 16) == 0, "error");
88 : r_assert(align_down(8, 16) == 0, "error");
89 : r_assert(align_down(15, 16) == 0, "error");
90 :
91 : r_assert(align_up(0, 16) == 0, "error");
92 : r_assert(align_up(1, 16) == 16, "error");
93 : r_assert(align_up(8, 16) == 16, "error");
94 : r_assert(align_up(15, 16) == 16, "error");
95 :
96 : r_assert(align_down(53, 16) == 48, "error");
97 : r_assert(align_up(53, 16) == 64, "error");
98 1 : }
99 :
100 1 : void open_test()
101 : {
102 : LOG(INFO, "===== open_test");
103 :
104 2 : file_ptr f1 = file::create("t1", 0, 0600);
105 : {
106 1 : bool exception_caught = false;
107 : try {
108 1 : file_ptr f2 = file::create("t1", 0, 0600);
109 2 : } catch (io_exception & ex) {
110 : LOG(INFO, "Expected exception: " << ex.what());
111 1 : exception_caught = true;
112 : }
113 : r_assert(exception_caught, "succeeded to create the same file twice!");
114 : }
115 1 : f1->close();
116 2 : file_ptr f2 = file::open("t1", 0);
117 1 : f2->close();
118 1 : }
119 :
120 1008 : void check_range(
121 : file & f,
122 : test_pattern const & pat,
123 : uint32_t start,
124 : uint32_t end,
125 : bool expect_except = false
126 : )
127 : {
128 : LOG(INFO, "checking range " << start << " " << end);
129 1008 : if (!expect_except) {
130 2012 : file::multi_buffer_ptr res = f.read(end - start, start);
131 2012 : shared_array<char> buf = shared_array<char>(new char[end - start]);
132 1006 : res->read(buf.get(), end - start, 0);
133 :
134 : r_assert(
135 : !memcmp(buf.get(), pat.pattern + start, end - start),
136 : "mismatch in range " << start << " " << end
137 : );
138 : } else {
139 : try {
140 2 : f.read(end - start, start);
141 4 : } catch (io_exception & ex) {
142 : LOG(INFO, "expected exception: " << ex.what());
143 : return;
144 : }
145 : r_assert(false, "expected exception not thrown");
146 : }
147 : }
148 :
149 2 : void prepare_file(string const & name, test_pattern const & pat)
150 : {
151 : //bypass any file realted code
152 2 : int fd = open(name.c_str(), O_WRONLY);
153 : r_assert(fd != -1, "open failed: " << fd);
154 :
155 : ssize_t res = write(fd, pat.pattern, file_size);
156 : r_assert(res >= 0, "pwrite: " << errno << " " << strerror(errno));
157 : r_assert(
158 : static_cast<ssize_t>(file_size) == res,
159 : "short write: " << res << " " << file_size
160 : );
161 0 :
162 : fd = close(fd);
163 : r_assert(fd == 0, "close: " << errno << " " << strerror(errno));
164 2 :
165 : }
166 2 :
167 : void read_test()
168 1 : {
169 : LOG(INFO, "===== read_test");
170 1 :
171 : //ensure it doesn't exist
172 : file_ptr f = file::create("read_test", 0, 0600);
173 2 : f->close();
174 1 :
175 : auto_ptr<test_pattern> pat(new test_pattern());
176 2 :
177 : prepare_file("read_test", *pat);
178 1 :
179 : f = file::open("read_test", O_RDONLY);
180 1 :
181 : check_range(*f, *pat, 0, file_size);
182 1 : check_range(*f, *pat, 0, 0);
183 1 :
184 : for (uint32_t i = 0; i < 1000; i++) {
185 1001 : uint32_t start = random() % file_size;
186 1000 : uint32_t end = start + random() % (file_size - start);
187 1000 :
188 : check_range(*f, *pat, start, end);
189 1000 : }
190 :
191 : //we expect an axception due to short read
192 : check_range(*f, *pat, file_size - 2, file_size + 1, true);
193 1 : check_range(*f, *pat, 2, file_size + 1, true);
194 1 :
195 : f->close();
196 : }
197 1 : typedef shared_ptr<multi_buffer> multi_buffer_ptr;
198 1 :
199 : pair<uint32_t, multi_buffer_ptr> gen_random_buffer(test_pattern const & pat)
200 : {
201 100 : uint32_t start = random() % (file_size - 1);
202 : uint32_t buf_start = start;
203 100 :
204 100 : multi_buffer::buffer_list bufs;
205 :
206 200 : uint32_t total_size = 0;
207 : for (uint32_t i = 0; i < 5; ++i) {
208 100 : uint32_t const shift = random() % 100 + 1;
209 600 : uint32_t const buf_end = min(file_size, buf_start + shift);
210 500 :
211 500 : bufs.push_back(multi_buffer::buffer_ptr(new buffer(shift)));
212 : memcpy(bufs.back()->get_data(), pat.pattern + buf_start, shift);
213 500 :
214 500 : total_size += shift;
215 :
216 500 : buf_start = buf_end;
217 :
218 500 : if (buf_end == file_size)
219 : break;
220 500 :
221 0 : }
222 :
223 : //add some shift from the left
224 : uint32_t const first_buf_shift = random() % bufs[0]->get_size();
225 : memset(bufs[0]->get_data(), 0, first_buf_shift);
226 100 :
227 100 : //cut some from the right
228 : uint32_t const last_buf_shift = (bufs.size() > 1)
229 : ? (random() % bufs.back()->get_size())
230 100 : : 0;
231 100 : memset(
232 200 : bufs.back()->get_data() + bufs.back()->get_size() - last_buf_shift,
233 : 0,
234 100 : last_buf_shift);
235 :
236 100 : uint32_t const res_off = start + first_buf_shift;
237 : multi_buffer_ptr res_buf(new multi_buffer(
238 100 : bufs,
239 : total_size - first_buf_shift - last_buf_shift, first_buf_shift
240 : )
241 : );
242 100 : return make_pair(res_off, res_buf);
243 300 : }
244 100 :
245 : void write_test()
246 : {
247 : LOG(INFO, "===== write_test");
248 :
249 1 : file_ptr f = file::create("write_test", O_RDWR, 0600);
250 : try {
251 2 : //ensure it doesn't exist
252 : auto_ptr<test_pattern> pat(new test_pattern());
253 :
254 2 : prepare_file("write_test", *pat);
255 :
256 1 : for (int i = 0; i < 100; ++i) {
257 : pair<uint32_t, multi_buffer_ptr> const & to_write =
258 101 : gen_random_buffer(*pat);
259 : f->write(*to_write.second, to_write.first);
260 200 : }
261 100 :
262 : check_range(*f, *pat, 0, file_size);
263 : } catch (io_exception const & ex) {
264 : r_assert(false, "Unexpected exception caught: " << ex.what());
265 0 : }
266 0 :
267 : f->close();
268 : }
269 1 :
270 1 : static int32_t iovec_len(vector<iovec> const & vec)
271 : {
272 2 : int32_t sum = 0;
273 : for (
274 2 : vector<iovec>::const_iterator i = vec.begin();
275 16 : i != vec.end();
276 2 : ++i
277 8 : )
278 : {
279 : sum += i->iov_len;
280 : }
281 6 : return sum;
282 : }
283 2 :
284 : class sync_file
285 : {
286 : public:
287 : sync_file(async_file & impl) :
288 : impl(impl)
289 1 : {
290 1 : }
291 :
292 1 : void open(
293 : int flags,
294 1 : int mode
295 : )
296 : {
297 : struct dummy_cb : public async_file::open_callback
298 : {
299 : dummy_cb(completion & comp) : comp(comp)
300 1 : {
301 1 : }
302 :
303 1 : virtual void open_completed(
304 : aio_open_req const & req,
305 1 : int err
306 : )
307 : {
308 : r_assert(
309 : err == 0,
310 : "err=" << err
311 : );
312 : comp.complete();
313 0 : }
314 1 :
315 1 : private:
316 : completion & comp;
317 : };
318 :
319 : completion comp;
320 : dummy_cb cb(comp);
321 2 :
322 2 : impl.submit_open(cb, flags, mode);
323 :
324 1 : comp.wait();
325 : }
326 1 :
327 1 : void pread(
328 : char * buf,
329 1 : off_t off,
330 : ssize_t size
331 : )
332 : {
333 : struct dummy_cb : public async_file::pread_callback
334 : {
335 : dummy_cb(completion & comp) : comp(comp)
336 1 : {
337 1 : }
338 :
339 1 : virtual void pread_completed(
340 : aio_pread_req const & req,
341 1 : ssize_t res,
342 : int err
343 : )
344 : {
345 : r_assert(
346 : res == req.size && err == 0,
347 : "res=" << res << " err=" << err
348 : );
349 : comp.complete();
350 0 : }
351 1 :
352 1 : private:
353 : completion & comp;
354 : };
355 :
356 : completion comp;
357 : dummy_cb cb(comp);
358 2 :
359 2 : impl.submit_pread(cb, buf, off, size);
360 :
361 1 : comp.wait();
362 : }
363 1 :
364 1 : //destroys the vector
365 : void preadv(
366 : std::vector<iovec> & iovecs,
367 1 : off_t off
368 : )
369 : {
370 : struct dummy_cb : public async_file::preadv_callback
371 : {
372 : dummy_cb(completion & comp) : comp(comp)
373 1 : {
374 1 : }
375 :
376 1 : virtual void preadv_completed(
377 : aio_preadv_req const & req,
378 1 : ssize_t res,
379 : int err
380 : )
381 : {
382 : r_assert(
383 : res == iovec_len(req.iovecs) && err == 0,
384 : "res=" << res << " err=" << err
385 : );
386 : comp.complete();
387 0 : }
388 1 :
389 1 : private:
390 : completion & comp;
391 : };
392 :
393 : completion comp;
394 : dummy_cb cb(comp);
395 2 :
396 2 : impl.submit_preadv(cb, iovecs, off);
397 :
398 1 : comp.wait();
399 : }
400 1 :
401 1 : void pwrite(
402 : char const * buf,
403 1 : off_t off,
404 : ssize_t size
405 : )
406 : {
407 : struct dummy_cb : public async_file::pwrite_callback
408 : {
409 : dummy_cb(completion & comp) : comp(comp)
410 1 : {
411 1 : }
412 :
413 1 : virtual void pwrite_completed(
414 : aio_pwrite_req const & req,
415 1 : ssize_t res,
416 : int err
417 : )
418 : {
419 : r_assert(
420 : res == req.size && err == 0,
421 : "res=" << res << " err=" << err
422 : );
423 : comp.complete();
424 0 : }
425 1 : private:
426 1 : completion & comp;
427 : };
428 :
429 : completion comp;
430 : dummy_cb cb(comp);
431 2 :
432 2 : impl.submit_pwrite(cb, buf, off, size);
433 :
434 1 : comp.wait();
435 : }
436 1 :
437 1 : //destroys the vector
438 : void pwritev(
439 : std::vector<iovec> & iovecs,
440 1 : off_t off
441 : )
442 : {
443 : struct dummy_cb : public async_file::pwritev_callback
444 : {
445 : dummy_cb(completion & comp) : comp(comp)
446 1 : {
447 1 : }
448 :
449 1 : virtual void pwritev_completed(
450 : aio_pwritev_req const & req,
451 1 : ssize_t res,
452 : int err
453 : )
454 : {
455 : r_assert(
456 : res == iovec_len(req.iovecs) && err == 0,
457 : "res=" << res << " err=" << err
458 : );
459 : comp.complete();
460 0 : }
461 1 :
462 1 : private:
463 : completion & comp;
464 :
465 : };
466 :
467 : completion comp;
468 : dummy_cb cb(comp);
469 2 :
470 2 : impl.submit_pwritev(cb, iovecs, off);
471 :
472 1 : comp.wait();
473 : }
474 1 :
475 1 : void close()
476 : {
477 1 : struct dummy_cb : public async_file::close_callback
478 : {
479 : dummy_cb(completion & comp) : comp(comp)
480 1 : {
481 1 : }
482 :
483 1 : virtual void close_completed(
484 : aio_close_req const & req,
485 1 : int res,
486 : int err
487 : )
488 : {
489 : r_assert(
490 : res == 0 && err == 0,
491 : "res=" << res << " err=" << err
492 : );
493 : comp.complete();
494 0 : }
495 1 :
496 1 : private:
497 : completion & comp;
498 : };
499 :
500 : completion comp;
501 : dummy_cb cb(comp);
502 2 :
503 2 : impl.submit_close(cb);
504 :
505 1 : comp.wait();
506 : }
507 1 :
508 1 : private:
509 : async_file & impl;
510 : };
511 :
512 : void aio_test()
513 : {
514 : LOG(INFO, "===== aio_test");
515 :
516 1 : aio_context ctx(15);
517 : async_file afile(ctx, "aio");
518 1 : sync_file sfile(afile);
519 :
520 2 : sfile.open(O_RDWR | O_CREAT | O_EXCL, 0600);
521 2 : file_ptr f = file::open("aio", O_RDONLY);
522 1 :
523 : auto_ptr<test_pattern> pat(new test_pattern());
524 1 : sfile.pwrite(pat->pattern, 0, 100);
525 2 : check_range(*f, *pat, 0, 100);
526 :
527 2 : {
528 1 : vector<iovec> vecs(3);
529 1 : vecs[0].iov_len = 50;
530 : vecs[0].iov_base = pat->pattern + 100;
531 : vecs[1].iov_len = 100;
532 2 : vecs[1].iov_base = pat->pattern + 150;
533 1 : vecs[2].iov_len = 50;
534 1 : vecs[2].iov_base = pat->pattern + 250;
535 1 :
536 1 : sfile.pwritev(vecs, 100);
537 1 : check_range(*f, *pat, 0, 300);
538 1 : }
539 :
540 1 : {
541 1 : char buf[200];
542 : vector<iovec> vecs(3);
543 : vecs[0].iov_len = 100;
544 : vecs[0].iov_base = buf;
545 : vecs[1].iov_len = 30;
546 2 : vecs[1].iov_base = buf + 100;
547 1 : vecs[2].iov_len = 70;
548 1 : vecs[2].iov_base = buf + 130;
549 1 :
550 1 : sfile.preadv(vecs, 50);
551 1 :
552 : r_assert(memcmp(buf, pat->pattern + 50, 200) == 0, "data corruption");
553 : }
554 1 :
555 : {
556 1 : char buf[300];
557 :
558 : sfile.pread(buf, 0, 300);
559 :
560 : r_assert(memcmp(buf, pat->pattern, 300) == 0, "data corruption");
561 : }
562 1 :
563 : sfile.close();
564 1 :
565 : check_range(*f, *pat, 0, 300);
566 : f->close();
567 1 :
568 : }
569 1 :
570 1 : int start_test(const int argc, const char *const *const argv)
571 : {
572 1 : scoped_test_enabler test_setup(argc, argv);
573 :
574 1 : Logger::getLogger("coherent.util")->setLevel(log_TRACE);
575 :
576 2 : is_pow_2_test();
577 : align_test();
578 1 : open_test();
579 : read_test();
580 1 : write_test();
581 1 : aio_test();
582 1 : return 0;
583 1 : }
584 1 :
585 1 : } // namespace unittests
586 1 : } // namespace util
587 : } // namespace coherent
588 :
589 : int main(const int argc, const char * const * const argv)
590 : {
591 : return coherent::util::unittests::start_test(argc, argv);
592 : }
|