libmoost
|
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()