libmoost
/home/mhx/git/github/libmoost/test/container/memory_mapped_dataset.cpp
Go to the documentation of this file.
00001 /* vim:set ts=3 sw=3 sts=3 et: */
00028 #include <boost/test/unit_test.hpp>
00029 #include <boost/filesystem.hpp>
00030 #include <boost/scoped_ptr.hpp>
00031 
00032 #include "../../include/moost/testing/error_matcher.hpp"
00033 #include "../../include/moost/container/memory_mapped_dataset.hpp"
00034 
00035 using namespace moost::container;
00036 
00037 typedef moost::testing::error_matcher matches;
00038 
00039 BOOST_AUTO_TEST_SUITE(memory_mapped_dataset_test)
00040 
00041 struct scoped_tempfile
00042 {
00043    scoped_tempfile(const std::string& name, bool keep = false)
00044       : m_path(name)
00045       , m_keep(keep)
00046    {
00047    }
00048 
00049    ~scoped_tempfile()
00050    {
00051       if (!keep())
00052       {
00053          boost::filesystem::remove(m_path);
00054       }
00055    }
00056 
00057    bool keep() const
00058    {
00059       const char *env = std::getenv("KEEP_TEMP");
00060 
00061       if (env)
00062       {
00063          try
00064          {
00065             return boost::lexical_cast<bool>(env);
00066          }
00067          catch (const boost::bad_lexical_cast& e)
00068          {
00069             BOOST_TEST_MESSAGE("invalid value [" << env << "] for KEEP_TEMP (" << e.what() << ")");
00070          }
00071       }
00072 
00073       return m_keep;
00074    }
00075 
00076    std::string path() const
00077    {
00078       return m_path.string();
00079    }
00080 
00081    bool exists() const
00082    {
00083       return boost::filesystem::exists(m_path);
00084    }
00085 
00086 private:
00087    const boost::filesystem::path m_path;
00088    const bool m_keep;
00089 };
00090 
00091 struct test_val
00092 {
00093    char str[16];
00094 };
00095 
00096 struct const_hash
00097 {
00098    template <typename T>
00099    size_t operator() (const T&) const
00100    {
00101       return 4711U;
00102    }
00103 };
00104 
00105 struct test_dataset : memory_mapped_dataset
00106 {
00107    struct writer : public memory_mapped_dataset::writer
00108    {
00109       writer(const std::string& file)
00110          : memory_mapped_dataset::writer(file, "test_dataset", 4711)
00111       {
00112       }
00113    };
00114 
00115    test_dataset(const std::string& file)
00116       : memory_mapped_dataset(file, "test_dataset", 4711)
00117    {
00118    }
00119 };
00120 
00121 template <class ArchiveType>
00122 void mmd_archive_test(const scoped_tempfile& dsfile)
00123 {
00124    {
00125       test_dataset::writer wr(dsfile.path());
00126       typename ArchiveType::writer arch_wr(wr, "archive");
00127 
00128       std::map<std::string, boost::int32_t> testmap;
00129       boost::uint64_t testval = 4711;
00130 
00131       testmap["answer"] = 42;
00132       testmap["pi"] = 4;
00133 
00134       arch_wr << testmap << testval;
00135 
00136       arch_wr.commit();
00137       wr.close();
00138    }
00139    BOOST_REQUIRE(dsfile.exists());
00140 
00141    test_dataset ds(dsfile.path());
00142    ArchiveType arch(ds, "archive");
00143 
00144    std::map<std::string, boost::int32_t> map;
00145    boost::uint64_t val;
00146 
00147    arch >> map >> val;
00148 
00149    BOOST_CHECK_EQUAL(map.size(), 2);
00150    BOOST_CHECK_EQUAL(map["answer"], 42);
00151    BOOST_CHECK_EQUAL(map["pi"], 4);
00152    BOOST_CHECK_EQUAL(val, 4711);
00153 }
00154 
00155 BOOST_AUTO_TEST_CASE(test_mmd_archive)
00156 {
00157    scoped_tempfile dsfile("archive.mmd");
00158 
00159    mmd_archive_test<mmd_archive>(dsfile);
00160 
00161    test_dataset ds(dsfile.path());
00162    boost::shared_ptr<mmd_binary_archive> bin;
00163    BOOST_CHECK_EXCEPTION(bin.reset(new mmd_binary_archive(ds, "archive")), std::runtime_error, matches("archive\\.mmd: invalid section type mmd_archive:text \\(expected mmd_archive:binary\\)"));
00164 }
00165 
00166 BOOST_AUTO_TEST_CASE(test_mmd_archive_binary)
00167 {
00168    scoped_tempfile dsfile("archive_binary.mmd");
00169 
00170    mmd_archive_test<mmd_binary_archive>(dsfile);
00171 
00172    test_dataset ds(dsfile.path());
00173    boost::shared_ptr<mmd_text_archive> bin;
00174    BOOST_CHECK_EXCEPTION(bin.reset(new mmd_text_archive(ds, "archive")), std::runtime_error, matches("archive_binary\\.mmd: invalid section type mmd_archive:binary \\(expected mmd_archive:text\\)"));
00175 }
00176 
00177 BOOST_AUTO_TEST_CASE(test_mmd_archive_generic)
00178 {
00179    scoped_tempfile dsfile("archive_generic.mmd");
00180    mmd_archive_test< mmd_generic_archive<TextArchivePolicy> >(dsfile);
00181 }
00182 
00183 BOOST_AUTO_TEST_CASE(test_mmd_vector)
00184 {
00185    scoped_tempfile dsfile("vector.mmd");
00186    {
00187       test_dataset::writer wr(dsfile.path());
00188       mmd_vector<boost::uint64_t>::writer vec_wr(wr, "vec");
00189 
00190       for (boost::uint64_t i = 0; i < 10; ++i)
00191       {
00192          vec_wr << 3*i;
00193       }
00194 
00195       vec_wr.commit();
00196       wr.close();
00197    }
00198    BOOST_REQUIRE(dsfile.exists());
00199 
00200    test_dataset ds(dsfile.path());
00201    mmd_vector<boost::uint64_t> vec(ds, "vec");
00202 
00203    for (boost::uint64_t i = 0; i < 10; ++i)
00204    {
00205       BOOST_CHECK_EQUAL(vec[i], 3*i);
00206    }
00207 
00208    boost::shared_ptr< mmd_dense_hash_map<int, int> > dhm;
00209    BOOST_CHECK_EXCEPTION(dhm.reset(new mmd_dense_hash_map<int, int>(ds, "vec")), std::runtime_error, matches("vector.mmd: invalid section type mmd_vector \\(expected mmd_dense_hash_map\\)"));
00210 }
00211 
00212 template <class HashFcn = MMD_DEFAULT_HASH_FCN<boost::uint32_t> >
00213 class test_dense_hash_map
00214 {
00215    typedef mmd_dense_hash_map<boost::uint32_t, boost::uint32_t, HashFcn> map_type;
00216 
00217 public:
00218    test_dense_hash_map(const scoped_tempfile& dsfile, size_t elements, float max_pop_ratio)
00219       : m_dsfile(dsfile)
00220       , m_elements(elements)
00221    {
00222       test_dataset::writer wr(m_dsfile.path());
00223       typename map_type::writer map_wr(wr, "dense", std::numeric_limits<boost::uint32_t>::max(), max_pop_ratio);
00224 
00225       for (size_t i = 0; i < m_elements; ++i)
00226       {
00227          std::pair<boost::uint32_t, boost::uint32_t> p;
00228          p.first = 7*i;
00229          p.second = 42 + i;
00230          map_wr << p;
00231       }
00232 
00233       BOOST_CHECK_EQUAL(map_wr.size(), m_elements);
00234 
00235       map_wr.commit();
00236       wr.close();
00237    }
00238 
00239    void operator() ()
00240    {
00241       BOOST_REQUIRE(m_dsfile.exists());
00242 
00243       test_dataset ds(m_dsfile.path());
00244       m_map.reset(new map_type(ds, "dense"));
00245 
00246       BOOST_CHECK_EQUAL(map().size(), m_elements);
00247       BOOST_CHECK_EQUAL(map().empty(), m_elements == 0);
00248 
00249       for (size_t i = 0; i < map().size(); ++i)
00250       {
00251          BOOST_CHECK_EQUAL(map()[7*i], 42 + i);
00252       }
00253 
00254       for (size_t i = 0; i < 8*map().size(); ++i)
00255       {
00256          typename map_type::const_iterator it = map().find(static_cast<typename map_type::key_type>(i));
00257          if (i % 7 == 0 && i < 7*map().size())
00258          {
00259             BOOST_REQUIRE(it != map().end());
00260             BOOST_CHECK_EQUAL(it->first, i);
00261             BOOST_CHECK_EQUAL(it->second, 42 + i/7);
00262          }
00263          else
00264          {
00265             BOOST_CHECK(it == map().end());
00266          }
00267       }
00268 
00269       std::vector<bool> existence(m_elements);
00270       size_t count = 0;
00271 
00272       for (typename map_type::const_iterator it = map().begin(); it != map().end(); ++it)
00273       {
00274          BOOST_CHECK_EQUAL(it->first % 7, 0U);
00275          const typename map_type::value_type& v = *it;
00276          BOOST_CHECK_EQUAL(it->second, 42 + v.first/7);
00277          BOOST_CHECK(!existence[v.first/7]);
00278          BOOST_CHECK(it->first/7 < m_elements);
00279          existence[it->first/7] = true;
00280          ++count;
00281       }
00282 
00283       BOOST_CHECK_EQUAL(count, m_elements);
00284    }
00285 
00286    const map_type& map() const
00287    {
00288       return *m_map;
00289    }
00290 
00291 private:
00292    const scoped_tempfile& m_dsfile;
00293    const size_t m_elements;
00294    boost::scoped_ptr<map_type> m_map;
00295 };
00296 
00297 BOOST_AUTO_TEST_CASE(test_mmd_dense_hash_map)
00298 {
00299    scoped_tempfile dsfile("dense_hash_map.mmd");
00300    test_dense_hash_map<> test(dsfile, 10, 0.8);
00301    test();
00302 }
00303 
00304 BOOST_AUTO_TEST_CASE(test_mmd_dense_hash_map_collision)
00305 {
00306    scoped_tempfile dsfile("dense_hash_map_collision.mmd");
00307    test_dense_hash_map<const_hash> test(dsfile, 10, 0.8);
00308    test();
00309 }
00310 
00311 BOOST_AUTO_TEST_CASE(test_mmd_dense_hash_map_full)
00312 {
00313    scoped_tempfile dsfile("dense_hash_map_full.mmd");
00314    test_dense_hash_map<> test(dsfile, 16, 0.99);
00315    test();
00316    BOOST_CHECK_EQUAL(test.map().capacity(), 16U);
00317    BOOST_CHECK_EQUAL(test.map().size(), 16U);
00318 }
00319 
00320 BOOST_AUTO_TEST_CASE(test_mmd_dense_hash_map_variety)
00321 {
00322    size_t elements[] = { 0, 1, 2, 3, 4, 128, 5000 };
00323    float max_pop_ratio[] = {0.01, 0.1, 0.5, 0.9, 0.99};
00324 
00325    for (size_t ei = 0; ei < sizeof(elements)/sizeof(elements[0]); ++ei)
00326    {
00327       for (size_t ri = 0; ri < sizeof(max_pop_ratio)/sizeof(max_pop_ratio[0]); ++ri)
00328       {
00329          std::ostringstream name;
00330          name << "dense_hash_map_" << elements[ei] << "_" << max_pop_ratio[ri] << ".mmd";
00331          scoped_tempfile dsfile(name.str());
00332          test_dense_hash_map<> test(dsfile, elements[ei], max_pop_ratio[ri]);
00333          test();
00334       }
00335    }
00336 }
00337 
00338 BOOST_AUTO_TEST_CASE(test_mmd_dense_hash_map_error)
00339 {
00340    typedef mmd_dense_hash_map<boost::int32_t, test_val, const_hash> map_type;
00341 
00342    test_val x[3] = {
00343       { "Hello World!" },
00344       { "012345678901234" },
00345       { "" },
00346    };
00347 
00348    scoped_tempfile dsfile("dense_hash_map_error.mmd");
00349    {
00350       test_dataset::writer wr(dsfile.path());
00351       boost::scoped_ptr<map_type::writer> map_wr;
00352 
00353       BOOST_CHECK_EXCEPTION(map_wr.reset(new map_type::writer(wr, "dense", -13, 1.0)), std::runtime_error, matches("invalid max_population_ratio.*"));
00354       BOOST_CHECK_EXCEPTION(map_wr.reset(new map_type::writer(wr, "dense", -13, 1e-4)), std::runtime_error, matches("invalid max_population_ratio.*"));
00355       map_wr.reset(new map_type::writer(wr, "dense", -13));
00356       BOOST_CHECK_EXCEPTION(*map_wr << std::make_pair(-13, x[0]), std::runtime_error, matches("attempt to insert empty key"));
00357       *map_wr << std::make_pair(-12, x[0]);
00358       BOOST_CHECK_EXCEPTION(*map_wr << std::make_pair(-13, x[1]), std::runtime_error, matches("attempt to insert empty key"));
00359       *map_wr << std::make_pair(-12, x[1]);
00360       BOOST_CHECK_EXCEPTION(map_wr->commit(), std::runtime_error, matches("duplicate key detected"));
00361    }
00362    {
00363       test_dataset::writer wr(dsfile.path());
00364       map_type::writer map_wr(wr, "dense", -13);
00365       map_wr << std::make_pair(-12, x[0]);
00366       map_wr << std::make_pair(12, x[1]);
00367       map_wr << std::make_pair(0, x[2]);
00368       map_wr.commit();
00369       wr.close();
00370    }
00371    BOOST_REQUIRE(dsfile.exists());
00372 
00373    test_dataset ds(dsfile.path());
00374 
00375    mmd_dense_hash_map<boost::int32_t, boost::int32_t, const_hash> map1;
00376    BOOST_CHECK_EXCEPTION(map1.set(ds, "dense"), std::runtime_error, matches("wrong mapped size.*"));
00377 
00378    mmd_dense_hash_map<boost::int64_t, test_val, const_hash> map2;
00379    BOOST_CHECK_EXCEPTION(map2.set(ds, "dense"), std::runtime_error, matches("wrong key size.*"));
00380 
00381    mmd_dense_hash_map<boost::int32_t, test_val, const_hash> map;
00382    map.set(ds, "dense");
00383    test_val target;
00384    BOOST_CHECK_EXCEPTION(target = map[33], std::runtime_error, matches("no such key"));
00385 
00386    boost::shared_ptr<test_dataset> doesnotexist;
00387    BOOST_CHECK_EXCEPTION(doesnotexist.reset(new test_dataset("/does/not/live/here.mmd")), BOOST_IOSTREAMS_FAILURE, matches("/does/not/live/here\\.mmd:.*"));
00388 }
00389 
00390 BOOST_AUTO_TEST_SUITE_END()