Commits

nat_linden  committed 11a6c70

Add test to call array-style functions with too-short array.
Also, finally got sick of hand-writing the official iterator-loop idiom and
dragged in BOOST_FOREACH(). Because LLSD has two completely different
iteration styles, added inArray and inMap helper classes to be able to write:
BOOST_FOREACH(LLSD item, inArray(someArray)) { ... }

  • Participants
  • Parent commits 8f2c536

Comments (0)

Files changed (1)

File indra/llcommon/tests/lleventdispatcher_test.cpp

 #include <boost/bind.hpp>
 #include <boost/function.hpp>
 #include <boost/range.hpp>
+#include <boost/foreach.hpp>
+#define foreach BOOST_FOREACH
 
 #include <boost/lambda/lambda.hpp>
 
 #endif
 
 /*****************************************************************************
+*   BOOST_FOREACH() helpers for LLSD
+*****************************************************************************/
+/// Usage: BOOST_FOREACH(LLSD item, inArray(someLLSDarray)) { ... }
+class inArray
+{
+public:
+    inArray(const LLSD& array):
+        _array(array)
+    {}
+
+    typedef LLSD::array_const_iterator const_iterator;
+    typedef LLSD::array_iterator iterator;
+
+    iterator begin() { return _array.beginArray(); }
+    iterator end()   { return _array.endArray(); }
+    const_iterator begin() const { return _array.beginArray(); }
+    const_iterator end()   const { return _array.endArray(); }
+
+private:
+    LLSD _array;
+};
+
+/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
+typedef std::pair<const LLSD::String, LLSD> MapEntry;
+
+/// Usage: BOOST_FOREACH([const] MapEntry& e, inMap(someLLSDmap)) { ... }
+class inMap
+{
+public:
+    inMap(const LLSD& map):
+        _map(map)
+    {}
+
+    typedef LLSD::map_const_iterator const_iterator;
+    typedef LLSD::map_iterator iterator;
+
+    iterator begin() { return _map.beginMap(); }
+    iterator end()   { return _map.endMap(); }
+    const_iterator begin() const { return _map.beginMap(); }
+    const_iterator end()   const { return _map.endMap(); }
+
+private:
+    LLSD _map;
+};
+
+/*****************************************************************************
 *   Example data, functions, classes
 *****************************************************************************/
 // We don't need a whole lot of different arbitrary-params methods, just (no |
     void methodnb(NPARAMSb)
     {
         std::ostringstream vbin;
-        for (size_t ix = 0, ixend = bin.size(); ix < ixend; ++ix)
+        foreach(U8 byte, bin)
         {
-            vbin << std::hex << std::setfill('0') << std::setw(2) << bin[ix];
+            vbin << std::hex << std::setfill('0') << std::setw(2) << byte;
         }
 
         cout << "methodnb(" << "'" << s << "'"
                                  (LLSDArray(paramsb)(dftb_array_full)));
 //            std::cout << "zipped:\n" << zipped << '\n';
             LLSD dft_maps_full, dft_maps_partial;
-            for (LLSD::array_const_iterator ai(zipped.beginArray()), aend(zipped.endArray());
-                 ai != aend; ++ai)
+            foreach(LLSD ae, inArray(zipped))
             {
                 LLSD dft_map_full;
-                LLSD params((*ai)[0]);
-                LLSD dft_array_full((*ai)[1]);
+                LLSD params(ae[0]);
+                LLSD dft_array_full(ae[1]);
 //                std::cout << "params:\n" << params << "\ndft_array_full:\n" << dft_array_full << '\n';
                 for (LLSD::Integer ix = 0, ixend = params.size(); ix < ixend; ++ix)
                 {
         {
             // Copy funcs to a temp map of same type.
             FuncMap forgotten(funcs.begin(), funcs.end());
-            for (LLEventDispatcher::const_iterator edi(work.begin()), edend(work.end());
-                 edi != edend; ++edi)
+            // LLEventDispatcher intentionally provides only const_iterator:
+            // since dereferencing that iterator generates values on the fly,
+            // it's meaningless to have a modifiable iterator. But since our
+            // 'work' object isn't const, by default BOOST_FOREACH() wants to
+            // use non-const iterators. Persuade it to use the const_iterator.
+            foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work))
             {
-                FuncMap::iterator found = forgotten.find(edi->first);
-                ensure(STRINGIZE("LLEventDispatcher records function '" << edi->first
+                FuncMap::iterator found = forgotten.find(nd.first);
+                ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first
                                  << "' we didn't enter"),
                        found != forgotten.end());
-                ensure_equals(STRINGIZE("LLEventDispatcher desc '" << edi->second <<
+                ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second <<
                                         "' doesn't match what we entered: '" << found->second << "'"),
-                              edi->second, found->second);
+                              nd.second, found->second);
                 // found in our map the name from LLEventDispatcher, good, erase
                 // our map entry
                 forgotten.erase(found);
                 std::ostringstream out;
                 out << "LLEventDispatcher failed to report";
                 const char* delim = ": ";
-                for (FuncMap::const_iterator fmi(forgotten.begin()), fmend(forgotten.end());
-                     fmi != fmend; ++fmi)
+                foreach(const FuncMap::value_type& fme, forgotten)
                 {
-                    out << delim << fmi->first;
+                    out << delim << fme.first;
                     delim = ", ";
                 }
                 ensure(out.str(), false);
     // Call cases:
     // - (try_call | call) (explicit name | event key) (real | bogus) name
     // - Callable with args that (do | do not) match required
-    // - (Free function | non-static method) array style with
+    // - (Free function | non-static method), no args, (array | map) style
+    // - (Free function | non-static method), arbitrary args, array style with
     //   (scalar | map | array (too short | too long | just right))
     //   [trap LL_WARNS for too-long case?]
-    // - (Free function | non-static method) map style with
+    // - (Free function | non-static method), arbitrary args, map style with
     //   (scalar | array | map (all | too many | holes (with | without) defaults))
     // - const char* param gets ("" | NULL)
 
         set_test_name("map-style registration with non-array params");
         // Pass "param names" as scalar or as map
         LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2)));
-        for (LLSD::array_const_iterator ai(attempts.beginArray()), aend(attempts.endArray());
-             ai != aend; ++ai)
+        foreach(LLSD ae, inArray(attempts))
         {
             std::string threw;
             try
             {
-                work.add("freena_err", "freena", freena, *ai);
+                work.add("freena_err", "freena", freena, ae);
             }
             catch (const std::exception& e)
             {
     {
         set_test_name("query Callables with/out required params");
         LLSD names(LLSDArray("free1")("Dmethod1")("Dcmethod1")("method1"));
-        for (LLSD::array_const_iterator ai(names.beginArray()), aend(names.endArray());
-             ai != aend; ++ai)
+        foreach(LLSD ae, inArray(names))
         {
-            LLSD metadata(getMetadata(*ai));
-            ensure_equals("name mismatch", metadata["name"], *ai);
-            ensure_equals(metadata["desc"].asString(), funcs[*ai]);
+            LLSD metadata(getMetadata(ae));
+            ensure_equals("name mismatch", metadata["name"], ae);
+            ensure_equals(metadata["desc"].asString(), funcs[ae]);
             ensure("should not have required structure", metadata["required"].isUndefined());
             ensure("should not have optional", metadata["optional"].isUndefined());
 
-            std::string name_req(ai->asString() + "_req");
+            std::string name_req(ae.asString() + "_req");
             metadata = getMetadata(name_req);
             ensure_equals(metadata["name"].asString(), name_req);
             ensure_equals(metadata["desc"].asString(), funcs[name_req]);
                        (5)(LLSDArray("freena_array")("smethodna_array")("methodna_array")))
                       (LLSDArray
                        (5)(LLSDArray("freenb_array")("smethodnb_array")("methodnb_array"))));
-        for (LLSD::array_const_iterator ai(expected.beginArray()), aend(expected.endArray());
-             ai != aend; ++ai)
+        foreach(LLSD ae, inArray(expected))
         {
-            LLSD::Integer arity((*ai)[0].asInteger());
-            LLSD names((*ai)[1]);
+            LLSD::Integer arity(ae[0].asInteger());
+            LLSD names(ae[1]);
             LLSD req(LLSD::emptyArray());
             if (arity)
                 req[arity - 1] = LLSD();
-            for (LLSD::array_const_iterator ni(names.beginArray()), nend(names.endArray());
-                 ni != nend; ++ni)
+            foreach(LLSD nm, inArray(names))
             {
-                LLSD metadata(getMetadata(*ni));
-                ensure_equals("name mismatch", metadata["name"], *ni);
-                ensure_equals(metadata["desc"].asString(), funcs[*ni]);
-                ensure_equals(STRINGIZE("mismatched required for " << ni->asString()),
+                LLSD metadata(getMetadata(nm));
+                ensure_equals("name mismatch", metadata["name"], nm);
+                ensure_equals(metadata["desc"].asString(), funcs[nm]);
+                ensure_equals(STRINGIZE("mismatched required for " << nm.asString()),
                               metadata["required"], req);
                 ensure("should not have optional", metadata["optional"].isUndefined());
             }
         // - (Free function | non-static method), map style, no params (ergo
         //   no defaults)
         LLSD names(LLSDArray("free0_map")("smethod0_map")("method0_map"));
-        for (LLSD::array_const_iterator ni(names.beginArray()), nend(names.endArray());
-             ni != nend; ++ni)
+        foreach(LLSD nm, inArray(names))
         {
-            LLSD metadata(getMetadata(*ni));
-            ensure_equals("name mismatch", metadata["name"], *ni);
-            ensure_equals(metadata["desc"].asString(), funcs[*ni]);
+            LLSD metadata(getMetadata(nm));
+            ensure_equals("name mismatch", metadata["name"], nm);
+            ensure_equals(metadata["desc"].asString(), funcs[nm]);
             ensure("should not have required",
                    (metadata["required"].isUndefined() || metadata["required"].size() == 0));
             ensure("should not have optional", metadata["optional"].isUndefined());
                           (LLSDArray("smethodnb_map_adft")("smethodnb_map_mdft"))
                           (LLSDArray("methodna_map_adft")("methodna_map_mdft"))
                           (LLSDArray("methodnb_map_adft")("methodnb_map_mdft")));
-        for (LLSD::array_const_iterator
-                 ei(equivalences.beginArray()), eend(equivalences.endArray());
-             ei != eend; ++ei)
+        foreach(LLSD eq, inArray(equivalences))
         {
-            LLSD adft((*ei)[0]);
-            LLSD mdft((*ei)[1]);
+            LLSD adft(eq[0]);
+            LLSD mdft(eq[1]);
             // We can't just compare the results of the two getMetadata()
             // calls, because they contain ["name"], which are different. So
             // capture them, verify that each ["name"] is as expected, then
         // Generate maps containing parameter names not provided by the
         // dft[ab]_map_partial maps.
         LLSD skipreqa(allreqa), skipreqb(allreqb);
-        for (LLSD::map_const_iterator mi(dfta_map_partial.beginMap()),
-                                      mend(dfta_map_partial.endMap());
-             mi != mend; ++mi)
+        foreach(const MapEntry& me, inMap(dfta_map_partial))
         {
-            skipreqa.erase(mi->first);
+            skipreqa.erase(me.first);
         }
-        for (LLSD::map_const_iterator mi(dftb_map_partial.beginMap()),
-                                      mend(dftb_map_partial.endMap());
-             mi != mend; ++mi)
+        foreach(const MapEntry& me, inMap(dftb_map_partial))
         {
-            skipreqb.erase(mi->first);
+            skipreqb.erase(me.first);
         }
 
         LLSD groups(LLSDArray       // array of groups
                      (LLSDArray("freenb_map_mdft")("smethodnb_map_mdft")("methodnb_map_mdft"))
                      (LLSDArray(LLSD::emptyMap())(dftb_map_full)))); // required, optional
 
-        for (LLSD::array_const_iterator gi(groups.beginArray()), gend(groups.endArray());
-             gi != gend; ++gi)
+        foreach(LLSD grp, inArray(groups))
         {
             // Internal structure of each group in 'groups':
-            LLSD names((*gi)[0]);
-            LLSD required((*gi)[1][0]);
-            LLSD optional((*gi)[1][1]);
+            LLSD names(grp[0]);
+            LLSD required(grp[1][0]);
+            LLSD optional(grp[1][1]);
             cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl;
 
             // Loop through 'names'
-            for (LLSD::array_const_iterator ni(names.beginArray()), nend(names.endArray());
-                 ni != nend; ++ni)
+            foreach(LLSD nm, inArray(names))
             {
-                LLSD metadata(getMetadata(*ni));
-                ensure_equals("name mismatch", metadata["name"], *ni);
-                ensure_equals(metadata["desc"].asString(), funcs[*ni]);
+                LLSD metadata(getMetadata(nm));
+                ensure_equals("name mismatch", metadata["name"], nm);
+                ensure_equals(metadata["desc"].asString(), funcs[nm]);
                 ensure_equals("required mismatch", metadata["required"], required);
                 ensure_equals("optional mismatch", metadata["optional"], optional);
             }
         // LLSD value matching 'required' according to llsd_matches() rules.
         LLSD matching(LLSDMap("d", 3.14)("array", LLSDArray("answer")(true)(answer)));
         // Okay, walk through 'tests'.
-        for (const CallablesTriple *ti(boost::begin(tests)), *tend(boost::end(tests));
-             ti != tend; ++ti)
+        foreach(const CallablesTriple& tr, tests)
         {
             // Should be able to pass 'answer' to Callables registered
             // without 'required'.
-            work(ti->name, answer);
-            ensure_equals("answer mismatch", ti->llsd, answer);
+            work(tr.name, answer);
+            ensure_equals("answer mismatch", tr.llsd, answer);
             // Should NOT be able to pass 'answer' to Callables registered
             // with 'required'.
-            call_exc(ti->name_req, answer, "bad request");
+            call_exc(tr.name_req, answer, "bad request");
             // But SHOULD be able to pass 'matching' to Callables registered
             // with 'required'.
-            work(ti->name_req, matching);
-            ensure_equals("matching mismatch", ti->llsd, matching);
+            work(tr.name_req, matching);
+            ensure_equals("matching mismatch", tr.llsd, matching);
         }
     }
 
 
     struct FunctionsTriple
     {
-        std::string name_array, name_map;
+        std::string name1, name2;
         Vars& vars;
     };
 
             { "smethod0_array", "smethod0_map", g },
             { "method0_array",  "method0_map",  v }
         };
-        for (const FunctionsTriple *ti(boost::begin(tests)), *tend(boost::end(tests));
-             ti != tend; ++ti)
+        foreach(const FunctionsTriple& tr, tests)
         {
             // Both the global and stack Vars instances are automatically
             // cleared at the start of each test<n> method. But since we're
             // calling these things several different times in the same
             // test<n> method, manually reset the Vars between each.
-            ti->vars = Vars();
-            ensure_equals(ti->vars.i, 0);
+            tr.vars = Vars();
+            ensure_equals(tr.vars.i, 0);
             // array-style call with empty array (or LLSD(), should be equivalent)
-            work(ti->name_array, LLSD());
-            ensure_equals(ti->vars.i, 17);
+            work(tr.name1, LLSD());
+            ensure_equals(tr.vars.i, 17);
             
-            ti->vars = Vars();
+            tr.vars = Vars();
             // map-style call with empty map (or LLSD(), should be equivalent)
-            work(ti->name_map, LLSD());
-            ensure_equals(ti->vars.i, 17);
+            work(tr.name2, LLSD());
+            ensure_equals(tr.vars.i, 17);
+        }
+    }
+
+    template<> template<>
+    void object::test<19>()
+    {
+        set_test_name("call array-style functions with too-short arrays");
+        FunctionsTriple tests[] =
+        {
+            { "freena_array",    "freenb_array",    g },
+            { "smethodna_array", "smethodnb_array", g },
+            { "methodna_array",  "methodnb_array",  v }
+        };
+        // Could have two different too-short arrays, one for *na and one for
+        // *nb, but since they both take 5 params...
+        LLSD tooshort(LLSDArray("this")("array")("too")("short"));
+        foreach(const FunctionsTriple& tr, tests)
+        {
+            call_exc(tr.name1, tooshort, "requires more arguments");
+            call_exc(tr.name2, tooshort, "requires more arguments");
         }
     }
 } // namespace tut