diff options
Diffstat (limited to 'indra/llcommon/tests')
| -rw-r--r-- | indra/llcommon/tests/bitpack_test.cpp | 125 | ||||
| -rw-r--r-- | indra/llcommon/tests/commonmisc_test.cpp | 677 | ||||
| -rw-r--r-- | indra/llcommon/tests/llbase64_test.cpp | 83 | ||||
| -rw-r--r-- | indra/llcommon/tests/lldate_test.cpp | 219 | ||||
| -rw-r--r-- | indra/llcommon/tests/llerror_test.cpp | 768 | ||||
| -rw-r--r-- | indra/llcommon/tests/llframetimer_test.cpp | 118 | ||||
| -rw-r--r-- | indra/llcommon/tests/llrand_test.cpp | 133 | ||||
| -rw-r--r-- | indra/llcommon/tests/llsdserialize_test.cpp | 1504 | ||||
| -rw-r--r-- | indra/llcommon/tests/llstring_test.cpp | 750 | ||||
| -rw-r--r-- | indra/llcommon/tests/lltreeiterators_test.cpp | 1222 | ||||
| -rw-r--r-- | indra/llcommon/tests/lluri_test.cpp | 370 | 
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(¬hing.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®ion=/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®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); +	} +} + +  | 
