summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests
diff options
context:
space:
mode:
authorMark Palange (Mani) <palange@lindenlab.com>2009-10-16 16:42:45 -0700
committerMark Palange (Mani) <palange@lindenlab.com>2009-10-16 16:42:45 -0700
commit3e10fa4d51a23bf6f1ced23e8d90c636d84fa5db (patch)
treed4991e4c1a9dd934f48d33804e55eb8ffa085679 /indra/llcommon/tests
parente9f7205ba9f4dfb3422759218609b62d61972722 (diff)
parentf20e9521a9b70f4e83cbb6888feae08a70681ea7 (diff)
merge from latest svn/viewer-2-0 to hg/viewer-2-0
Diffstat (limited to 'indra/llcommon/tests')
-rw-r--r--indra/llcommon/tests/bitpack_test.cpp125
-rw-r--r--indra/llcommon/tests/commonmisc_test.cpp677
-rw-r--r--indra/llcommon/tests/llbase64_test.cpp83
-rw-r--r--indra/llcommon/tests/lldate_test.cpp219
-rw-r--r--indra/llcommon/tests/llerror_test.cpp768
-rw-r--r--indra/llcommon/tests/llframetimer_test.cpp118
-rw-r--r--indra/llcommon/tests/llrand_test.cpp133
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp1504
-rw-r--r--indra/llcommon/tests/llstring_test.cpp750
-rw-r--r--indra/llcommon/tests/lltreeiterators_test.cpp1222
-rw-r--r--indra/llcommon/tests/lluri_test.cpp370
11 files changed, 5969 insertions, 0 deletions
diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp
new file mode 100644
index 0000000000..09fd037f02
--- /dev/null
+++ b/indra/llcommon/tests/bitpack_test.cpp
@@ -0,0 +1,125 @@
+/**
+ * @file bitpack_test.cpp
+ * @author Adroit
+ * @date 2007-02
+ * @brief llstreamtools test cases.
+ *
+ * $LicenseInfo:firstyear=2007&license=viewergpl$
+ *
+ * Copyright (c) 2007-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../bitpack.h"
+
+#include "../test/lltut.h"
+
+
+namespace tut
+{
+ struct bit_pack
+ {
+ };
+ typedef test_group<bit_pack> bit_pack_t;
+ typedef bit_pack_t::object bit_pack_object_t;
+ tut::bit_pack_t tut_bit_pack("bitpack");
+
+ // pack -> unpack
+ template<> template<>
+ void bit_pack_object_t::test<1>()
+ {
+ U8 packbuffer[255];
+ U8 unpackbuffer[255];
+ int pack_bufsize = 0;
+ int unpack_bufsize = 0;
+
+ LLBitPack bitpack(packbuffer, 255);
+
+ char str[] = "SecondLife is a 3D virtual world";
+ int len = sizeof(str);
+ pack_bufsize = bitpack.bitPack((U8*) str, len*8);
+ pack_bufsize = bitpack.flushBitPack();
+
+ LLBitPack bitunpack(packbuffer, pack_bufsize*8);
+ unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, len*8);
+ ensure("bitPack: unpack size should be same as string size prior to pack", len == unpack_bufsize);
+ ensure_memory_matches("str->bitPack->bitUnpack should be equal to string", str, len, unpackbuffer, unpack_bufsize);
+ }
+
+ // pack large, unpack in individual bytes
+ template<> template<>
+ void bit_pack_object_t::test<2>()
+ {
+ U8 packbuffer[255];
+ U8 unpackbuffer[255];
+ int pack_bufsize = 0;
+ int unpack_bufsize = 0;
+
+ LLBitPack bitpack(packbuffer, 255);
+
+ char str[] = "SecondLife";
+ int len = sizeof(str);
+ pack_bufsize = bitpack.bitPack((U8*) str, len*8);
+ pack_bufsize = bitpack.flushBitPack();
+
+ LLBitPack bitunpack(packbuffer, pack_bufsize*8);
+ unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ ensure("bitPack: individual unpack: 0", unpackbuffer[0] == (U8) str[0]);
+ unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ ensure("bitPack: individual unpack: 1", unpackbuffer[0] == (U8) str[1]);
+ unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ ensure("bitPack: individual unpack: 2", unpackbuffer[0] == (U8) str[2]);
+ unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ ensure("bitPack: individual unpack: 3", unpackbuffer[0] == (U8) str[3]);
+ unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ ensure("bitPack: individual unpack: 4", unpackbuffer[0] == (U8) str[4]);
+ unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]);
+ unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, 8*4); // Life
+ ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4);
+ }
+
+ // U32 packing
+ template<> template<>
+ void bit_pack_object_t::test<3>()
+ {
+ U8 packbuffer[255];
+ int pack_bufsize = 0;
+
+ LLBitPack bitpack(packbuffer, 255);
+ U32 num = 0x41fab67a;
+ pack_bufsize = bitpack.bitPack((U8*)&num, 8*sizeof(U32));
+ pack_bufsize = bitpack.flushBitPack();
+
+ LLBitPack bitunpack(packbuffer, pack_bufsize*8);
+ U32 res = 0;
+ // since packing and unpacking is done on same machine in the unit test run,
+ // endianness should not matter
+ bitunpack.bitUnpack((U8*) &res, sizeof(res)*8);
+ ensure("U32->bitPack->bitUnpack->U32 should be equal", num == res);
+ }
+}
diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp
new file mode 100644
index 0000000000..ca27fe9b23
--- /dev/null
+++ b/indra/llcommon/tests/commonmisc_test.cpp
@@ -0,0 +1,677 @@
+/**
+ * @file common.cpp
+ * @author Phoenix
+ * @date 2005-10-12
+ * @brief Common templates for test framework
+ *
+ * $LicenseInfo:firstyear=2005&license=viewergpl$
+ *
+ * Copyright (c) 2005-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+/**
+ *
+ * THOROUGH_DESCRIPTION of common.cpp
+ *
+ */
+
+#include <algorithm>
+#include <iomanip>
+#include <iterator>
+
+#include "linden_common.h"
+
+#include "../llmemorystream.h"
+#include "../llsd.h"
+#include "../llsdserialize.h"
+#include "../u64.h"
+#include "../llhash.h"
+
+#include "../test/lltut.h"
+
+
+#if LL_WINDOWS
+// disable overflow warnings
+#pragma warning(disable: 4307)
+#endif
+
+namespace tut
+{
+ struct sd_data
+ {
+ };
+ typedef test_group<sd_data> sd_test;
+ typedef sd_test::object sd_object;
+ tut::sd_test sd("llsd");
+
+ template<> template<>
+ void sd_object::test<1>()
+ {
+ std::ostringstream resp;
+ resp << "{'connect':true, 'position':[r128,r128,r128], 'look_at':[r0,r1,r0], 'agent_access':'M', 'region_x':i8192, 'region_y':i8192}";
+ std::string str = resp.str();
+ LLMemoryStream mstr((U8*)str.c_str(), str.size());
+ LLSD response;
+ S32 count = LLSDSerialize::fromNotation(response, mstr, str.size());
+ ensure("stream parsed", response.isDefined());
+ ensure_equals("stream parse count", count, 13);
+ ensure_equals("sd type", response.type(), LLSD::TypeMap);
+ ensure_equals("map element count", response.size(), 6);
+ ensure_equals("value connect", response["connect"].asBoolean(), true);
+ ensure_equals("value region_x", response["region_x"].asInteger(),8192);
+ ensure_equals("value region_y", response["region_y"].asInteger(),8192);
+ }
+
+ template<> template<>
+ void sd_object::test<2>()
+ {
+ const std::string decoded("random");
+ //const std::string encoded("cmFuZG9t\n");
+ const std::string streamed("b(6)\"random\"");
+ typedef std::vector<U8> buf_t;
+ buf_t buf;
+ std::copy(
+ decoded.begin(),
+ decoded.end(),
+ std::back_insert_iterator<buf_t>(buf));
+ LLSD sd;
+ sd = buf;
+ std::stringstream str;
+ S32 count = LLSDSerialize::toNotation(sd, str);
+ ensure_equals("output count", count, 1);
+ std::string actual(str.str());
+ ensure_equals("formatted binary encoding", actual, streamed);
+ sd.clear();
+ LLSDSerialize::fromNotation(sd, str, str.str().size());
+ std::vector<U8> after;
+ after = sd.asBinary();
+ ensure_equals("binary decoded size", after.size(), decoded.size());
+ ensure("binary decoding", (0 == memcmp(
+ &after[0],
+ decoded.c_str(),
+ decoded.size())));
+ }
+
+ template<> template<>
+ void sd_object::test<3>()
+ {
+ for(S32 i = 0; i < 100; ++i)
+ {
+ // gen up a starting point
+ typedef std::vector<U8> buf_t;
+ buf_t source;
+ srand(i); /* Flawfinder: ignore */
+ S32 size = rand() % 1000 + 10;
+ std::generate_n(
+ std::back_insert_iterator<buf_t>(source),
+ size,
+ rand);
+ LLSD sd(source);
+ std::stringstream str;
+ S32 count = LLSDSerialize::toNotation(sd, str);
+ sd.clear();
+ ensure_equals("format count", count, 1);
+ LLSD sd2;
+ count = LLSDSerialize::fromNotation(sd2, str, str.str().size());
+ ensure_equals("parse count", count, 1);
+ buf_t dest = sd2.asBinary();
+ str.str("");
+ str << "binary encoding size " << i;
+ ensure_equals(str.str().c_str(), dest.size(), source.size());
+ str.str("");
+ str << "binary encoding " << i;
+ ensure(str.str().c_str(), (source == dest));
+ }
+ }
+
+ template<> template<>
+ void sd_object::test<4>()
+ {
+ std::ostringstream ostr;
+ ostr << "{'task_id':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n"
+ << "{\n\tname\tObject|\n}\n";
+ std::string expected = ostr.str();
+ std::stringstream serialized;
+ serialized << "'" << LLSDNotationFormatter::escapeString(expected)
+ << "'";
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(
+ sd,
+ serialized,
+ serialized.str().size());
+ ensure_equals("parse count", count, 1);
+ ensure_equals("String streaming", sd.asString(), expected);
+ }
+
+ template<> template<>
+ void sd_object::test<5>()
+ {
+ for(S32 i = 0; i < 100; ++i)
+ {
+ // gen up a starting point
+ typedef std::vector<U8> buf_t;
+ buf_t source;
+ srand(666 + i); /* Flawfinder: ignore */
+ S32 size = rand() % 1000 + 10;
+ std::generate_n(
+ std::back_insert_iterator<buf_t>(source),
+ size,
+ rand);
+ std::stringstream str;
+ str << "b(" << size << ")\"";
+ str.write((const char*)&source[0], size);
+ str << "\"";
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
+ ensure_equals("binary parse", count, 1);
+ buf_t actual = sd.asBinary();
+ ensure_equals("binary size", actual.size(), (size_t)size);
+ ensure("binary data", (0 == memcmp(&source[0], &actual[0], size)));
+ }
+ }
+
+ template<> template<>
+ void sd_object::test<6>()
+ {
+ std::string expected("'{\"task_id\":u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}'\t\n\t\t");
+ std::stringstream str;
+ str << "s(" << expected.size() << ")'";
+ str.write(expected.c_str(), expected.size());
+ str << "'";
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
+ ensure_equals("parse count", count, 1);
+ std::string actual = sd.asString();
+ ensure_equals("string sizes", actual.size(), expected.size());
+ ensure_equals("string content", actual, expected);
+ }
+
+ template<> template<>
+ void sd_object::test<7>()
+ {
+ std::string msg("come on in");
+ std::stringstream stream;
+ stream << "{'connect':1, 'message':'" << msg << "',"
+ << " 'position':[r45.65,r100.1,r25.5],"
+ << " 'look_at':[r0,r1,r0],"
+ << " 'agent_access':'PG'}";
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(
+ sd,
+ stream,
+ stream.str().size());
+ ensure_equals("parse count", count, 12);
+ ensure_equals("bool value", sd["connect"].asBoolean(), true);
+ ensure_equals("message value", sd["message"].asString(), msg);
+ ensure_equals("pos x", sd["position"][0].asReal(), 45.65);
+ ensure_equals("pos y", sd["position"][1].asReal(), 100.1);
+ ensure_equals("pos z", sd["position"][2].asReal(), 25.5);
+ ensure_equals("look x", sd["look_at"][0].asReal(), 0.0);
+ ensure_equals("look y", sd["look_at"][1].asReal(), 1.0);
+ ensure_equals("look z", sd["look_at"][2].asReal(), 0.0);
+ }
+
+ template<> template<>
+ void sd_object::test<8>()
+ {
+ std::stringstream resp;
+ resp << "{'label':'short string test', 'singlechar':'a', 'empty':'', 'endoftest':'end' }";
+ LLSD response;
+ S32 count = LLSDSerialize::fromNotation(
+ response,
+ resp,
+ resp.str().size());
+ ensure_equals("parse count", count, 5);
+ ensure_equals("sd type", response.type(), LLSD::TypeMap);
+ ensure_equals("map element count", response.size(), 4);
+ ensure_equals("singlechar", response["singlechar"].asString(), "a");
+ ensure_equals("empty", response["empty"].asString(), "");
+ }
+
+ template<> template<>
+ void sd_object::test<9>()
+ {
+ std::ostringstream resp;
+ resp << "{'label':'short binary test', 'singlebinary':b(1)\"A\", 'singlerawstring':s(1)\"A\", 'endoftest':'end' }";
+ std::string str = resp.str();
+ LLSD sd;
+ LLMemoryStream mstr((U8*)str.c_str(), str.size());
+ S32 count = LLSDSerialize::fromNotation(sd, mstr, str.size());
+ ensure_equals("parse count", count, 5);
+ ensure("sd created", sd.isDefined());
+ ensure_equals("sd type", sd.type(), LLSD::TypeMap);
+ ensure_equals("map element count", sd.size(), 4);
+ ensure_equals(
+ "label",
+ sd["label"].asString(),
+ "short binary test");
+ std::vector<U8> bin = sd["singlebinary"].asBinary();
+ std::vector<U8> expected;
+ expected.resize(1);
+ expected[0] = 'A';
+ ensure("single binary", (0 == memcmp(&bin[0], &expected[0], 1)));
+ ensure_equals(
+ "single string",
+ sd["singlerawstring"].asString(),
+ std::string("A"));
+ ensure_equals("end", sd["endoftest"].asString(), "end");
+ }
+
+ template<> template<>
+ void sd_object::test<10>()
+ {
+
+ std::string message("parcel '' is naughty.");
+ std::stringstream str;
+ str << "{'message':'" << LLSDNotationFormatter::escapeString(message)
+ << "'}";
+ std::string expected_str("{'message':'parcel \\'\\' is naughty.'}");
+ std::string actual_str = str.str();
+ ensure_equals("stream contents", actual_str, expected_str);
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, str, actual_str.size());
+ ensure_equals("parse count", count, 2);
+ ensure("valid parse", sd.isDefined());
+ std::string actual = sd["message"].asString();
+ ensure_equals("message contents", actual, message);
+ }
+
+ template<> template<>
+ void sd_object::test<11>()
+ {
+ std::string expected("\"\"\"\"''''''\"");
+ std::stringstream str;
+ str << "'" << LLSDNotationFormatter::escapeString(expected) << "'";
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
+ ensure_equals("parse count", count, 1);
+ ensure_equals("string value", sd.asString(), expected);
+ }
+
+ template<> template<>
+ void sd_object::test<12>()
+ {
+ std::string expected("mytest\\");
+ std::stringstream str;
+ str << "'" << LLSDNotationFormatter::escapeString(expected) << "'";
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
+ ensure_equals("parse count", count, 1);
+ ensure_equals("string value", sd.asString(), expected);
+ }
+
+ template<> template<>
+ void sd_object::test<13>()
+ {
+ for(S32 i = 0; i < 1000; ++i)
+ {
+ // gen up a starting point
+ std::string expected;
+ srand(1337 + i); /* Flawfinder: ignore */
+ S32 size = rand() % 30 + 5;
+ std::generate_n(
+ std::back_insert_iterator<std::string>(expected),
+ size,
+ rand);
+ std::stringstream str;
+ str << "'" << LLSDNotationFormatter::escapeString(expected) << "'";
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, str, expected.size());
+ ensure_equals("parse count", count, 1);
+ std::string actual = sd.asString();
+/*
+ if(actual != expected)
+ {
+ llwarns << "iteration " << i << llendl;
+ std::ostringstream e_str;
+ std::string::iterator iter = expected.begin();
+ std::string::iterator end = expected.end();
+ for(; iter != end; ++iter)
+ {
+ e_str << (S32)((U8)(*iter)) << " ";
+ }
+ e_str << std::endl;
+ llsd_serialize_string(e_str, expected);
+ llwarns << "expected size: " << expected.size() << llendl;
+ llwarns << "expected: " << e_str.str() << llendl;
+
+ std::ostringstream a_str;
+ iter = actual.begin();
+ end = actual.end();
+ for(; iter != end; ++iter)
+ {
+ a_str << (S32)((U8)(*iter)) << " ";
+ }
+ a_str << std::endl;
+ llsd_serialize_string(a_str, actual);
+ llwarns << "actual size: " << actual.size() << llendl;
+ llwarns << "actual: " << a_str.str() << llendl;
+ }
+*/
+ ensure_equals("string value", actual, expected);
+ }
+ }
+
+ template<> template<>
+ void sd_object::test<14>()
+ {
+//#if LL_WINDOWS && _MSC_VER >= 1400
+// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser.");
+//#endif
+ std::string param = "[{'version':i1},{'data':{'binary_bucket':b(0)\"\"},'from_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'from_name':'Phoenix Linden','id':u004e45e5-5576-277a-fba7-859d6a4cb5c8,'message':'hey','offline':i0,'timestamp':i0,'to_id':u3c5f1bb4-5182-7546-6401-1d329b4ff2f8,'type':i0},{'agent_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'god_level':i0,'limited_to_estate':i1}]";
+ std::istringstream istr;
+ istr.str(param);
+ LLSD param_sd;
+ LLSDSerialize::fromNotation(param_sd, istr, param.size());
+ ensure_equals("parsed type", param_sd.type(), LLSD::TypeArray);
+ LLSD version_sd = param_sd[0];
+ ensure_equals("version type", version_sd.type(), LLSD::TypeMap);
+ ensure("has version", version_sd.has("version"));
+ ensure_equals("version number", version_sd["version"].asInteger(), 1);
+ LLSD src_sd = param_sd[1];
+ ensure_equals("src type", src_sd.type(), LLSD::TypeMap);
+ LLSD dst_sd = param_sd[2];
+ ensure_equals("dst type", dst_sd.type(), LLSD::TypeMap);
+ }
+
+ template<> template<>
+ void sd_object::test<15>()
+ {
+ std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, istr, val.size());
+ ensure_equals("parsed type", sd.type(), LLSD::TypeArray);
+ ensure_equals("parsed size", sd.size(), 1);
+ LLSD failures = sd[0]["failures"];
+ ensure("no failures.", failures.isUndefined());
+ LLSD success = sd[0]["successfuls"];
+ ensure_equals("success type", success.type(), LLSD::TypeArray);
+ ensure_equals("success size", success.size(), 1);
+ ensure_equals("success instance type", success[0].type(), LLSD::TypeUUID);
+ }
+
+ template<> template<>
+ void sd_object::test<16>()
+ {
+ std::string val = "[f,t,0,1,{'foo':t,'bar':f}]";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, istr, val.size());
+ ensure_equals("parsed type", sd.type(), LLSD::TypeArray);
+ ensure_equals("parsed size", sd.size(), 5);
+ ensure_equals("element 0 false", sd[0].asBoolean(), false);
+ ensure_equals("element 1 true", sd[1].asBoolean(), true);
+ ensure_equals("element 2 false", sd[2].asBoolean(), false);
+ ensure_equals("element 3 true", sd[3].asBoolean(), true);
+ LLSD map = sd[4];
+ ensure_equals("element 4 type", map.type(), LLSD::TypeMap);
+ ensure_equals("map foo type", map["foo"].type(), LLSD::TypeBoolean);
+ ensure_equals("map foo value", map["foo"].asBoolean(), true);
+ ensure_equals("map bar type", map["bar"].type(), LLSD::TypeBoolean);
+ ensure_equals("map bar value", map["bar"].asBoolean(), false);
+ }
+
+/*
+ template<> template<>
+ void sd_object::test<16>()
+ {
+ }
+*/
+}
+
+#if 0
+'{\'task_id\':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t00082000\n\t\tcreator_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t00000000-0000-0000-0000-000000000000\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t10284\n\ttotal_crc\t35\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t21\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t0\t0\t0\n\toldpos\t0\t0\t0\n\trotation\t4.371139183945160766597837e-08\t1\t4.371139183945160766597837e-08\t0\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.2816932\t0.2816932\t0.2816932\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t80\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t1132625972249870\n\tbirthtime\t1132625953120694\n\treztime\t1132625953120694\n\tparceltime\t1132625953120694\n\ttax_rate\t1.01615\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.141593, 0.000000, -3.141593\n\tnamevalue\tAttachmentOffset VEC3 RW DS 0.000000, 0.000000, 0.000000\n\tnamevalue\tAttachPt U32 RW S 5\n\tnamevalue\tAttachItemID STRING RW SV 1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t52019cdd-b464-ba19-e66d-3da751fef9da\n\torig_item_id\t1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n'
+#endif
+
+namespace tut
+{
+ struct mem_data
+ {
+ };
+ typedef test_group<mem_data> mem_test;
+ typedef mem_test::object mem_object;
+ tut::mem_test mem_stream("memory_stream");
+
+ template<> template<>
+ void mem_object::test<1>()
+ {
+ const char HELLO_WORLD[] = "hello world";
+ LLMemoryStream mem((U8*)&HELLO_WORLD[0], strlen(HELLO_WORLD)); /* Flawfinder: ignore */
+ std::string hello;
+ std::string world;
+ mem >> hello >> world;
+ ensure_equals("first word", hello, std::string("hello"));
+ ensure_equals("second word", world, std::string("world"));
+ }
+}
+
+namespace tut
+{
+ struct U64_data
+ {
+ };
+ typedef test_group<U64_data> U64_test;
+ typedef U64_test::object U64_object;
+ tut::U64_test U64_testcase("U64_conversion");
+
+ // U64_to_str
+ template<> template<>
+ void U64_object::test<1>()
+ {
+ U64 val;
+ std::string val_str;
+ char result[256];
+ std::string result_str;
+
+ val = U64L(18446744073709551610); // slightly less than MAX_U64
+ val_str = "18446744073709551610";
+
+ U64_to_str(val, result, sizeof(result));
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.1", val_str, result_str);
+
+ val = 0;
+ val_str = "0";
+ U64_to_str(val, result, sizeof(result));
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.2", val_str, result_str);
+
+ val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF
+ val_str = "18446744073709551615";
+ U64_to_str(val, result, sizeof(result));
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.3", val_str, result_str);
+
+ // overflow - will result in warning at compile time
+ val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0
+ val_str = "0";
+ U64_to_str(val, result, sizeof(result));
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.4", val_str, result_str);
+
+ val = U64L(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615
+ val_str = "18446744073709551615";
+ U64_to_str(val, result, sizeof(result));
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.5", val_str, result_str);
+
+ val = U64L(10000000000000000000); // testing preserving of 0s
+ val_str = "10000000000000000000";
+ U64_to_str(val, result, sizeof(result));
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.6", val_str, result_str);
+
+ val = 1; // testing no leading 0s
+ val_str = "1";
+ U64_to_str(val, result, sizeof(result));
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.7", val_str, result_str);
+
+ val = U64L(18446744073709551615); // testing exact sized buffer for result
+ val_str = "18446744073709551615";
+ memset(result, 'A', sizeof(result)); // initialize buffer with all 'A'
+ U64_to_str(val, result, sizeof("18446744073709551615")); //pass in the exact size
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.8", val_str, result_str);
+
+ val = U64L(18446744073709551615); // testing smaller sized buffer for result
+ val_str = "1844";
+ memset(result, 'A', sizeof(result)); // initialize buffer with all 'A'
+ U64_to_str(val, result, 5); //pass in a size of 5. should only copy first 4 integers and add a null terminator
+ result_str = (const char*) result;
+ ensure_equals("U64_to_str converted 1.9", val_str, result_str);
+ }
+
+ // str_to_U64
+ template<> template<>
+ void U64_object::test<2>()
+ {
+ U64 val;
+ U64 result;
+
+ val = U64L(18446744073709551610); // slightly less than MAX_U64
+ result = str_to_U64("18446744073709551610");
+ ensure_equals("str_to_U64 converted 2.1", val, result);
+
+ val = U64L(0); // empty string
+ result = str_to_U64(LLStringUtil::null);
+ ensure_equals("str_to_U64 converted 2.2", val, result);
+
+ val = U64L(0); // 0
+ result = str_to_U64("0");
+ ensure_equals("str_to_U64 converted 2.3", val, result);
+
+ val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF
+ result = str_to_U64("18446744073709551615");
+ ensure_equals("str_to_U64 converted 2.4", val, result);
+
+ // overflow - will result in warning at compile time
+ val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0
+ result = str_to_U64("18446744073709551616");
+ ensure_equals("str_to_U64 converted 2.5", val, result);
+
+ val = U64L(1234); // process till first non-integral character
+ result = str_to_U64("1234A5678");
+ ensure_equals("str_to_U64 converted 2.6", val, result);
+
+ val = U64L(5678); // skip all non-integral characters
+ result = str_to_U64("ABCD5678");
+ ensure_equals("str_to_U64 converted 2.7", val, result);
+
+ // should it skip negative sign and process
+ // rest of string or return 0
+ val = U64L(1234); // skip initial negative sign
+ result = str_to_U64("-1234");
+ ensure_equals("str_to_U64 converted 2.8", val, result);
+
+ val = U64L(5678); // stop at negative sign in the middle
+ result = str_to_U64("5678-1234");
+ ensure_equals("str_to_U64 converted 2.9", val, result);
+
+ val = U64L(0); // no integers
+ result = str_to_U64("AaCD");
+ ensure_equals("str_to_U64 converted 2.10", val, result);
+ }
+
+ // U64_to_F64
+ template<> template<>
+ void U64_object::test<3>()
+ {
+ F64 val;
+ F64 result;
+
+ result = 18446744073709551610.0;
+ val = U64_to_F64(U64L(18446744073709551610));
+ ensure_equals("U64_to_F64 converted 3.1", val, result);
+
+ result = 18446744073709551615.0; // 0xFFFFFFFFFFFFFFFF
+ val = U64_to_F64(U64L(18446744073709551615));
+ ensure_equals("U64_to_F64 converted 3.2", val, result);
+
+ result = 0.0; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0
+ // overflow - will result in warning at compile time
+ val = U64_to_F64(U64L(18446744073709551615)+1);
+ ensure_equals("U64_to_F64 converted 3.3", val, result);
+
+ result = 0.0; // 0
+ val = U64_to_F64(U64L(0));
+ ensure_equals("U64_to_F64 converted 3.4", val, result);
+
+ result = 1.0; // odd
+ val = U64_to_F64(U64L(1));
+ ensure_equals("U64_to_F64 converted 3.5", val, result);
+
+ result = 2.0; // even
+ val = U64_to_F64(U64L(2));
+ ensure_equals("U64_to_F64 converted 3.6", val, result);
+
+ result = U64L(0x7FFFFFFFFFFFFFFF) * 1.0L; // 0x7FFFFFFFFFFFFFFF
+ val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF));
+ ensure_equals("U64_to_F64 converted 3.7", val, result);
+ }
+
+ // llstrtou64
+ // seems to be deprecated - could not find it being used
+ // anywhere in the tarball - skipping unit tests for now
+}
+
+
+namespace tut
+{
+ struct hash_data
+ {
+ };
+ typedef test_group<hash_data> hash_test;
+ typedef hash_test::object hash_object;
+ tut::hash_test hash_tester("hash_test");
+
+ template<> template<>
+ void hash_object::test<1>()
+ {
+ const char * str1 = "test string one";
+ const char * same_as_str1 = "test string one";
+
+ size_t hash1 = llhash(str1);
+ size_t same_as_hash1 = llhash(same_as_str1);
+
+
+ ensure("Hashes from identical strings should be equal", hash1 == same_as_hash1);
+
+ char str[100];
+ strcpy( str, "Another test" );
+
+ size_t hash2 = llhash(str);
+
+ strcpy( str, "Different string, same pointer" );
+
+ size_t hash3 = llhash(str);
+
+ ensure("Hashes from same pointer but different string should not be equal", hash2 != hash3);
+ }
+}
diff --git a/indra/llcommon/tests/llbase64_test.cpp b/indra/llcommon/tests/llbase64_test.cpp
new file mode 100644
index 0000000000..dde43b5169
--- /dev/null
+++ b/indra/llcommon/tests/llbase64_test.cpp
@@ -0,0 +1,83 @@
+/**
+ * @file llbase64_tut.cpp
+ * @author James Cook
+ * @date 2007-02-04
+ *
+ * $LicenseInfo:firstyear=2007&license=viewergpl$
+ *
+ * Copyright (c) 2007-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include <string>
+
+#include "linden_common.h"
+
+#include "../llbase64.h"
+#include "../lluuid.h"
+
+#include "../test/lltut.h"
+
+namespace tut
+{
+ struct base64_data
+ {
+ };
+ typedef test_group<base64_data> base64_test;
+ typedef base64_test::object base64_object;
+ tut::base64_test base64("base64");
+
+ template<> template<>
+ void base64_object::test<1>()
+ {
+ std::string result;
+
+ result = LLBase64::encode(NULL, 0);
+ ensure("encode nothing", (result == "") );
+
+ LLUUID nothing;
+ result = LLBase64::encode(&nothing.mData[0], UUID_BYTES);
+ ensure("encode blank uuid",
+ (result == "AAAAAAAAAAAAAAAAAAAAAA==") );
+
+ LLUUID id("526a1e07-a19d-baed-84c4-ff08a488d15e");
+ result = LLBase64::encode(&id.mData[0], UUID_BYTES);
+ ensure("encode random uuid",
+ (result == "UmoeB6Gduu2ExP8IpIjRXg==") );
+
+ }
+
+ template<> template<>
+ void base64_object::test<2>()
+ {
+ std::string result;
+
+ U8 blob[40] = { 115, 223, 172, 255, 140, 70, 49, 125, 236, 155, 45, 199, 101, 17, 164, 131, 230, 19, 80, 64, 112, 53, 135, 98, 237, 12, 26, 72, 126, 14, 145, 143, 118, 196, 11, 177, 132, 169, 195, 134 };
+ result = LLBase64::encode(&blob[0], 40);
+ ensure("encode 40 bytes",
+ (result == "c9+s/4xGMX3smy3HZRGkg+YTUEBwNYdi7QwaSH4OkY92xAuxhKnDhg==") );
+ }
+
+}
diff --git a/indra/llcommon/tests/lldate_test.cpp b/indra/llcommon/tests/lldate_test.cpp
new file mode 100644
index 0000000000..1e36fdd119
--- /dev/null
+++ b/indra/llcommon/tests/lldate_test.cpp
@@ -0,0 +1,219 @@
+/**
+ * @file lldate_tut.cpp
+ * @author Adroit
+ * @date 2007-02
+ * @brief LLDate test cases.
+ *
+ * $LicenseInfo:firstyear=2007&license=viewergpl$
+ *
+ * Copyright (c) 2007-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../llstring.h"
+#include "../lldate.h"
+
+#include "../test/lltut.h"
+
+
+#define VALID_DATE "2003-04-30T04:00:00Z"
+#define VALID_DATE_LEAP "2004-02-29T04:00:00Z"
+#define VALID_DATE_HOUR_BOUNDARY "2003-04-30T23:59:59Z"
+#define VALID_DATE_FRACTIONAL_SECS "2007-09-26T20:31:33.70Z"
+
+// invalid format
+#define INVALID_DATE_MISSING_YEAR "-04-30T22:59:59Z"
+#define INVALID_DATE_MISSING_MONTH "1900-0430T22:59:59Z"
+#define INVALID_DATE_MISSING_DATE "1900-0430-T22:59:59Z"
+#define INVALID_DATE_MISSING_T "1900-04-30-22:59:59Z"
+#define INVALID_DATE_MISSING_HOUR "1900-04-30T:59:59Z"
+#define INVALID_DATE_MISSING_MIN "1900-04-30T01::59Z"
+#define INVALID_DATE_MISSING_SEC "1900-04-30T01:59Z"
+#define INVALID_DATE_MISSING_Z "1900-04-30T01:59:23"
+#define INVALID_DATE_EMPTY ""
+
+// invalid values
+// apr 1.1.1 seems to not care about constraining the date to valid
+// dates. Put these back when the parser checks.
+#define LL_DATE_PARSER_CHECKS_BOUNDARY 0
+//#define INVALID_DATE_24HOUR_BOUNDARY "2003-04-30T24:00:00Z"
+//#define INVALID_DATE_LEAP "2003-04-29T04:00:00Z"
+//#define INVALID_DATE_HOUR "2003-04-30T24:59:59Z"
+//#define INVALID_DATE_MIN "2003-04-30T22:69:59Z"
+//#define INVALID_DATE_SEC "2003-04-30T22:59:69Z"
+//#define INVALID_DATE_YEAR "0-04-30T22:59:59Z"
+//#define INVALID_DATE_MONTH "2003-13-30T22:59:59Z"
+//#define INVALID_DATE_DAY "2003-04-35T22:59:59Z"
+
+namespace tut
+{
+ struct date_test
+ {
+
+ };
+ typedef test_group<date_test> date_test_t;
+ typedef date_test_t::object date_test_object_t;
+ tut::date_test_t tut_date_test("date_test");
+
+ /* format validation */
+ template<> template<>
+ void date_test_object_t::test<1>()
+ {
+ LLDate date(VALID_DATE);
+ std::string expected_string;
+ bool result;
+ expected_string = VALID_DATE;
+ ensure_equals("Valid Date failed" , expected_string, date.asString());
+
+ result = date.fromString(VALID_DATE_LEAP);
+ expected_string = VALID_DATE_LEAP;
+ ensure_equals("VALID_DATE_LEAP failed" , expected_string, date.asString());
+
+ result = date.fromString(VALID_DATE_HOUR_BOUNDARY);
+ expected_string = VALID_DATE_HOUR_BOUNDARY;
+ ensure_equals("VALID_DATE_HOUR_BOUNDARY failed" , expected_string, date.asString());
+
+ result = date.fromString(VALID_DATE_FRACTIONAL_SECS);
+ expected_string = VALID_DATE_FRACTIONAL_SECS;
+ ensure_equals("VALID_DATE_FRACTIONAL_SECS failed" , expected_string, date.asString());
+
+ result = date.fromString(INVALID_DATE_MISSING_YEAR);
+ ensure_equals("INVALID_DATE_MISSING_YEAR should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MISSING_MONTH);
+ ensure_equals("INVALID_DATE_MISSING_MONTH should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MISSING_DATE);
+ ensure_equals("INVALID_DATE_MISSING_DATE should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MISSING_T);
+ ensure_equals("INVALID_DATE_MISSING_T should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MISSING_HOUR);
+ ensure_equals("INVALID_DATE_MISSING_HOUR should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MISSING_MIN);
+ ensure_equals("INVALID_DATE_MISSING_MIN should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MISSING_SEC);
+ ensure_equals("INVALID_DATE_MISSING_SEC should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MISSING_Z);
+ ensure_equals("INVALID_DATE_MISSING_Z should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_EMPTY);
+ ensure_equals("INVALID_DATE_EMPTY should have failed" , result, false);
+ }
+
+ /* Invalid Value Handling */
+ template<> template<>
+ void date_test_object_t::test<2>()
+ {
+#if LL_DATE_PARSER_CHECKS_BOUNDARY
+ LLDate date;
+ std::string expected_string;
+ bool result;
+
+ result = date.fromString(INVALID_DATE_24HOUR_BOUNDARY);
+ ensure_equals("INVALID_DATE_24HOUR_BOUNDARY should have failed" , result, false);
+ ensure_equals("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!" , date.secondsSinceEpoch(), 0);
+
+ result = date.fromString(INVALID_DATE_LEAP);
+ ensure_equals("INVALID_DATE_LEAP should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_HOUR);
+ ensure_equals("INVALID_DATE_HOUR should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MIN);
+ ensure_equals("INVALID_DATE_MIN should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_SEC);
+ ensure_equals("INVALID_DATE_SEC should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_YEAR);
+ ensure_equals("INVALID_DATE_YEAR should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_MONTH);
+ ensure_equals("INVALID_DATE_MONTH should have failed" , result, false);
+
+ result = date.fromString(INVALID_DATE_DAY);
+ ensure_equals("INVALID_DATE_DAY should have failed" , result, false);
+#endif
+ }
+
+ /* API checks */
+ template<> template<>
+ void date_test_object_t::test<3>()
+ {
+ LLDate date;
+ std::istringstream stream(VALID_DATE);
+ std::string expected_string = VALID_DATE;
+ date.fromStream(stream);
+ ensure_equals("fromStream failed", date.asString(), expected_string);
+ }
+
+ template<> template<>
+ void date_test_object_t::test<4>()
+ {
+ LLDate date1(VALID_DATE);
+ LLDate date2(date1);
+ ensure_equals("LLDate(const LLDate& date) constructor failed", date1.asString(), date2.asString());
+ }
+
+ template<> template<>
+ void date_test_object_t::test<5>()
+ {
+ LLDate date1(VALID_DATE);
+ LLDate date2(date1.secondsSinceEpoch());
+ ensure_equals("secondsSinceEpoch not equal",date1.secondsSinceEpoch(), date2.secondsSinceEpoch());
+ ensure_equals("LLDate created using secondsSinceEpoch not equal", date1.asString(), date2.asString());
+ }
+
+ template<> template<>
+ void date_test_object_t::test<6>()
+ {
+ LLDate date(VALID_DATE);
+ std::ostringstream stream;
+ stream << date;
+ std::string expected_str = VALID_DATE;
+ ensure_equals("ostringstream failed", expected_str, stream.str());
+ }
+
+ template<> template<>
+ void date_test_object_t::test<7>()
+ {
+ LLDate date;
+ std::istringstream stream(VALID_DATE);
+ stream >> date;
+ std::string expected_str = VALID_DATE;
+ std::ostringstream out_stream;
+ out_stream << date;
+
+ ensure_equals("<< failed", date.asString(),expected_str);
+ ensure_equals("<< to >> failed", stream.str(),out_stream.str());
+ }
+}
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
new file mode 100644
index 0000000000..8055647b94
--- /dev/null
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -0,0 +1,768 @@
+/**
+ * @file llerror_tut.cpp
+ * @date December 2006
+ * @brief error unit tests
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include <vector>
+
+#include "linden_common.h"
+
+#include "../llerror.h"
+
+#include "../llerrorcontrol.h"
+#include "../llsd.h"
+
+#include "../test/lltut.h"
+
+namespace
+{
+ void test_that_error_h_includes_enough_things_to_compile_a_message()
+ {
+ llinfos << "!" << llendl;
+ }
+}
+
+namespace
+{
+ static bool fatalWasCalled;
+ void fatalCall(const std::string&) { fatalWasCalled = true; }
+
+ class TestRecorder : public LLError::Recorder
+ {
+ public:
+ TestRecorder() : mWantsTime(false) { }
+ ~TestRecorder() { LLError::removeRecorder(this); }
+
+ void recordMessage(LLError::ELevel level,
+ const std::string& message)
+ {
+ mMessages.push_back(message);
+ }
+
+ int countMessages() { return (int) mMessages.size(); }
+ void clearMessages() { mMessages.clear(); }
+
+ void setWantsTime(bool t) { mWantsTime = t; }
+ bool wantsTime() { return mWantsTime; }
+
+ std::string message(int n)
+ {
+ std::ostringstream test_name;
+ test_name << "testing message " << n << ", not enough messages";
+
+ tut::ensure(test_name.str(), n < countMessages());
+ return mMessages[n];
+ }
+
+ private:
+ typedef std::vector<std::string> MessageVector;
+ MessageVector mMessages;
+
+ bool mWantsTime;
+ };
+}
+
+namespace tut
+{
+ struct ErrorTestData
+ {
+ TestRecorder mRecorder;
+ LLError::Settings* mPriorErrorSettings;
+
+ ErrorTestData()
+ {
+ fatalWasCalled = false;
+
+ mPriorErrorSettings = LLError::saveAndResetSettings();
+ LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
+ LLError::setFatalFunction(fatalCall);
+ LLError::addRecorder(&mRecorder);
+ }
+
+ ~ErrorTestData()
+ {
+ LLError::removeRecorder(&mRecorder);
+ LLError::restoreSettings(mPriorErrorSettings);
+ }
+
+ void ensure_message_count(int expectedCount)
+ {
+ ensure_equals("message count", mRecorder.countMessages(), expectedCount);
+ }
+
+ void ensure_message_contains(int n, const std::string& expectedText)
+ {
+ std::ostringstream test_name;
+ test_name << "testing message " << n;
+
+ ensure_contains(test_name.str(), mRecorder.message(n), expectedText);
+ }
+
+ void ensure_message_does_not_contain(int n, const std::string& expectedText)
+ {
+ std::ostringstream test_name;
+ test_name << "testing message " << n;
+
+ ensure_does_not_contain(test_name.str(), mRecorder.message(n), expectedText);
+ }
+ };
+
+ typedef test_group<ErrorTestData> ErrorTestGroup;
+ typedef ErrorTestGroup::object ErrorTestObject;
+
+ ErrorTestGroup errorTestGroup("error");
+
+ template<> template<>
+ void ErrorTestObject::test<1>()
+ // basic test of output
+ {
+ llinfos << "test" << llendl;
+ llinfos << "bob" << llendl;
+
+ ensure_message_contains(0, "test");
+ ensure_message_contains(1, "bob");
+ }
+}
+
+namespace
+{
+ void writeSome()
+ {
+ lldebugs << "one" << llendl;
+ llinfos << "two" << llendl;
+ llwarns << "three" << llendl;
+ llerrs << "four" << llendl;
+ // fatal messages write out and addtional "error" message
+ }
+};
+
+namespace tut
+{
+ template<> template<>
+ void ErrorTestObject::test<2>()
+ // messages are filtered based on default level
+ {
+ LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
+ writeSome();
+ ensure_message_contains(0, "one");
+ ensure_message_contains(1, "two");
+ ensure_message_contains(2, "three");
+ ensure_message_contains(3, "error");
+ ensure_message_contains(4, "four");
+ ensure_message_count(5);
+
+ LLError::setDefaultLevel(LLError::LEVEL_INFO);
+ writeSome();
+ ensure_message_contains(5, "two");
+ ensure_message_contains(6, "three");
+ ensure_message_contains(7, "error");
+ ensure_message_contains(8, "four");
+ ensure_message_count(9);
+
+ LLError::setDefaultLevel(LLError::LEVEL_WARN);
+ writeSome();
+ ensure_message_contains(9, "three");
+ ensure_message_contains(10, "error");
+ ensure_message_contains(11, "four");
+ ensure_message_count(12);
+
+ LLError::setDefaultLevel(LLError::LEVEL_ERROR);
+ writeSome();
+ ensure_message_contains(12, "error");
+ ensure_message_contains(13, "four");
+ ensure_message_count(14);
+
+ LLError::setDefaultLevel(LLError::LEVEL_NONE);
+ writeSome();
+ ensure_message_count(14);
+ }
+
+ template<> template<>
+ void ErrorTestObject::test<3>()
+ // error type string in output
+ {
+ writeSome();
+ ensure_message_contains(0, "DEBUG: ");
+ ensure_message_contains(1, "INFO: ");
+ ensure_message_contains(2, "WARNING: ");
+ ensure_message_does_not_contain(3, "ERROR");
+ ensure_message_contains(4, "ERROR: ");
+ ensure_message_count(5);
+ }
+
+ template<> template<>
+ void ErrorTestObject::test<4>()
+ // file abbreviation
+ {
+ std::string thisFile = __FILE__;
+ std::string abbreviateFile = LLError::abbreviateFile(thisFile);
+
+ ensure_ends_with("file name abbreviation",
+ abbreviateFile,
+ "llcommon/tests/llerror_test.cpp"
+ );
+ ensure_does_not_contain("file name abbreviation",
+ abbreviateFile, "indra");
+
+ std::string someFile =
+#if LL_WINDOWS
+ "C:/amy/bob/cam.cpp"
+#else
+ "/amy/bob/cam.cpp"
+#endif
+ ;
+ std::string someAbbreviation = LLError::abbreviateFile(someFile);
+
+ ensure_equals("non-indra file abbreviation",
+ someAbbreviation, someFile);
+ }
+}
+
+namespace
+{
+ std::string locationString(int line)
+ {
+ std::ostringstream location;
+ location << LLError::abbreviateFile(__FILE__)
+ << "(" << line << ") : ";
+
+ return location.str();
+ }
+
+ std::string writeReturningLocation()
+ {
+ llinfos << "apple" << llendl; int this_line = __LINE__;
+ return locationString(this_line);
+ }
+
+ std::string writeReturningLocationAndFunction()
+ {
+ llinfos << "apple" << llendl; int this_line = __LINE__;
+ return locationString(this_line) + __FUNCTION__;
+ }
+
+ std::string errorReturningLocation()
+ {
+ llerrs << "die" << llendl; int this_line = __LINE__;
+ return locationString(this_line);
+ }
+}
+
+namespace tut
+{
+ template<> template<>
+ void ErrorTestObject::test<5>()
+ // file and line information in log messages
+ {
+ std::string location = writeReturningLocation();
+ // expecting default to not print location information
+
+ LLError::setPrintLocation(true);
+ writeReturningLocation();
+
+ LLError::setPrintLocation(false);
+ writeReturningLocation();
+
+ ensure_message_does_not_contain(0, location);
+ ensure_message_contains(1, location);
+ ensure_message_does_not_contain(2, location);
+ }
+}
+
+/* The following helper functions and class members all log a simple message
+ from some particular function scope. Each function takes a bool argument
+ that indicates if it should log its own name or not (in the manner that
+ existing log messages often do.) The functions all return their C++
+ name so that test can be substantial mechanized.
+ */
+
+std::string logFromGlobal(bool id)
+{
+ llinfos << (id ? "logFromGlobal: " : "") << "hi" << llendl;
+ return "logFromGlobal";
+}
+
+static std::string logFromStatic(bool id)
+{
+ llinfos << (id ? "logFromStatic: " : "") << "hi" << llendl;
+ return "logFromStatic";
+}
+
+namespace
+{
+ std::string logFromAnon(bool id)
+ {
+ llinfos << (id ? "logFromAnon: " : "") << "hi" << llendl;
+ return "logFromAnon";
+ }
+}
+
+namespace Foo {
+ std::string logFromNamespace(bool id)
+ {
+ llinfos << (id ? "Foo::logFromNamespace: " : "") << "hi" << llendl;
+ //return "Foo::logFromNamespace";
+ // there is no standard way to get the namespace name, hence
+ // we won't be testing for it
+ return "logFromNamespace";
+ }
+}
+
+namespace
+{
+ class ClassWithNoLogType {
+ public:
+ std::string logFromMember(bool id)
+ {
+ llinfos << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << llendl;
+ return "ClassWithNoLogType::logFromMember";
+ }
+ static std::string logFromStatic(bool id)
+ {
+ llinfos << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << llendl;
+ return "ClassWithNoLogType::logFromStatic";
+ }
+ };
+
+ class ClassWithLogType {
+ LOG_CLASS(ClassWithLogType);
+ public:
+ std::string logFromMember(bool id)
+ {
+ llinfos << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << llendl;
+ return "ClassWithLogType::logFromMember";
+ }
+ static std::string logFromStatic(bool id)
+ {
+ llinfos << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << llendl;
+ return "ClassWithLogType::logFromStatic";
+ }
+ };
+
+ std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); }
+ std::string logFromClassWithNoLogTypeMember(bool id) { ClassWithNoLogType c; return c.logFromMember(id); }
+ std::string logFromClassWithNoLogTypeStatic(bool id) { return ClassWithNoLogType::logFromStatic(id); }
+ std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); }
+ std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); }
+
+ void ensure_has(const std::string& message,
+ const std::string& actual, const std::string& expected)
+ {
+ std::string::size_type n1 = actual.find(expected);
+ if (n1 == std::string::npos)
+ {
+ std::stringstream ss;
+ ss << message << ": " << "expected to find a copy of " << expected
+ << " in actual " << actual;
+ throw tut::failure(ss.str().c_str());
+ }
+ }
+
+ typedef std::string (*LogFromFunction)(bool);
+ void testLogName(TestRecorder& recorder, LogFromFunction f,
+ const std::string& class_name = "")
+ {
+ recorder.clearMessages();
+ std::string name = f(false);
+ f(true);
+
+ std::string messageWithoutName = recorder.message(0);
+ std::string messageWithName = recorder.message(1);
+
+ ensure_has(name + " logged without name",
+ messageWithoutName, name);
+ ensure_has(name + " logged with name",
+ messageWithName, name);
+
+ if (!class_name.empty())
+ {
+ ensure_has(name + "logged without name",
+ messageWithoutName, class_name);
+ ensure_has(name + "logged with name",
+ messageWithName, class_name);
+ }
+ }
+}
+
+namespace tut
+{
+ template<> template<>
+ // class/function information in output
+ void ErrorTestObject::test<6>()
+ {
+ testLogName(mRecorder, logFromGlobal);
+ testLogName(mRecorder, logFromStatic);
+ testLogName(mRecorder, logFromAnon);
+ testLogName(mRecorder, logFromNamespace);
+ //testLogName(mRecorder, logFromClassWithNoLogTypeMember, "ClassWithNoLogType");
+ //testLogName(mRecorder, logFromClassWithNoLogTypeStatic, "ClassWithNoLogType");
+ // XXX: figure out what the exepcted response is for these
+ testLogName(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType");
+ testLogName(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType");
+ }
+}
+
+namespace
+{
+ std::string innerLogger()
+ {
+ llinfos << "inside" << llendl;
+ return "moo";
+ }
+
+ std::string outerLogger()
+ {
+ llinfos << "outside(" << innerLogger() << ")" << llendl;
+ return "bar";
+ }
+
+ void uberLogger()
+ {
+ llinfos << "uber(" << outerLogger() << "," << innerLogger() << ")" << llendl;
+ }
+
+ class LogWhileLogging
+ {
+ public:
+ void print(std::ostream& out) const
+ {
+ llinfos << "logging" << llendl;
+ out << "baz";
+ }
+ };
+
+ std::ostream& operator<<(std::ostream& out, const LogWhileLogging& l)
+ { l.print(out); return out; }
+
+ void metaLogger()
+ {
+ LogWhileLogging l;
+ llinfos << "meta(" << l << ")" << llendl;
+ }
+
+}
+
+namespace tut
+{
+ template<> template<>
+ // handle nested logging
+ void ErrorTestObject::test<7>()
+ {
+ outerLogger();
+ ensure_message_contains(0, "inside");
+ ensure_message_contains(1, "outside(moo)");
+ ensure_message_count(2);
+
+ uberLogger();
+ ensure_message_contains(2, "inside");
+ ensure_message_contains(3, "inside");
+ ensure_message_contains(4, "outside(moo)");
+ ensure_message_contains(5, "uber(bar,moo)");
+ ensure_message_count(6);
+
+ metaLogger();
+ ensure_message_contains(6, "logging");
+ ensure_message_contains(7, "meta(baz)");
+ ensure_message_count(8);
+ }
+
+ template<> template<>
+ // special handling of llerrs calls
+ void ErrorTestObject::test<8>()
+ {
+ LLError::setPrintLocation(false);
+ std::string location = errorReturningLocation();
+
+ ensure_message_contains(0, location + "error");
+ ensure_message_contains(1, "die");
+ ensure_message_count(2);
+
+ ensure("fatal callback called", fatalWasCalled);
+ }
+}
+
+namespace
+{
+ std::string roswell()
+ {
+ return "1947-07-08T03:04:05Z";
+ }
+
+ void ufoSighting()
+ {
+ llinfos << "ufo" << llendl;
+ }
+}
+
+namespace tut
+{
+ template<> template<>
+ // time in output (for recorders that need it)
+ void ErrorTestObject::test<9>()
+ {
+ LLError::setTimeFunction(roswell);
+
+ mRecorder.setWantsTime(false);
+ ufoSighting();
+ ensure_message_contains(0, "ufo");
+ ensure_message_does_not_contain(0, roswell());
+
+ mRecorder.setWantsTime(true);
+ ufoSighting();
+ ensure_message_contains(1, "ufo");
+ ensure_message_contains(1, roswell());
+ }
+
+ template<> template<>
+ // output order
+ void ErrorTestObject::test<10>()
+ {
+ LLError::setPrintLocation(true);
+ LLError::setTimeFunction(roswell);
+ mRecorder.setWantsTime(true);
+ std::string locationAndFunction = writeReturningLocationAndFunction();
+
+ ensure_equals("order is time type location function message",
+ mRecorder.message(0),
+ roswell() + " INFO: " + locationAndFunction + ": apple");
+ }
+
+ template<> template<>
+ // multiple recorders
+ void ErrorTestObject::test<11>()
+ {
+ TestRecorder altRecorder;
+ LLError::addRecorder(&altRecorder);
+
+ llinfos << "boo" << llendl;
+
+ ensure_message_contains(0, "boo");
+ ensure_equals("alt recorder count", altRecorder.countMessages(), 1);
+ ensure_contains("alt recorder message 0", altRecorder.message(0), "boo");
+
+ LLError::setTimeFunction(roswell);
+
+ TestRecorder anotherRecorder;
+ anotherRecorder.setWantsTime(true);
+ LLError::addRecorder(&anotherRecorder);
+
+ llinfos << "baz" << llendl;
+
+ std::string when = roswell();
+
+ ensure_message_does_not_contain(1, when);
+ ensure_equals("alt recorder count", altRecorder.countMessages(), 2);
+ ensure_does_not_contain("alt recorder message 1", altRecorder.message(1), when);
+ ensure_equals("another recorder count", anotherRecorder.countMessages(), 1);
+ ensure_contains("another recorder message 0", anotherRecorder.message(0), when);
+ }
+}
+
+class TestAlpha
+{
+ LOG_CLASS(TestAlpha);
+public:
+ static void doDebug() { lldebugs << "add dice" << llendl; }
+ static void doInfo() { llinfos << "any idea" << llendl; }
+ static void doWarn() { llwarns << "aim west" << llendl; }
+ static void doError() { llerrs << "ate eels" << llendl; }
+ static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
+};
+
+class TestBeta
+{
+ LOG_CLASS(TestBeta);
+public:
+ static void doDebug() { lldebugs << "bed down" << llendl; }
+ static void doInfo() { llinfos << "buy iron" << llendl; }
+ static void doWarn() { llwarns << "bad word" << llendl; }
+ static void doError() { llerrs << "big easy" << llendl; }
+ static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
+};
+
+namespace tut
+{
+ template<> template<>
+ // filtering by class
+ void ErrorTestObject::test<12>()
+ {
+ LLError::setDefaultLevel(LLError::LEVEL_WARN);
+ LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO);
+
+ TestAlpha::doAll();
+ TestBeta::doAll();
+
+ ensure_message_contains(0, "aim west");
+ ensure_message_contains(1, "error");
+ ensure_message_contains(2, "ate eels");
+ ensure_message_contains(3, "buy iron");
+ ensure_message_contains(4, "bad word");
+ ensure_message_contains(5, "error");
+ ensure_message_contains(6, "big easy");
+ ensure_message_count(7);
+ }
+
+ template<> template<>
+ // filtering by function, and that it will override class filtering
+ void ErrorTestObject::test<13>()
+ {
+ LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
+ LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN);
+ LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG);
+ LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE);
+
+ TestBeta::doAll();
+ ensure_message_contains(0, "buy iron");
+ ensure_message_contains(1, "bad word");
+ ensure_message_count(2);
+ }
+
+ template<> template<>
+ // filtering by file
+ // and that it is overridden by both class and function filtering
+ void ErrorTestObject::test<14>()
+ {
+ LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
+ LLError::setFileLevel(LLError::abbreviateFile(__FILE__),
+ LLError::LEVEL_WARN);
+ LLError::setClassLevel("TestAlpha", LLError::LEVEL_INFO);
+ LLError::setFunctionLevel("TestAlpha::doError",
+ LLError::LEVEL_NONE);
+ LLError::setFunctionLevel("TestBeta::doError",
+ LLError::LEVEL_NONE);
+
+ TestAlpha::doAll();
+ TestBeta::doAll();
+ ensure_message_contains(0, "any idea");
+ ensure_message_contains(1, "aim west");
+ ensure_message_contains(2, "bad word");
+ ensure_message_count(3);
+ }
+
+ template<> template<>
+ // proper cached, efficient lookup of filtering
+ void ErrorTestObject::test<15>()
+ {
+ LLError::setDefaultLevel(LLError::LEVEL_NONE);
+
+ TestAlpha::doInfo();
+ ensure_message_count(0);
+ ensure_equals("first check", LLError::shouldLogCallCount(), 1);
+ TestAlpha::doInfo();
+ ensure_message_count(0);
+ ensure_equals("second check", LLError::shouldLogCallCount(), 1);
+
+ LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG);
+ TestAlpha::doInfo();
+ ensure_message_count(1);
+ ensure_equals("third check", LLError::shouldLogCallCount(), 2);
+ TestAlpha::doInfo();
+ ensure_message_count(2);
+ ensure_equals("fourth check", LLError::shouldLogCallCount(), 2);
+
+ LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN);
+ TestAlpha::doInfo();
+ ensure_message_count(2);
+ ensure_equals("fifth check", LLError::shouldLogCallCount(), 3);
+ TestAlpha::doInfo();
+ ensure_message_count(2);
+ ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
+ }
+
+ template<> template<>
+ // configuration from LLSD
+ void ErrorTestObject::test<16>()
+ {
+ std::string this_file = LLError::abbreviateFile(__FILE__);
+ LLSD config;
+ config["print-location"] = true;
+ config["default-level"] = "DEBUG";
+
+ LLSD set1;
+ set1["level"] = "WARN";
+ set1["files"][0] = this_file;
+
+ LLSD set2;
+ set2["level"] = "INFO";
+ set2["classes"][0] = "TestAlpha";
+
+ LLSD set3;
+ set3["level"] = "NONE";
+ set3["functions"][0] = "TestAlpha::doError";
+ set3["functions"][1] = "TestBeta::doError";
+
+ config["settings"][0] = set1;
+ config["settings"][1] = set2;
+ config["settings"][2] = set3;
+
+ LLError::configure(config);
+
+ TestAlpha::doAll();
+ TestBeta::doAll();
+ ensure_message_contains(0, "any idea");
+ ensure_message_contains(0, this_file);
+ ensure_message_contains(1, "aim west");
+ ensure_message_contains(2, "bad word");
+ ensure_message_count(3);
+
+ // make sure reconfiguring works
+ LLSD config2;
+ config2["default-level"] = "WARN";
+
+ LLError::configure(config2);
+
+ TestAlpha::doAll();
+ TestBeta::doAll();
+ ensure_message_contains(3, "aim west");
+ ensure_message_does_not_contain(3, this_file);
+ ensure_message_contains(4, "error");
+ ensure_message_contains(5, "ate eels");
+ ensure_message_contains(6, "bad word");
+ ensure_message_contains(7, "error");
+ ensure_message_contains(8, "big easy");
+ ensure_message_count(9);
+ }
+}
+
+/* Tests left:
+ handling of classes without LOG_CLASS
+
+ live update of filtering from file
+
+ syslog recorder
+ file recorder
+ cerr/stderr recorder
+ fixed buffer recorder
+ windows recorder
+
+ mutex use when logging (?)
+ strange careful about to crash handling (?)
+*/
diff --git a/indra/llcommon/tests/llframetimer_test.cpp b/indra/llcommon/tests/llframetimer_test.cpp
new file mode 100644
index 0000000000..737c996d0f
--- /dev/null
+++ b/indra/llcommon/tests/llframetimer_test.cpp
@@ -0,0 +1,118 @@
+/**
+ * @file lltiming_tut.cpp
+ * @date 2006-07-23
+ * @brief Tests the timers.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../llframetimer.h"
+#include "../llsd.h"
+
+#include "../test/lltut.h"
+
+namespace tut
+{
+ struct frametimer_test
+ {
+ frametimer_test()
+ {
+ LLFrameTimer::updateFrameTime();
+ }
+ };
+ typedef test_group<frametimer_test> frametimer_group_t;
+ typedef frametimer_group_t::object frametimer_object_t;
+ tut::frametimer_group_t frametimer_instance("frametimer");
+
+ template<> template<>
+ void frametimer_object_t::test<1>()
+ {
+ F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds();
+ LLFrameTimer timer;
+ timer.setExpiryAt(seconds_since_epoch);
+ F64 expires_at = timer.expiresAt();
+ ensure_distance(
+ "set expiry matches get expiry",
+ expires_at,
+ seconds_since_epoch,
+ 0.001);
+ }
+
+ template<> template<>
+ void frametimer_object_t::test<2>()
+ {
+ F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds();
+ seconds_since_epoch += 10.0;
+ LLFrameTimer timer;
+ timer.setExpiryAt(seconds_since_epoch);
+ F64 expires_at = timer.expiresAt();
+ ensure_distance(
+ "set expiry matches get expiry 1",
+ expires_at,
+ seconds_since_epoch,
+ 0.001);
+ seconds_since_epoch += 10.0;
+ timer.setExpiryAt(seconds_since_epoch);
+ expires_at = timer.expiresAt();
+ ensure_distance(
+ "set expiry matches get expiry 2",
+ expires_at,
+ seconds_since_epoch,
+ 0.001);
+ }
+ template<> template<>
+ void frametimer_object_t::test<3>()
+ {
+ F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds();
+ seconds_since_epoch += 2.0;
+ LLFrameTimer timer;
+ timer.setExpiryAt(seconds_since_epoch);
+ ensure("timer not expired on create", !timer.hasExpired());
+ int ii;
+ for(ii = 0; ii < 10; ++ii)
+ {
+ ms_sleep(150);
+ LLFrameTimer::updateFrameTime();
+ }
+ ensure("timer not expired after a bit", !timer.hasExpired());
+ for(ii = 0; ii < 10; ++ii)
+ {
+ ms_sleep(100);
+ LLFrameTimer::updateFrameTime();
+ }
+ ensure("timer expired", timer.hasExpired());
+ }
+/*
+ template<> template<>
+ void frametimer_object_t::test<4>()
+ {
+ }
+*/
+}
diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp
new file mode 100644
index 0000000000..e5100e51dc
--- /dev/null
+++ b/indra/llcommon/tests/llrand_test.cpp
@@ -0,0 +1,133 @@
+/**
+ * @file llrandom_tut.cpp
+ * @author Phoenix
+ * @date 2007-01-25
+ *
+ * $LicenseInfo:firstyear=2007&license=viewergpl$
+ *
+ * Copyright (c) 2007-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "../test/lltut.h"
+
+#include "../llrand.h"
+
+
+namespace tut
+{
+ struct random
+ {
+ };
+
+ typedef test_group<random> random_t;
+ typedef random_t::object random_object_t;
+ tut::random_t tut_random("random");
+
+ template<> template<>
+ void random_object_t::test<1>()
+ {
+ F32 number = 0.0f;
+ for(S32 ii = 0; ii < 100000; ++ii)
+ {
+ number = ll_frand();
+ ensure("frand >= 0", (number >= 0.0f));
+ ensure("frand < 1", (number < 1.0f));
+ }
+ }
+
+ template<> template<>
+ void random_object_t::test<2>()
+ {
+ F64 number = 0.0f;
+ for(S32 ii = 0; ii < 100000; ++ii)
+ {
+ number = ll_drand();
+ ensure("drand >= 0", (number >= 0.0));
+ ensure("drand < 1", (number < 1.0));
+ }
+ }
+
+ template<> template<>
+ void random_object_t::test<3>()
+ {
+ F32 number = 0.0f;
+ for(S32 ii = 0; ii < 100000; ++ii)
+ {
+ number = ll_frand(2.0f) - 1.0f;
+ ensure("frand >= 0", (number >= -1.0f));
+ ensure("frand < 1", (number <= 1.0f));
+ }
+ }
+
+ template<> template<>
+ void random_object_t::test<4>()
+ {
+ F32 number = 0.0f;
+ for(S32 ii = 0; ii < 100000; ++ii)
+ {
+ number = ll_frand(-7.0);
+ ensure("drand <= 0", (number <= 0.0));
+ ensure("drand > -7", (number > -7.0));
+ }
+ }
+
+ template<> template<>
+ void random_object_t::test<5>()
+ {
+ F64 number = 0.0f;
+ for(S32 ii = 0; ii < 100000; ++ii)
+ {
+ number = ll_drand(-2.0);
+ ensure("drand <= 0", (number <= 0.0));
+ ensure("drand > -2", (number > -2.0));
+ }
+ }
+
+ template<> template<>
+ void random_object_t::test<6>()
+ {
+ S32 number = 0;
+ for(S32 ii = 0; ii < 100000; ++ii)
+ {
+ number = ll_rand(100);
+ ensure("rand >= 0", (number >= 0));
+ ensure("rand < 100", (number < 100));
+ }
+ }
+
+ template<> template<>
+ void random_object_t::test<7>()
+ {
+ S32 number = 0;
+ for(S32 ii = 0; ii < 100000; ++ii)
+ {
+ number = ll_rand(-127);
+ ensure("rand <= 0", (number <= 0));
+ ensure("rand > -127", (number > -127));
+ }
+ }
+}
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
new file mode 100644
index 0000000000..8f6d9d8e26
--- /dev/null
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -0,0 +1,1504 @@
+/**
+ * @file llsdserialize_tut.cpp
+ * @date 2006-04
+ * @brief LLSDSerialize unit tests
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#if !LL_WINDOWS
+#include <netinet/in.h>
+#endif
+
+#include "linden_common.h"
+#include "../llsd.h"
+#include "../llsdserialize.h"
+#include "../llformat.h"
+
+#include "../test/lltut.h"
+
+
+#if LL_WINDOWS
+#include <winsock2.h>
+typedef U32 uint32_t;
+#endif
+
+std::vector<U8> string_to_vector(std::string str)
+{
+ // bc LLSD can't...
+ size_t len = (size_t)str.length();
+ std::vector<U8> v(len);
+ for (size_t i = 0; i < len ; i++)
+ {
+ v[i] = str[i];
+ }
+ return v;
+}
+
+namespace tut
+{
+ struct sd_xml_data
+ {
+ sd_xml_data()
+ {
+ mFormatter = new LLSDXMLFormatter;
+ }
+ LLSD mSD;
+ LLPointer<LLSDXMLFormatter> mFormatter;
+ void xml_test(const char* name, const std::string& expected)
+ {
+ std::ostringstream ostr;
+ mFormatter->format(mSD, ostr);
+ ensure_equals(name, ostr.str(), expected);
+ }
+ };
+
+ typedef test_group<sd_xml_data> sd_xml_test;
+ typedef sd_xml_test::object sd_xml_object;
+ tut::sd_xml_test sd_xml_stream("sd_xml_serialization");
+
+ template<> template<>
+ void sd_xml_object::test<1>()
+ {
+ // random atomic tests
+ std::string expected;
+
+ expected = "<llsd><undef /></llsd>\n";
+ xml_test("undef", expected);
+
+ mSD = 3463;
+ expected = "<llsd><integer>3463</integer></llsd>\n";
+ xml_test("integer", expected);
+
+ mSD = "";
+ expected = "<llsd><string /></llsd>\n";
+ xml_test("empty string", expected);
+
+ mSD = "foobar";
+ expected = "<llsd><string>foobar</string></llsd>\n";
+ xml_test("string", expected);
+
+ mSD = LLUUID::null;
+ expected = "<llsd><uuid /></llsd>\n";
+ xml_test("null uuid", expected);
+
+ mSD = LLUUID("c96f9b1e-f589-4100-9774-d98643ce0bed");
+ expected = "<llsd><uuid>c96f9b1e-f589-4100-9774-d98643ce0bed</uuid></llsd>\n";
+ xml_test("uuid", expected);
+
+ mSD = LLURI("https://secondlife.com/login");
+ expected = "<llsd><uri>https://secondlife.com/login</uri></llsd>\n";
+ xml_test("uri", expected);
+
+ mSD = LLDate("2006-04-24T16:11:33Z");
+ expected = "<llsd><date>2006-04-24T16:11:33Z</date></llsd>\n";
+ xml_test("date", expected);
+
+ // Generated by: echo -n 'hello' | openssl enc -e -base64
+ std::vector<U8> hello;
+ hello.push_back('h');
+ hello.push_back('e');
+ hello.push_back('l');
+ hello.push_back('l');
+ hello.push_back('o');
+ mSD = hello;
+ expected = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n";
+ xml_test("binary", expected);
+ }
+
+ template<> template<>
+ void sd_xml_object::test<2>()
+ {
+ // tests with boolean values.
+ std::string expected;
+
+ mFormatter->boolalpha(true);
+ mSD = true;
+ expected = "<llsd><boolean>true</boolean></llsd>\n";
+ xml_test("bool alpha true", expected);
+ mSD = false;
+ expected = "<llsd><boolean>false</boolean></llsd>\n";
+ xml_test("bool alpha false", expected);
+
+ mFormatter->boolalpha(false);
+ mSD = true;
+ expected = "<llsd><boolean>1</boolean></llsd>\n";
+ xml_test("bool true", expected);
+ mSD = false;
+ expected = "<llsd><boolean>0</boolean></llsd>\n";
+ xml_test("bool false", expected);
+ }
+
+
+ template<> template<>
+ void sd_xml_object::test<3>()
+ {
+ // tests with real values.
+ std::string expected;
+
+ mFormatter->realFormat("%.2f");
+ mSD = 1.0;
+ expected = "<llsd><real>1.00</real></llsd>\n";
+ xml_test("real 1", expected);
+
+ mSD = -34379.0438;
+ expected = "<llsd><real>-34379.04</real></llsd>\n";
+ xml_test("real reduced precision", expected);
+ mFormatter->realFormat("%.4f");
+ expected = "<llsd><real>-34379.0438</real></llsd>\n";
+ xml_test("higher precision", expected);
+
+ mFormatter->realFormat("%.0f");
+ mSD = 0.0;
+ expected = "<llsd><real>0</real></llsd>\n";
+ xml_test("no decimal 0", expected);
+ mSD = 3287.4387;
+ expected = "<llsd><real>3287</real></llsd>\n";
+ xml_test("no decimal real number", expected);
+ }
+
+ template<> template<>
+ void sd_xml_object::test<4>()
+ {
+ // tests with arrays
+ std::string expected;
+
+ mSD = LLSD::emptyArray();
+ expected = "<llsd><array /></llsd>\n";
+ xml_test("empty array", expected);
+
+ mSD.append(LLSD());
+ expected = "<llsd><array><undef /></array></llsd>\n";
+ xml_test("1 element array", expected);
+
+ mSD.append(1);
+ expected = "<llsd><array><undef /><integer>1</integer></array></llsd>\n";
+ xml_test("2 element array", expected);
+ }
+
+ template<> template<>
+ void sd_xml_object::test<5>()
+ {
+ // tests with arrays
+ std::string expected;
+
+ mSD = LLSD::emptyMap();
+ expected = "<llsd><map /></llsd>\n";
+ xml_test("empty map", expected);
+
+ mSD["foo"] = "bar";
+ expected = "<llsd><map><key>foo</key><string>bar</string></map></llsd>\n";
+ xml_test("1 element map", expected);
+
+ mSD["baz"] = LLSD();
+ expected = "<llsd><map><key>baz</key><undef /><key>foo</key><string>bar</string></map></llsd>\n";
+ xml_test("2 element map", expected);
+ }
+
+ template<> template<>
+ void sd_xml_object::test<6>()
+ {
+ // tests with binary
+ std::string expected;
+
+ // Generated by: echo -n 'hello' | openssl enc -e -base64
+ mSD = string_to_vector("hello");
+ expected = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n";
+ xml_test("binary", expected);
+
+ mSD = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0");
+ expected = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n";
+ xml_test("binary", expected);
+ }
+
+ class TestLLSDSerializeData
+ {
+ public:
+ TestLLSDSerializeData();
+ ~TestLLSDSerializeData();
+
+ void doRoundTripTests(const std::string&);
+ void checkRoundTrip(const std::string&, const LLSD& v);
+
+ LLPointer<LLSDFormatter> mFormatter;
+ LLPointer<LLSDParser> mParser;
+ };
+
+ TestLLSDSerializeData::TestLLSDSerializeData()
+ {
+ }
+
+ TestLLSDSerializeData::~TestLLSDSerializeData()
+ {
+ }
+
+ void TestLLSDSerializeData::checkRoundTrip(const std::string& msg, const LLSD& v)
+ {
+ std::stringstream stream;
+ mFormatter->format(v, stream);
+ //llinfos << "checkRoundTrip: length " << stream.str().length() << llendl;
+ LLSD w;
+ mParser->reset(); // reset() call is needed since test code re-uses mParser
+ mParser->parse(stream, w, stream.str().size());
+
+ try
+ {
+ ensure_equals(msg.c_str(), w, v);
+ }
+ catch (...)
+ {
+ std::cerr << "the serialized string was:" << std::endl;
+ std::cerr << stream.str() << std::endl;
+ throw;
+ }
+ }
+
+ static void fillmap(LLSD& root, U32 width, U32 depth)
+ {
+ if(depth == 0)
+ {
+ root["foo"] = "bar";
+ return;
+ }
+
+ for(U32 i = 0; i < width; ++i)
+ {
+ std::string key = llformat("child %d", i);
+ root[key] = LLSD::emptyMap();
+ fillmap(root[key], width, depth - 1);
+ }
+ }
+
+ void TestLLSDSerializeData::doRoundTripTests(const std::string& msg)
+ {
+ LLSD v;
+ checkRoundTrip(msg + " undefined", v);
+
+ v = true;
+ checkRoundTrip(msg + " true bool", v);
+
+ v = false;
+ checkRoundTrip(msg + " false bool", v);
+
+ v = 1;
+ checkRoundTrip(msg + " positive int", v);
+
+ v = 0;
+ checkRoundTrip(msg + " zero int", v);
+
+ v = -1;
+ checkRoundTrip(msg + " negative int", v);
+
+ v = 1234.5f;
+ checkRoundTrip(msg + " positive float", v);
+
+ v = 0.0f;
+ checkRoundTrip(msg + " zero float", v);
+
+ v = -1234.5f;
+ checkRoundTrip(msg + " negative float", v);
+
+ // FIXME: need a NaN test
+
+ v = LLUUID::null;
+ checkRoundTrip(msg + " null uuid", v);
+
+ LLUUID newUUID;
+ newUUID.generate();
+ v = newUUID;
+ checkRoundTrip(msg + " new uuid", v);
+
+ v = "";
+ checkRoundTrip(msg + " empty string", v);
+
+ v = "some string";
+ checkRoundTrip(msg + " non-empty string", v);
+
+ v =
+"Second Life is a 3-D virtual world entirely built and owned by its residents. "
+"Since opening to the public in 2003, it has grown explosively and today is "
+"inhabited by nearly 100,000 people from around the globe.\n"
+"\n"
+"From the moment you enter the World you'll discover a vast digital continent, "
+"teeming with people, entertainment, experiences and opportunity. Once you've "
+"explored a bit, perhaps you'll find a perfect parcel of land to build your "
+"house or business.\n"
+"\n"
+"You'll also be surrounded by the Creations of your fellow residents. Because "
+"residents retain the rights to their digital creations, they can buy, sell "
+"and trade with other residents.\n"
+"\n"
+"The Marketplace currently supports millions of US dollars in monthly "
+"transactions. This commerce is handled with the in-world currency, the Linden "
+"dollar, which can be converted to US dollars at several thriving online "
+"currency exchanges.\n"
+"\n"
+"Welcome to Second Life. We look forward to seeing you in-world!\n"
+ ;
+ checkRoundTrip(msg + " long string", v);
+
+ static const U32 block_size = 0x000020;
+ for (U32 block = 0x000000; block <= 0x10ffff; block += block_size)
+ {
+ std::ostringstream out;
+
+ for (U32 c = block; c < block + block_size; ++c)
+ {
+ if (c <= 0x000001f
+ && c != 0x000009
+ && c != 0x00000a)
+ {
+ // see XML standard, sections 2.2 and 4.1
+ continue;
+ }
+ if (0x00d800 <= c && c <= 0x00dfff) { continue; }
+ if (0x00fdd0 <= c && c <= 0x00fdef) { continue; }
+ if ((c & 0x00fffe) == 0x00fffe) { continue; }
+ // see Unicode standard, section 15.8
+
+ if (c <= 0x00007f)
+ {
+ out << (char)(c & 0x7f);
+ }
+ else if (c <= 0x0007ff)
+ {
+ out << (char)(0xc0 | ((c >> 6) & 0x1f));
+ out << (char)(0x80 | ((c >> 0) & 0x3f));
+ }
+ else if (c <= 0x00ffff)
+ {
+ out << (char)(0xe0 | ((c >> 12) & 0x0f));
+ out << (char)(0x80 | ((c >> 6) & 0x3f));
+ out << (char)(0x80 | ((c >> 0) & 0x3f));
+ }
+ else
+ {
+ out << (char)(0xf0 | ((c >> 18) & 0x07));
+ out << (char)(0x80 | ((c >> 12) & 0x3f));
+ out << (char)(0x80 | ((c >> 6) & 0x3f));
+ out << (char)(0x80 | ((c >> 0) & 0x3f));
+ }
+ }
+
+ v = out.str();
+
+ std::ostringstream blockmsg;
+ blockmsg << msg << " unicode string block 0x" << std::hex << block;
+ checkRoundTrip(blockmsg.str(), v);
+ }
+
+ LLDate epoch;
+ v = epoch;
+ checkRoundTrip(msg + " epoch date", v);
+
+ LLDate aDay("2002-12-07T05:07:15.00Z");
+ v = aDay;
+ checkRoundTrip(msg + " date", v);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ v = path;
+ checkRoundTrip(msg + " url", v);
+
+ const char source[] = "it must be a blue moon again";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+
+ v = data;
+ checkRoundTrip(msg + " binary", v);
+
+ v = LLSD::emptyMap();
+ checkRoundTrip(msg + " empty map", v);
+
+ v = LLSD::emptyMap();
+ v["name"] = "luke"; //v.insert("name", "luke");
+ v["age"] = 3; //v.insert("age", 3);
+ checkRoundTrip(msg + " map", v);
+
+ v.clear();
+ v["a"]["1"] = true;
+ v["b"]["0"] = false;
+ checkRoundTrip(msg + " nested maps", v);
+
+ v = LLSD::emptyArray();
+ checkRoundTrip(msg + " empty array", v);
+
+ v = LLSD::emptyArray();
+ v.append("ali");
+ v.append(28);
+ checkRoundTrip(msg + " array", v);
+
+ v.clear();
+ v[0][0] = true;
+ v[1][0] = false;
+ checkRoundTrip(msg + " nested arrays", v);
+
+ v = LLSD::emptyMap();
+ fillmap(v, 10, 6); // 10^6 maps
+ checkRoundTrip(msg + " many nested maps", v);
+ }
+
+ typedef tut::test_group<TestLLSDSerializeData> TestLLSDSerialzeGroup;
+ typedef TestLLSDSerialzeGroup::object TestLLSDSerializeObject;
+ TestLLSDSerialzeGroup gTestLLSDSerializeGroup("llsd serialization");
+
+ template<> template<>
+ void TestLLSDSerializeObject::test<1>()
+ {
+ mFormatter = new LLSDNotationFormatter();
+ mParser = new LLSDNotationParser();
+ doRoundTripTests("notation serialization");
+ }
+
+ template<> template<>
+ void TestLLSDSerializeObject::test<2>()
+ {
+ mFormatter = new LLSDXMLFormatter();
+ mParser = new LLSDXMLParser();
+ doRoundTripTests("xml serialization");
+ }
+
+ template<> template<>
+ void TestLLSDSerializeObject::test<3>()
+ {
+ mFormatter = new LLSDBinaryFormatter();
+ mParser = new LLSDBinaryParser();
+ doRoundTripTests("binary serialization");
+ }
+
+
+ /**
+ * @class TestLLSDParsing
+ * @brief Base class for of a parse tester.
+ */
+ template <class parser_t>
+ class TestLLSDParsing
+ {
+ public:
+ TestLLSDParsing()
+ {
+ mParser = new parser_t;
+ }
+
+ void ensureParse(
+ const std::string& msg,
+ const std::string& in,
+ const LLSD& expected_value,
+ S32 expected_count)
+ {
+ std::stringstream input;
+ input.str(in);
+
+ LLSD parsed_result;
+ mParser->reset(); // reset() call is needed since test code re-uses mParser
+ S32 parsed_count = mParser->parse(input, parsed_result, in.size());
+ ensure_equals(msg.c_str(), parsed_result, expected_value);
+
+ // This count check is really only useful for expected
+ // parse failures, since the ensures equal will already
+ // require eqality.
+ std::string count_msg(msg);
+ count_msg += " (count)";
+ ensure_equals(count_msg, parsed_count, expected_count);
+ }
+
+ LLPointer<parser_t> mParser;
+ };
+
+
+ /**
+ * @class TestLLSDXMLParsing
+ * @brief Concrete instance of a parse tester.
+ */
+ class TestLLSDXMLParsing : public TestLLSDParsing<LLSDXMLParser>
+ {
+ public:
+ TestLLSDXMLParsing() {}
+ };
+
+ typedef tut::test_group<TestLLSDXMLParsing> TestLLSDXMLParsingGroup;
+ typedef TestLLSDXMLParsingGroup::object TestLLSDXMLParsingObject;
+ TestLLSDXMLParsingGroup gTestLLSDXMLParsingGroup("llsd XML parsing");
+
+ template<> template<>
+ void TestLLSDXMLParsingObject::test<1>()
+ {
+ // test handling of xml not recognized as llsd results in an
+ // LLSD Undefined
+ ensureParse(
+ "malformed xml",
+ "<llsd><string>ha ha</string>",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "not llsd",
+ "<html><body><p>ha ha</p></body></html>",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "value without llsd",
+ "<string>ha ha</string>",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "key without llsd",
+ "<key>ha ha</key>",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+
+ template<> template<>
+ void TestLLSDXMLParsingObject::test<2>()
+ {
+ // test handling of unrecognized or unparseable llsd values
+ LLSD v;
+ v["amy"] = 23;
+ v["bob"] = LLSD();
+ v["cam"] = 1.23;
+
+ ensureParse(
+ "unknown data type",
+ "<llsd><map>"
+ "<key>amy</key><integer>23</integer>"
+ "<key>bob</key><bigint>99999999999999999</bigint>"
+ "<key>cam</key><real>1.23</real>"
+ "</map></llsd>",
+ v,
+ v.size() + 1);
+ }
+
+ template<> template<>
+ void TestLLSDXMLParsingObject::test<3>()
+ {
+ // test handling of nested bad data
+
+ LLSD v;
+ v["amy"] = 23;
+ v["cam"] = 1.23;
+
+ ensureParse(
+ "map with html",
+ "<llsd><map>"
+ "<key>amy</key><integer>23</integer>"
+ "<html><body>ha ha</body></html>"
+ "<key>cam</key><real>1.23</real>"
+ "</map></llsd>",
+ v,
+ v.size() + 1);
+
+ v.clear();
+ v["amy"] = 23;
+ v["cam"] = 1.23;
+ ensureParse(
+ "map with value for key",
+ "<llsd><map>"
+ "<key>amy</key><integer>23</integer>"
+ "<string>ha ha</string>"
+ "<key>cam</key><real>1.23</real>"
+ "</map></llsd>",
+ v,
+ v.size() + 1);
+
+ v.clear();
+ v["amy"] = 23;
+ v["bob"] = LLSD::emptyMap();
+ v["cam"] = 1.23;
+ ensureParse(
+ "map with map of html",
+ "<llsd><map>"
+ "<key>amy</key><integer>23</integer>"
+ "<key>bob</key>"
+ "<map>"
+ "<html><body>ha ha</body></html>"
+ "</map>"
+ "<key>cam</key><real>1.23</real>"
+ "</map></llsd>",
+ v,
+ v.size() + 1);
+
+ v.clear();
+ v[0] = 23;
+ v[1] = LLSD();
+ v[2] = 1.23;
+
+ ensureParse(
+ "array value of html",
+ "<llsd><array>"
+ "<integer>23</integer>"
+ "<html><body>ha ha</body></html>"
+ "<real>1.23</real>"
+ "</array></llsd>",
+ v,
+ v.size() + 1);
+
+ v.clear();
+ v[0] = 23;
+ v[1] = LLSD::emptyMap();
+ v[2] = 1.23;
+ ensureParse(
+ "array with map of html",
+ "<llsd><array>"
+ "<integer>23</integer>"
+ "<map>"
+ "<html><body>ha ha</body></html>"
+ "</map>"
+ "<real>1.23</real>"
+ "</array></llsd>",
+ v,
+ v.size() + 1);
+ }
+
+ template<> template<>
+ void TestLLSDXMLParsingObject::test<4>()
+ {
+ // test handling of binary object in XML
+ std::string xml;
+ LLSD expected;
+
+ // Generated by: echo -n 'hello' | openssl enc -e -base64
+ expected = string_to_vector("hello");
+ xml = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n";
+ ensureParse(
+ "the word 'hello' packed in binary encoded base64",
+ xml,
+ expected,
+ 1);
+
+ expected = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0");
+ xml = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n";
+ ensureParse(
+ "a common binary blob for object -> agent offline inv transfer",
+ xml,
+ expected,
+ 1);
+
+ expected = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0");
+ xml = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBl\n";
+ xml += "NDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5\n";
+ xml += "LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZm\n";
+ xml += "ZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMy\n";
+ xml += "OXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n";
+ ensureParse(
+ "a common binary blob for object -> agent offline inv transfer",
+ xml,
+ expected,
+ 1);
+ }
+ /*
+ TODO:
+ test XML parsing
+ binary with unrecognized encoding
+ nested LLSD tags
+ multiple values inside an LLSD
+ */
+
+
+ /**
+ * @class TestLLSDNotationParsing
+ * @brief Concrete instance of a parse tester.
+ */
+ class TestLLSDNotationParsing : public TestLLSDParsing<LLSDNotationParser>
+ {
+ public:
+ TestLLSDNotationParsing() {}
+ };
+
+ typedef tut::test_group<TestLLSDNotationParsing> TestLLSDNotationParsingGroup;
+ typedef TestLLSDNotationParsingGroup::object TestLLSDNotationParsingObject;
+ TestLLSDNotationParsingGroup gTestLLSDNotationParsingGroup(
+ "llsd notation parsing");
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<1>()
+ {
+ // test handling of xml not recognized as llsd results in an
+ // LLSD Undefined
+ ensureParse(
+ "malformed notation map",
+ "{'ha ha'",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "malformed notation array",
+ "['ha ha'",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "malformed notation string",
+ "'ha ha",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "bad notation noise",
+ "g48ejlnfr",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<2>()
+ {
+ ensureParse("valid undef", "!", LLSD(), 1);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<3>()
+ {
+ LLSD val = false;
+ ensureParse("valid boolean false 0", "false", val, 1);
+ ensureParse("valid boolean false 1", "f", val, 1);
+ ensureParse("valid boolean false 2", "0", val, 1);
+ ensureParse("valid boolean false 3", "F", val, 1);
+ ensureParse("valid boolean false 4", "FALSE", val, 1);
+ val = true;
+ ensureParse("valid boolean true 0", "true", val, 1);
+ ensureParse("valid boolean true 1", "t", val, 1);
+ ensureParse("valid boolean true 2", "1", val, 1);
+ ensureParse("valid boolean true 3", "T", val, 1);
+ ensureParse("valid boolean true 4", "TRUE", val, 1);
+
+ val.clear();
+ ensureParse("invalid true", "TR", val, LLSDParser::PARSE_FAILURE);
+ ensureParse("invalid false", "FAL", val, LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<4>()
+ {
+ LLSD val = 123;
+ ensureParse("valid integer", "i123", val, 1);
+ val.clear();
+ ensureParse("invalid integer", "421", val, LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<5>()
+ {
+ LLSD val = 456.7;
+ ensureParse("valid real", "r456.7", val, 1);
+ val.clear();
+ ensureParse("invalid real", "456.7", val, LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<6>()
+ {
+ LLUUID id;
+ LLSD val = id;
+ ensureParse(
+ "unparseable uuid",
+ "u123",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ id.generate();
+ val = id;
+ std::string uuid_str("u");
+ uuid_str += id.asString();
+ ensureParse("valid uuid", uuid_str.c_str(), val, 1);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<7>()
+ {
+ LLSD val = std::string("foolish");
+ ensureParse("valid string 1", "\"foolish\"", val, 1);
+ val = std::string("g'day");
+ ensureParse("valid string 2", "\"g'day\"", val, 1);
+ val = std::string("have a \"nice\" day");
+ ensureParse("valid string 3", "'have a \"nice\" day'", val, 1);
+ val = std::string("whatever");
+ ensureParse("valid string 4", "s(8)\"whatever\"", val, 1);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<8>()
+ {
+ ensureParse(
+ "invalid string 1",
+ "s(7)\"whatever\"",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "invalid string 2",
+ "s(9)\"whatever\"",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<9>()
+ {
+ LLSD val = LLURI("http://www.google.com");
+ ensureParse("valid uri", "l\"http://www.google.com\"", val, 1);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<10>()
+ {
+ LLSD val = LLDate("2007-12-28T09:22:53.10Z");
+ ensureParse("valid date", "d\"2007-12-28T09:22:53.10Z\"", val, 1);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<11>()
+ {
+ std::vector<U8> vec;
+ vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c');
+ vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1');
+ LLSD val = vec;
+ ensureParse("valid binary b64", "b64\"YWJjMzIx\"", val, 1);
+ ensureParse("valid bainry b16", "b16\"616263333231\"", val, 1);
+ ensureParse("valid bainry raw", "b(6)\"abc321\"", val, 1);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<12>()
+ {
+ ensureParse(
+ "invalid -- binary length specified too long",
+ "b(7)\"abc321\"",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "invalid -- binary length specified way too long",
+ "b(1000000)\"abc321\"",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<13>()
+ {
+ LLSD val;
+ val["amy"] = 23;
+ val["bob"] = LLSD();
+ val["cam"] = 1.23;
+ ensureParse("simple map", "{'amy':i23,'bob':!,'cam':r1.23}", val, 4);
+
+ val["bob"] = LLSD::emptyMap();
+ val["bob"]["vehicle"] = std::string("bicycle");
+ ensureParse(
+ "nested map",
+ "{'amy':i23,'bob':{'vehicle':'bicycle'},'cam':r1.23}",
+ val,
+ 5);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<14>()
+ {
+ LLSD val;
+ val.append(23);
+ val.append(LLSD());
+ val.append(1.23);
+ ensureParse("simple array", "[i23,!,r1.23]", val, 4);
+ val[1] = LLSD::emptyArray();
+ val[1].append("bicycle");
+ ensureParse("nested array", "[i23,['bicycle'],r1.23]", val, 5);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<15>()
+ {
+ LLSD val;
+ val["amy"] = 23;
+ val["bob"]["dogs"] = LLSD::emptyArray();
+ val["bob"]["dogs"].append(LLSD::emptyMap());
+ val["bob"]["dogs"][0]["name"] = std::string("groove");
+ val["bob"]["dogs"][0]["breed"] = std::string("samoyed");
+ val["bob"]["dogs"].append(LLSD::emptyMap());
+ val["bob"]["dogs"][1]["name"] = std::string("greyley");
+ val["bob"]["dogs"][1]["breed"] = std::string("chow/husky");
+ val["cam"] = 1.23;
+ ensureParse(
+ "nested notation",
+ "{'amy':i23,"
+ " 'bob':{'dogs':["
+ "{'name':'groove', 'breed':'samoyed'},"
+ "{'name':'greyley', 'breed':'chow/husky'}]},"
+ " 'cam':r1.23}",
+ val,
+ 11);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<16>()
+ {
+ // text to make sure that incorrect sizes bail because
+ std::string bad_str("s(5)\"hi\"");
+ ensureParse(
+ "size longer than bytes left",
+ bad_str,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDNotationParsingObject::test<17>()
+ {
+ // text to make sure that incorrect sizes bail because
+ std::string bad_bin("b(5)\"hi\"");
+ ensureParse(
+ "size longer than bytes left",
+ bad_bin,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ /**
+ * @class TestLLSDBinaryParsing
+ * @brief Concrete instance of a parse tester.
+ */
+ class TestLLSDBinaryParsing : public TestLLSDParsing<LLSDBinaryParser>
+ {
+ public:
+ TestLLSDBinaryParsing() {}
+ };
+
+ typedef tut::test_group<TestLLSDBinaryParsing> TestLLSDBinaryParsingGroup;
+ typedef TestLLSDBinaryParsingGroup::object TestLLSDBinaryParsingObject;
+ TestLLSDBinaryParsingGroup gTestLLSDBinaryParsingGroup(
+ "llsd binary parsing");
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<1>()
+ {
+ std::vector<U8> vec;
+ vec.resize(6);
+ vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c';
+ vec[3] = '3'; vec[4] = '2'; vec[5] = '1';
+ std::string string_expected((char*)&vec[0], vec.size());
+ LLSD value = string_expected;
+
+ vec.resize(11);
+ vec[0] = 's'; // for string
+ vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c';
+ vec[8] = '3'; vec[9] = '2'; vec[10] = '1';
+
+ uint32_t size = htonl(6);
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_good((char*)&vec[0], vec.size());
+ ensureParse("correct string parse", str_good, value, 1);
+
+ size = htonl(7);
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_1((char*)&vec[0], vec.size());
+ ensureParse(
+ "incorrect size string parse",
+ str_bad_1,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ size = htonl(100000);
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_2((char*)&vec[0], vec.size());
+ ensureParse(
+ "incorrect size string parse",
+ str_bad_2,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<2>()
+ {
+ std::vector<U8> vec;
+ vec.resize(6);
+ vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c';
+ vec[3] = '3'; vec[4] = '2'; vec[5] = '1';
+ LLSD value = vec;
+
+ vec.resize(11);
+ vec[0] = 'b'; // for binary
+ vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c';
+ vec[8] = '3'; vec[9] = '2'; vec[10] = '1';
+
+ uint32_t size = htonl(6);
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_good((char*)&vec[0], vec.size());
+ ensureParse("correct binary parse", str_good, value, 1);
+
+ size = htonl(7);
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_1((char*)&vec[0], vec.size());
+ ensureParse(
+ "incorrect size binary parse 1",
+ str_bad_1,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ size = htonl(100000);
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_2((char*)&vec[0], vec.size());
+ ensureParse(
+ "incorrect size binary parse 2",
+ str_bad_2,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<3>()
+ {
+ // test handling of xml not recognized as llsd results in an
+ // LLSD Undefined
+ ensureParse(
+ "malformed binary map",
+ "{'ha ha'",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "malformed binary array",
+ "['ha ha'",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "malformed binary string",
+ "'ha ha",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ ensureParse(
+ "bad noise",
+ "g48ejlnfr",
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<4>()
+ {
+ ensureParse("valid undef", "!", LLSD(), 1);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<5>()
+ {
+ LLSD val = false;
+ ensureParse("valid boolean false 2", "0", val, 1);
+ val = true;
+ ensureParse("valid boolean true 2", "1", val, 1);
+
+ val.clear();
+ ensureParse("invalid true", "t", val, LLSDParser::PARSE_FAILURE);
+ ensureParse("invalid false", "f", val, LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<6>()
+ {
+ std::vector<U8> vec;
+ vec.push_back('{');
+ vec.resize(vec.size() + 4);
+ uint32_t size = htonl(1);
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ vec.push_back('k');
+ int key_size_loc = vec.size();
+ size = htonl(1); // 1 too short
+ vec.resize(vec.size() + 4);
+ memcpy(&vec[key_size_loc], &size, sizeof(uint32_t));
+ vec.push_back('a'); vec.push_back('m'); vec.push_back('y');
+ vec.push_back('i');
+ int integer_loc = vec.size();
+ vec.resize(vec.size() + 4);
+ uint32_t val_int = htonl(23);
+ memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t));
+ std::string str_bad_1((char*)&vec[0], vec.size());
+ ensureParse(
+ "invalid key size",
+ str_bad_1,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ // check with correct size, but unterminated map (missing '}')
+ size = htonl(3); // correct size
+ memcpy(&vec[key_size_loc], &size, sizeof(uint32_t));
+ std::string str_bad_2((char*)&vec[0], vec.size());
+ ensureParse(
+ "valid key size, unterminated map",
+ str_bad_2,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ // check w/ correct size and correct map termination
+ LLSD val;
+ val["amy"] = 23;
+ vec.push_back('}');
+ std::string str_good((char*)&vec[0], vec.size());
+ ensureParse(
+ "valid map",
+ str_good,
+ val,
+ 2);
+
+ // check w/ incorrect sizes and correct map termination
+ size = htonl(0); // 1 too few (for the map entry)
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_3((char*)&vec[0], vec.size());
+ ensureParse(
+ "invalid map too long",
+ str_bad_3,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ size = htonl(2); // 1 too many
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_4((char*)&vec[0], vec.size());
+ ensureParse(
+ "invalid map too short",
+ str_bad_4,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<7>()
+ {
+ std::vector<U8> vec;
+ vec.push_back('[');
+ vec.resize(vec.size() + 4);
+ uint32_t size = htonl(1); // 1 too short
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ vec.push_back('"'); vec.push_back('a'); vec.push_back('m');
+ vec.push_back('y'); vec.push_back('"'); vec.push_back('i');
+ int integer_loc = vec.size();
+ vec.resize(vec.size() + 4);
+ uint32_t val_int = htonl(23);
+ memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t));
+
+ std::string str_bad_1((char*)&vec[0], vec.size());
+ ensureParse(
+ "invalid array size",
+ str_bad_1,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ // check with correct size, but unterminated map (missing ']')
+ size = htonl(2); // correct size
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_2((char*)&vec[0], vec.size());
+ ensureParse(
+ "unterminated array",
+ str_bad_2,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ // check w/ correct size and correct map termination
+ LLSD val;
+ val.append("amy");
+ val.append(23);
+ vec.push_back(']');
+ std::string str_good((char*)&vec[0], vec.size());
+ ensureParse(
+ "valid array",
+ str_good,
+ val,
+ 3);
+
+ // check with too many elements
+ size = htonl(3); // 1 too long
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_bad_3((char*)&vec[0], vec.size());
+ ensureParse(
+ "array too short",
+ str_bad_3,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<8>()
+ {
+ std::vector<U8> vec;
+ vec.push_back('{');
+ vec.resize(vec.size() + 4);
+ memset(&vec[1], 0, 4);
+ vec.push_back('}');
+ std::string str_good((char*)&vec[0], vec.size());
+ LLSD val = LLSD::emptyMap();
+ ensureParse(
+ "empty map",
+ str_good,
+ val,
+ 1);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<9>()
+ {
+ std::vector<U8> vec;
+ vec.push_back('[');
+ vec.resize(vec.size() + 4);
+ memset(&vec[1], 0, 4);
+ vec.push_back(']');
+ std::string str_good((char*)&vec[0], vec.size());
+ LLSD val = LLSD::emptyArray();
+ ensureParse(
+ "empty array",
+ str_good,
+ val,
+ 1);
+ }
+
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<10>()
+ {
+ std::vector<U8> vec;
+ vec.push_back('l');
+ vec.resize(vec.size() + 4);
+ uint32_t size = htonl(14); // 1 too long
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ vec.push_back('h'); vec.push_back('t'); vec.push_back('t');
+ vec.push_back('p'); vec.push_back(':'); vec.push_back('/');
+ vec.push_back('/'); vec.push_back('s'); vec.push_back('l');
+ vec.push_back('.'); vec.push_back('c'); vec.push_back('o');
+ vec.push_back('m');
+ std::string str_bad((char*)&vec[0], vec.size());
+ ensureParse(
+ "invalid uri length size",
+ str_bad,
+ LLSD(),
+ LLSDParser::PARSE_FAILURE);
+
+ LLSD val;
+ val = LLURI("http://sl.com");
+ size = htonl(13); // correct length
+ memcpy(&vec[1], &size, sizeof(uint32_t));
+ std::string str_good((char*)&vec[0], vec.size());
+ ensureParse(
+ "valid key size",
+ str_good,
+ val,
+ 1);
+ }
+
+/*
+ template<> template<>
+ void TestLLSDBinaryParsingObject::test<11>()
+ {
+ }
+*/
+
+ /**
+ * @class TestLLSDCrossCompatible
+ * @brief Miscellaneous serialization and parsing tests
+ */
+ class TestLLSDCrossCompatible
+ {
+ public:
+ TestLLSDCrossCompatible() {}
+
+ void ensureBinaryAndNotation(
+ const std::string& msg,
+ const LLSD& input)
+ {
+ // to binary, and back again
+ std::stringstream str1;
+ S32 count1 = LLSDSerialize::toBinary(input, str1);
+ LLSD actual_value_bin;
+ S32 count2 = LLSDSerialize::fromBinary(
+ actual_value_bin,
+ str1,
+ LLSDSerialize::SIZE_UNLIMITED);
+ ensure_equals(
+ "ensureBinaryAndNotation binary count",
+ count2,
+ count1);
+
+ // to notation and back again
+ std::stringstream str2;
+ S32 count3 = LLSDSerialize::toNotation(actual_value_bin, str2);
+ ensure_equals(
+ "ensureBinaryAndNotation notation count1",
+ count3,
+ count2);
+ LLSD actual_value_notation;
+ S32 count4 = LLSDSerialize::fromNotation(
+ actual_value_notation,
+ str2,
+ LLSDSerialize::SIZE_UNLIMITED);
+ ensure_equals(
+ "ensureBinaryAndNotation notation count2",
+ count4,
+ count3);
+ ensure_equals(
+ (msg + " (binaryandnotation)").c_str(),
+ actual_value_notation,
+ input);
+ }
+
+ void ensureBinaryAndXML(
+ const std::string& msg,
+ const LLSD& input)
+ {
+ // to binary, and back again
+ std::stringstream str1;
+ S32 count1 = LLSDSerialize::toBinary(input, str1);
+ LLSD actual_value_bin;
+ S32 count2 = LLSDSerialize::fromBinary(
+ actual_value_bin,
+ str1,
+ LLSDSerialize::SIZE_UNLIMITED);
+ ensure_equals(
+ "ensureBinaryAndXML binary count",
+ count2,
+ count1);
+
+ // to xml and back again
+ std::stringstream str2;
+ S32 count3 = LLSDSerialize::toXML(actual_value_bin, str2);
+ ensure_equals(
+ "ensureBinaryAndXML xml count1",
+ count3,
+ count2);
+ LLSD actual_value_xml;
+ S32 count4 = LLSDSerialize::fromXML(actual_value_xml, str2);
+ ensure_equals(
+ "ensureBinaryAndXML xml count2",
+ count4,
+ count3);
+ ensure_equals((msg + " (binaryandxml)").c_str(), actual_value_xml, input);
+ }
+ };
+
+ typedef tut::test_group<TestLLSDCrossCompatible> TestLLSDCompatibleGroup;
+ typedef TestLLSDCompatibleGroup::object TestLLSDCompatibleObject;
+ TestLLSDCompatibleGroup gTestLLSDCompatibleGroup(
+ "llsd serialize compatible");
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<1>()
+ {
+ LLSD test;
+ ensureBinaryAndNotation("undef", test);
+ ensureBinaryAndXML("undef", test);
+ test = true;
+ ensureBinaryAndNotation("boolean true", test);
+ ensureBinaryAndXML("boolean true", test);
+ test = false;
+ ensureBinaryAndNotation("boolean false", test);
+ ensureBinaryAndXML("boolean false", test);
+ test = 0;
+ ensureBinaryAndNotation("integer zero", test);
+ ensureBinaryAndXML("integer zero", test);
+ test = 1;
+ ensureBinaryAndNotation("integer positive", test);
+ ensureBinaryAndXML("integer positive", test);
+ test = -234567;
+ ensureBinaryAndNotation("integer negative", test);
+ ensureBinaryAndXML("integer negative", test);
+ test = 0.0;
+ ensureBinaryAndNotation("real zero", test);
+ ensureBinaryAndXML("real zero", test);
+ test = 1.0;
+ ensureBinaryAndNotation("real positive", test);
+ ensureBinaryAndXML("real positive", test);
+ test = -1.0;
+ ensureBinaryAndNotation("real negative", test);
+ ensureBinaryAndXML("real negative", test);
+ }
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<2>()
+ {
+ LLSD test;
+ test = "foobar";
+ ensureBinaryAndNotation("string", test);
+ ensureBinaryAndXML("string", test);
+ }
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<3>()
+ {
+ LLSD test;
+ LLUUID id;
+ id.generate();
+ test = id;
+ ensureBinaryAndNotation("uuid", test);
+ ensureBinaryAndXML("uuid", test);
+ }
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<4>()
+ {
+ LLSD test;
+ test = LLDate(12345.0);
+ ensureBinaryAndNotation("date", test);
+ ensureBinaryAndXML("date", test);
+ }
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<5>()
+ {
+ LLSD test;
+ test = LLURI("http://www.secondlife.com/");
+ ensureBinaryAndNotation("uri", test);
+ ensureBinaryAndXML("uri", test);
+ }
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<6>()
+ {
+ LLSD test;
+ typedef std::vector<U8> buf_t;
+ buf_t val;
+ for(int ii = 0; ii < 100; ++ii)
+ {
+ srand(ii); /* Flawfinder: ignore */
+ S32 size = rand() % 100 + 10;
+ std::generate_n(
+ std::back_insert_iterator<buf_t>(val),
+ size,
+ rand);
+ }
+ test = val;
+ ensureBinaryAndNotation("binary", test);
+ ensureBinaryAndXML("binary", test);
+ }
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<7>()
+ {
+ LLSD test;
+ test = LLSD::emptyArray();
+ test.append(1);
+ test.append("hello");
+ ensureBinaryAndNotation("array", test);
+ ensureBinaryAndXML("array", test);
+ }
+
+ template<> template<>
+ void TestLLSDCompatibleObject::test<8>()
+ {
+ LLSD test;
+ test = LLSD::emptyArray();
+ test["foo"] = "bar";
+ test["baz"] = 100;
+ ensureBinaryAndNotation("map", test);
+ ensureBinaryAndXML("map", test);
+ }
+}
+
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
new file mode 100644
index 0000000000..6a2ebc61f5
--- /dev/null
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -0,0 +1,750 @@
+/**
+ * @file llstring_test.cpp
+ * @author Adroit, Steve Linden, Tofu Linden
+ * @date 2006-12-24
+ * @brief Test cases of llstring.cpp
+ *
+ * $LicenseInfo:firstyear=2007&license=viewergpl$
+ *
+ * Copyright (c) 2007-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "../test/lltut.h"
+
+#include "../llstring.h"
+
+namespace tut
+{
+ struct string_index
+ {
+ };
+ typedef test_group<string_index> string_index_t;
+ typedef string_index_t::object string_index_object_t;
+ tut::string_index_t tut_string_index("string_test");
+
+ template<> template<>
+ void string_index_object_t::test<1>()
+ {
+ std::string llstr1;
+ ensure("Empty std::string", (llstr1.size() == 0) && llstr1.empty());
+
+ std::string llstr2("Hello");
+ ensure("std::string = Hello", (!strcmp(llstr2.c_str(), "Hello")) && (llstr2.size() == 5) && !llstr2.empty());
+
+ std::string llstr3(llstr2);
+ ensure("std::string = std::string(std::string)", (!strcmp(llstr3.c_str(), "Hello")) && (llstr3.size() == 5) && !llstr3.empty());
+
+ std::string str("Hello World");
+ std::string llstr4(str, 6);
+ ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (!strcmp(llstr4.c_str(), "World")) && (llstr4.size() == 5) && !llstr4.empty());
+
+ std::string llstr5(str, str.size());
+ ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (llstr5.size() == 0) && llstr5.empty());
+
+ std::string llstr6(5, 'A');
+ ensure("std::string = std::string(count, c)", (!strcmp(llstr6.c_str(), "AAAAA")) && (llstr6.size() == 5) && !llstr6.empty());
+
+ std::string llstr7("Hello World", 5);
+ ensure("std::string(s, n)", (!strcmp(llstr7.c_str(), "Hello")) && (llstr7.size() == 5) && !llstr7.empty());
+
+ std::string llstr8("Hello World", 6, 5);
+ ensure("std::string(s, n, count)", (!strcmp(llstr8.c_str(), "World")) && (llstr8.size() == 5) && !llstr8.empty());
+
+ std::string llstr9("Hello World", sizeof("Hello World")-1, 5); // go past end
+ ensure("std::string(s, n, count) goes past end", (llstr9.size() == 0) && llstr9.empty());
+ }
+
+ template<> template<>
+ void string_index_object_t::test<3>()
+ {
+ std::string str("Len=5");
+ ensure("isValidIndex failed", LLStringUtil::isValidIndex(str, 0) == TRUE &&
+ LLStringUtil::isValidIndex(str, 5) == TRUE &&
+ LLStringUtil::isValidIndex(str, 6) == FALSE);
+
+ std::string str1;
+ ensure("isValidIndex failed fo rempty string", LLStringUtil::isValidIndex(str1, 0) == FALSE);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<4>()
+ {
+ std::string str_val(" Testing the extra whitespaces ");
+ LLStringUtil::trimHead(str_val);
+ ensure_equals("1: trimHead failed", str_val, "Testing the extra whitespaces ");
+
+ std::string str_val1("\n\t\r\n Testing the extra whitespaces ");
+ LLStringUtil::trimHead(str_val1);
+ ensure_equals("2: trimHead failed", str_val1, "Testing the extra whitespaces ");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<5>()
+ {
+ std::string str_val(" Testing the extra whitespaces ");
+ LLStringUtil::trimTail(str_val);
+ ensure_equals("1: trimTail failed", str_val, " Testing the extra whitespaces");
+
+ std::string str_val1("\n Testing the extra whitespaces \n\t\r\n ");
+ LLStringUtil::trimTail(str_val1);
+ ensure_equals("2: trimTail failed", str_val1, "\n Testing the extra whitespaces");
+ }
+
+
+ template<> template<>
+ void string_index_object_t::test<6>()
+ {
+ std::string str_val(" \t \r Testing the extra \r\n whitespaces \n \t ");
+ LLStringUtil::trim(str_val);
+ ensure_equals("1: trim failed", str_val, "Testing the extra \r\n whitespaces");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<7>()
+ {
+ std::string str("Second LindenLabs");
+ LLStringUtil::truncate(str, 6);
+ ensure_equals("1: truncate", str, "Second");
+
+ // further truncate more than the length
+ LLStringUtil::truncate(str, 0);
+ ensure_equals("2: truncate", str, "");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<8>()
+ {
+ std::string str_val("SecondLife Source");
+ LLStringUtil::toUpper(str_val);
+ ensure_equals("toUpper failed", str_val, "SECONDLIFE SOURCE");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<9>()
+ {
+ std::string str_val("SecondLife Source");
+ LLStringUtil::toLower(str_val);
+ ensure_equals("toLower failed", str_val, "secondlife source");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<10>()
+ {
+ std::string str_val("Second");
+ ensure("1. isHead failed", LLStringUtil::isHead(str_val, "SecondLife Source") == TRUE);
+ ensure("2. isHead failed", LLStringUtil::isHead(str_val, " SecondLife Source") == FALSE);
+ std::string str_val2("");
+ ensure("3. isHead failed", LLStringUtil::isHead(str_val2, "") == FALSE);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<11>()
+ {
+ std::string str_val("Hello.\n\n Lindenlabs. \n This is \na simple test.\n");
+ std::string orig_str_val(str_val);
+ LLStringUtil::addCRLF(str_val);
+ ensure_equals("addCRLF failed", str_val, "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n");
+ LLStringUtil::removeCRLF(str_val);
+ ensure_equals("removeCRLF failed", str_val, orig_str_val);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<12>()
+ {
+ std::string str_val("Hello.\n\n\t \t Lindenlabs. \t\t");
+ std::string orig_str_val(str_val);
+ LLStringUtil::replaceTabsWithSpaces(str_val, 1);
+ ensure_equals("replaceTabsWithSpaces failed", str_val, "Hello.\n\n Lindenlabs. ");
+ LLStringUtil::replaceTabsWithSpaces(orig_str_val, 0);
+ ensure_equals("replaceTabsWithSpaces failed for 0", orig_str_val, "Hello.\n\n Lindenlabs. ");
+
+ str_val = "\t\t\t\t";
+ LLStringUtil::replaceTabsWithSpaces(str_val, 0);
+ ensure_equals("replaceTabsWithSpaces failed for all tabs", str_val, "");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<13>()
+ {
+ std::string str_val("Hello.\n\n\t\t\r\nLindenlabsX.");
+ LLStringUtil::replaceNonstandardASCII(str_val, 'X');
+ ensure_equals("replaceNonstandardASCII failed", str_val, "Hello.\n\nXXX\nLindenlabsX.");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<14>()
+ {
+ std::string str_val("Hello.\n\t\r\nABCDEFGHIABABAB");
+ LLStringUtil::replaceChar(str_val, 'A', 'X');
+ ensure_equals("1: replaceChar failed", str_val, "Hello.\n\t\r\nXBCDEFGHIXBXBXB");
+ std::string str_val1("Hello.\n\t\r\nABCDEFGHIABABAB");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<15>()
+ {
+ std::string str_val("Hello.\n\r\t");
+ ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == TRUE);
+
+ str_val = "ABC ";
+ ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == FALSE);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<16>()
+ {
+ std::string str_val("Hello.\n\r\t Again!");
+ LLStringUtil::stripNonprintable(str_val);
+ ensure_equals("stripNonprintable failed", str_val, "Hello. Again!");
+
+ str_val = "\r\n\t\t";
+ LLStringUtil::stripNonprintable(str_val);
+ ensure_equals("stripNonprintable resulting in empty string failed", str_val, "");
+
+ str_val = "";
+ LLStringUtil::stripNonprintable(str_val);
+ ensure_equals("stripNonprintable of empty string resulting in empty string failed", str_val, "");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<17>()
+ {
+ BOOL value;
+ std::string str_val("1");
+ ensure("convertToBOOL 1 failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "T";
+ ensure("convertToBOOL T failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "t";
+ ensure("convertToBOOL t failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "TRUE";
+ ensure("convertToBOOL TRUE failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "True";
+ ensure("convertToBOOL True failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "true";
+ ensure("convertToBOOL true failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+
+ str_val = "0";
+ ensure("convertToBOOL 0 failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "F";
+ ensure("convertToBOOL F failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "f";
+ ensure("convertToBOOL f failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "FALSE";
+ ensure("convertToBOOL FASLE failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "False";
+ ensure("convertToBOOL False failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "false";
+ ensure("convertToBOOL false failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+
+ str_val = "Tblah";
+ ensure("convertToBOOL false failed", !LLStringUtil::convertToBOOL(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<18>()
+ {
+ U8 value;
+ std::string str_val("255");
+ ensure("1: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 255);
+
+ str_val = "0";
+ ensure("2: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 0);
+
+ str_val = "-1";
+ ensure("3: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));
+
+ str_val = "256"; // bigger than MAX_U8
+ ensure("4: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<19>()
+ {
+ S8 value;
+ std::string str_val("127");
+ ensure("1: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 127);
+
+ str_val = "0";
+ ensure("2: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 0);
+
+ str_val = "-128";
+ ensure("3: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == -128);
+
+ str_val = "128"; // bigger than MAX_S8
+ ensure("4: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));
+
+ str_val = "-129";
+ ensure("5: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<20>()
+ {
+ S16 value;
+ std::string str_val("32767");
+ ensure("1: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 32767);
+
+ str_val = "0";
+ ensure("2: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 0);
+
+ str_val = "-32768";
+ ensure("3: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == -32768);
+
+ str_val = "32768";
+ ensure("4: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));
+
+ str_val = "-32769";
+ ensure("5: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<21>()
+ {
+ U16 value;
+ std::string str_val("65535"); //0xFFFF
+ ensure("1: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 65535);
+
+ str_val = "0";
+ ensure("2: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 0);
+
+ str_val = "-1";
+ ensure("3: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));
+
+ str_val = "65536";
+ ensure("4: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<22>()
+ {
+ U32 value;
+ std::string str_val("4294967295"); //0xFFFFFFFF
+ ensure("1: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 4294967295UL);
+
+ str_val = "0";
+ ensure("2: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 0);
+
+ str_val = "4294967296";
+ ensure("3: convertToU32 failed", !LLStringUtil::convertToU32(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<23>()
+ {
+ S32 value;
+ std::string str_val("2147483647"); //0x7FFFFFFF
+ ensure("1: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 2147483647);
+
+ str_val = "0";
+ ensure("2: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 0);
+
+ // Avoid "unary minus operator applied to unsigned type" warning on VC++. JC
+ S32 min_val = -2147483647 - 1;
+ str_val = "-2147483648";
+ ensure("3: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == min_val);
+
+ str_val = "2147483648";
+ ensure("4: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));
+
+ str_val = "-2147483649";
+ ensure("5: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<24>()
+ {
+ F32 value;
+ std::string str_val("2147483647"); //0x7FFFFFFF
+ ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647);
+
+ str_val = "0";
+ ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0);
+
+ /* Need to find max/min F32 values
+ str_val = "-2147483648";
+ ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648);
+
+ str_val = "2147483648";
+ ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+
+ str_val = "-2147483649";
+ ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+ */
+ }
+
+ template<> template<>
+ void string_index_object_t::test<25>()
+ {
+ F64 value;
+ std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF
+ ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL);
+
+ str_val = "0";
+ ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);
+
+ /* Need to find max/min F64 values
+ str_val = "-2147483648";
+ ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648);
+
+ str_val = "2147483648";
+ ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+
+ str_val = "-2147483649";
+ ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+ */
+ }
+
+ template<> template<>
+ void string_index_object_t::test<26>()
+ {
+ const char* str1 = NULL;
+ const char* str2 = NULL;
+
+ ensure("1: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0);
+ str2 = "A";
+ ensure("2: compareStrings failed", LLStringUtil::compareStrings(str1, str2) > 0);
+ ensure("3: compareStrings failed", LLStringUtil::compareStrings(str2, str1) < 0);
+
+ str1 = "A is smaller than B";
+ str2 = "B is greater than A";
+ ensure("4: compareStrings failed", LLStringUtil::compareStrings(str1, str2) < 0);
+
+ str2 = "A is smaller than B";
+ ensure("5: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<27>()
+ {
+ const char* str1 = NULL;
+ const char* str2 = NULL;
+
+ ensure("1: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0);
+ str2 = "A";
+ ensure("2: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) > 0);
+ ensure("3: compareInsensitive failed", LLStringUtil::compareInsensitive(str2, str1) < 0);
+
+ str1 = "A is equal to a";
+ str2 = "a is EQUAL to A";
+ ensure("4: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<28>()
+ {
+ std::string lhs_str("PROgraM12files");
+ std::string rhs_str("PROgram12Files");
+ ensure("compareDict 1 failed", LLStringUtil::compareDict(lhs_str, rhs_str) < 0);
+ ensure("precedesDict 1 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == TRUE);
+
+ lhs_str = "PROgram12Files";
+ rhs_str = "PROgram12Files";
+ ensure("compareDict 2 failed", LLStringUtil::compareDict(lhs_str, rhs_str) == 0);
+ ensure("precedesDict 2 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == FALSE);
+
+ lhs_str = "PROgram12Files";
+ rhs_str = "PROgRAM12FILES";
+ ensure("compareDict 3 failed", LLStringUtil::compareDict(lhs_str, rhs_str) > 0);
+ ensure("precedesDict 3 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == FALSE);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<29>()
+ {
+ char str1[] = "First String...";
+ char str2[100];
+
+ LLStringUtil::copy(str2, str1, 100);
+ ensure("LLStringUtil::copy with enough dest length failed", strcmp(str2, str1) == 0);
+ LLStringUtil::copy(str2, str1, sizeof("First"));
+ ensure("LLStringUtil::copy with less dest length failed", strcmp(str2, "First") == 0);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<30>()
+ {
+ std::string str1 = "This is the sentence...";
+ std::string str2 = "This is the ";
+ std::string str3 = "first ";
+ std::string str4 = "This is the first sentence...";
+ std::string str5 = "This is the sentence...first ";
+ std::string dest;
+
+ dest = str1;
+ LLStringUtil::copyInto(dest, str3, str2.length());
+ ensure("LLStringUtil::copyInto insert failed", dest == str4);
+
+ dest = str1;
+ LLStringUtil::copyInto(dest, str3, dest.length());
+ ensure("LLStringUtil::copyInto append failed", dest == str5);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<31>()
+ {
+ std::string stripped;
+
+ // Plain US ASCII text, including spaces and punctuation,
+ // should not be altered.
+ std::string simple_text = "Hello, world!";
+ stripped = LLStringFn::strip_invalid_xml(simple_text);
+ ensure("Simple text passed unchanged", stripped == simple_text);
+
+ // Control characters should be removed
+ // except for 0x09, 0x0a, 0x0d
+ std::string control_chars;
+ for (char c = 0x01; c < 0x20; c++)
+ {
+ control_chars.push_back(c);
+ }
+ std::string allowed_control_chars;
+ allowed_control_chars.push_back( (char)0x09 );
+ allowed_control_chars.push_back( (char)0x0a );
+ allowed_control_chars.push_back( (char)0x0d );
+
+ stripped = LLStringFn::strip_invalid_xml(control_chars);
+ ensure("Only tab, LF, CR control characters allowed",
+ stripped == allowed_control_chars);
+
+ // UTF-8 should be passed intact, including high byte
+ // characters. Try Francais (with C squiggle cedilla)
+ std::string french = "Fran";
+ french.push_back( (char)0xC3 );
+ french.push_back( (char)0xA7 );
+ french += "ais";
+ stripped = LLStringFn::strip_invalid_xml( french );
+ ensure("UTF-8 high byte text is allowed", french == stripped );
+ }
+
+ template<> template<>
+ void string_index_object_t::test<32>()
+ {
+ // Test LLStringUtil::format() string interpolation
+ LLStringUtil::format_map_t fmt_map;
+ std::string s;
+ int subcount;
+
+ fmt_map["[TRICK1]"] = "[A]";
+ fmt_map["[A]"] = "a";
+ fmt_map["[B]"] = "b";
+ fmt_map["[AAA]"] = "aaa";
+ fmt_map["[BBB]"] = "bbb";
+ fmt_map["[TRICK2]"] = "[A]";
+ fmt_map["[EXPLOIT]"] = "!!!!!!!!!!!![EXPLOIT]!!!!!!!!!!!!";
+ fmt_map["[KEYLONGER]"] = "short";
+ fmt_map["[KEYSHORTER]"] = "Am I not a long string?";
+ fmt_map["?"] = "?";
+ fmt_map["[DELETE]"] = "";
+ fmt_map["[]"] = "[]"; // doesn't do a substitution, but shouldn't crash either
+
+ for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
+ {
+ // Test when source string is entirely one key
+ std::string s1 = (std::string)iter->first;
+ std::string s2 = (std::string)iter->second;
+ subcount = LLStringUtil::format(s1, fmt_map);
+ ensure_equals("LLStringUtil::format: Raw interpolation result", s1, s2);
+ if (s1 == "?" || s1 == "[]") // no interp expected
+ {
+ ensure_equals("LLStringUtil::format: Raw interpolation result count", 0, subcount);
+ }
+ else
+ {
+ ensure_equals("LLStringUtil::format: Raw interpolation result count", 1, subcount);
+ }
+ }
+
+ for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
+ {
+ // Test when source string is one key, duplicated
+ std::string s1 = (std::string)iter->first;
+ std::string s2 = (std::string)iter->second;
+ s = s1 + s1 + s1 + s1;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Rawx4 interpolation result", s, s2 + s2 + s2 + s2);
+ if (s1 == "?" || s1 == "[]") // no interp expected
+ {
+ ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 0, subcount);
+ }
+ else
+ {
+ ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 4, subcount);
+ }
+ }
+
+ // Test when source string has no keys
+ std::string srcs = "!!!!!!!!!!!!!!!!";
+ s = srcs;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: No key test result", s, srcs);
+ ensure_equals("LLStringUtil::format: No key test result count", 0, subcount);
+
+ // Test when source string has no keys and is empty
+ std::string srcs3;
+ s = srcs3;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure("LLStringUtil::format: No key test3 result", s.empty());
+ ensure_equals("LLStringUtil::format: No key test3 result count", 0, subcount);
+
+ // Test a substitution where a key is substituted with blankness
+ std::string srcs2 = "[DELETE]";
+ s = srcs2;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure("LLStringUtil::format: Delete key test2 result", s.empty());
+ ensure_equals("LLStringUtil::format: Delete key test2 result count", 1, subcount);
+
+ // Test an assorted substitution
+ std::string srcs4 = "[TRICK1][A][B][AAA][BBB][TRICK2][KEYLONGER][KEYSHORTER]?[DELETE]";
+ s = srcs4;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test1 result", s, "[A]abaaabbb[A]shortAm I not a long string??");
+ ensure_equals("LLStringUtil::format: Assorted Test1 result count", 9, subcount);
+
+ // Test an assorted substitution
+ std::string srcs5 = "[DELETE]?[KEYSHORTER][KEYLONGER][TRICK2][BBB][AAA][B][A][TRICK1]";
+ s = srcs5;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "?Am I not a long string?short[A]bbbaaaba[A]");
+ ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
+ // Test an assorted substitution
+ std::string srcs8 = "foo[DELETE]bar?";
+ s = srcs8;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test3 result", s, "foobar?");
+ ensure_equals("LLStringUtil::format: Assorted Test3 result count", 1, subcount);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<33>()
+ {
+ // Test LLStringUtil::format() string interpolation
+ LLStringUtil::format_map_t blank_fmt_map;
+ std::string s;
+ int subcount;
+
+ // Test substituting out of a blank format_map
+ std::string srcs6 = "12345";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, blank_fmt_map);
+ ensure_equals("LLStringUtil::format: Blankfmt Test1 result", s, "12345");
+ ensure_equals("LLStringUtil::format: Blankfmt Test1 result count", 0, subcount);
+
+ // Test substituting a blank string out of a blank format_map
+ std::string srcs7;
+ s = srcs7;
+ subcount = LLStringUtil::format(s, blank_fmt_map);
+ ensure("LLStringUtil::format: Blankfmt Test2 result", s.empty());
+ ensure_equals("LLStringUtil::format: Blankfmt Test2 result count", 0, subcount);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<34>()
+ {
+ // Test that incorrect LLStringUtil::format() use does not explode.
+ LLStringUtil::format_map_t nasty_fmt_map;
+ std::string s;
+ int subcount;
+
+ nasty_fmt_map[""] = "never used"; // see, this is nasty.
+
+ // Test substituting out of a nasty format_map
+ std::string srcs6 = "12345";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, nasty_fmt_map);
+ ensure_equals("LLStringUtil::format: Nastyfmt Test1 result", s, "12345");
+ ensure_equals("LLStringUtil::format: Nastyfmt Test1 result count", 0, subcount);
+
+ // Test substituting a blank string out of a nasty format_map
+ std::string srcs7;
+ s = srcs7;
+ subcount = LLStringUtil::format(s, nasty_fmt_map);
+ ensure("LLStringUtil::format: Nastyfmt Test2 result", s.empty());
+ ensure_equals("LLStringUtil::format: Nastyfmt Test2 result count", 0, subcount);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<35>()
+ {
+ // Make sure startsWith works
+ std::string string("anybody in there?");
+ std::string substr("anybody");
+ ensure("startsWith works.", LLStringUtil::startsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<36>()
+ {
+ // Make sure startsWith correctly fails
+ std::string string("anybody in there?");
+ std::string substr("there");
+ ensure("startsWith fails.", !LLStringUtil::startsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<37>()
+ {
+ // startsWith fails on empty strings
+ std::string value("anybody in there?");
+ std::string empty;
+ ensure("empty string.", !LLStringUtil::startsWith(value, empty));
+ ensure("empty substr.", !LLStringUtil::startsWith(empty, value));
+ ensure("empty everything.", !LLStringUtil::startsWith(empty, empty));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<38>()
+ {
+ // Make sure endsWith works correctly
+ std::string string("anybody in there?");
+ std::string substr("there?");
+ ensure("endsWith works.", LLStringUtil::endsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<39>()
+ {
+ // Make sure endsWith correctly fails
+ std::string string("anybody in there?");
+ std::string substr("anybody");
+ ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
+ substr = "there";
+ ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
+ substr = "ther?";
+ ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<40>()
+ {
+ // endsWith fails on empty strings
+ std::string value("anybody in there?");
+ std::string empty;
+ ensure("empty string.", !LLStringUtil::endsWith(value, empty));
+ ensure("empty substr.", !LLStringUtil::endsWith(empty, value));
+ ensure("empty everything.", !LLStringUtil::endsWith(empty, empty));
+ }
+}
diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp
new file mode 100644
index 0000000000..d6d9f68110
--- /dev/null
+++ b/indra/llcommon/tests/lltreeiterators_test.cpp
@@ -0,0 +1,1222 @@
+/**
+ * @file lltreeiterators.cpp
+ * @author Nat Goodspeed
+ * @date 2008-08-20
+ * @brief Test of lltreeiterators.h
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ *
+ * Copyright (c) 2008-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+
+#if LL_WINDOWS
+#pragma warning (disable : 4180) // qualifier applied to function type has no meaning; ignored
+#endif
+
+// STL headers
+// std headers
+#include <iostream>
+#include <sstream>
+#include <string>
+// external library headers
+#include <boost/bind.hpp>
+#include <boost/range/iterator_range.hpp>
+#include <boost/foreach.hpp>
+
+// associated header
+#include "../lltreeiterators.h"
+#include "../llpointer.h"
+
+#include "../test/lltut.h"
+
+/*****************************************************************************
+* tut test group
+*****************************************************************************/
+namespace tut
+{
+ struct iter_data
+ {
+ };
+ typedef test_group<iter_data> iter_group;
+ typedef iter_group::object iter_object;
+ tut::iter_group ig("lltreeiterators");
+} // namespace tut
+
+/*****************************************************************************
+* boost::get_pointer() specialization for LLPointer<>
+*****************************************************************************/
+// This specialization of boost::get_pointer() undoubtedly belongs in
+// llmemory.h. It's used by boost::bind() so that you can pass an
+// LLPointer<Foo> as well as a Foo* to a functor such as
+// boost::bind(&Foo::method, _1).
+//namespace boost
+//{
+ template <class NODE>
+ NODE* get_pointer(const LLPointer<NODE>& ptr) { return ptr.get(); }
+//};
+
+/*****************************************************************************
+* ScopeLabel
+*****************************************************************************/
+class ScopeLabel
+{
+public:
+ ScopeLabel(const std::string& label): mLabel(label)
+ {
+ std::cout << "Entering " << mLabel << '\n';
+ }
+ ~ScopeLabel()
+ {
+ std::cout << "Leaving " << mLabel << '\n';
+ }
+private:
+ std::string mLabel;
+};
+
+/*****************************************************************************
+* Cleanup
+*****************************************************************************/
+// Yes, we realize this is redundant with auto_ptr and LLPointer and all
+// kinds of better mechanisms. But in this particular source file, we need to
+// test nodes managed with plain old dumb pointers as well as nodes managed
+// with LLPointer, so we introduce this mechanism.
+//
+// In the general case, when we declare a Cleanup for some pointer, delete the
+// pointer when the Cleanup goes out of scope.
+template <typename PTRTYPE>
+struct Cleanup
+{
+ Cleanup(const PTRTYPE& ptr): mPtr(ptr) {}
+ ~Cleanup()
+ {
+ delete mPtr;
+ }
+ PTRTYPE mPtr;
+};
+
+// But when the pointer is an LLPointer<something>, Cleanup is a no-op:
+// LLPointer will handle the cleanup automagically.
+template <typename NODE>
+struct Cleanup< LLPointer<NODE> >
+{
+ Cleanup(const LLPointer<NODE>& ptr) {}
+ ~Cleanup() {}
+};
+
+/*****************************************************************************
+* Expected
+*****************************************************************************/
+// Expected is a base class used to capture the expected results -- a sequence
+// of string node names -- from one of our traversals of this example data.
+// Its subclasses initialize it with a pair of string iterators. It's not
+// strictly necessary to customize Expected to model Boost.Range, it's just
+// convenient.
+struct Expected
+{
+ template <typename ITER>
+ Expected(ITER begin, ITER end):
+ strings(begin, end)
+ {}
+ /*------ The following are to make Expected work with Boost.Range ------*/
+ typedef std::vector<std::string> container_type;
+ typedef container_type::iterator iterator;
+ typedef container_type::const_iterator const_iterator;
+ typedef container_type::size_type size_type;
+ container_type strings;
+ iterator begin() { return strings.begin(); }
+ iterator end() { return strings.end(); }
+ size_type size() { return strings.size(); }
+ const_iterator begin() const { return strings.begin(); }
+ const_iterator end() const { return strings.end(); }
+};
+
+// We have a couple of generic Expected template subclasses. This list of
+// strings is used for the "else" case when all specializations fail.
+const char* bad_strings[] = { "FAIL" };
+
+/*****************************************************************************
+* verify()
+*****************************************************************************/
+// test function: given (an object modeling) a Boost.Range of tree nodes,
+// compare the sequence of visited node names with a range of expected name
+// strings. Report success both with std::cout output and a bool return. The
+// string desc parameter is to identify the different tests.
+template <typename NODERANGE, typename STRINGRANGE>
+bool verify(const std::string& desc, NODERANGE noderange, STRINGRANGE expected)
+{
+ typename boost::range_iterator<NODERANGE>::type
+ nri = boost::begin(noderange),
+ nrend = boost::end(noderange);
+ typename boost::range_iterator<STRINGRANGE>::type
+ sri = boost::begin(expected),
+ srend = boost::end(expected);
+ // We choose to loop over both sequences explicitly rather than using
+ // std::equal() or std::lexicographical_compare(). The latter tells you
+ // whether one sequence is *less* than the other -- it doesn't tell you
+ // equality. std::equal() needs you to verify the sequence lengths ahead
+ // of time. Anyway, comparing explicitly allows us to report much more
+ // information about any sequence mismatch.
+ for ( ; nri != nrend && sri != srend; ++nri, ++sri)
+ {
+ if ((*nri)->name() != *sri)
+ {
+ std::cout << desc << " mismatch: "
+ << "expected " << *sri << ", got " << (*nri)->name() << "\n";
+ return false;
+ }
+ }
+ if (nri != nrend)
+ {
+ std::cout << desc << " produced too many items:\n";
+ for ( ; nri != nrend; ++nri)
+ {
+ std::cout << " " << (*nri)->name() << '\n';
+ }
+ return false;
+ }
+ if (sri != srend)
+ {
+ std::cout << desc << " produced too few items, omitting:\n";
+ for ( ; sri != srend; ++sri)
+ {
+ std::cout << " " << *sri << '\n';
+ }
+ return false;
+ }
+// std::cout << desc << " test passed\n";
+ return true;
+}
+
+/*****************************************************************************
+* PlainNode: LLLinkIter, non-refcounted
+*****************************************************************************/
+class PlainNode
+{
+public:
+ PlainNode(const std::string& name, PlainNode* next=NULL):
+ mName(name),
+ mNext(next)
+ {}
+ ~PlainNode()
+ {
+ delete mNext;
+ }
+ std::string name() const { return mName; }
+ PlainNode* next() const { return mNext; }
+public: // if this were 'private', couldn't bind mNext
+ PlainNode* mNext;
+private:
+ std::string mName;
+};
+
+namespace tut
+{
+ template<> template<>
+ void iter_object::test<1>()
+ {
+// set_test_name("LLLinkedIter -- non-refcounted class");
+ PlainNode* last(new PlainNode("c"));
+ PlainNode* second(new PlainNode("b", last));
+ PlainNode* first(new PlainNode("a", second));
+ Cleanup<PlainNode*> cleanup(first);
+ static const char* cseq[] = { "a", "b", "c" };
+ Expected seq(boost::begin(cseq), boost::end(cseq));
+ std::string desc1("Iterate by public link member");
+// std::cout << desc1 << ":\n";
+ // Try instantiating an iterator with NULL. This test is less about
+ // "did we iterate once?" than "did we avoid blowing up?"
+ for (LLLinkedIter<PlainNode> pni(NULL, boost::bind(&PlainNode::mNext, _1)), end;
+ pni != end; ++pni)
+ {
+// std::cout << (*pni)->name() << '\n';
+ ensure("LLLinkedIter<PlainNode>(NULL)", false);
+ }
+ ensure(desc1,
+ verify(desc1,
+ boost::make_iterator_range(LLLinkedIter<PlainNode>(first,
+ boost::bind(&PlainNode::mNext, _1)),
+ LLLinkedIter<PlainNode>()),
+ seq));
+ std::string desc2("Iterate by next() method");
+// std::cout << desc2 << ":\n";
+// for (LLLinkedIter<PlainNode> pni(first, boost::bind(&PlainNode::next, _1)); ! (pni == end); ++pni)
+// std::cout << (**pni).name() << '\n';
+ ensure(desc2,
+ verify(desc2,
+ boost::make_iterator_range(LLLinkedIter<PlainNode>(first,
+ boost::bind(&PlainNode::next, _1)),
+ LLLinkedIter<PlainNode>()),
+ seq));
+ {
+// LLLinkedIter<PlainNode> pni(first, boost::bind(&PlainNode::next, _1));
+// std::cout << "First is " << (*pni++)->name() << '\n';
+// std::cout << "Second is " << (*pni )->name() << '\n';
+ }
+ {
+ LLLinkedIter<PlainNode> pni(first, boost::bind(&PlainNode::next, _1));
+ ensure_equals("first", (*pni++)->name(), "a");
+ ensure_equals("second", (*pni )->name(), "b");
+ }
+ }
+} // tut
+
+/*****************************************************************************
+* RCNode: LLLinkIter, refcounted
+*****************************************************************************/
+class RCNode;
+typedef LLPointer<RCNode> RCNodePtr;
+
+class RCNode: public LLRefCount
+{
+public:
+ RCNode(const std::string& name, const RCNodePtr& next=RCNodePtr()):
+ mName(name),
+ mNext(next)
+ {
+// std::cout << "New RCNode(" << mName << ")\n";
+ }
+ RCNode(const RCNode& that):
+ mName(that.mName),
+ mNext(that.mNext)
+ {
+// std::cout << "Copy RCNode(" << mName << ")\n";
+ }
+ virtual ~RCNode();
+ std::string name() const { return mName; }
+ RCNodePtr next() const { return mNext; }
+public: // if this were 'private', couldn't bind mNext
+ RCNodePtr mNext;
+private:
+ std::string mName;
+};
+
+std::ostream& operator<<(std::ostream& out, const RCNode& node)
+{
+ out << "RCNode(" << node.name() << ')';
+ return out;
+}
+
+// This string contains the node name of the last RCNode destroyed. We use it
+// to validate that LLLinkedIter<RCNode> in fact contains LLPointer<RCNode>,
+// and that therefore an outstanding LLLinkedIter to an instance of a
+// refcounted class suffices to keep that instance alive.
+std::string last_RCNode_destroyed;
+
+RCNode::~RCNode()
+{
+// std::cout << "Kill " << *this << "\n";
+ last_RCNode_destroyed = mName;
+}
+
+namespace tut
+{
+ template<> template<>
+ void iter_object::test<2>()
+ {
+// set_test_name("LLLinkedIter -- refcounted class");
+ LLLinkedIter<RCNode> rcni, end2;
+ {
+// ScopeLabel label("inner scope");
+ RCNodePtr head(new RCNode("x", new RCNode("y", new RCNode("z"))));
+// for (rcni = LLLinkedIter<RCNode>(head, boost::bind(&RCNode::mNext, _1)); rcni != end2; ++rcni)
+// std::cout << **rcni << '\n';
+ rcni = LLLinkedIter<RCNode>(head, boost::bind(&RCNode::next, _1));
+ }
+// std::cout << "Now the LLLinkedIter<RCNode> is the only remaining reference to RCNode chain\n";
+ ensure_equals(last_RCNode_destroyed, "");
+ ensure(rcni != end2);
+ ensure_equals((*rcni)->name(), "x");
+ ++rcni;
+ ensure_equals(last_RCNode_destroyed, "x");
+ ensure(rcni != end2);
+ ensure_equals((*rcni)->name(), "y");
+ ++rcni;
+ ensure_equals(last_RCNode_destroyed, "y");
+ ensure(rcni != end2);
+ ensure_equals((*rcni)->name(), "z");
+ ++rcni;
+ ensure_equals(last_RCNode_destroyed, "z");
+ ensure(rcni == end2);
+ }
+}
+
+/*****************************************************************************
+* TreeNode
+*****************************************************************************/
+class TreeNode;
+typedef LLPointer<TreeNode> TreeNodePtr;
+
+/**
+ * TreeNode represents a refcounted tree-node class that hasn't (yet) been
+ * modified to incorporate LLTreeIter methods. This illustrates how you can
+ * use tree iterators either standalone, or with free functions.
+ */
+class TreeNode: public LLRefCount
+{
+public:
+ typedef std::vector<TreeNodePtr> list_type;
+ typedef list_type::const_iterator child_iterator;
+
+ // To avoid cycles, use a "weak" raw pointer for the parent link
+ TreeNode(const std::string& name, TreeNode* parent=0):
+ mParent(parent),
+ mName(name)
+ {}
+ TreeNodePtr newChild(const std::string& name)
+ {
+ TreeNodePtr child(new TreeNode(name, this));
+ mChildren.push_back(child);
+ return child;
+ }
+ std::string name() const { return mName; }
+ TreeNodePtr getParent() const { return mParent; }
+ child_iterator child_begin() const { return mChildren.begin(); }
+ child_iterator child_end() const { return mChildren.end(); }
+private:
+ std::string mName;
+ // To avoid cycles, use a "weak" raw pointer for the parent link
+ TreeNode* mParent;
+ list_type mChildren;
+};
+
+/**
+ * This is an example of a helper function to facilitate iterating from a
+ * TreeNode up to the root or down from the root (see LLTreeIter::RootIter).
+ *
+ * Example:
+ * @code
+ * BOOST_FOREACH(TreeNodePtr node, getRootRange<LLTreeIter::UP>(somenode))
+ * {
+ * std::cout << node->name() << '\n';
+ * }
+ * @endcode
+ */
+template <LLTreeIter::RootIter DISCRIM>
+boost::iterator_range< LLTreeRootIter<DISCRIM, TreeNode> >
+getRootRange(const TreeNodePtr& node)
+{
+ typedef LLTreeRootIter<DISCRIM, TreeNode> iter_type;
+ typedef boost::iterator_range<iter_type> range_type;
+ return range_type(iter_type(node, boost::bind(&TreeNode::getParent, _1)),
+ iter_type());
+}
+
+/**
+ * This is an example of a helper function to facilitate walking a given
+ * TreeNode's subtree in any supported order (see LLTreeIter::WalkIter).
+ *
+ * Example:
+ * @code
+ * BOOST_FOREACH(TreeNodePtr node, getWalkRange<LLTreeIter::DFS_PRE>(root))
+ * {
+ * std::cout << node->name() << '\n';
+ * }
+ * @endcode
+ */
+template <LLTreeIter::WalkIter DISCRIM>
+boost::iterator_range< LLTreeWalkIter<DISCRIM, TreeNode, TreeNode::child_iterator> >
+getWalkRange(const TreeNodePtr& node)
+{
+ typedef LLTreeWalkIter<DISCRIM, TreeNode, TreeNode::child_iterator> iter_type;
+ typedef boost::iterator_range<iter_type> range_type;
+ return range_type(iter_type(node,
+ boost::bind(&TreeNode::child_begin, _1),
+ boost::bind(&TreeNode::child_end, _1)),
+ iter_type());
+}
+
+/*****************************************************************************
+* EnhancedTreeNode
+*****************************************************************************/
+class EnhancedTreeNode;
+typedef LLPointer<EnhancedTreeNode> EnhancedTreeNodePtr;
+
+/**
+ * More typically, you enhance the tree-node class itself with template
+ * methods like the above. This EnhancedTreeNode class illustrates the
+ * technique. Normally, of course, you'd simply add these methods to TreeNode;
+ * we put them in a separate class to preserve the undecorated TreeNode class
+ * to illustrate (and test) the use of plain tree iterators and standalone
+ * helper functions.
+ *
+ * We originally implemented EnhancedTreeNode as a subclass of TreeNode -- but
+ * because TreeNode stores and manipulates TreeNodePtrs and TreeNode*s,
+ * reusing its methods required so much ugly downcast logic that we gave up
+ * and restated the whole class. Bear in mind that logically these aren't two
+ * separate classes; logically they're two snapshots of the @em same class at
+ * different moments in time.
+ */
+class EnhancedTreeNode: public LLRefCount
+{
+public:
+ /*-------------- The following is restated from TreeNode ---------------*/
+ typedef std::vector<EnhancedTreeNodePtr> list_type;
+ typedef list_type::const_iterator child_iterator;
+
+ // To avoid cycles, use a "weak" raw pointer for the parent link
+ EnhancedTreeNode(const std::string& name, EnhancedTreeNode* parent=0):
+ mParent(parent),
+ mName(name)
+ {}
+ EnhancedTreeNodePtr newChild(const std::string& name)
+ {
+ EnhancedTreeNodePtr child(new EnhancedTreeNode(name, this));
+ mChildren.push_back(child);
+ return child;
+ }
+ std::string name() const { return mName; }
+ EnhancedTreeNodePtr getParent() const { return mParent; }
+ child_iterator child_begin() const { return mChildren.begin(); }
+ child_iterator child_end() const { return mChildren.end(); }
+
+private:
+ std::string mName;
+ // To avoid cycles, use a "weak" raw pointer for the parent link
+ EnhancedTreeNode* mParent;
+ list_type mChildren;
+public:
+ /*----- End of TreeNode; what follows is new with EnhancedTreeNode -----*/
+
+ /**
+ * Because the type of the iterator range returned by getRootRange()
+ * depends on the discriminator enum value, instead of a simple typedef we
+ * use a templated struct. Example usage:
+ *
+ * @code
+ * for (EnhancedTreeNode::root_range<LLTreeIter::UP>::type range =
+ * somenode->getRootRange<LLTreeIter::UP>();
+ * range.first != range.second; ++range.first)
+ * {
+ * std::cout << (*range.first)->name() << '\n';
+ * }
+ * @endcode
+ */
+ template <LLTreeIter::RootIter DISCRIM>
+ struct root_range
+ {
+ typedef boost::iterator_range< LLTreeRootIter<DISCRIM, EnhancedTreeNode> > type;
+ };
+
+ /**
+ * Helper method for walking up to (or down from) the tree root. See
+ * LLTreeIter::RootIter.
+ *
+ * Example usage:
+ * @code
+ * BOOST_FOREACH(EnhancedTreeNodePtr node, somenode->getRootRange<LLTreeIter::UP>())
+ * {
+ * std::cout << node->name() << '\n';
+ * }
+ * @endcode
+ */
+ template <LLTreeIter::RootIter DISCRIM>
+ typename root_range<DISCRIM>::type getRootRange() const
+ {
+ typedef typename root_range<DISCRIM>::type range_type;
+ typedef typename range_type::iterator iter_type;
+ return range_type(iter_type(const_cast<EnhancedTreeNode*>(this),
+ boost::bind(&EnhancedTreeNode::getParent, _1)),
+ iter_type());
+ }
+
+ /**
+ * Because the type of the iterator range returned by getWalkRange()
+ * depends on the discriminator enum value, instead of a simple typedef we
+ * use a templated stuct. Example usage:
+ *
+ * @code
+ * for (EnhancedTreeNode::walk_range<LLTreeIter::DFS_PRE>::type range =
+ * somenode->getWalkRange<LLTreeIter::DFS_PRE>();
+ * range.first != range.second; ++range.first)
+ * {
+ * std::cout << (*range.first)->name() << '\n';
+ * }
+ * @endcode
+ */
+ template <LLTreeIter::WalkIter DISCRIM>
+ struct walk_range
+ {
+ typedef boost::iterator_range< LLTreeWalkIter<DISCRIM,
+ EnhancedTreeNode,
+ EnhancedTreeNode::child_iterator> > type;
+ };
+
+ /**
+ * Helper method for walking a given node's subtree in any supported
+ * order (see LLTreeIter::WalkIter).
+ *
+ * Example usage:
+ * @code
+ * BOOST_FOREACH(EnhancedTreeNodePtr node, somenode->getWalkRange<LLTreeIter::DFS_PRE>())
+ * {
+ * std::cout << node->name() << '\n';
+ * }
+ * @endcode
+ */
+ template <LLTreeIter::WalkIter DISCRIM>
+ typename walk_range<DISCRIM>::type getWalkRange() const
+ {
+ typedef typename walk_range<DISCRIM>::type range_type;
+ typedef typename range_type::iterator iter_type;
+ return range_type(iter_type(const_cast<EnhancedTreeNode*>(this),
+ boost::bind(&EnhancedTreeNode::child_begin, _1),
+ boost::bind(&EnhancedTreeNode::child_end, _1)),
+ iter_type());
+ }
+};
+
+/*****************************************************************************
+* PlainTree
+*****************************************************************************/
+struct PlainTree
+{
+ PlainTree(const std::string& name, PlainTree* parent=0):
+ mName(name),
+ mParent(parent),
+ mNextSibling(0),
+ mFirstChild(0)
+ {
+ mLastChildLink = &mFirstChild;
+ }
+ ~PlainTree()
+ {
+ delete mNextSibling;
+ delete mFirstChild;
+ }
+ PlainTree* newChild(const std::string& name)
+ {
+ PlainTree* child(new PlainTree(name, this));
+ *mLastChildLink = child;
+ mLastChildLink = &child->mNextSibling;
+ return child;
+ }
+ std::string name() const { return mName; }
+
+ std::string mName;
+ PlainTree* mParent;
+ PlainTree* mNextSibling;
+ PlainTree* mFirstChild;
+ PlainTree** mLastChildLink;
+};
+
+// This "classic" tree tracks each node's children with a linked list anchored
+// at the parent's mFirstChild and linked through each child's mNextSibling.
+// LLTreeDFSIter<> and LLTreeBFSIter<> need functors to return begin()/end()
+// iterators over a given node's children. But because this tree's children
+// aren't stored in an STL container, we can't just export that container's
+// begin()/end(). Instead we'll use LLLinkedIter<> to view the hand-maintained
+// linked list as an iterator range. The straightforward way to do that would
+// be to add child_begin() and child_end() methods. But let's say (for the
+// sake of argument) that this struct is so venerable we don't dare modify it
+// even to add new methods. Well, we can use free functions (or functors) too.
+LLLinkedIter<PlainTree> PlainTree_child_begin(PlainTree* node)
+{
+ return LLLinkedIter<PlainTree>(node->mFirstChild, boost::bind(&PlainTree::mNextSibling, _1));
+}
+
+LLLinkedIter<PlainTree> PlainTree_child_end(PlainTree* node)
+{
+ return LLLinkedIter<PlainTree>();
+}
+
+/**
+ * This is an example of a helper function to facilitate iterating from a
+ * PlainTree up to the root or down from the root (see LLTreeIter::RootIter).
+ * Note that we're simply overloading the same getRootRange() helper function
+ * name we used for TreeNode.
+ *
+ * Example:
+ * @code
+ * BOOST_FOREACH(PlainTree* node, getRootRange<LLTreeIter::UP>(somenode))
+ * {
+ * std::cout << node->name() << '\n';
+ * }
+ * @endcode
+ */
+template <LLTreeIter::RootIter DISCRIM>
+boost::iterator_range< LLTreeRootIter<DISCRIM, PlainTree> >
+getRootRange(PlainTree* node)
+{
+ typedef LLTreeRootIter<DISCRIM, PlainTree> iter_type;
+ typedef boost::iterator_range<iter_type> range_type;
+ return range_type(iter_type(node, boost::bind(&PlainTree::mParent, _1)),
+ iter_type());
+}
+
+/**
+ * This is an example of a helper function to facilitate walking a given
+ * PlainTree's subtree in any supported order (see LLTreeIter::WalkIter). Note
+ * that we're simply overloading the same getWalkRange() helper function name
+ * we used for TreeNode.
+ *
+ * Example:
+ * @code
+ * BOOST_FOREACH(PlainTree* node, getWalkRange<LLTreeIter::DFS_PRE>(root))
+ * {
+ * std::cout << node->name() << '\n';
+ * }
+ * @endcode
+ */
+template <LLTreeIter::WalkIter DISCRIM>
+boost::iterator_range< LLTreeWalkIter<DISCRIM, PlainTree, LLLinkedIter<PlainTree> > >
+getWalkRange(PlainTree* node)
+{
+ typedef LLTreeWalkIter<DISCRIM, PlainTree, LLLinkedIter<PlainTree> > iter_type;
+ typedef boost::iterator_range<iter_type> range_type;
+ return range_type(iter_type(node,
+ PlainTree_child_begin,
+ PlainTree_child_end),
+ iter_type());
+}
+
+// We could go through the exercise of writing EnhancedPlainTree containing
+// root_range, getRootRange(), walk_range and getWalkRange() members -- but we
+// won't. See EnhancedTreeNode for examples.
+
+/*****************************************************************************
+* Generic tree test data
+*****************************************************************************/
+template <class NODE>
+typename LLPtrTo<NODE>::type example_tree()
+{
+ typedef typename LLPtrTo<NODE>::type NodePtr;
+ NodePtr root(new NODE("root"));
+ NodePtr A(root->newChild("A"));
+ NodePtr A1(A->newChild("A1"));
+/* NodePtr A1a*/(A1->newChild("A1a"));
+/* NodePtr A1b*/(A1->newChild("A1b"));
+/* NodePtr A1c*/(A1->newChild("A1c"));
+ NodePtr A2(A->newChild("A2"));
+/* NodePtr A2a*/(A2->newChild("A2a"));
+/* NodePtr A2b*/(A2->newChild("A2b"));
+/* NodePtr A2c*/(A2->newChild("A2c"));
+ NodePtr A3(A->newChild("A3"));
+/* NodePtr A3a*/(A3->newChild("A3a"));
+/* NodePtr A3b*/(A3->newChild("A3b"));
+/* NodePtr A3c*/(A3->newChild("A3c"));
+ NodePtr B(root->newChild("B"));
+ NodePtr B1(B->newChild("B1"));
+/* NodePtr B1a*/(B1->newChild("B1a"));
+/* NodePtr B1b*/(B1->newChild("B1b"));
+/* NodePtr B1c*/(B1->newChild("B1c"));
+ NodePtr B2(B->newChild("B2"));
+/* NodePtr B2a*/(B2->newChild("B2a"));
+/* NodePtr B2b*/(B2->newChild("B2b"));
+/* NodePtr B2c*/(B2->newChild("B2c"));
+ NodePtr B3(B->newChild("B3"));
+/* NodePtr B3a*/(B3->newChild("B3a"));
+/* NodePtr B3b*/(B3->newChild("B3b"));
+/* NodePtr B3c*/(B3->newChild("B3c"));
+ NodePtr C(root->newChild("C"));
+ NodePtr C1(C->newChild("C1"));
+/* NodePtr C1a*/(C1->newChild("C1a"));
+/* NodePtr C1b*/(C1->newChild("C1b"));
+/* NodePtr C1c*/(C1->newChild("C1c"));
+ NodePtr C2(C->newChild("C2"));
+/* NodePtr C2a*/(C2->newChild("C2a"));
+/* NodePtr C2b*/(C2->newChild("C2b"));
+/* NodePtr C2c*/(C2->newChild("C2c"));
+ NodePtr C3(C->newChild("C3"));
+/* NodePtr C3a*/(C3->newChild("C3a"));
+/* NodePtr C3b*/(C3->newChild("C3b"));
+/* NodePtr C3c*/(C3->newChild("C3c"));
+ return root;
+}
+
+// WalkExpected<WalkIter> is the list of string node names we expect from a
+// WalkIter traversal of our example_tree() data.
+template <LLTreeIter::WalkIter DISCRIM>
+struct WalkExpected: public Expected
+{
+ // Initialize with bad_strings: we don't expect to use this generic case,
+ // only the specializations. Note that for a classic C-style array we must
+ // pass a pair of iterators rather than extracting boost::begin() and
+ // boost::end() within the target constructor: a template ctor accepts
+ // these classic C-style arrays as char** rather than char*[length]. Oh well.
+ WalkExpected(): Expected(boost::begin(bad_strings), boost::end(bad_strings)) {}
+};
+
+// list of string node names we expect from traversing example_tree() in
+// DFS_PRE order
+const char* dfs_pre_strings[] =
+{
+ "root",
+ "A",
+ "A1",
+ "A1a",
+ "A1b",
+ "A1c",
+ "A2",
+ "A2a",
+ "A2b",
+ "A2c",
+ "A3",
+ "A3a",
+ "A3b",
+ "A3c",
+ "B",
+ "B1",
+ "B1a",
+ "B1b",
+ "B1c",
+ "B2",
+ "B2a",
+ "B2b",
+ "B2c",
+ "B3",
+ "B3a",
+ "B3b",
+ "B3c",
+ "C",
+ "C1",
+ "C1a",
+ "C1b",
+ "C1c",
+ "C2",
+ "C2a",
+ "C2b",
+ "C2c",
+ "C3",
+ "C3a",
+ "C3b",
+ "C3c"
+};
+
+// specialize WalkExpected<DFS_PRE> with the expected strings
+template <>
+struct WalkExpected<LLTreeIter::DFS_PRE>: public Expected
+{
+ WalkExpected(): Expected(boost::begin(dfs_pre_strings), boost::end(dfs_pre_strings)) {}
+};
+
+// list of string node names we expect from traversing example_tree() in
+// DFS_POST order
+const char* dfs_post_strings[] =
+{
+ "A1a",
+ "A1b",
+ "A1c",
+ "A1",
+ "A2a",
+ "A2b",
+ "A2c",
+ "A2",
+ "A3a",
+ "A3b",
+ "A3c",
+ "A3",
+ "A",
+ "B1a",
+ "B1b",
+ "B1c",
+ "B1",
+ "B2a",
+ "B2b",
+ "B2c",
+ "B2",
+ "B3a",
+ "B3b",
+ "B3c",
+ "B3",
+ "B",
+ "C1a",
+ "C1b",
+ "C1c",
+ "C1",
+ "C2a",
+ "C2b",
+ "C2c",
+ "C2",
+ "C3a",
+ "C3b",
+ "C3c",
+ "C3",
+ "C",
+ "root"
+};
+
+// specialize WalkExpected<DFS_POST> with the expected strings
+template <>
+struct WalkExpected<LLTreeIter::DFS_POST>: public Expected
+{
+ WalkExpected(): Expected(boost::begin(dfs_post_strings), boost::end(dfs_post_strings)) {}
+};
+
+// list of string node names we expect from traversing example_tree() in BFS order
+const char* bfs_strings[] =
+{
+ "root",
+ "A",
+ "B",
+ "C",
+ "A1",
+ "A2",
+ "A3",
+ "B1",
+ "B2",
+ "B3",
+ "C1",
+ "C2",
+ "C3",
+ "A1a",
+ "A1b",
+ "A1c",
+ "A2a",
+ "A2b",
+ "A2c",
+ "A3a",
+ "A3b",
+ "A3c",
+ "B1a",
+ "B1b",
+ "B1c",
+ "B2a",
+ "B2b",
+ "B2c",
+ "B3a",
+ "B3b",
+ "B3c",
+ "C1a",
+ "C1b",
+ "C1c",
+ "C2a",
+ "C2b",
+ "C2c",
+ "C3a",
+ "C3b",
+ "C3c"
+};
+
+// specialize WalkExpected<BFS> with the expected strings
+template <>
+struct WalkExpected<LLTreeIter::BFS>: public Expected
+{
+ WalkExpected(): Expected(boost::begin(bfs_strings), boost::end(bfs_strings)) {}
+};
+
+// extract a particular "arbitrary" node from the example_tree() data: the
+// second (middle) node at each child level
+template <class NODE, typename CHILDITER>
+typename LLPtrTo<NODE>::type
+get_B2b(const typename LLPtrTo<NODE>::type& root,
+ const boost::function<CHILDITER(const typename LLPtrTo<NODE>::type&)>& child_begin)
+{
+ typedef typename LLPtrTo<NODE>::type NodePtr;
+ CHILDITER Bi(child_begin(root));
+ ++Bi;
+ NodePtr B(*Bi);
+ CHILDITER B2i(child_begin(B));
+ ++B2i;
+ NodePtr B2(*B2i);
+ CHILDITER B2bi(child_begin(B2));
+ ++B2bi;
+ NodePtr B2b(*B2bi);
+ return B2b;
+}
+
+// RootExpected<RootIter> is the list of string node names we expect from a
+// RootIter traversal of our example_tree() data.
+template <LLTreeIter::RootIter DISCRIM>
+struct RootExpected: public Expected
+{
+ // Initialize with bad_strings: we don't expect to use this generic case,
+ // only the specializations.
+ RootExpected(): Expected(boost::begin(bad_strings), boost::end(bad_strings)) {}
+};
+
+// list of string node names we expect from traversing UP from
+// example_tree()'s B2b node
+const char* up_from_B2b[] =
+{
+ "B2b",
+ "B2",
+ "B",
+ "root"
+};
+
+// specialize RootExpected<UP> with the expected strings
+template <>
+struct RootExpected<LLTreeIter::UP>: public Expected
+{
+ RootExpected(): Expected(boost::begin(up_from_B2b), boost::end(up_from_B2b)) {}
+};
+
+// list of string node names we expect from traversing DOWN to
+// example_tree()'s B2b node
+const char* down_to_B2b[] =
+{
+ "root",
+ "B",
+ "B2",
+ "B2b"
+};
+
+// specialize RootExpected<DOWN> with the expected strings
+template <>
+struct RootExpected<LLTreeIter::DOWN>: public Expected
+{
+ RootExpected(): Expected(boost::begin(down_to_B2b), boost::end(down_to_B2b)) {}
+};
+
+/*****************************************************************************
+* Generic tree test functions
+*****************************************************************************/
+template<LLTreeIter::RootIter DISCRIM, class NODE, typename PARENTFUNC>
+bool LLTreeRootIter_test(const std::string& itername, const std::string& nodename,
+ const typename LLPtrTo<NODE>::type& node,
+ PARENTFUNC parentfunc)
+{
+ std::ostringstream desc;
+ desc << itername << '<' << nodename << "> from " << node->name();
+ if (! verify(desc.str(),
+ boost::make_iterator_range(LLTreeRootIter<DISCRIM, NODE>(node, parentfunc),
+ LLTreeRootIter<DISCRIM, NODE>()),
+ RootExpected<DISCRIM>()))
+ return false;
+// std::cout << desc.str() << '\n';
+ // Try instantiating an iterator with NULL (that is, a default-constructed
+ // node pointer). This test is less about "did we iterate once?" than "did
+ // we avoid blowing up?"
+ for (LLTreeRootIter<DISCRIM, NODE> hri = LLTreeRootIter<DISCRIM, NODE>(typename LLPtrTo<NODE>::type(), parentfunc), hrend;
+ hri != hrend; /* ++hri */) // incrementing is moot, and MSVC complains
+ {
+// std::cout << nodename << '(' << (*hri)->name() << ")\n";
+ std::cout << itername << '<' << nodename << ">(NULL)\n";
+ return false;
+ }
+ return true;
+}
+
+template<class NODE, typename CHILDITER, typename PARENTFUNC, typename CHILDFUNC>
+bool LLTreeUpIter_test(const std::string& nodename, PARENTFUNC parentfunc, CHILDFUNC childfunc)
+{
+ bool success = true;
+ typedef typename LLPtrTo<NODE>::type ptr_type;
+ ptr_type root(example_tree<NODE>());
+ Cleanup<ptr_type> cleanup(root);
+ ptr_type B2b(get_B2b<NODE, CHILDITER>(root, childfunc));
+ if (! LLTreeRootIter_test<LLTreeIter::UP, NODE>("LLTreeUpIter", nodename, B2b, parentfunc))
+ success = false;
+ if (! LLTreeRootIter_test<LLTreeIter::DOWN, NODE>("LLTreeDownIter", nodename, B2b, parentfunc))
+ success = false;
+ return success;
+}
+
+template <LLTreeIter::WalkIter DISCRIM, class NODE, typename CHILDITER,
+ typename CHILDBEGINFUNC, typename CHILDENDFUNC>
+bool LLTreeWalkIter_test(const std::string& itername, const std::string& nodename,
+ CHILDBEGINFUNC childbegin, CHILDENDFUNC childend)
+{
+ typename LLPtrTo<NODE>::type root(example_tree<NODE>());
+ Cleanup<typename LLPtrTo<NODE>::type> cleanup(root);
+ std::ostringstream desc;
+ desc << itername << '<' << nodename << "> from " << root->name();
+ if (! verify(desc.str(),
+ boost::make_iterator_range(LLTreeWalkIter<DISCRIM, NODE, CHILDITER>(root,
+ childbegin,
+ childend),
+ LLTreeWalkIter<DISCRIM, NODE, CHILDITER>()),
+ WalkExpected<DISCRIM>()))
+ return false;
+ // Try instantiating an iterator with NULL (that is, a default-constructed
+ // node pointer). This test is less about "did we iterate once?" than "did
+ // we avoid blowing up?"
+ for (LLTreeWalkIter<DISCRIM, NODE, CHILDITER> twi = LLTreeWalkIter<DISCRIM, NODE, CHILDITER>(typename LLPtrTo<NODE>::type(),
+ childbegin,
+ childend),
+ twend;
+ twi != twend; /* ++twi */) // incrementing is moot, and MSVC complains
+ {
+ std::cout << itername << '<' << nodename << ">(NULL)\n";
+ return false;
+ }
+ return true;
+}
+
+template <class NODE, typename CHILDITER,
+ typename PARENTFUNC, typename CHILDBEGINFUNC, typename CHILDENDFUNC>
+bool LLTreeIter_tests(const std::string& nodename,
+ PARENTFUNC parentfunc, CHILDBEGINFUNC childbegin, CHILDENDFUNC childend)
+{
+ bool success = true;
+ if (! LLTreeUpIter_test<NODE, CHILDITER>(nodename, parentfunc, childbegin))
+ success = false;
+/*==========================================================================*|
+ LLTreeIter_test<NODE, LLTreeDFSIter<NODE, CHILDITER> >("LLTreeDFSIter", nodename,
+ childbegin, childend);
+ LLTreeIter_test<NODE, LLTreeDFSPostIter<NODE, CHILDITER> >("LLTreeDFSPostIter", nodename,
+ childbegin, childend);
+ LLTreeIter_test<NODE, LLTreeBFSIter<NODE, CHILDITER> >("LLTreeBFSIter", nodename,
+ childbegin, childend);
+|*==========================================================================*/
+ if (! LLTreeWalkIter_test<LLTreeIter::DFS_PRE, NODE, CHILDITER>("LLTreeDFSIter", nodename,
+ childbegin, childend))
+ success = false;
+ if (! LLTreeWalkIter_test<LLTreeIter::DFS_POST, NODE, CHILDITER>("LLTreeDFSPostIter", nodename,
+ childbegin, childend))
+ success = false;
+ if (! LLTreeWalkIter_test<LLTreeIter::BFS, NODE, CHILDITER>("LLTreeBFSIter", nodename,
+ childbegin, childend))
+ success = false;
+ return success;
+}
+
+namespace tut
+{
+ template<> template<>
+ void iter_object::test<3>()
+ {
+// set_test_name("LLTreeIter tests");
+ ensure(LLTreeIter_tests<TreeNode, TreeNode::child_iterator>
+ ("TreeNode",
+ boost::bind(&TreeNode::getParent, _1),
+ boost::bind(&TreeNode::child_begin, _1),
+ boost::bind(&TreeNode::child_end, _1)));
+ ensure(LLTreeIter_tests<PlainTree, LLLinkedIter<PlainTree> >
+ ("PlainTree",
+ boost::bind(&PlainTree::mParent, _1),
+ PlainTree_child_begin,
+ PlainTree_child_end));
+ }
+
+ template<> template<>
+ void iter_object::test<4>()
+ {
+// set_test_name("getRootRange() tests");
+ // This test function illustrates the looping techniques described in the
+ // comments for the getRootRange() free function, the
+ // EnhancedTreeNode::root_range template and the
+ // EnhancedTreeNode::getRootRange() method. Obviously the BOOST_FOREACH()
+ // forms are more succinct.
+ TreeNodePtr tnroot(example_tree<TreeNode>());
+ TreeNodePtr tnB2b(get_B2b<TreeNode, TreeNode::child_iterator>
+ (tnroot, boost::bind(&TreeNode::child_begin, _1)));
+
+ std::string desc1("BOOST_FOREACH(TreeNodePr, getRootRange<LLTreeIter::UP>(tnB2b))");
+// std::cout << desc1 << "\n";
+ // Although we've commented out the output statement, ensure that the
+ // loop construct is still valid, as promised by the getRootRange()
+ // documentation.
+ BOOST_FOREACH(TreeNodePtr node, getRootRange<LLTreeIter::UP>(tnB2b))
+ {
+// std::cout << node->name() << '\n';
+ }
+ ensure(desc1,
+ verify(desc1, getRootRange<LLTreeIter::UP>(tnB2b), RootExpected<LLTreeIter::UP>()));
+
+ EnhancedTreeNodePtr etnroot(example_tree<EnhancedTreeNode>());
+ EnhancedTreeNodePtr etnB2b(get_B2b<EnhancedTreeNode, EnhancedTreeNode::child_iterator>
+ (etnroot, boost::bind(&EnhancedTreeNode::child_begin, _1)));
+
+// std::cout << "EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type range =\n"
+// << " etnB2b->getRootRange<LLTreeIter::DOWN>();\n"
+// << "for (EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type::iterator ri = range.begin();\n"
+// << " ri != range.end(); ++ri)\n";
+ EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type range =
+ etnB2b->getRootRange<LLTreeIter::DOWN>();
+ for (EnhancedTreeNode::root_range<LLTreeIter::DOWN>::type::iterator ri = range.begin();
+ ri != range.end(); ++ri)
+ {
+// std::cout << (*ri)->name() << '\n';
+ }
+
+ std::string desc2("BOOST_FOREACH(EnhancedTreeNodePtr node, etnB2b->getRootRange<LLTreeIter::UP>())");
+// std::cout << desc2 << '\n';
+ BOOST_FOREACH(EnhancedTreeNodePtr node, etnB2b->getRootRange<LLTreeIter::UP>())
+ {
+// std::cout << node->name() << '\n';
+ }
+ ensure(desc2,
+ verify(desc2, etnB2b->getRootRange<LLTreeIter::UP>(), RootExpected<LLTreeIter::UP>()));
+ }
+
+ template<> template<>
+ void iter_object::test<5>()
+ {
+// set_test_name("getWalkRange() tests");
+ // This test function doesn't illustrate the looping permutations for
+ // getWalkRange(); see getRootRange_tests() for such examples. This
+ // function simply verifies that they all work.
+
+ // TreeNode, using helper function
+ TreeNodePtr tnroot(example_tree<TreeNode>());
+ std::string desc_tnpre("getWalkRange<LLTreeIter::DFS_PRE>(tnroot)");
+ ensure(desc_tnpre,
+ verify(desc_tnpre,
+ getWalkRange<LLTreeIter::DFS_PRE>(tnroot),
+ WalkExpected<LLTreeIter::DFS_PRE>()));
+ std::string desc_tnpost("getWalkRange<LLTreeIter::DFS_POST>(tnroot)");
+ ensure(desc_tnpost,
+ verify(desc_tnpost,
+ getWalkRange<LLTreeIter::DFS_POST>(tnroot),
+ WalkExpected<LLTreeIter::DFS_POST>()));
+ std::string desc_tnb("getWalkRange<LLTreeIter::BFS>(tnroot)");
+ ensure(desc_tnb,
+ verify(desc_tnb,
+ getWalkRange<LLTreeIter::BFS>(tnroot),
+ WalkExpected<LLTreeIter::BFS>()));
+
+ // EnhancedTreeNode, using method
+ EnhancedTreeNodePtr etnroot(example_tree<EnhancedTreeNode>());
+ std::string desc_etnpre("etnroot->getWalkRange<LLTreeIter::DFS_PRE>()");
+ ensure(desc_etnpre,
+ verify(desc_etnpre,
+ etnroot->getWalkRange<LLTreeIter::DFS_PRE>(),
+ WalkExpected<LLTreeIter::DFS_PRE>()));
+ std::string desc_etnpost("etnroot->getWalkRange<LLTreeIter::DFS_POST>()");
+ ensure(desc_etnpost,
+ verify(desc_etnpost,
+ etnroot->getWalkRange<LLTreeIter::DFS_POST>(),
+ WalkExpected<LLTreeIter::DFS_POST>()));
+ std::string desc_etnb("etnroot->getWalkRange<LLTreeIter::BFS>()");
+ ensure(desc_etnb,
+ verify(desc_etnb,
+ etnroot->getWalkRange<LLTreeIter::BFS>(),
+ WalkExpected<LLTreeIter::BFS>()));
+
+ // PlainTree, using helper function
+ PlainTree* ptroot(example_tree<PlainTree>());
+ Cleanup<PlainTree*> cleanup(ptroot);
+ std::string desc_ptpre("getWalkRange<LLTreeIter::DFS_PRE>(ptroot)");
+ ensure(desc_ptpre,
+ verify(desc_ptpre,
+ getWalkRange<LLTreeIter::DFS_PRE>(ptroot),
+ WalkExpected<LLTreeIter::DFS_PRE>()));
+ std::string desc_ptpost("getWalkRange<LLTreeIter::DFS_POST>(ptroot)");
+ ensure(desc_ptpost,
+ verify(desc_ptpost,
+ getWalkRange<LLTreeIter::DFS_POST>(ptroot),
+ WalkExpected<LLTreeIter::DFS_POST>()));
+ std::string desc_ptb("getWalkRange<LLTreeIter::BFS>(ptroot)");
+ ensure(desc_ptb,
+ verify(desc_ptb,
+ getWalkRange<LLTreeIter::BFS>(ptroot),
+ WalkExpected<LLTreeIter::BFS>()));
+ }
+} // tut
diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp
new file mode 100644
index 0000000000..4e5ad0df58
--- /dev/null
+++ b/indra/llcommon/tests/lluri_test.cpp
@@ -0,0 +1,370 @@
+/**
+ * @file lluri_tut.cpp
+ * @brief LLURI unit tests
+ * @date September 2006
+ *
+ * $LicenseInfo:firstyear=2006&license=viewergpl$
+ *
+ * Copyright (c) 2006-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../llsd.h"
+#include "../lluri.h"
+
+#include "../test/lltut.h"
+
+namespace tut
+{
+ struct URITestData {
+ void checkParts(const LLURI& u,
+ const char* expectedScheme,
+ const char* expectedOpaque,
+ const char* expectedAuthority,
+ const char* expectedPath,
+ const char* expectedQuery = "")
+ {
+ ensure_equals("scheme", u.scheme(), expectedScheme);
+ ensure_equals("opaque", u.opaque(), expectedOpaque);
+ ensure_equals("authority", u.authority(), expectedAuthority);
+ ensure_equals("path", u.path(), expectedPath);
+ ensure_equals("query", u.query(), expectedQuery);
+ }
+
+ void escapeRoundTrip(const std::string& uri_raw_1)
+ {
+ std::string uri_esc_1(LLURI::escape(uri_raw_1));
+ std::string uri_raw_2(LLURI::unescape(uri_esc_1));
+ ensure_equals("escape/unescape raw", uri_raw_2, uri_raw_1);
+ std::string uri_esc_2(LLURI::escape(uri_raw_2));
+ ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1);
+ }
+ };
+
+ typedef test_group<URITestData> URITestGroup;
+ typedef URITestGroup::object URITestObject;
+
+ URITestGroup uriTestGroup("LLURI");
+
+ template<> template<>
+ void URITestObject::test<1>()
+ {
+ LLURI u("http://abc.com/def/ghi?x=37&y=hello");
+
+ ensure_equals("scheme", u.scheme(), "http");
+ ensure_equals("authority", u.authority(), "abc.com");
+ ensure_equals("path", u.path(), "/def/ghi");
+ ensure_equals("query", u.query(), "x=37&y=hello");
+
+ ensure_equals("host name", u.hostName(), "abc.com");
+ ensure_equals("host port", u.hostPort(), 80);
+
+ LLSD query = u.queryMap();
+ ensure_equals("query x", query["x"].asInteger(), 37);
+ ensure_equals("query y", query["y"].asString(), "hello");
+
+ query = LLURI::queryMap("x=22.23&y=https://lindenlab.com/");
+ ensure_equals("query x", query["x"].asReal(), 22.23);
+ ensure_equals("query y", query["y"].asURI().asString(), "https://lindenlab.com/");
+ }
+
+ template<> template<>
+ void URITestObject::test<2>()
+ {
+ // empty string
+ checkParts(LLURI(""), "", "", "", "");
+ }
+
+ template<> template<>
+ void URITestObject::test<3>()
+ {
+ // no scheme
+ checkParts(LLURI("foo"), "", "foo", "", "");
+ checkParts(LLURI("foo%3A"), "", "foo:", "", "");
+ }
+
+ template<> template<>
+ void URITestObject::test<4>()
+ {
+ // scheme w/o paths
+ checkParts(LLURI("mailto:zero@ll.com"),
+ "mailto", "zero@ll.com", "", "");
+ checkParts(LLURI("silly://abc/def?foo"),
+ "silly", "//abc/def?foo", "", "");
+ }
+
+ template<> template<>
+ void URITestObject::test<5>()
+ {
+ // authority section
+ checkParts(LLURI("http:///"),
+ "http", "///", "", "/");
+
+ checkParts(LLURI("http://abc"),
+ "http", "//abc", "abc", "");
+
+ checkParts(LLURI("http://a%2Fb/cd"),
+ "http", "//a/b/cd", "a/b", "/cd");
+
+ checkParts(LLURI("http://host?"),
+ "http", "//host?", "host", "");
+ }
+
+ template<> template<>
+ void URITestObject::test<6>()
+ {
+ // path section
+ checkParts(LLURI("http://host/a/b/"),
+ "http", "//host/a/b/", "host", "/a/b/");
+
+ checkParts(LLURI("http://host/a%3Fb/"),
+ "http", "//host/a?b/", "host", "/a?b/");
+
+ checkParts(LLURI("http://host/a:b/"),
+ "http", "//host/a:b/", "host", "/a:b/");
+ }
+
+ template<> template<>
+ void URITestObject::test<7>()
+ {
+ // query string
+ checkParts(LLURI("http://host/?"),
+ "http", "//host/?", "host", "/", "");
+
+ checkParts(LLURI("http://host/?x"),
+ "http", "//host/?x", "host", "/", "x");
+
+ checkParts(LLURI("http://host/??"),
+ "http", "//host/??", "host", "/", "?");
+
+ checkParts(LLURI("http://host/?%3F"),
+ "http", "//host/??", "host", "/", "?");
+ }
+
+ template<> template<>
+ void URITestObject::test<8>()
+ {
+ LLSD path;
+ path.append("x");
+ path.append("123");
+ checkParts(LLURI::buildHTTP("host", path),
+ "http", "//host/x/123", "host", "/x/123");
+
+ LLSD query;
+ query["123"] = "12";
+ query["abcd"] = "abc";
+ checkParts(LLURI::buildHTTP("host", path, query),
+ "http", "//host/x/123?123=12&abcd=abc",
+ "host", "/x/123", "123=12&abcd=abc");
+ }
+
+ template<> template<>
+ void URITestObject::test<9>()
+ {
+ // test unescaped path components
+ LLSD path;
+ path.append("x@*//*$&^");
+ path.append("123");
+ checkParts(LLURI::buildHTTP("host", path),
+ "http", "//host/x@*//*$&^/123", "host", "/x@*//*$&^/123");
+ }
+
+ template<> template<>
+ void URITestObject::test<10>()
+ {
+ // test unescaped query components
+ LLSD path;
+ path.append("x");
+ path.append("123");
+ LLSD query;
+ query["123"] = "?&*#//";
+ query["**@&?//"] = "abc";
+ checkParts(LLURI::buildHTTP("host", path, query),
+ "http", "//host/x/123?**@&?//=abc&123=?&*#//",
+ "host", "/x/123", "**@&?//=abc&123=?&*#//");
+ }
+
+ template<> template<>
+ void URITestObject::test<11>()
+ {
+ // test unescaped host components
+ LLSD path;
+ path.append("x");
+ path.append("123");
+ LLSD query;
+ query["123"] = "12";
+ query["abcd"] = "abc";
+ checkParts(LLURI::buildHTTP("hi123*33--}{:portstuffs", path, query),
+ "http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc",
+ "hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc");
+ }
+
+ template<> template<>
+ void URITestObject::test<12>()
+ {
+ // test funky host_port values that are actually prefixes
+
+ checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()),
+ "http", "//example.com:8080",
+ "example.com:8080", "");
+
+ checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()),
+ "http", "//example.com:8080/",
+ "example.com:8080", "/");
+
+ checkParts(LLURI::buildHTTP("http://example.com:8080/a/b", LLSD()),
+ "http", "//example.com:8080/a/b",
+ "example.com:8080", "/a/b");
+ }
+
+ template<> template<>
+ void URITestObject::test<13>()
+ {
+ const std::string unreserved =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-._~";
+ // test escape
+ ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67");
+ ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40");
+ ensure_equals("escaping as query variable",
+ LLURI::escape("http://10.0.1.4:12032/agent/god/agent-id/map/layer/?resume=http://station3.ll.com:12032/agent/203ad6df-b522-491d-ba48-4e24eb57aeff/send-postcard", unreserved + ":@!$'()*+,="),
+ "http:%2F%2F10.0.1.4:12032%2Fagent%2Fgod%2Fagent-id%2Fmap%2Flayer%2F%3Fresume=http:%2F%2Fstation3.ll.com:12032%2Fagent%2F203ad6df-b522-491d-ba48-4e24eb57aeff%2Fsend-postcard");
+ // French cedilla (C with squiggle, like in the word Francais) is UTF-8 C3 A7
+
+#if LL_WINDOWS
+#pragma warning(disable: 4309)
+#endif
+
+ std::string cedilla;
+ cedilla.push_back( (char)0xC3 );
+ cedilla.push_back( (char)0xA7 );
+ ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7");
+ }
+
+
+ template<> template<>
+ void URITestObject::test<14>()
+ {
+ // make sure escape and unescape of empty strings return empty
+ // strings.
+ std::string uri_esc(LLURI::escape(""));
+ ensure("escape string empty", uri_esc.empty());
+ std::string uri_raw(LLURI::unescape(""));
+ ensure("unescape string empty", uri_raw.empty());
+ }
+
+ template<> template<>
+ void URITestObject::test<15>()
+ {
+ // do some round-trip tests
+ escapeRoundTrip("http://secondlife.com");
+ escapeRoundTrip("http://secondlife.com/url with spaces");
+ escapeRoundTrip("http://bad[domain]name.com/");
+ escapeRoundTrip("ftp://bill.gates@ms/micro$oft.com/c:\\autoexec.bat");
+ escapeRoundTrip("");
+ }
+
+ template<> template<>
+ void URITestObject::test<16>()
+ {
+ // Test the default escaping
+ // yes -- this mangles the url. This is expected behavior
+ std::string simple("http://secondlife.com");
+ ensure_equals(
+ "simple http",
+ LLURI::escape(simple),
+ "http%3A%2F%2Fsecondlife.com");
+ ensure_equals(
+ "needs escape",
+ LLURI::escape("http://get.secondlife.com/windows viewer"),
+ "http%3A%2F%2Fget.secondlife.com%2Fwindows%20viewer");
+ }
+
+ template<> template<>
+ void URITestObject::test<17>()
+ {
+ // do some round-trip tests with very long strings.
+ escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six");
+ escapeRoundTrip(
+ "'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale"
+ "\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tf"
+ "aces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204"
+ "\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffff"
+ "ff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t"
+ "0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t"
+ "\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t"
+ "\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreat"
+ "or_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundra"
+ "dius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0"
+ "\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f"
+ "25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000"
+ "000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1"
+ ".57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemI"
+ "D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n");
+ }
+
+
+ template<> template<>
+ void URITestObject::test<18>()
+ {
+ LLURI u("secondlife:///app/login?first_name=Testert4&last_name=Tester&web_login_key=test");
+ // if secondlife is the scheme, LLURI should parse /app/login as path, with no authority
+ ensure_equals("scheme", u.scheme(), "secondlife");
+ ensure_equals("authority", u.authority(), "");
+ ensure_equals("path", u.path(), "/app/login");
+ ensure_equals("pathmap", u.pathArray()[0].asString(), "app");
+ ensure_equals("pathmap", u.pathArray()[1].asString(), "login");
+ ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test");
+ ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester");
+
+ u = LLURI("secondlife://Da Boom/128/128/128");
+ // if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority
+ ensure_equals("scheme", u.scheme(), "secondlife");
+ ensure_equals("authority", u.authority(), "Da Boom");
+ ensure_equals("path", u.path(), "/128/128/128");
+ ensure_equals("pathmap", u.pathArray()[0].asString(), "128");
+ ensure_equals("pathmap", u.pathArray()[1].asString(), "128");
+ ensure_equals("pathmap", u.pathArray()[2].asString(), "128");
+ ensure_equals("query", u.query(), "");
+ }
+
+ template<> template<>
+ void URITestObject::test<19>()
+ {
+ // Parse about: schemes
+ LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78");
+ ensure_equals("scheme", u.scheme(), "about");
+ ensure_equals("authority", u.authority(), "");
+ ensure_equals("path", u.path(), "blank");
+ ensure_equals("pathmap", u.pathArray()[0].asString(), "blank");
+ ensure_equals("query", u.query(), "redirect-http-hack=secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak&region=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78");
+ ensure_equals("query map element", u.queryMap()["redirect-http-hack"].asString(), "secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak&region=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78");
+ }
+}
+
+