00001 // 00002 // Copyright (c) 2003 00003 // Paul Hamilton; pHamtec P/L 00004 // 00005 // Permission to use, copy, modify, distribute and sell this software 00006 // and its documentation for any purpose is hereby granted without fee, 00007 // provided that the above copyright notice appears in all copies and 00008 // that both the copyright notice and this permission notice appear in 00009 // supporting documentation. No representations are made about the 00010 // suitability of this software for any purpose. It is provided "as is" 00011 // without express or implied warranty. 00012 // 00013 // unit_test.cpp 00014 // 00015 // 4-Jul-2003 phamilton Created 00016 // 00017 00018 #include <iostream> 00019 #include <vector> 00020 #include <string> 00021 #include <boost/test/included/unit_test_framework.hpp> 00022 #include "../object_visitor.hpp" 00023 #include "../jscript_object_navigator.hpp" 00024 #include "../jscript_object_nav_errors.h" 00025 #include "../member_visitor.hpp" 00026 #include "root.hpp" 00027 #include "foo.hpp" 00028 #include "bar.hpp" 00029 00030 using namespace ph::reflect; 00031 using namespace reflect_test; 00032 using boost::unit_test_framework::test_suite; 00033 using boost::test_toolbox::output_test_stream; 00034 00035 static void set(base *obj, const std::string &name, const std::string &val) 00036 { 00037 set_member_visitor v(name, val); 00038 obj->accept(&v); 00039 } 00040 00041 static std::string get(base *obj, const std::string &name) 00042 { 00043 ph::common::get_member_value_visitor v(name); 00044 obj->accept(&v); 00045 return v.get(); 00046 } 00047 00048 // here are some macros we use to test whether an object with a particular path has the correct name. 00049 // they are macros so that they will display the correct line number. 00050 00051 #define TEST_OBJ_NAME(_obj_, _path_, _name_) \ 00052 { \ 00053 ph::common::object_base *o = nav.navigate(_obj_, _path_); \ 00054 BOOST_CHECK(output.is_empty()); \ 00055 BOOST_REQUIRE_MESSAGE(o, "could not find [" _path_ "]"); \ 00056 std::string name; \ 00057 BOOST_CHECK_MESSAGE(ph::common::get_visitable_obj_name(o, &name), \ 00058 "[" _path_ "] object does not have a name."); \ 00059 BOOST_CHECK_MESSAGE(name == _name_, \ 00060 "[" _path_ "] was named [" + name + "] not [" _name_ "]"); \ 00061 } 00062 00063 #define TEST_BAD_PARSE(_obj_, _path_, _msg_, _name_) \ 00064 { \ 00065 BOOST_CHECK(nav.navigate(_obj_, _path_) == 0); \ 00066 BOOST_CHECK_MESSAGE(output.is_equal( \ 00067 "Syntax Error. " _msg_ " [" _name_ "]\n"), "Syntax Error. " _msg_ " [" _name_ "]"); \ 00068 } 00069 00070 namespace reflect_test { 00071 00072 class test_simple 00073 { 00074 public: 00075 void test() 00076 { 00077 foo *o = new foo("foo", "foo"); 00078 BOOST_REQUIRE_MESSAGE(o, "could not create foo object."); 00079 set(o, "x", "1"); 00080 set(o, "y", "2.1"); 00081 set(o, "z", "true"); 00082 BOOST_CHECK(get(o, "x") == "1"); 00083 BOOST_CHECK(get(o, "y") == "2.1"); 00084 BOOST_CHECK(get(o, "z") == "true"); 00085 delete o; 00086 } 00087 }; 00088 00089 class test_composite 00090 { 00091 public: 00092 00093 void test() 00094 { 00095 { 00096 // create a root object. 00097 root *r = new root("root", "root"); 00098 00099 // now search for a particular object using the object parser. 00100 output_test_stream output; 00101 jscript_object_navigator nav(r, &output, false, false); 00102 00103 // test adding new members to the composites. 00104 ph::common::object_base *p = nav.navigate(r, "root().foos()"); 00105 BOOST_CHECK(output.is_empty()); 00106 BOOST_REQUIRE(p); 00107 BOOST_REQUIRE_MESSAGE(p->composition(), 00108 "object does not implement composite interface."); 00109 ph::common::composite_object_base *c = 00110 p->composition()->composite(); 00111 BOOST_REQUIRE_MESSAGE(c, "foos was not a composite object."); 00112 00113 // create a new foo object. 00114 foo *foo_1 = new foo("foo", "foo_1"); 00115 BOOST_REQUIRE_MESSAGE(foo_1, "could not create foo object."); 00116 BOOST_CHECK(c->add(foo_1, true)); 00117 00118 // make sure it's there. 00119 TEST_OBJ_NAME(r, "root().foos(1)", "foo_1"); 00120 00121 // now remove it. 00122 BOOST_CHECK(c->remove(foo_1)); 00123 00124 // make sure it's not there. 00125 TEST_BAD_PARSE(r, "root().foos(1)", 00126 "Object with numeric subscript was not found.", "1"); 00127 00128 // test the singleton composite to make sure that errors working. 00129 TEST_BAD_PARSE(r, "root().a_foo", "Singleton composite was empty.", "a_foo"); 00130 00131 // now add foo onto the a_foo 00132 p = nav.navigate(r, "root().a_foo()"); 00133 BOOST_CHECK(output.is_empty()); 00134 BOOST_REQUIRE(p); 00135 BOOST_REQUIRE_MESSAGE(p->composition(), 00136 "object does not implement composite interface."); 00137 c = p->composition()->composite(); 00138 BOOST_REQUIRE_MESSAGE(c, "object was not a composite object."); 00139 00140 BOOST_CHECK(c->add(foo_1, true)); 00141 00142 // make sure it's there. 00143 TEST_OBJ_NAME(r, "root().a_foo", "foo_1"); 00144 00145 delete r; 00146 } 00147 { 00148 // create a root object. 00149 root *r = new root("root", "root"); 00150 00151 // now search for a particular object using the object parser. 00152 output_test_stream output; 00153 jscript_object_navigator nav(r, &output, false); 00154 00155 // now add a bunch of foos and bars to this object. 00156 r->addfoosandbars(); 00157 00158 // test the parent-relationship of composites. 00159 ph::common::object_base *bar_2 = nav.navigate(r, "root().bars('bar_2')"); 00160 BOOST_CHECK(output.is_empty()); 00161 BOOST_CHECK(bar_2); 00162 if (bar_2) 00163 { 00164 // get bars 00165 ph::common::object_base *bars = nav.navigate(r, "root().bars()"); 00166 BOOST_CHECK(output.is_empty()); 00167 BOOST_CHECK(bars); 00168 if (bars) 00169 test_parents(bar_2, bars, "bars"); 00170 } 00171 00172 // test the parent-relationship of references. 00173 ph::common::object_base *foo_3 = nav.navigate(r, "root().a_foo"); 00174 BOOST_CHECK(output.is_empty()); 00175 BOOST_CHECK(foo_3); 00176 if (foo_3) 00177 { 00178 // get a_foo 00179 ph::common::object_base *a_foo = nav.navigate(r, "root().a_foo()"); 00180 BOOST_CHECK(output.is_empty()); 00181 BOOST_CHECK(a_foo); 00182 if (a_foo) 00183 test_parents(foo_3, a_foo, "a_foo"); 00184 } 00185 00186 delete r; 00187 } 00188 } 00189 00190 void test_parents(ph::common::object_base *obj, 00191 ph::common::object_base *composite, const std::string &parentname) 00192 /* 00193 test the parent-relationship of composites. 00194 */ 00195 { 00196 // see what the name of the parent of this guy is. 00197 BOOST_REQUIRE_MESSAGE(obj->outerable(), 00198 "object doesn't implement outer interface."); 00199 ph::common::object_base *parent = obj->outerable()->outer(); 00200 BOOST_CHECK(parent); 00201 if (parent) 00202 { 00203 // test comparable interface. 00204 BOOST_REQUIRE_MESSAGE(parent->comparable(), 00205 "parent doesn't implement comparable interface"); 00206 BOOST_CHECK(parent->comparable()->equal(composite)); 00207 00208 // now walk out and make sure all the parents are correct. 00209 std::string name; 00210 BOOST_CHECK_MESSAGE(ph::common::get_visitable_obj_name(parent, &name), 00211 "object does not have a name."); 00212 BOOST_CHECK(name == parentname); 00213 BOOST_REQUIRE_MESSAGE(parent->outerable(), 00214 "parent doesn't implement outer interface."); 00215 parent = parent->outerable()->outer(); 00216 BOOST_CHECK(parent); 00217 if (parent) 00218 { 00219 BOOST_CHECK_MESSAGE(ph::common::get_visitable_obj_name(parent, &name), 00220 "object does not have a name."); 00221 BOOST_CHECK(name == "root"); 00222 BOOST_REQUIRE_MESSAGE(parent->outerable(), 00223 "parent doesn't implement outer interface."); 00224 BOOST_CHECK(parent->outerable()->outer() == 0); 00225 } 00226 } 00227 } 00228 }; 00229 00230 class test_navigation 00231 { 00232 public: 00233 00234 void test() 00235 { 00236 // create a root object. 00237 root *r = new root("root", "root"); 00238 00239 // now add a bunch of foos and bars to this object. 00240 r->addfoosandbars(); 00241 00242 // now search for a particular object using the object parser. 00243 output_test_stream output; 00244 jscript_object_navigator nav(r, &output, false, false); 00245 00246 // test root() navigation. 00247 TEST_OBJ_NAME(r, "root()", "root"); 00248 00249 // test sub-object navigation. 00250 TEST_OBJ_NAME(r, "root().foos('foo_2')", "foo_2"); 00251 TEST_OBJ_NAME(r, "root().foos(1)", "foo_1"); 00252 TEST_OBJ_NAME(r, "root().foos(2)", "foo_2"); 00253 TEST_OBJ_NAME(r, "root().bars('bar_1')", "bar_1"); 00254 TEST_OBJ_NAME(r, "root().bars(1)", "bar_1"); 00255 TEST_OBJ_NAME(r, "root().bars(2)", "bar_2"); 00256 00257 // test for invalid composites. 00258 TEST_BAD_PARSE(r, "root().foos", 00259 "Object was not a singleton composite. Did you mean to use ()?.", "foos"); 00260 00261 // test composite navigation. 00262 TEST_OBJ_NAME(r, "root().foos()", "foos"); 00263 TEST_OBJ_NAME(r, "root().a_foo()", "a_foo"); 00264 00265 // test for our object reference special case. 00266 // The name of the composite actually returns the 00267 // object attached. 00268 TEST_OBJ_NAME(r, "root().a_foo", "foo_3"); 00269 00270 // test complicated queries. 00271 TEST_OBJ_NAME(r, "root().foos('foo_2').parent().bars('bar_1')", "bar_1"); 00272 00273 // test for gobbledegook. 00274 00275 // no composites. 00276 TEST_BAD_PARSE(r, "()", "No builtin or composite found.", ""); 00277 TEST_BAD_PARSE(r, "xxxxx()", "No builtin or composite found.", "xxxxx"); 00278 TEST_BAD_PARSE(r, "root().xxxxx()", "No builtin or composite found.", "xxxxx"); 00279 TEST_BAD_PARSE(r, "root().()", "No builtin or composite found.", ""); 00280 00281 TEST_BAD_PARSE(r, "yyyy(1)", "No composite found.", "yyyy"); 00282 00283 TEST_BAD_PARSE(r, "root().foos(1).parent('xxxx')", "Parent was not found.", "xxxx"); 00284 TEST_BAD_PARSE(r, "root().foos(1).parent(2)", 00285 "Parent with subscript was not found.", "2"); 00286 00287 TEST_BAD_PARSE(r, "root().foos(5)", 00288 "Object with numeric subscript was not found.", "5"); 00289 00290 TEST_BAD_PARSE(r, "root().foos('xxxx')", 00291 "Object with name subscript was not found.", "'xxxx'"); 00292 00293 // couldn't find objects. 00294 TEST_BAD_PARSE(r, "xxxxx", "No composite found.", "xxxxx"); 00295 TEST_BAD_PARSE(r, "root().xxxxx", "No composite found.", "xxxxx"); 00296 TEST_BAD_PARSE(r, "root().foos('foo_1", "No composite found.", "foos('foo_1"); 00297 TEST_BAD_PARSE(r, "root().foos('foo_1'", "No composite found.", "foos('foo_1'"); 00298 TEST_BAD_PARSE(r, "root().foos('", "No composite found.", "foos('"); 00299 TEST_BAD_PARSE(r, "root().foos(", "No composite found.", "foos("); 00300 TEST_BAD_PARSE(r, "root(", "No composite found.", "root("); 00301 00302 // couldn't parse. 00303 TEST_BAD_PARSE(r, "....", "Empty part. Couldn't Parse.", "...."); 00304 00305 delete r; 00306 } 00307 }; 00308 00309 class test_dump 00310 { 00311 public: 00312 test_dump(const std::string &filename) : 00313 _filename(filename) 00314 {}; 00315 00316 void test() 00317 { 00318 // create a root object. 00319 root *r = new root("root", "root"); 00320 00321 // now add a bunch of foos and bars to this object. 00322 r->addfoosandbars(); 00323 00324 { 00325 output_test_stream output(_filename, true); 00326 { 00327 dump_object_visitor v(&output); 00328 BOOST_CHECK(r->accept(&v)); 00329 } 00330 BOOST_CHECK(output.match_pattern()); 00331 } 00332 00333 delete r; 00334 } 00335 00336 private: 00337 std::string _filename; 00338 }; 00339 00340 }; 00341 00342 test_suite *init_unit_test_suite(int argc, char* argv[]) 00343 { 00344 test_suite * test = BOOST_TEST_SUITE("reflect units tests"); 00345 00346 test->add(BOOST_CLASS_TEST_CASE(&test_simple::test, 00347 boost::shared_ptr<test_simple>(new test_simple()))); 00348 test->add(BOOST_CLASS_TEST_CASE(&test_composite::test, 00349 boost::shared_ptr<test_composite>(new test_composite()))); 00350 test->add(BOOST_CLASS_TEST_CASE(&test_navigation::test, 00351 boost::shared_ptr<test_navigation>(new test_navigation()))); 00352 test->add(BOOST_CLASS_TEST_CASE(&test_dump::test, 00353 boost::shared_ptr<test_dump>(new test_dump(argv[1])))); 00354 00355 return test; 00356 }