libmoost
/home/mhx/git/github/libmoost/test/io/file_backed_data_source.cpp
Go to the documentation of this file.
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()