diff options
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 1 | ||||
-rw-r--r-- | indra/llcommon/tests/lldependencies_test.cpp | 275 | ||||
-rw-r--r-- | indra/test/CMakeLists.txt | 1 |
3 files changed, 276 insertions, 1 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index afbebcf2f8..7468e3dde4 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -245,6 +245,7 @@ LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}") +LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}") diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp new file mode 100644 index 0000000000..f3c25de8b5 --- /dev/null +++ b/indra/llcommon/tests/lldependencies_test.cpp @@ -0,0 +1,275 @@ +/** + * @file lldependencies_tut.cpp + * @author Nat Goodspeed + * @date 2008-09-17 + * @brief Test of lldependencies.h + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * Copyright (c) 2008, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// STL headers +#include <iostream> +#include <string> +// std headers +// external library headers +#include <boost/assign/list_of.hpp> +// Precompiled header +#include "linden_common.h" +// associated header +#include "../lldependencies.h" +// other Linden headers +#include "../test/lltut.h" + +using boost::assign::list_of; + +#if LL_WINDOWS +#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want! +#endif + +typedef LLDependencies<> StringDeps; +typedef StringDeps::KeyList StringList; + +// We use the very cool boost::assign::list_of() construct to specify vectors +// of strings inline. For reasons on which I'm not entirely clear, though, it +// needs a helper function. You can use list_of() to construct an implicit +// StringList (std::vector<std::string>) by conversion, e.g. for a function +// parameter -- but if you simply write StringList(list_of("etc.")), you get +// ambiguity errors. Shrug! +template<typename CONTAINER> +CONTAINER make(const CONTAINER& data) +{ + return data; +} + +// Display an arbitary value as itself... +template<typename T> +std::ostream& display(std::ostream& out, const T& value) +{ + out << value; + return out; +} + +// ...but display std::string enclosed in double quotes. +template<> +std::ostream& display(std::ostream& out, const std::string& value) +{ + out << '"' << value << '"'; + return out; +} + +// display any sequence compatible with Boost.Range +template<typename SEQUENCE> +std::ostream& display_seq(std::ostream& out, + const std::string& open, const SEQUENCE& seq, const std::string& close) +{ + out << open; + typename boost::range_const_iterator<SEQUENCE>::type + sli = boost::begin(seq), + slend = boost::end(seq); + if (sli != slend) + { + display(out, *sli); + while (++sli != slend) + { + out << ", "; + display(out, *sli); + } + } + out << close; + return out; +} + +// helper to dump a StringList to std::cout if needed +template<typename ENTRY> +std::ostream& operator<<(std::ostream& out, const std::vector<ENTRY>& list) +{ + display_seq(out, "(", list, ")"); + return out; +} + +template<typename ENTRY> +std::ostream& operator<<(std::ostream& out, const std::set<ENTRY>& set) +{ + display_seq(out, "{", set, "}"); + return out; +} + +const std::string& extract_key(const LLDependencies<>::value_type& entry) +{ + return entry.first; +} + +// helper to return a StringList of keys from LLDependencies::sort() +StringList sorted_keys(LLDependencies<>& deps) +{ + // 1. Call deps.sort(), returning a value_type range of (key, node) pairs. + // 2. Use make_transform_range() to obtain a range of just keys. + // 3. Use instance_from_range to instantiate a StringList from that range. + // 4. Return by value "slices" instance_from_range<StringList> (a subclass + // of StringList) to its base class StringList. + return instance_from_range<StringList>(make_transform_range(deps.sort(), extract_key)); +} + +template<typename RANGE> +bool is_empty(const RANGE& range) +{ + return boost::begin(range) == boost::end(range); +} + +/***************************************************************************** +* tut test group +*****************************************************************************/ +namespace tut +{ + struct deps_data + { + }; + typedef test_group<deps_data> deps_group; + typedef deps_group::object deps_object; + tut::deps_group depsgr("lldependencies"); + + template<> template<> + void deps_object::test<1>() + { + StringDeps deps; + StringList empty; + // The quick brown fox jumps over the lazy yellow dog. + // (note, "The" and "the" are distinct, else this test wouldn't work) + deps.add("lazy"); + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy"))); + deps.add("jumps"); + ensure("found lazy", deps.get("lazy")); + ensure("not found dog.", ! deps.get("dog.")); + // NOTE: Maybe it's overkill to test each of these intermediate + // results before all the interdependencies have been specified. My + // thought is simply that if the order changes, I'd like to know why. + // A change to the implementation of boost::topological_sort() would + // be an acceptable reason, and you can simply update the expected + // test output. + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps"))); + deps.add("The", 0, empty, list_of("fox")("dog.")); + // Test key accessors + ensure("empty before deps for missing key", is_empty(deps.get_before_range("bogus"))); + ensure("empty before deps for jumps", is_empty(deps.get_before_range("jumps"))); + ensure_equals(instance_from_range< std::set<std::string> >(deps.get_before_range("The")), + make< std::set<std::string> >(list_of("dog.")("fox"))); + // resume building dependencies + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")("The"))); + deps.add("the", 0, list_of("The")); + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")("The")("the"))); + deps.add("fox", 0, list_of("The"), list_of("jumps")); + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps"))); + deps.add("the", 0, list_of("The")); // same, see if cache works + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps"))); + deps.add("jumps", 0, empty, list_of("over")); // update jumps deps + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps"))); +/*==========================================================================*| + // It drives me nuts that this test doesn't work in the test + // framework, because -- for reasons unknown -- running the test + // framework on Mac OS X 10.5 Leopard and Windows XP Pro, the catch + // clause below doesn't catch the exception. Something about the TUT + // test framework?!? The identical code works fine in a standalone + // test program. Commenting out the test for now, in hopes that our + // real builds will be able to catch Cycle exceptions... + try + { + // We've already specified fox -> jumps and jumps -> over. Try an + // impossible constraint. + deps.add("over", 0, empty, list_of("fox")); + } + catch (const StringDeps::Cycle& e) + { + std::cout << "Cycle detected: " << e.what() << '\n'; + // It's legal to add() an impossible constraint because we don't + // detect the cycle until sort(). So sort() can't know the minimum set + // of nodes to remove to make the StringDeps object valid again. + // Therefore we must break the cycle by hand. + deps.remove("over"); + } +|*==========================================================================*/ + deps.add("dog.", 0, list_of("yellow")("lazy")); + ensure_equals(instance_from_range< std::set<std::string> >(deps.get_after_range("dog.")), + make< std::set<std::string> >(list_of("lazy")("yellow"))); + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")("dog."))); + deps.add("quick", 0, list_of("The"), list_of("fox")("brown")); + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("quick")("fox")("jumps")("dog."))); + deps.add("over", 0, list_of("jumps"), list_of("yellow")("the")); + ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("quick")("fox")("jumps")("over")("the")("dog."))); + deps.add("yellow", 0, list_of("the"), list_of("lazy")); + ensure_equals(sorted_keys(deps), make<StringList>(list_of("The")("quick")("fox")("jumps")("over")("the")("yellow")("lazy")("dog."))); + deps.add("brown"); + // By now the dependencies are pretty well in place. A change to THIS + // order should be viewed with suspicion. + ensure_equals(sorted_keys(deps), make<StringList>(list_of("The")("quick")("brown")("fox")("jumps")("over")("the")("yellow")("lazy")("dog."))); + + StringList keys(make<StringList>(list_of("The")("brown")("dog.")("fox")("jumps")("lazy")("over")("quick")("the")("yellow"))); + ensure_equals(instance_from_range<StringList>(deps.get_key_range()), keys); +#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) + // This is the succinct way, works on modern compilers + ensure_equals(instance_from_range<StringList>(make_transform_range(deps.get_range(), extract_key)), keys); +#else // gcc 3.3 + StringDeps::range got_range(deps.get_range()); + StringDeps::iterator kni = got_range.begin(), knend = got_range.end(); + StringList::iterator ki = keys.begin(), kend = keys.end(); + for ( ; kni != knend && ki != kend; ++kni, ++ki) + { + ensure_equals(kni->first, *ki); + } + ensure("get_range() returns proper length", kni == knend && ki == kend); +#endif // gcc 3.3 + // blow off get_node_range() because they're all LLDependenciesEmpty instances + } + + template<> template<> + void deps_object::test<2>() + { + typedef LLDependencies<std::string, int> NameIndexDeps; + NameIndexDeps nideps; + const NameIndexDeps& const_nideps(nideps); + nideps.add("def", 2, list_of("ghi")); + nideps.add("ghi", 3); + nideps.add("abc", 1, list_of("def")); + NameIndexDeps::range range(nideps.get_range()); + ensure_equals(range.begin()->first, "abc"); + ensure_equals(range.begin()->second, 1); + range.begin()->second = 0; + range.begin()->second = 1; + NameIndexDeps::const_range const_range(const_nideps.get_range()); + NameIndexDeps::const_iterator const_iterator(const_range.begin()); + ++const_iterator; + ensure_equals(const_iterator->first, "def"); + ensure_equals(const_iterator->second, 2); + NameIndexDeps::node_range node_range(nideps.get_node_range()); + ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1)(2)(3))); + *node_range.begin() = 0; + *node_range.begin() = 1; + NameIndexDeps::const_node_range const_node_range(const_nideps.get_node_range()); + ensure_equals(instance_from_range<std::vector<int> >(const_node_range), make< std::vector<int> >(list_of(1)(2)(3))); + NameIndexDeps::const_key_range const_key_range(const_nideps.get_key_range()); + ensure_equals(instance_from_range<StringList>(const_key_range), make<StringList>(list_of("abc")("def")("ghi"))); + NameIndexDeps::sorted_range sorted(const_nideps.sort()); + NameIndexDeps::sorted_iterator sortiter(sorted.begin()); + ensure_equals(sortiter->first, "ghi"); + ensure_equals(sortiter->second, 3); + + // test all iterator-flavored versions of get_after_range() + StringList def(make<StringList>(list_of("def"))); + ensure("empty abc before list", is_empty(nideps.get_before_range(nideps.get_range().begin()))); + ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_range().begin())), + def); + ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_range().begin())), + def); + ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_node_range().begin())), + def); + ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_node_range().begin())), + def); + ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_key_range().begin())), + def); + // advance from "ghi" to "def", which must come after "ghi" + ++sortiter; + ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(sortiter)), + make<StringList>(list_of("ghi"))); + } +} // namespace tut diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 4ace1c9df8..920211ce39 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -31,7 +31,6 @@ set(test_SOURCE_FILES # llapp_tut.cpp # Temporarily removed until thread issues can be solved llblowfish_tut.cpp llbuffer_tut.cpp - lldependencies_tut.cpp lldoubledispatch_tut.cpp llevents_tut.cpp llhttpdate_tut.cpp |