whitebox-Component.C

Go to the documentation of this file.
00001 /*!@file TestSuite/whitebox-Component.C */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file:
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/TestSuite/whitebox-Component.C $
00035 // $Id: whitebox-Component.C 14376 2011-01-11 02:44:34Z pez $
00036 //
00037 
00038 #ifndef TESTSUITE_WHITEBOX_COMPONENT_C_DEFINED
00039 #define TESTSUITE_WHITEBOX_COMPONENT_C_DEFINED
00040 
00041 #include "Component/ModelManager.H"
00042 #include "Component/ModelOptionDef.H"
00043 #include "Component/ModelParam.H"
00044 #include "Component/OptionManager.H"
00045 #include "Component/ParamClient.H"
00046 #include "Component/ParamMap.H"
00047 #include "TestSuite/TestSuite.H"
00048 #include "Util/sformat.H"
00049 
00050 #include <exception>
00051 #include <vector>
00052 
00053 namespace
00054 {
00055   // Dummy implementation of ParamClient that we can use to make sure
00056   // our model params are behaving as expected
00057   class TestParamClient : public ParamClient
00058   {
00059   public:
00060     bool param_has_registered,
00061       param_has_unregistered,
00062       param_has_changed,
00063       should_reject_changes;
00064 
00065     TestParamClient() :
00066       param_has_registered(false),
00067       param_has_unregistered(false),
00068       param_has_changed(false),
00069       should_reject_changes(false) {}
00070 
00071     virtual ~TestParamClient() {}
00072 
00073     virtual void registerParam(ModelParamBase*)
00074     { param_has_registered = true; }
00075 
00076     virtual void registerOptionedParam(OptionedModelParam*, int)
00077     { param_has_registered = true; }
00078 
00079     virtual void unregisterParam(const ModelParamBase*)
00080     { param_has_unregistered = true; }
00081 
00082     virtual void paramChanged(ModelParamBase* param,
00083                               const bool valueChanged,
00084                               ParamClient::ChangeStatus* status)
00085     {
00086       param_has_changed = true;
00087       if (should_reject_changes)
00088         *status = ParamClient::CHANGE_REJECTED;
00089     }
00090   };
00091 
00092   const ModelOptionDef OPT_testOption =
00093     {
00094       MODOPT_ARG(int),
00095       "testOption",
00096       &MOC_GENERAL,
00097       OPTEXP_CORE,
00098       "this is a test option",
00099       "call-it-like-this",
00100       's',
00101       NULL,
00102       "1"
00103     };
00104 
00105   class TestComponent : public ModelComponent
00106   {
00107   public:
00108     TestComponent(OptionManager& m) :
00109       ModelComponent(m, "test", "test") {}
00110   };
00111 
00112   // Like TestComponent, but with virtual inheritance
00113   class TestComponent2 : public virtual ModelComponent
00114   {
00115   public:
00116     TestComponent2(OptionManager& m) :
00117       ModelComponent(m, "test2", "test2") {}
00118   };
00119 
00120   class SlaveComponent : public ModelComponent
00121   {
00122   public:
00123     SlaveComponent(OptionManager& m) :
00124       ModelComponent(m, "slave", "slave"),
00125       itsParam(&OPT_testOption, this)
00126     {}
00127 
00128     OModelParam<int> itsParam;
00129   };
00130 
00131   class MasterComponent : public ModelComponent
00132   {
00133   public:
00134     MasterComponent(OptionManager& m) :
00135       ModelComponent(m, "master", "master"),
00136       itsSub(new SlaveComponent(m)),
00137       itsParam(&OPT_testOption, this)
00138     {
00139       this->addSubComponent(itsSub);
00140     }
00141 
00142     virtual void paramChanged(ModelParamBase* const param,
00143                               const bool valueChanged,
00144                               ParamClient::ChangeStatus* status)
00145     {
00146       if (param == &itsParam)
00147         {
00148           itsSub->setModelParamString("testOption", "42");
00149         }
00150     }
00151 
00152     nub::ref<SlaveComponent> itsSub;
00153 
00154     OModelParam<int> itsParam;
00155   };
00156 }
00157 
00158 struct WeirdParamType
00159 {
00160   int x;
00161 
00162   bool operator==(const WeirdParamType& that) { return this->x == that.x; }
00163 };
00164 
00165 void convertFromString(const std::string& str, WeirdParamType& p)
00166 {
00167   // this converter function always throws an exception, but it messes
00168   // with p's value beforehand; we do this so that we can make sure
00169   // that calling code is careful so that exceptions don't
00170   // unnecessarily garble param values
00171   p.x = -1;
00172   conversion_error::raise<WeirdParamType>(str);
00173 }
00174 
00175 std::string convertToString(const WeirdParamType& p)
00176 {
00177   return convertToString(p.x);
00178 }
00179 
00180 static void modelparam_xx_getset_xx_1(TestSuite& suite)
00181 {
00182   // do some idiot checks to make sure trivial things are working as
00183   // expected (e.g. set a value, then read it back... this tests that
00184   // the string conversions are working properly)
00185 
00186   TestParamClient client;
00187 
00188   NModelParam<int> p1("foo", &client, 1);
00189 
00190   p1.setVal(2);
00191   REQUIRE_EQ(p1.getVal(), 2);
00192 
00193   p1.setValString("13579");
00194   REQUIRE_EQ(p1.getVal(), 13579);
00195 
00196   p1.setVal(-45);
00197   REQUIRE_EQ(p1.getValString(), "-45");
00198 
00199   // now check that we correctly throw an exception for bogus
00200   // conversions:
00201 
00202   bool caught;
00203 
00204   try                         { caught = false; p1.setValString("abcde"); }
00205   catch (conversion_error& e) { caught = true; }
00206   REQUIRE(caught);
00207 
00208   try                         { caught = false; p1.setValString(""); }
00209   catch (conversion_error& e) { caught = true; }
00210   REQUIRE(caught);
00211 }
00212 
00213 static void modelparam_xx_getset_xx_2(TestSuite& suite)
00214 {
00215   TestParamClient client;
00216 
00217   WeirdParamType v; v.x = 42;
00218   NModelParam<WeirdParamType> p1("foo", &client, v);
00219 
00220   REQUIRE_EQ(p1.getValString(), std::string("42"));
00221 
00222   bool caught;
00223   try                         { caught = false; p1.setValString(""); }
00224   catch (conversion_error& e) { caught = true; }
00225   REQUIRE(caught);
00226 
00227   // make sure that p1's value is still the same even though an
00228   // exception was thrown during the setValString() call
00229   REQUIRE_EQ(p1.getValString(), std::string("42"));
00230 }
00231 
00232 static void modelparam_xx_getset_xx_3(TestSuite& suite)
00233 {
00234   // Here we are testing the ParamClient::ChangeStatus system where
00235   // implementors of paramChanged() can reject a particular parameter
00236   // change by setting *status to ParamClient::CHANGE_REJECTED
00237 
00238   TestParamClient client;
00239 
00240   NModelParam<int> p1("foo", &client, 0);
00241   REQUIRE_EQ(p1.getVal(), 0);
00242 
00243   p1.setVal(1);
00244   REQUIRE_EQ(p1.getVal(), 1);
00245 
00246   client.should_reject_changes = true;
00247   p1.setVal(2);
00248   // the change should be rejected in paramChanged() which should
00249   // cause NModelParam to restore the original value in p1
00250   REQUIRE_EQ(p1.getVal(), 1);
00251 
00252   client.should_reject_changes = false;
00253   p1.setVal(3);
00254   // now we should be allowing changes to get through again:
00255   REQUIRE_EQ(p1.getVal(), 3);
00256 }
00257 
00258 static void modelparam_xx_readfrom_xx_1(TestSuite& suite)
00259 {
00260   // we want to ensure that any value change happening in readFrom()
00261   // gets passed on to the proper ParamClient
00262 
00263   TestParamClient client;
00264 
00265   REQUIRE(!client.param_has_registered);
00266   REQUIRE(!client.param_has_unregistered);
00267   REQUIRE(!client.param_has_changed);
00268 
00269   {
00270     NModelParam<int> param("foo", &client, 42);
00271 
00272     // make sure that the param registered with us
00273     REQUIRE(client.param_has_registered);
00274     REQUIRE(!client.param_has_unregistered);
00275     REQUIRE(!client.param_has_changed);
00276 
00277     // make the param get its value from a ParamMap that won't
00278     // change its existing value
00279     ParamMap pmap1;
00280     pmap1.putIntParam("foo", 42);
00281     param.readFrom(pmap1, false);
00282 
00283     REQUIRE(client.param_has_registered);
00284     REQUIRE(!client.param_has_unregistered);
00285     REQUIRE(!client.param_has_changed);
00286 
00287     // now make the param get its value from a ParamMap that WILL change
00288     // its existing value
00289     ParamMap pmap2;
00290     pmap2.putIntParam("foo", 49);
00291 
00292     param.readFrom(pmap2, false);
00293 
00294     REQUIRE(client.param_has_registered);
00295     REQUIRE(!client.param_has_unregistered);
00296     REQUIRE(client.param_has_changed);
00297   }
00298 
00299   // now the model param has gone out of scope and has been destroyed,
00300   // so it should have un-registered:
00301 
00302   REQUIRE(client.param_has_registered);
00303   REQUIRE(client.param_has_unregistered);
00304   REQUIRE(client.param_has_changed);
00305 }
00306 
00307 static void modelparam_xx_writeto_xx_1(TestSuite& suite)
00308 {
00309   TestParamClient client;
00310 
00311   NModelParam<double> param("foo", &client, 3.5);
00312 
00313   ParamMap pmap;
00314   param.writeTo(pmap);
00315 
00316   REQUIRE_EQ(pmap.getDoubleParam("foo", 0.0), 3.5);
00317 }
00318 
00319 static void modelmanager_xx_findoptiondef_xx_1(TestSuite& suite)
00320 {
00321   ModelManager m;
00322   m.setOptionValString(&OPT_testOption, "59");
00323   std::string s = std::string("test") + "Option";
00324   REQUIRE(m.findOptionDef(s.c_str()) == &OPT_testOption);
00325   REQUIRE_EQ(m.getOptionValString(&OPT_testOption), "59");
00326 }
00327 
00328 static void modelmanager_xx_defaultvalue_xx_1(TestSuite& suite)
00329 {
00330   ModelManager m;
00331 
00332   ModelComponent c1(m, "c1", "c1"), c2(m, "c2", "c2");
00333 
00334   // set up an OModelParam that doesn't force its value as default
00335   OModelParam<int> p1(&OPT_testOption, &c1);
00336   REQUIRE_EQ(p1.getVal(), 1);
00337   p1.setVal(5);
00338   REQUIRE_EQ(p1.getVal(), 5);
00339   m.requestOption(p1, false);
00340   REQUIRE_EQ(p1.getVal(), 1);
00341 
00342   // set up an OModelParam that DOES force its value as default
00343   OModelParam<int> p2(&OPT_testOption, &c2, 6, USE_MY_VAL);
00344   REQUIRE_EQ(p1.getVal(), 1);
00345   REQUIRE_EQ(p2.getVal(), 6);
00346   m.requestOption(p2, true);
00347   REQUIRE_EQ(p1.getVal(), 6);
00348   REQUIRE_EQ(p2.getVal(), 6);
00349 
00350   m.setOptionValString(&OPT_testOption, "42");
00351   REQUIRE_EQ(p1.getVal(), 42);
00352   REQUIRE_EQ(p1.getVal(), 42);
00353 }
00354 
00355 static void modelcomponent_xx_forbidsharedptr_xx_1(TestSuite& suite)
00356 {
00357   ModelManager m;
00358 
00359   bool got_exception = false;
00360   try { rutz::shared_ptr<ModelComponent> p(new TestComponent(m)); }
00361   catch (std::exception& e) { got_exception = true; }
00362 
00363   REQUIRE(got_exception);
00364 }
00365 
00366 static void modelcomponent_xx_forbidsharedptr_xx_2(TestSuite& suite)
00367 {
00368   ModelManager m;
00369 
00370   try { rutz::shared_ptr<ModelComponent> p(new TestComponent2(m)); }
00371   catch (std::exception& e) {}
00372 
00373   // FIXME: We don't have a good way to make this test actually work:
00374   /* REQUIRE(got_exception); */
00375 
00376   // The problem is that ModelComponent's pointer-checking mechanisms
00377   // runs into trouble when we have virtual inheritance. When somebody
00378   // inherits virtually from ModelComponent, then ModelComponent's
00379   // constructor has no way of knowing where the address of the full
00380   // object actually begins.
00381 }
00382 
00383 static void modelcomponent_xx_root_object_xx_1(TestSuite& suite)
00384 {
00385   ModelManager m;
00386 
00387   REQUIRE(m.getRootObject() == &m);
00388   REQUIRE(m.getParent() == 0);
00389 
00390   nub::soft_ref<ModelComponent> c1(new TestComponent(m));
00391 
00392   REQUIRE(c1->getRootObject() == c1.get());
00393   REQUIRE(c1->getParent() == 0);
00394 
00395   m.addSubComponent(c1);
00396 
00397   REQUIRE(c1->getRootObject() == &m);
00398   REQUIRE(c1->getParent() == &m);
00399 
00400   nub::soft_ref<ModelComponent> c2(new TestComponent2(m));
00401 
00402   c1->addSubComponent(c2);
00403 
00404   REQUIRE(c2->getRootObject() == &m);
00405   REQUIRE(c2->getParent() == c1.get());
00406 }
00407 
00408 #include "Util/Assert.H"
00409 
00410 namespace
00411 {
00412   class ReentrantTester : public ModelComponent
00413   {
00414   public:
00415     ReentrantTester(OptionManager& mgr)
00416       :
00417       ModelComponent(mgr, "Reentrant Tester", "ReentrantTester"),
00418       itsOption(&OPT_testOption, this)
00419     {}
00420 
00421     virtual void paramChanged(ModelParamBase* const param,
00422                               const bool valueChanged,
00423                               ParamClient::ChangeStatus* status)
00424     {
00425       if (param == &itsOption)
00426         {
00427           for (int i = 0; i < 10000; ++i)
00428             {
00429               itsNewParams.push_back
00430                 (NModelParam<int>::make(sformat("foo%d", i), this, i));
00431             }
00432         }
00433     }
00434 
00435     OModelParam<int> itsOption;
00436     std::vector<rutz::shared_ptr<NModelParam<int> > > itsNewParams;
00437   };
00438 }
00439 
00440 static void modelcomponent_xx_reentrant_pinfos_xx_1(TestSuite& suite)
00441 {
00442   // Here we are testing that it is safe to create new model params on
00443   // the fly during a paramChanged() callback triggered by
00444   // exportOptions(); previously this had the potential to cause a
00445   // crash because ModelComponent's rep->pinfos would be appended to
00446   // (by the creation of a new NModelParam) thus invalidating
00447   // iterators, while we were in the midst of iterating over
00448   // rep->pinfos in exportOptions(). The new implementation of
00449   // ModelComponent::exportOptions() eschews iterators in favor of
00450   // simple iteration using array indexing, so that it is safe to
00451   // change rep->pinfos while we are iterating over it.
00452 
00453   ModelManager m;
00454 
00455   nub::ref<ReentrantTester> r(new ReentrantTester(m));
00456 
00457   m.addSubComponent(r);
00458 
00459   m.exportOptions(MC_RECURSE);
00460 
00461   REQUIRE_EQ(r->getNumModelParams(), size_t(10001));
00462 }
00463 
00464 static void modelcomponent_xx_master_slave_export_xx_1(TestSuite& suite)
00465 {
00466   ModelManager m;
00467   nub::ref<MasterComponent> master(new MasterComponent(m));
00468   m.addSubComponent(master);
00469   m.exportOptions(MC_RECURSE);
00470 
00471   // when exportOptions() is called on MasterComponent, it sets
00472   // SlaveComponent::itsParam to 42, and we are testing that
00473   // SlaveComponent::itsParam should not be overwritten by a later
00474   // exportOptions() calls -- that is, we are testing that the
00475   // implementation of exportOptions() first propagates to
00476   // subcomponents, and then exports the options of the calling
00477   // ModelComponent
00478 
00479   REQUIRE_EQ(master->itsSub->itsParam.getVal(), 42);
00480 }
00481 
00482 ///////////////////////////////////////////////////////////////////////
00483 //
00484 // main
00485 //
00486 ///////////////////////////////////////////////////////////////////////
00487 
00488 int main(int argc, const char** argv)
00489 {
00490   TestSuite suite;
00491 
00492   suite.ADD_TEST(modelparam_xx_getset_xx_1);
00493   suite.ADD_TEST(modelparam_xx_getset_xx_2);
00494   suite.ADD_TEST(modelparam_xx_getset_xx_3);
00495   suite.ADD_TEST(modelparam_xx_readfrom_xx_1);
00496   suite.ADD_TEST(modelparam_xx_writeto_xx_1);
00497   suite.ADD_TEST(modelmanager_xx_findoptiondef_xx_1);
00498   suite.ADD_TEST(modelmanager_xx_defaultvalue_xx_1);
00499 #if defined(GVX_MEM_DEBUG)
00500   suite.ADD_TEST(modelcomponent_xx_forbidsharedptr_xx_1);
00501   suite.ADD_TEST(modelcomponent_xx_forbidsharedptr_xx_2);
00502 #else
00503   // prevent "unused static function" warnings:
00504   (void) &modelcomponent_xx_forbidsharedptr_xx_1;
00505   (void) &modelcomponent_xx_forbidsharedptr_xx_2;
00506 #endif
00507   suite.ADD_TEST(modelcomponent_xx_root_object_xx_1);
00508 
00509   suite.ADD_TEST(modelcomponent_xx_reentrant_pinfos_xx_1);
00510 
00511   suite.ADD_TEST(modelcomponent_xx_master_slave_export_xx_1);
00512 
00513   suite.parseAndRun(argc, argv);
00514 
00515   return 0;
00516 }
00517 
00518 // ######################################################################
00519 /* So things look consistent in everyone's emacs... */
00520 /* Local Variables: */
00521 /* indent-tabs-mode: nil */
00522 /* End: */
00523 
00524 #endif // TESTSUITE_WHITEBOX_COMPONENT_C_DEFINED
Generated on Sun May 8 08:42:23 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3