LCOV - code coverage report
Current view: top level - util/test - file.cpp (source / functions) Hit Total Coverage
Test: CoherentDB code coverage Lines: 207 217 95.4 %
Date: 2011-02-13 Functions: 40 46 87.0 %

          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             : }

Generated by: LCOV version 1.9