libmoost
|
00001 /* vim:set ts=3 sw=3 sts=3 et: */ 00028 #include <map> 00029 #include <fstream> 00030 #include <cstdio> 00031 #include <stdexcept> 00032 00033 #include <boost/test/unit_test.hpp> 00034 #include <boost/test/test_tools.hpp> 00035 #include <boost/filesystem.hpp> 00036 #include <boost/shared_ptr.hpp> 00037 00038 #include "../../include/moost/io/file_backed_data_source.hpp" 00039 #include "../../include/moost/testing/test_directory_creator.hpp" 00040 00041 using namespace moost::io; 00042 using namespace std; 00043 00044 BOOST_AUTO_TEST_SUITE( file_backed_data_source_test ) 00045 00046 namespace 00047 { 00048 const int MIN_SECS = 100; 00049 const bool THROW_ON_FIRST = false; 00050 const double MIN_PROPORTION = 0.8; 00051 00052 char const VALID_OPTIONS_XML[] = { 00053 "<FileBackedDataSource>\n" 00054 " <Filepath>testpath</Filepath>\n" 00055 " <MinSecsSinceLastLoad>100</MinSecsSinceLastLoad>\n" 00056 " <ThrowOnFirstLoadFail>false</ThrowOnFirstLoadFail>\n" 00057 " <MinProportionOfLastLoad>0.8</MinProportionOfLastLoad>\n" 00058 "</FileBackedDataSource>" 00059 }; 00060 00061 char const SOME_OPTIONS_XML[] = { 00062 "<FileBackedDataSource>\n" 00063 " <Filepath>testpath</Filepath>\n" 00064 " <MinSecsSinceLastLoad>100</MinSecsSinceLastLoad>\n" 00065 " <ThrowOnFirstLoadFail>false</ThrowOnFirstLoadFail>\n" 00066 "</FileBackedDataSource>" 00067 }; 00068 00069 char const INVALID_OPTIONS_XML[] = { 00070 "<FileBackedDataSource>\n" 00071 " <Filepath>testpath</Filepath>\n" 00072 " <MinSecsSinceLastLoad>100</MinSecsSinceLastLoad>\n" 00073 " <ThrowOnFirstLoadFail>zog</ThrowOnFirstLoadFail>\n" 00074 " <MinProportionOfLastLoad>0.8</MinProportionOfLastLoad>\n" 00075 "</FileBackedDataSource>" 00076 }; 00077 00078 char const WRONG_OPTIONS_XML[] = { 00079 "<FileBackedDataSource>\n" 00080 " <Filepath>testpath</Filepath>\n" 00081 " <MinSecsSinceLastLoad>100</MinSecsSinceLastLoad>\n" 00082 " <ThrowOnFirstLoadFail>false</ThrowOnFirstLoadFail>\n" 00083 " <MinProportionOfLastLoad>0.8</MinProportionOfLastLoad>\n" 00084 " <Zog>0.8</Zog>\n" 00085 "</FileBackedDataSource>" 00086 }; 00087 } 00088 00089 class IncrementingIntDataPolicy : public data_policy_base<int> 00090 { 00091 public: 00092 IncrementingIntDataPolicy(int iThrow = -1) : m_i(0), m_iThrow(iThrow) { } 00093 00094 std::string getName() const 00095 { 00096 return "IncrementingIntData"; 00097 } 00098 00099 boost::shared_ptr<int> loadFromFile(const std::string& /*filepath*/) const 00100 { 00101 // doesn't actually read from file! 00102 if (m_i == m_iThrow) 00103 { 00104 ++m_i; 00105 throw runtime_error("this policy just threw"); 00106 } 00107 boost::shared_ptr<int> pData(new int); 00108 *pData = m_i; 00109 ++m_i; 00110 return pData; 00111 } 00112 00113 size_t size(boost::shared_ptr<int> /*pData*/) const 00114 { 00115 return m_i; 00116 } 00117 00118 private: 00119 mutable int m_i, m_iThrow; 00120 }; 00121 00122 struct Fixture 00123 { 00124 typedef file_backed_data_source<IncrementingIntDataPolicy> int_source_t; 00125 00126 Fixture() : m_tdc("MoostFileBackedDataSourceTestDirectory") 00127 { 00128 00129 } 00130 00131 virtual ~Fixture() 00132 { 00133 } 00134 00135 void waitASecond() const 00136 { 00137 boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); 00138 } 00139 00140 void writeToFile(const std::string& filepath, const std::string& toWrite, bool wait) const 00141 { 00142 if (wait) // wait briefly so we know that the write time of the file will change 00143 waitASecond(); 00144 00145 ofstream ofs(filepath.c_str()); 00146 ofs << toWrite; 00147 ofs.close(); 00148 00149 // useful for debug to see the time that the file_watcher is seeing 00150 MLOG_DEFAULT_INFO(filepath << " updated at " << boost::filesystem::last_write_time(filepath) << endl); 00151 00152 if (wait) // give the source a chance to update 00153 waitASecond(); 00154 } 00155 00156 void testReloadFails(boost::shared_ptr<int_source_t> pSource, const std::string& filepath) const 00157 { 00158 writeToFile(filepath, "1", false); 00159 00160 pSource->load(); 00161 00162 size_t currentSize = pSource->size(); 00163 boost::shared_ptr<int> pData = pSource->get_shared_ptr(); 00164 int currentData = *pData; 00165 00166 writeToFile(filepath, "2", true); // force a reload by updating the file 00167 00168 BOOST_CHECK(pSource->size() == currentSize); 00169 00170 boost::shared_ptr<int> pNewData = pSource->get_shared_ptr(); 00171 BOOST_CHECK(*pNewData == currentData); 00172 } 00173 00174 moost::testing::test_directory_creator m_tdc; 00175 file_backed_data_source_factory m_sourceFactory; 00176 }; 00177 00178 BOOST_FIXTURE_TEST_CASE( test_initial_load, Fixture ) 00179 { 00180 file_backed_data_source_config conf; 00181 IncrementingIntDataPolicy dataPolicy; 00182 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00183 00184 BOOST_CHECK(pSource->size() == 0); 00185 00186 pSource->load(); 00187 00188 BOOST_CHECK(pSource->size() == 1); 00189 00190 boost::shared_ptr<int> pData = pSource->get_shared_ptr(); 00191 BOOST_CHECK(*pData == 0); 00192 } 00193 00194 BOOST_FIXTURE_TEST_CASE( test_auto_reload, Fixture ) 00195 { 00196 string filepath(m_tdc.GetFilePath("test_auto_reload")); 00197 writeToFile(filepath, "1", false); 00198 00199 file_backed_data_source_config conf; 00200 conf.minSecsSinceLastLoad = 0; // so we can force a reload straight away 00201 conf.filepath = filepath; 00202 IncrementingIntDataPolicy dataPolicy; 00203 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00204 00205 pSource->load(); 00206 00207 BOOST_CHECK(pSource->size() == 1); 00208 00209 boost::shared_ptr<int> pData = pSource->get_shared_ptr(); 00210 BOOST_CHECK(*pData == 0); 00211 00212 writeToFile(filepath, "2", true); // force a reload by updating the file 00213 00214 BOOST_CHECK(pSource->size() == 2); 00215 00216 boost::shared_ptr<int> pNewData = pSource->get_shared_ptr(); 00217 BOOST_CHECK(*pNewData == 1); 00218 BOOST_CHECK(*pData == 0); // old pointer to data remains unaffected 00219 } 00220 00221 BOOST_FIXTURE_TEST_CASE( test_first_load_fail_throw, Fixture ) 00222 { 00223 file_backed_data_source_config conf; 00224 00225 // configure policy to throw on first load 00226 IncrementingIntDataPolicy dataPolicy(0); 00227 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00228 00229 BOOST_CHECK_THROW(pSource->load(), std::runtime_error); 00230 } 00231 00232 BOOST_FIXTURE_TEST_CASE( test_first_load_fail_log, Fixture ) 00233 { 00234 file_backed_data_source_config conf; 00235 conf.throwOnFirstLoadFail = false; 00236 00237 // configure policy to throw on first load 00238 IncrementingIntDataPolicy dataPolicy(0); 00239 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00240 00241 BOOST_CHECK_NO_THROW(pSource->load()); 00242 } 00243 00244 BOOST_FIXTURE_TEST_CASE( test_reload_too_soon, Fixture ) 00245 { 00246 string filepath(m_tdc.GetFilePath("test_reload_too_soon")); 00247 00248 file_backed_data_source_config conf; 00249 conf.minSecsSinceLastLoad = 60; 00250 conf.filepath = filepath; 00251 IncrementingIntDataPolicy dataPolicy; 00252 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00253 00254 testReloadFails(pSource, filepath); 00255 } 00256 00257 BOOST_FIXTURE_TEST_CASE( test_new_data_too_small, Fixture ) 00258 { 00259 string filepath(m_tdc.GetFilePath("test_new_data_too_small")); 00260 00261 file_backed_data_source_config conf; 00262 conf.minProportionOfLastLoad = 2; // usually this would be less than 1 of course 00263 conf.filepath = filepath; 00264 IncrementingIntDataPolicy dataPolicy; 00265 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00266 00267 testReloadFails(pSource, filepath); 00268 } 00269 00270 BOOST_FIXTURE_TEST_CASE( test_load_throws, Fixture ) 00271 { 00272 string filepath(m_tdc.GetFilePath("test_load_throws")); 00273 00274 file_backed_data_source_config conf; 00275 00276 // configure policy to throw on second load 00277 IncrementingIntDataPolicy dataPolicy(1); 00278 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00279 00280 testReloadFails(pSource, filepath); 00281 } 00282 00283 BOOST_FIXTURE_TEST_CASE( test_register, Fixture ) 00284 { 00285 file_backed_data_source_config conf; 00286 IncrementingIntDataPolicy dataPolicy; 00287 boost::shared_ptr<int_source_t> pSource = m_sourceFactory.createFromConfig(dataPolicy, conf); 00288 boost::shared_ptr<int_source_t> pOther = m_sourceFactory.createFromConfig(dataPolicy, conf); 00289 00290 pSource->registerLoadable(pOther); 00291 pSource->load(); 00292 00293 BOOST_CHECK(pOther->size() == 1); 00294 00295 boost::shared_ptr<int> pData = pOther->get_shared_ptr(); 00296 BOOST_CHECK(*pData == 0); 00297 } 00298 00299 BOOST_FIXTURE_TEST_CASE( test_valid_options_from_xml, Fixture ) 00300 { 00301 string filepath(m_tdc.GetFilePath("test_options_from_xml")); 00302 00303 std::string xml(VALID_OPTIONS_XML); 00304 std::ofstream ofs(filepath.c_str()); 00305 ofs << xml; 00306 ofs.close(); 00307 00308 file_backed_data_source_config_factory factory; 00309 file_backed_data_source_config conf = factory.createFromXml(filepath); 00310 00311 BOOST_CHECK(conf.minSecsSinceLastLoad == MIN_SECS); 00312 BOOST_CHECK(conf.throwOnFirstLoadFail == THROW_ON_FIRST); 00313 BOOST_CHECK(conf.minProportionOfLastLoad == MIN_PROPORTION); 00314 } 00315 00316 BOOST_FIXTURE_TEST_CASE( test_some_valid_options_from_xml, Fixture ) 00317 { 00318 string filepath(m_tdc.GetFilePath("test_some_valid_options_from_xml")); 00319 00320 std::string xml(SOME_OPTIONS_XML); 00321 std::ofstream ofs(filepath.c_str()); 00322 ofs << xml; 00323 ofs.close(); 00324 00325 file_backed_data_source_config_factory factory; 00326 file_backed_data_source_config conf = factory.createFromXml(filepath); 00327 00328 BOOST_CHECK(conf.minSecsSinceLastLoad == MIN_SECS); 00329 BOOST_CHECK(conf.throwOnFirstLoadFail == THROW_ON_FIRST); 00330 } 00331 00332 BOOST_FIXTURE_TEST_CASE( test_invalid_options_from_xml, Fixture ) 00333 { 00334 string filepath(m_tdc.GetFilePath("test_invalid_options_from_xml")); 00335 00336 std::string xml(INVALID_OPTIONS_XML); 00337 std::ofstream ofs(filepath.c_str()); 00338 ofs << xml; 00339 ofs.close(); 00340 00341 file_backed_data_source_config_factory factory; 00342 BOOST_CHECK_THROW(factory.createFromXml(filepath), std::runtime_error); 00343 } 00344 00345 BOOST_FIXTURE_TEST_CASE( test_wrong_options_from_xml, Fixture ) 00346 { 00347 string filepath(m_tdc.GetFilePath("test_wrong_options_from_xml")); 00348 00349 std::string xml(WRONG_OPTIONS_XML); 00350 std::ofstream ofs(filepath.c_str()); 00351 ofs << xml; 00352 ofs.close(); 00353 00354 file_backed_data_source_config_factory factory; 00355 BOOST_CHECK_THROW(factory.createFromXml(filepath), std::runtime_error); 00356 } 00357 00358 BOOST_AUTO_TEST_SUITE_END()