summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/tests')
-rwxr-xr-xindra/llcommon/tests/StringVec.h37
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/bitpack_test.cpp55
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/commonmisc_test.cpp52
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/listener.h42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llallocator_heap_profile_test.cpp42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llallocator_test.cpp42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llbase64_test.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lldate_test.cpp38
-rw-r--r--indra/llcommon/tests/lldeadmantimer_test.cpp628
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lldependencies_test.cpp81
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llerror_test.cpp411
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lleventcoro_test.cpp79
-rwxr-xr-xindra/llcommon/tests/lleventdispatcher_test.cpp1324
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lleventfilter_test.cpp25
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llframetimer_test.cpp71
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llinstancetracker_test.cpp176
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lllazy_test.cpp21
-rwxr-xr-xindra/llcommon/tests/llleap_test.cpp671
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llmemtype_test.cpp42
-rwxr-xr-xindra/llcommon/tests/llprocess_test.cpp1262
-rwxr-xr-xindra/llcommon/tests/llprocessor_test.cpp61
-rw-r--r--indra/llcommon/tests/llprocinfo_test.cpp91
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llrand_test.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llsdserialize_test.cpp310
-rwxr-xr-xindra/llcommon/tests/llsingleton_test.cpp76
-rwxr-xr-xindra/llcommon/tests/llstreamqueue_test.cpp197
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llstring_test.cpp166
-rw-r--r--indra/llcommon/tests/lltrace_test.cpp142
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lltreeiterators_test.cpp38
-rw-r--r--indra/llcommon/tests/llunits_test.cpp388
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lluri_test.cpp130
-rw-r--r--indra/llcommon/tests/reflection_test.cpp226
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/stringize_test.cpp58
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/wrapllerrs.h167
34 files changed, 6288 insertions, 937 deletions
diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h
new file mode 100755
index 0000000000..a380b00a05
--- /dev/null
+++ b/indra/llcommon/tests/StringVec.h
@@ -0,0 +1,37 @@
+/**
+ * @file StringVec.h
+ * @author Nat Goodspeed
+ * @date 2012-02-24
+ * @brief Extend TUT ensure_equals() to handle std::vector<std::string>
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_STRINGVEC_H)
+#define LL_STRINGVEC_H
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+typedef std::vector<std::string> StringVec;
+
+std::ostream& operator<<(std::ostream& out, const StringVec& strings)
+{
+ out << '(';
+ StringVec::const_iterator begin(strings.begin()), end(strings.end());
+ if (begin != end)
+ {
+ out << '"' << *begin << '"';
+ while (++begin != end)
+ {
+ out << ", \"" << *begin << '"';
+ }
+ }
+ out << ')';
+ return out;
+}
+
+#endif /* ! defined(LL_STRINGVEC_H) */
diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp
index 09fd037f02..9bfd567068 100644..100755
--- a/indra/llcommon/tests/bitpack_test.cpp
+++ b/indra/llcommon/tests/bitpack_test.cpp
@@ -4,37 +4,31 @@
* @date 2007-02
* @brief llstreamtools test cases.
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * 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
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
-#include "../bitpack.h"
+#include "../llbitpack.h"
#include "../test/lltut.h"
@@ -46,7 +40,7 @@ namespace tut
};
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");
+ tut::bit_pack_t tut_bit_pack("LLBitPack");
// pack -> unpack
template<> template<>
@@ -77,7 +71,6 @@ namespace tut
U8 packbuffer[255];
U8 unpackbuffer[255];
int pack_bufsize = 0;
- int unpack_bufsize = 0;
LLBitPack bitpack(packbuffer, 255);
@@ -87,19 +80,19 @@ namespace tut
pack_bufsize = bitpack.flushBitPack();
LLBitPack bitunpack(packbuffer, pack_bufsize*8);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 0", unpackbuffer[0] == (U8) str[0]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 1", unpackbuffer[0] == (U8) str[1]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 2", unpackbuffer[0] == (U8) str[2]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 3", unpackbuffer[0] == (U8) str[3]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 4", unpackbuffer[0] == (U8) str[4]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]);
- unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, 8*4); // Life
+ bitunpack.bitUnpack(unpackbuffer, 8*4); // Life
ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4);
}
diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp
index ca27fe9b23..4b3e07fa75 100644..100755
--- a/indra/llcommon/tests/commonmisc_test.cpp
+++ b/indra/llcommon/tests/commonmisc_test.cpp
@@ -4,31 +4,25 @@
* @date 2005-10-12
* @brief Common templates for test framework
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -65,7 +59,7 @@ namespace tut
};
typedef test_group<sd_data> sd_test;
typedef sd_test::object sd_object;
- tut::sd_test sd("llsd");
+ tut::sd_test sd("LLSD");
template<> template<>
void sd_object::test<1>()
@@ -345,7 +339,7 @@ namespace tut
/*
if(actual != expected)
{
- llwarns << "iteration " << i << llendl;
+ LL_WARNS() << "iteration " << i << LL_ENDL;
std::ostringstream e_str;
std::string::iterator iter = expected.begin();
std::string::iterator end = expected.end();
@@ -355,8 +349,8 @@ namespace tut
}
e_str << std::endl;
llsd_serialize_string(e_str, expected);
- llwarns << "expected size: " << expected.size() << llendl;
- llwarns << "expected: " << e_str.str() << llendl;
+ LL_WARNS() << "expected size: " << expected.size() << LL_ENDL;
+ LL_WARNS() << "expected: " << e_str.str() << LL_ENDL;
std::ostringstream a_str;
iter = actual.begin();
@@ -367,8 +361,8 @@ namespace tut
}
a_str << std::endl;
llsd_serialize_string(a_str, actual);
- llwarns << "actual size: " << actual.size() << llendl;
- llwarns << "actual: " << a_str.str() << llendl;
+ LL_WARNS() << "actual size: " << actual.size() << LL_ENDL;
+ LL_WARNS() << "actual: " << a_str.str() << LL_ENDL;
}
*/
ensure_equals("string value", actual, expected);
@@ -456,7 +450,7 @@ namespace tut
};
typedef test_group<mem_data> mem_test;
typedef mem_test::object mem_object;
- tut::mem_test mem_stream("memory_stream");
+ tut::mem_test mem_stream("LLMemoryStream");
template<> template<>
void mem_object::test<1>()
@@ -649,7 +643,7 @@ namespace tut
};
typedef test_group<hash_data> hash_test;
typedef hash_test::object hash_object;
- tut::hash_test hash_tester("hash_test");
+ tut::hash_test hash_tester("LLHash");
template<> template<>
void hash_object::test<1>()
diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h
index fa12f944ef..9c5c18a150 100644..100755
--- a/indra/llcommon/tests/listener.h
+++ b/indra/llcommon/tests/listener.h
@@ -4,8 +4,25 @@
* @date 2009-03-06
* @brief Useful for tests of the LLEventPump family of classes
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -13,6 +30,8 @@
#define LL_LISTENER_H
#include "llsd.h"
+#include "llevents.h"
+#include "tests/StringVec.h"
#include <iostream>
/*****************************************************************************
@@ -116,24 +135,7 @@ struct Collect
return false;
}
void clear() { result.clear(); }
- typedef std::vector<std::string> StringList;
- StringList result;
+ StringVec result;
};
-std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings)
-{
- out << '(';
- Collect::StringList::const_iterator begin(strings.begin()), end(strings.end());
- if (begin != end)
- {
- out << '"' << *begin << '"';
- while (++begin != end)
- {
- out << ", \"" << *begin << '"';
- }
- }
- out << ')';
- return out;
-}
-
#endif /* ! defined(LL_LISTENER_H) */
diff --git a/indra/llcommon/tests/llallocator_heap_profile_test.cpp b/indra/llcommon/tests/llallocator_heap_profile_test.cpp
index 7369fdc8bc..44a9705803 100644..100755
--- a/indra/llcommon/tests/llallocator_heap_profile_test.cpp
+++ b/indra/llcommon/tests/llallocator_heap_profile_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-02-
* @brief Test for llallocator_heap_profile.cpp.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* 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.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llallocator_test.cpp b/indra/llcommon/tests/llallocator_test.cpp
index 9db95f4273..4e62eaee67 100644..100755
--- a/indra/llcommon/tests/llallocator_test.cpp
+++ b/indra/llcommon/tests/llallocator_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-02-
* @brief Test for llallocator.cpp.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* 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.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llbase64_test.cpp b/indra/llcommon/tests/llbase64_test.cpp
index 6009788b22..d0394150fa 100644..100755
--- a/indra/llcommon/tests/llbase64_test.cpp
+++ b/indra/llcommon/tests/llbase64_test.cpp
@@ -3,31 +3,25 @@
* @author James Cook
* @date 2007-02-04
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -47,7 +41,7 @@ namespace tut
};
typedef test_group<base64_data> base64_test;
typedef base64_test::object base64_object;
- tut::base64_test base64("base64");
+ tut::base64_test base64("LLBase64");
template<> template<>
void base64_object::test<1>()
diff --git a/indra/llcommon/tests/lldate_test.cpp b/indra/llcommon/tests/lldate_test.cpp
index c31259227a..7c95ccb91f 100644..100755
--- a/indra/llcommon/tests/lldate_test.cpp
+++ b/indra/llcommon/tests/lldate_test.cpp
@@ -4,31 +4,25 @@
* @date 2007-02
* @brief LLDate test cases.
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -77,7 +71,7 @@ namespace tut
};
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");
+ tut::date_test_t tut_date_test("LLDate");
/* format validation */
template<> template<>
diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
new file mode 100644
index 0000000000..23167762c3
--- /dev/null
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -0,0 +1,628 @@
+/**
+ * @file lldeadmantimer_test.cpp
+ * @brief Tests for the LLDeadmanTimer class.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../lldeadmantimer.h"
+#include "../llsd.h"
+#include "../lltimer.h"
+
+#include "../test/lltut.h"
+
+// Convert between floating point time deltas and U64 time deltas.
+// Reflects an implementation detail inside lldeadmantimer.cpp
+
+static LLDeadmanTimer::time_type float_time_to_u64(F64 delta)
+{
+ return LLDeadmanTimer::time_type(delta * get_timer_info().mClockFrequency);
+}
+
+static F64 u64_time_to_float(LLDeadmanTimer::time_type delta)
+{
+ return delta * get_timer_info().mClockFrequencyInv;
+}
+
+
+namespace tut
+{
+
+struct deadmantimer_test
+{
+ deadmantimer_test()
+ {
+ // LLTimer internals updating
+ get_timer_info().update();
+ }
+};
+
+typedef test_group<deadmantimer_test> deadmantimer_group_t;
+typedef deadmantimer_group_t::object deadmantimer_object_t;
+tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
+
+// Basic construction test and isExpired() call
+template<> template<>
+void deadmantimer_object_t::test<1>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8));
+ }
+
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+ ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// Construct with zero horizon - not useful generally but will be useful in testing
+template<> template<>
+void deadmantimer_object_t::test<2>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(0.0, false); // Zero is pre-expired
+
+ ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()",
+ timer.isExpired(0, started, stopped, count), false);
+ }
+
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(0.0, true); // Zero is pre-expired
+
+ ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+ }
+}
+
+
+// "pre-expired" timer - starting a timer with a 0.0 horizon will result in
+// expiration on first test.
+template<> template<>
+void deadmantimer_object_t::test<3>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(0.0, false);
+
+ timer.start(0);
+ ensure_equals("WOCM isExpired() returns true with 0.0 horizon time",
+ timer.isExpired(0, started, stopped, count), true);
+ ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(0.0, true);
+
+ timer.start(0);
+ ensure_equals("WCM isExpired() returns true with 0.0 horizon time",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
+ }
+}
+
+
+// "pre-expired" timer - bell rings are ignored as we're already expired.
+template<> template<>
+void deadmantimer_object_t::test<4>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(0.0, false);
+
+ timer.start(0);
+ timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
+ ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring",
+ timer.isExpired(0, started, stopped, count), true);
+ ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(0.0, true);
+
+ timer.start(0);
+ timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
+ ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+ }
+}
+
+
+// start(0) test - unexpired timer reports unexpired
+template<> template<>
+void deadmantimer_object_t::test<5>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ timer.start(0);
+ ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time",
+ timer.isExpired(0, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8));
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ timer.start(0);
+ ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+ ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// start() test - start in the past but not beyond 1 horizon
+template<> template<>
+void deadmantimer_object_t::test<6>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
+ timer.start(the_past);
+ ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
+ timer.isExpired(now, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8));
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
+ timer.start(the_past);
+ ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// start() test - start in the past but well beyond 1 horizon
+template<> template<>
+void deadmantimer_object_t::test<7>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now,started, stopped, count), true);
+ ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
+ }
+}
+
+
+// isExpired() test - results are read-once. Probes after first true are false.
+template<> template<>
+void deadmantimer_object_t::test<8>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now, started, stopped, count), true);
+
+ started = 42.0;
+ stopped = 97.0;
+ count = U64L(8);
+ ensure_equals("WOCM t8 - second isExpired() returns false after true",
+ timer.isExpired(now, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+
+ started = 42.0;
+ stopped = 97.0;
+ count = U64L(8);
+ user_cpu = 29000;
+ sys_cpu = 57000;
+ ensure_equals("WCM t8 - second isExpired() returns false after true",
+ timer.isExpired(now, started, stopped, count), false);
+ ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// ringBell() test - see that we can keep a timer from expiring
+template<> template<>
+void deadmantimer_object_t::test<9>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(5.0, false);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count), true);
+ ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(5.0, true);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ }
+}
+
+
+// restart after expiration test - verify that restarts behave well
+template<> template<>
+void deadmantimer_object_t::test<10>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(5.0, false);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count), true);
+ ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false);
+
+ // Jump forward and restart
+ now += float_time_to_u64(1.0);
+ real_start = u64_time_to_float(now);
+ timer.start(now);
+
+ // Run a modified bell ring sequence
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
+ timer.isExpired(now, started, stopped, count), false);
+ last_good_ring = u64_time_to_float(now);
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump",
+ timer.isExpired(now, started, stopped, count), true);
+ ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8));
+ ensure_equals("WOCM t10 - single read only - 2nd start",
+ timer.isExpired(now, started, stopped, count), false);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+
+ LLDeadmanTimer timer(5.0, true);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+
+ // Jump forward and restart
+ now += float_time_to_u64(1.0);
+ real_start = u64_time_to_float(now);
+ timer.start(now);
+
+ // Run a modified bell ring sequence
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ last_good_ring = u64_time_to_float(now);
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8));
+ ensure_equals("WCM t10 - single read only - 2nd start",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ }
+}
+
+
+
+} // end namespace tut
diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp
index f3c25de8b5..b5e189a465 100644..100755
--- a/indra/llcommon/tests/lldependencies_test.cpp
+++ b/indra/llcommon/tests/lldependencies_test.cpp
@@ -4,8 +4,25 @@
* @date 2008-09-17
* @brief Test of lldependencies.h
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -20,29 +37,14 @@
// associated header
#include "../lldependencies.h"
// other Linden headers
-#include "../test/lltut.h"
-
-using boost::assign::list_of;
#if LL_WINDOWS
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
#endif
-typedef LLDependencies<> StringDeps;
-typedef StringDeps::KeyList StringList;
-
-// We use the very cool boost::assign::list_of() construct to specify vectors
-// of strings inline. For reasons on which I'm not entirely clear, though, it
-// needs a helper function. You can use list_of() to construct an implicit
-// StringList (std::vector<std::string>) by conversion, e.g. for a function
-// parameter -- but if you simply write StringList(list_of("etc.")), you get
-// ambiguity errors. Shrug!
-template<typename CONTAINER>
-CONTAINER make(const CONTAINER& data)
-{
- return data;
-}
-
+/*****************************************************************************
+* Display helpers: must be defined BEFORE lltut.h!
+*****************************************************************************/
// Display an arbitary value as itself...
template<typename T>
std::ostream& display(std::ostream& out, const T& value)
@@ -96,6 +98,31 @@ std::ostream& operator<<(std::ostream& out, const std::set<ENTRY>& set)
return out;
}
+/*****************************************************************************
+* Now we can #include lltut.h
+*****************************************************************************/
+#include "../test/lltut.h"
+
+/*****************************************************************************
+* Other helpers
+*****************************************************************************/
+using boost::assign::list_of;
+
+typedef LLDependencies<> StringDeps;
+typedef StringDeps::KeyList StringList;
+
+// We use the very cool boost::assign::list_of() construct to specify vectors
+// of strings inline. For reasons on which I'm not entirely clear, though, it
+// needs a helper function. You can use list_of() to construct an implicit
+// StringList (std::vector<std::string>) by conversion, e.g. for a function
+// parameter -- but if you simply write StringList(list_of("etc.")), you get
+// ambiguity errors. Shrug!
+template<typename CONTAINER>
+CONTAINER make(const CONTAINER& data)
+{
+ return data;
+}
+
const std::string& extract_key(const LLDependencies<>::value_type& entry)
{
return entry.first;
@@ -128,7 +155,7 @@ namespace tut
};
typedef test_group<deps_data> deps_group;
typedef deps_group::object deps_object;
- tut::deps_group depsgr("lldependencies");
+ tut::deps_group depsgr("LLDependencies");
template<> template<>
void deps_object::test<1>()
@@ -241,10 +268,10 @@ namespace tut
++const_iterator;
ensure_equals(const_iterator->first, "def");
ensure_equals(const_iterator->second, 2);
- NameIndexDeps::node_range node_range(nideps.get_node_range());
- ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1)(2)(3)));
- *node_range.begin() = 0;
- *node_range.begin() = 1;
+// NameIndexDeps::node_range node_range(nideps.get_node_range());
+// ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1)(2)(3)));
+// *node_range.begin() = 0;
+// *node_range.begin() = 1;
NameIndexDeps::const_node_range const_node_range(const_nideps.get_node_range());
ensure_equals(instance_from_range<std::vector<int> >(const_node_range), make< std::vector<int> >(list_of(1)(2)(3)));
NameIndexDeps::const_key_range const_key_range(const_nideps.get_key_range());
@@ -261,8 +288,8 @@ namespace tut
def);
ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_range().begin())),
def);
- ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_node_range().begin())),
- def);
+// ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_node_range().begin())),
+// def);
ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_node_range().begin())),
def);
ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_key_range().begin())),
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index 6785d0cf17..f51279e817 100644..100755
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -1,33 +1,27 @@
-/**
+/**
* @file llerror_test.cpp
* @date December 2006
* @brief error unit tests
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* 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.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,9 +38,12 @@
namespace
{
+#ifdef __clang__
+# pragma clang diagnostic ignored "-Wunused-function"
+#endif
void test_that_error_h_includes_enough_things_to_compile_a_message()
{
- llinfos << "!" << llendl;
+ LL_INFOS() << "!" << LL_ENDL;
}
}
@@ -54,24 +51,26 @@ namespace
{
static bool fatalWasCalled;
void fatalCall(const std::string&) { fatalWasCalled = true; }
-
+}
+
+namespace tut
+{
class TestRecorder : public LLError::Recorder
{
public:
- TestRecorder() : mWantsTime(false) { }
- ~TestRecorder() { LLError::removeRecorder(this); }
-
- void recordMessage(LLError::ELevel level,
- const std::string& message)
+ TestRecorder() { mWantsTime = false; }
+ virtual ~TestRecorder() { }
+
+ virtual 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)
{
@@ -81,41 +80,57 @@ namespace
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()
+ LLError::RecorderPtr mRecorder;
+ LLError::SettingsStoragePtr mPriorErrorSettings;
+
+ ErrorTestData():
+ mRecorder(new TestRecorder())
{
fatalWasCalled = false;
-
+
mPriorErrorSettings = LLError::saveAndResetSettings();
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
LLError::setFatalFunction(fatalCall);
- LLError::addRecorder(&mRecorder);
+ LLError::addRecorder(mRecorder);
}
-
+
~ErrorTestData()
{
- LLError::removeRecorder(&mRecorder);
+ LLError::removeRecorder(mRecorder);
LLError::restoreSettings(mPriorErrorSettings);
}
-
+
+ int countMessages()
+ {
+ return boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->countMessages();
+ }
+
+ void clearMessages()
+ {
+ boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->clearMessages();
+ }
+
+ void setWantsTime(bool t)
+ {
+ boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->setWantsTime(t);
+ }
+
+ std::string message(int n)
+ {
+ return boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->message(n);
+ }
+
void ensure_message_count(int expectedCount)
{
- ensure_equals("message count", mRecorder.countMessages(), expectedCount);
+ ensure_equals("message count", countMessages(), expectedCount);
}
void ensure_message_contains(int n, const std::string& expectedText)
@@ -123,7 +138,7 @@ namespace tut
std::ostringstream test_name;
test_name << "testing message " << n;
- ensure_contains(test_name.str(), mRecorder.message(n), expectedText);
+ ensure_contains(test_name.str(), message(n), expectedText);
}
void ensure_message_does_not_contain(int n, const std::string& expectedText)
@@ -131,22 +146,22 @@ namespace tut
std::ostringstream test_name;
test_name << "testing message " << n;
- ensure_does_not_contain(test_name.str(), mRecorder.message(n), expectedText);
+ ensure_does_not_contain(test_name.str(), 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;
-
+ LL_INFOS() << "test" << LL_ENDL;
+ LL_INFOS() << "bob" << LL_ENDL;
+
ensure_message_contains(0, "test");
ensure_message_contains(1, "bob");
}
@@ -156,16 +171,16 @@ namespace
{
void writeSome()
{
- lldebugs << "one" << llendl;
- llinfos << "two" << llendl;
- llwarns << "three" << llendl;
- llerrs << "four" << llendl;
- // fatal messages write out and addtional "error" message
+ LL_DEBUGS() << "one" << LL_ENDL;
+ LL_INFOS() << "two" << LL_ENDL;
+ LL_WARNS() << "three" << LL_ENDL;
+ // fatal messages write out an additional "error" message
+ LL_ERRS() << "four" << LL_ENDL;
}
};
namespace tut
-{
+{
template<> template<>
void ErrorTestObject::test<2>()
// messages are filtered based on default level
@@ -178,7 +193,7 @@ namespace tut
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");
@@ -186,20 +201,20 @@ namespace tut
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);
@@ -224,14 +239,14 @@ namespace tut
{
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"
@@ -240,12 +255,12 @@ namespace tut
#endif
;
std::string someAbbreviation = LLError::abbreviateFile(someFile);
-
+
ensure_equals("non-indra file abbreviation",
someAbbreviation, someFile);
}
}
-
+
namespace
{
std::string locationString(int line)
@@ -253,44 +268,45 @@ namespace
std::ostringstream location;
location << LLError::abbreviateFile(__FILE__)
<< "(" << line << ") : ";
-
+
return location.str();
}
-
+
std::string writeReturningLocation()
{
- llinfos << "apple" << llendl; int this_line = __LINE__;
+ LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__;
return locationString(this_line);
}
-
- std::string writeReturningLocationAndFunction()
+
+ void writeReturningLocationAndFunction(std::string& location, std::string& function)
{
- llinfos << "apple" << llendl; int this_line = __LINE__;
- return locationString(this_line) + __FUNCTION__;
+ LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__;
+ location = locationString(this_line);
+ function = __FUNCTION__;
}
-
+
std::string errorReturningLocation()
{
- llerrs << "die" << llendl; int this_line = __LINE__;
+ LL_ERRS() << "die" << LL_ENDL; 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);
@@ -303,16 +319,16 @@ namespace tut
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;
+ LL_INFOS() << (id ? "logFromGlobal: " : "") << "hi" << LL_ENDL;
return "logFromGlobal";
}
static std::string logFromStatic(bool id)
{
- llinfos << (id ? "logFromStatic: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "logFromStatic: " : "") << "hi" << LL_ENDL;
return "logFromStatic";
}
@@ -320,7 +336,7 @@ namespace
{
std::string logFromAnon(bool id)
{
- llinfos << (id ? "logFromAnon: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "logFromAnon: " : "") << "hi" << LL_ENDL;
return "logFromAnon";
}
}
@@ -328,7 +344,7 @@ namespace
namespace Foo {
std::string logFromNamespace(bool id)
{
- llinfos << (id ? "Foo::logFromNamespace: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "Foo::logFromNamespace: " : "") << "hi" << LL_ENDL;
//return "Foo::logFromNamespace";
// there is no standard way to get the namespace name, hence
// we won't be testing for it
@@ -342,37 +358,35 @@ namespace
public:
std::string logFromMember(bool id)
{
- llinfos << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << LL_ENDL;
return "ClassWithNoLogType::logFromMember";
}
static std::string logFromStatic(bool id)
{
- llinfos << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << LL_ENDL;
return "ClassWithNoLogType::logFromStatic";
}
};
-
+
class ClassWithLogType {
LOG_CLASS(ClassWithLogType);
public:
std::string logFromMember(bool id)
{
- llinfos << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << LL_ENDL;
return "ClassWithLogType::logFromMember";
}
static std::string logFromStatic(bool id)
{
- llinfos << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << LL_ENDL;
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)
{
@@ -380,23 +394,23 @@ namespace
if (n1 == std::string::npos)
{
std::stringstream ss;
- ss << message << ": " << "expected to find a copy of " << expected
- << " in actual " << actual;
+ 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,
+ void testLogName(LLError::RecorderPtr recorder, LogFromFunction f,
const std::string& class_name = "")
{
- recorder.clearMessages();
+ boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->clearMessages();
std::string name = f(false);
f(true);
-
- std::string messageWithoutName = recorder.message(0);
- std::string messageWithName = recorder.message(1);
-
+
+ std::string messageWithoutName = boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(0);
+ std::string messageWithName = boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(1);
+
ensure_has(name + " logged without name",
messageWithoutName, name);
ensure_has(name + " logged with name",
@@ -422,9 +436,6 @@ namespace tut
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");
}
@@ -434,27 +445,22 @@ namespace
{
std::string innerLogger()
{
- llinfos << "inside" << llendl;
+ LL_INFOS() << "inside" << LL_ENDL;
return "moo";
}
-
+
std::string outerLogger()
{
- llinfos << "outside(" << innerLogger() << ")" << llendl;
+ LL_INFOS() << "outside(" << innerLogger() << ")" << LL_ENDL;
return "bar";
}
-
- void uberLogger()
- {
- llinfos << "uber(" << outerLogger() << "," << innerLogger() << ")" << llendl;
- }
-
+
class LogWhileLogging
{
public:
void print(std::ostream& out) const
{
- llinfos << "logging" << llendl;
+ LL_INFOS() << "logging" << LL_ENDL;
out << "baz";
}
};
@@ -465,13 +471,13 @@ namespace
void metaLogger()
{
LogWhileLogging l;
- llinfos << "meta(" << l << ")" << llendl;
+ LL_INFOS() << "meta(" << l << ")" << LL_ENDL;
}
-
+
}
namespace tut
-{
+{
template<> template<>
// handle nested logging
void ErrorTestObject::test<7>()
@@ -480,31 +486,24 @@ namespace tut
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);
+ ensure_message_contains(2, "logging");
+ ensure_message_contains(3, "meta(baz)");
+ ensure_message_count(4);
}
-
+
template<> template<>
- // special handling of llerrs calls
+ // special handling of LL_ERRS() 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);
}
}
@@ -515,83 +514,79 @@ namespace
{
return "1947-07-08T03:04:05Z";
}
-
+
void ufoSighting()
{
- llinfos << "ufo" << llendl;
+ LL_INFOS() << "ufo" << LL_ENDL;
}
}
namespace tut
-{
+{
template<> template<>
// time in output (for recorders that need it)
void ErrorTestObject::test<9>()
{
LLError::setTimeFunction(roswell);
- mRecorder.setWantsTime(false);
+ setWantsTime(false);
ufoSighting();
ensure_message_contains(0, "ufo");
ensure_message_does_not_contain(0, roswell());
-
- mRecorder.setWantsTime(true);
+
+ setWantsTime(true);
ufoSighting();
ensure_message_contains(1, "ufo");
ensure_message_contains(1, roswell());
}
-
+
template<> template<>
// output order
void ErrorTestObject::test<10>()
{
-#if LL_LINUX
- skip("Fails on Linux, see comments");
-// on Linux:
-// [error, 10] fail: 'order is time type location function message: expected
-// '1947-07-08T03:04:05Z INFO: llcommon/tests/llerror_test.cpp(268) :
-// writeReturningLocationAndFunction: apple' actual
-// '1947-07-08T03:04:05Z INFO: llcommon/tests/llerror_test.cpp(268) :
-// LLError::NoClassInfo::writeReturningLocationAndFunction: apple''
-#endif
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");
+ setWantsTime(true);
+ std::string location,
+ function;
+ writeReturningLocationAndFunction(location, function);
+
+ ensure_equals("order is location time type function message",
+ message(0),
+ location + roswell() + " INFO: " + function + ": apple");
}
template<> template<>
// multiple recorders
void ErrorTestObject::test<11>()
{
- TestRecorder altRecorder;
- LLError::addRecorder(&altRecorder);
-
- llinfos << "boo" << llendl;
+ LLError::RecorderPtr altRecorder(new TestRecorder());
+ LLError::addRecorder(altRecorder);
+
+ LL_INFOS() << "boo" << LL_ENDL;
ensure_message_contains(0, "boo");
- ensure_equals("alt recorder count", altRecorder.countMessages(), 1);
- ensure_contains("alt recorder message 0", altRecorder.message(0), "boo");
-
+ ensure_equals("alt recorder count", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 1);
+ ensure_contains("alt recorder message 0", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(0), "boo");
+
LLError::setTimeFunction(roswell);
- TestRecorder anotherRecorder;
- anotherRecorder.setWantsTime(true);
- LLError::addRecorder(&anotherRecorder);
-
- llinfos << "baz" << llendl;
+ LLError::RecorderPtr anotherRecorder(new TestRecorder());
+ boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->setWantsTime(true);
+ LLError::addRecorder(anotherRecorder);
+
+ LL_INFOS() << "baz" << LL_ENDL;
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);
+ ensure_equals("alt recorder count", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 2);
+ ensure_does_not_contain("alt recorder message 1", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(1), when);
+ ensure_equals("another recorder count", boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->countMessages(), 1);
+ ensure_contains("another recorder message 0", boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->message(0), when);
+
+ LLError::removeRecorder(altRecorder);
+ LLError::removeRecorder(anotherRecorder);
}
}
@@ -599,10 +594,10 @@ 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 doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; }
+ static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; }
+ static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; }
+ static void doError() { LL_ERRS() << "ate eels" << LL_ENDL; }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
@@ -610,10 +605,10 @@ 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 doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; }
+ static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; }
+ static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; }
+ static void doError() { LL_ERRS() << "big easy" << LL_ENDL; }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
@@ -625,10 +620,10 @@ namespace tut
{
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");
@@ -638,7 +633,7 @@ namespace tut
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>()
@@ -647,13 +642,13 @@ namespace tut
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
@@ -667,7 +662,7 @@ namespace tut
LLError::LEVEL_NONE);
LLError::setFunctionLevel("TestBeta::doError",
LLError::LEVEL_NONE);
-
+
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(0, "any idea");
@@ -675,7 +670,7 @@ namespace tut
ensure_message_contains(2, "bad word");
ensure_message_count(3);
}
-
+
template<> template<>
// proper cached, efficient lookup of filtering
void ErrorTestObject::test<15>()
@@ -705,7 +700,7 @@ namespace tut
ensure_message_count(2);
ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
}
-
+
template<> template<>
// configuration from LLSD
void ErrorTestObject::test<16>()
@@ -714,26 +709,26 @@ namespace tut
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");
@@ -741,13 +736,13 @@ namespace tut
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");
@@ -759,13 +754,13 @@ namespace tut
ensure_message_contains(8, "big easy");
ensure_message_count(9);
}
-}
+}
/* Tests left:
handling of classes without LOG_CLASS
- live update of filtering from file
-
+ live update of filtering from file
+
syslog recorder
file recorder
cerr/stderr recorder
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index 3a2cda7735..2096807e53 100644..100755
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-04-22
* @brief Test for coroutine.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -47,10 +64,10 @@
// Boost.Coroutine #include is the *first* #include of the platform header.
// That means that client code must generally #include Boost.Coroutine headers
// before anything else.
-#include <boost/coroutine/coroutine.hpp>
+#include <boost/dcoroutine/coroutine.hpp>
// Normally, lleventcoro.h obviates future.hpp. We only include this because
// we implement a "by hand" test of future functionality.
-#include <boost/coroutine/future.hpp>
+#include <boost/dcoroutine/future.hpp>
#include <boost/bind.hpp>
#include <boost/range.hpp>
@@ -61,6 +78,7 @@
#include "../test/lltut.h"
#include "llsd.h"
+#include "llsdutil.h"
#include "llevents.h"
#include "tests/wrapllerrs.h"
#include "stringize.h"
@@ -70,13 +88,12 @@
/*****************************************************************************
* from the banana.cpp example program borrowed for test<1>()
*****************************************************************************/
-namespace coroutines = boost::coroutines;
+namespace coroutines = boost::dcoroutines;
using coroutines::coroutine;
template<typename Iter>
bool match(Iter first, Iter last, std::string match) {
std::string::iterator i = match.begin();
- i != match.end();
for(; (first != last) && (i != match.end()); ++i) {
if (*first != *i)
return false;
@@ -91,7 +108,7 @@ match_substring(BidirectionalIterator begin,
BidirectionalIterator end,
std::string xmatch,
BOOST_DEDUCED_TYPENAME coroutine<BidirectionalIterator(void)>::self& self) {
- BidirectionalIterator begin_ = begin;
+//BidirectionalIterator begin_ = begin;
for(; begin != end; ++begin)
if(match(begin, end, xmatch)) {
self.yield(begin);
@@ -105,7 +122,7 @@ typedef coroutine<std::string::iterator(void)> match_coroutine_type;
* Test helpers
*****************************************************************************/
// I suspect this will be typical of coroutines used in Linden software
-typedef boost::coroutines::coroutine<void()> coroutine_type;
+typedef boost::dcoroutines::coroutine<void()> coroutine_type;
/// Simulate an event API whose response is immediate: sent on receipt of the
/// initial request, rather than after some delay. This is the case that
@@ -156,10 +173,10 @@ namespace tut
// ... do whatever preliminary stuff must happen ...
// declare the future
- boost::coroutines::future<LLSD> future(self);
+ boost::dcoroutines::future<LLSD> future(self);
// tell the future what to wait for
LLTempBoundListener connection(
- LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::coroutines::make_callback(future))));
+ LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future))));
ensure("Not yet", ! future);
// attempting to dereference ("resolve") the future causes the calling
// coroutine to wait for it
@@ -196,7 +213,7 @@ namespace tut
BEGIN
{
result = postAndWait(self,
- LLSD().insert("value", 17), // request event
+ LLSDMap("value", 17), // request event
immediateAPI.getPump(), // requestPump
"reply1", // replyPump
"reply"); // request["reply"] = name
@@ -209,7 +226,7 @@ namespace tut
BEGIN
{
LLEventWithID pair = ::postAndWait2(self,
- LLSD().insert("value", 18),
+ LLSDMap("value", 18),
immediateAPI.getPump(),
"reply2",
"error2",
@@ -227,7 +244,7 @@ namespace tut
BEGIN
{
LLEventWithID pair = ::postAndWait2(self,
- LLSD().insert("value", 18).insert("fail", LLSD()),
+ LLSDMap("value", 18)("fail", LLSD()),
immediateAPI.getPump(),
"reply2",
"error2",
@@ -256,7 +273,7 @@ namespace tut
BEGIN
{
LLCoroEventPump waiter;
- result = waiter.postAndWait(self, LLSD().insert("value", 17),
+ result = waiter.postAndWait(self, LLSDMap("value", 17),
immediateAPI.getPump(), "reply");
}
END
@@ -328,13 +345,13 @@ namespace tut
LLCoroEventPumps waiter;
replyName = waiter.getName0();
errorName = waiter.getName1();
- WrapLL_ERRS capture;
+ WrapLLErrs capture;
try
{
result = waiter.waitWithLog(self);
debug("no exception");
}
- catch (const WrapLL_ERRS::FatalException& e)
+ catch (const WrapLLErrs::FatalException& e)
{
debug(STRINGIZE("exception " << e.what()));
threw = e.what();
@@ -348,7 +365,7 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- LLEventWithID pair(waiter.postAndWait(self, LLSD().insert("value", 23),
+ LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23),
immediateAPI.getPump(), "reply", "error"));
result = pair.first;
which = pair.second;
@@ -362,7 +379,7 @@ namespace tut
{
LLCoroEventPumps waiter;
LLEventWithID pair(
- waiter.postAndWait(self, LLSD().insert("value", 23).insert("fail", LLSD()),
+ waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()),
immediateAPI.getPump(), "reply", "error"));
result = pair.first;
which = pair.second;
@@ -375,7 +392,7 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithException(self, LLSD().insert("value", 8),
+ result = waiter.postAndWaitWithException(self, LLSDMap("value", 8),
immediateAPI.getPump(), "reply", "error");
}
END
@@ -389,7 +406,7 @@ namespace tut
try
{
result = waiter.postAndWaitWithException(self,
- LLSD().insert("value", 9).insert("fail", LLSD()),
+ LLSDMap("value", 9)("fail", LLSD()),
immediateAPI.getPump(), "reply", "error");
debug("no exception");
}
@@ -407,7 +424,7 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithLog(self, LLSD().insert("value", 30),
+ result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30),
immediateAPI.getPump(), "reply", "error");
}
END
@@ -418,15 +435,15 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- WrapLL_ERRS capture;
+ WrapLLErrs capture;
try
{
result = waiter.postAndWaitWithLog(self,
- LLSD().insert("value", 31).insert("fail", LLSD()),
+ LLSDMap("value", 31)("fail", LLSD()),
immediateAPI.getPump(), "reply", "error");
debug("no exception");
}
- catch (const WrapLL_ERRS::FatalException& e)
+ catch (const WrapLLErrs::FatalException& e)
{
debug(STRINGIZE("exception " << e.what()));
threw = e.what();
@@ -779,4 +796,18 @@ namespace tut
ensure("no result", result.isUndefined());
ensure_contains("got error", threw, "32");
}
+}
+
+/*==========================================================================*|
+#include <boost/context/guarded_stack_allocator.hpp>
+
+namespace tut
+{
+ template<> template<>
+ void object::test<23>()
+ {
+ set_test_name("stacksize");
+ std::cout << "default_stacksize: " << boost::context::guarded_stack_allocator::default_stacksize() << '\n';
+ }
} // namespace tut
+|*==========================================================================*/
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
new file mode 100755
index 0000000000..5a4df81bf1
--- /dev/null
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -0,0 +1,1324 @@
+/**
+ * @file lleventdispatcher_test.cpp
+ * @author Nat Goodspeed
+ * @date 2011-01-20
+ * @brief Test for lleventdispatcher.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventdispatcher.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "llsd.h"
+#include "llsdutil.h"
+#include "stringize.h"
+#include "tests/wrapllerrs.h"
+
+#include <map>
+#include <string>
+#include <stdexcept>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/range.hpp>
+#include <boost/foreach.hpp>
+#define foreach BOOST_FOREACH
+
+#include <boost/lambda/lambda.hpp>
+
+#include <iostream>
+#include <iomanip>
+
+using boost::lambda::constant;
+using boost::lambda::constant_ref;
+using boost::lambda::var;
+
+using namespace llsd;
+
+/*****************************************************************************
+* Output control
+*****************************************************************************/
+#ifdef DEBUG_ON
+using std::cout;
+#else
+static std::ostringstream cout;
+#endif
+
+/*****************************************************************************
+* Example data, functions, classes
+*****************************************************************************/
+// We don't need a whole lot of different arbitrary-params methods, just (no |
+// (const LLSD&) | arbitrary) args (function | static method | non-static
+// method), where 'arbitrary' is (every LLSD datatype + (const char*)).
+// But we need to register each one under different names for the different
+// registration styles. Don't forget LLEventDispatcher subclass methods(const
+// LLSD&).
+
+// However, the number of target parameter conversions we want to try exceeds
+// boost::fusion::invoke()'s supported parameter-list size. Break out two
+// different lists.
+#define NPARAMSa bool b, int i, float f, double d, const char* cp
+#define NPARAMSb const std::string& s, const LLUUID& uuid, const LLDate& date, \
+ const LLURI& uri, const std::vector<U8>& bin
+#define NARGSa b, i, f, d, cp
+#define NARGSb s, uuid, date, uri, bin
+
+// For some registration methods we need methods on a subclass of
+// LLEventDispatcher. To simplify things, we'll use this Dispatcher subclass
+// for all our testing, including testing its own methods.
+class Dispatcher: public LLEventDispatcher
+{
+public:
+ Dispatcher(const std::string& name, const std::string& key):
+ LLEventDispatcher(name, key)
+ {}
+
+ // sensing member, mutable because we want to know when we've reached our
+ // const method too
+ mutable LLSD llsd;
+
+ void method1(const LLSD& obj) { llsd = obj; }
+ void cmethod1(const LLSD& obj) const { llsd = obj; }
+};
+
+// sensing vars, captured in a struct to make it convenient to clear them
+struct Vars
+{
+ LLSD llsd;
+ bool b;
+ int i;
+ float f;
+ double d;
+ // Capture param passed as char*. But merely storing a char* received from
+ // our caller, possibly the .c_str() from a concatenation expression,
+ // would be Bad: the pointer will be invalidated long before we can query
+ // it. We could allocate a new chunk of memory, copy the string data and
+ // point to that instead -- but hey, guess what, we already have a class
+ // that does that!
+ std::string cp;
+ std::string s;
+ LLUUID uuid;
+ LLDate date;
+ LLURI uri;
+ std::vector<U8> bin;
+
+ Vars():
+ // Only need to initialize the POD types, the rest should take care of
+ // default-constructing themselves.
+ b(false),
+ i(0),
+ f(0),
+ d(0)
+ {}
+
+ // Detect any non-default values for convenient testing
+ LLSD inspect() const
+ {
+ LLSD result;
+
+ if (llsd.isDefined())
+ result["llsd"] = llsd;
+ if (b)
+ result["b"] = b;
+ if (i)
+ result["i"] = i;
+ if (f)
+ result["f"] = f;
+ if (d)
+ result["d"] = d;
+ if (! cp.empty())
+ result["cp"] = cp;
+ if (! s.empty())
+ result["s"] = s;
+ if (uuid != LLUUID())
+ result["uuid"] = uuid;
+ if (date != LLDate())
+ result["date"] = date;
+ if (uri != LLURI())
+ result["uri"] = uri;
+ if (! bin.empty())
+ result["bin"] = bin;
+
+ return result;
+ }
+
+ /*------------- no-args (non-const, const, static) methods -------------*/
+ void method0()
+ {
+ cout << "method0()\n";
+ i = 17;
+ }
+
+ void cmethod0() const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->method0();
+ }
+
+ static void smethod0();
+
+ /*------------ Callable (non-const, const, static) methods -------------*/
+ void method1(const LLSD& obj)
+ {
+ cout << "method1(" << obj << ")\n";
+ llsd = obj;
+ }
+
+ void cmethod1(const LLSD& obj) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->method1(obj);
+ }
+
+ static void smethod1(const LLSD& obj);
+
+ /*-------- Arbitrary-params (non-const, const, static) methods ---------*/
+ void methodna(NPARAMSa)
+ {
+ // Because our const char* param cp might be NULL, and because we
+ // intend to capture the value in a std::string, have to distinguish
+ // between the NULL value and any non-NULL value. Use a convention
+ // easy for a human reader: enclose any non-NULL value in single
+ // quotes, reserving the unquoted string "NULL" to represent a NULL ptr.
+ std::string vcp;
+ if (cp == NULL)
+ vcp = "NULL";
+ else
+ vcp = std::string("'") + cp + "'";
+
+ cout << "methodna(" << b
+ << ", " << i
+ << ", " << f
+ << ", " << d
+ << ", " << vcp
+ << ")\n";
+
+ this->b = b;
+ this->i = i;
+ this->f = f;
+ this->d = d;
+ this->cp = vcp;
+ }
+
+ void methodnb(NPARAMSb)
+ {
+ std::ostringstream vbin;
+ foreach(U8 byte, bin)
+ {
+ vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
+ }
+
+ cout << "methodnb(" << "'" << s << "'"
+ << ", " << uuid
+ << ", " << date
+ << ", '" << uri << "'"
+ << ", " << vbin.str()
+ << ")\n";
+
+ this->s = s;
+ this->uuid = uuid;
+ this->date = date;
+ this->uri = uri;
+ this->bin = bin;
+ }
+
+ void cmethodna(NPARAMSa) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->methodna(NARGSa);
+ }
+
+ void cmethodnb(NPARAMSb) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->methodnb(NARGSb);
+ }
+
+ static void smethodna(NPARAMSa);
+ static void smethodnb(NPARAMSb);
+};
+/*------- Global Vars instance for free functions and static methods -------*/
+static Vars g;
+
+/*------------ Static Vars method implementations reference 'g' ------------*/
+void Vars::smethod0()
+{
+ cout << "smethod0() -> ";
+ g.method0();
+}
+
+void Vars::smethod1(const LLSD& obj)
+{
+ cout << "smethod1(" << obj << ") -> ";
+ g.method1(obj);
+}
+
+void Vars::smethodna(NPARAMSa)
+{
+ cout << "smethodna(...) -> ";
+ g.methodna(NARGSa);
+}
+
+void Vars::smethodnb(NPARAMSb)
+{
+ cout << "smethodnb(...) -> ";
+ g.methodnb(NARGSb);
+}
+
+/*--------------------------- Reset global Vars ----------------------------*/
+void clear()
+{
+ g = Vars();
+}
+
+/*------------------- Free functions also reference 'g' --------------------*/
+void free0()
+{
+ cout << "free0() -> ";
+ g.method0();
+}
+
+void free1(const LLSD& obj)
+{
+ cout << "free1(" << obj << ") -> ";
+ g.method1(obj);
+}
+
+void freena(NPARAMSa)
+{
+ cout << "freena(...) -> ";
+ g.methodna(NARGSa);
+}
+
+void freenb(NPARAMSb)
+{
+ cout << "freenb(...) -> ";
+ g.methodnb(NARGSb);
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct lleventdispatcher_data
+ {
+ WrapLLErrs redirect;
+ Dispatcher work;
+ Vars v;
+ std::string name, desc;
+ // Capture our own copy of all registered functions' descriptions
+ typedef std::map<std::string, std::string> DescMap;
+ DescMap descs;
+ // Capture the Vars instance on which we expect each function to operate
+ typedef std::map<std::string, Vars*> VarsMap;
+ VarsMap funcvars;
+ // Required structure for Callables with requirements
+ LLSD required;
+ // Parameter names for freena(), freenb()
+ LLSD params;
+ // Full, partial defaults arrays for params for freena(), freenb()
+ LLSD dft_array_full, dft_array_partial;
+ // Start index of partial defaults arrays
+ const LLSD::Integer partial_offset;
+ // Full, partial defaults maps for params for freena(), freenb()
+ LLSD dft_map_full, dft_map_partial;
+ // Most of the above are indexed by "a" or "b". Useful to have an
+ // array containing those strings for iterating.
+ std::vector<LLSD::String> ab;
+
+ lleventdispatcher_data():
+ work("test dispatcher", "op"),
+ // map {d=double, array=[3 elements]}
+ required(LLSDMap("d", LLSD::Real(0))("array", LLSDArray(LLSD())(LLSD())(LLSD()))),
+ // first several params are required, last couple optional
+ partial_offset(3)
+ {
+ // This object is reconstructed for every test<n> method. But
+ // clear global variables every time too.
+ ::clear();
+
+ const char* abs[] = { "a", "b" };
+ ab.assign(boost::begin(abs), boost::end(abs));
+
+ // Registration cases:
+ // - (Callable | subclass const method | subclass non-const method |
+ // non-subclass method) (with | without) required
+ // - (Free function | static method | non-static method), (no | arbitrary) params,
+ // array style
+ // - (Free function | static method | non-static method), (no | arbitrary) params,
+ // map style, (empty | partial | full) (array | map) defaults
+ // - Map-style errors:
+ // - (scalar | map) param names
+ // - defaults scalar
+ // - defaults array longer than params array
+ // - defaults map with plural unknown param names
+
+ // I hate to have to write things twice, because of having to keep
+ // them consistent. If we had variadic functions, addf() would be
+ // a variadic method, capturing the name and desc and passing them
+ // plus "everything else" to work.add(). If I could return a pair
+ // and use that pair as the first two args to work.add(), I'd do
+ // that. But the best I can do with present C++ is to set two
+ // instance variables as a side effect of addf(), and pass those
+ // variables to each work.add() call. :-P
+
+ /*------------------------- Callables --------------------------*/
+
+ // Arbitrary Callable with/out required params
+ addf("free1", "free1", &g);
+ work.add(name, desc, free1);
+ addf("free1_req", "free1", &g);
+ work.add(name, desc, free1, required);
+ // Subclass non-const method with/out required params
+ addf("Dmethod1", "method1", NULL);
+ work.add(name, desc, &Dispatcher::method1);
+ addf("Dmethod1_req", "method1", NULL);
+ work.add(name, desc, &Dispatcher::method1, required);
+ // Subclass const method with/out required params
+ addf("Dcmethod1", "cmethod1", NULL);
+ work.add(name, desc, &Dispatcher::cmethod1);
+ addf("Dcmethod1_req", "cmethod1", NULL);
+ work.add(name, desc, &Dispatcher::cmethod1, required);
+ // Non-subclass method with/out required params
+ addf("method1", "method1", &v);
+ work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1));
+ addf("method1_req", "method1", &v);
+ work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required);
+
+ /*--------------- Arbitrary params, array style ----------------*/
+
+ // (Free function | static method) with (no | arbitrary) params, array style
+ addf("free0_array", "free0", &g);
+ work.add(name, desc, free0);
+ addf("freena_array", "freena", &g);
+ work.add(name, desc, freena);
+ addf("freenb_array", "freenb", &g);
+ work.add(name, desc, freenb);
+ addf("smethod0_array", "smethod0", &g);
+ work.add(name, desc, &Vars::smethod0);
+ addf("smethodna_array", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna);
+ addf("smethodnb_array", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb);
+ // Non-static method with (no | arbitrary) params, array style
+ addf("method0_array", "method0", &v);
+ work.add(name, desc, &Vars::method0, boost::lambda::var(v));
+ addf("methodna_array", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, boost::lambda::var(v));
+ addf("methodnb_array", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, boost::lambda::var(v));
+
+ /*---------------- Arbitrary params, map style -----------------*/
+
+ // We lay out each params list as an array, also each array of
+ // default values we'll register. We'll zip these into
+ // (param=value) maps. Why not define them as maps and just
+ // extract the keys and values to arrays? Because that wouldn't
+ // give us the right params-list order.
+
+ // freena(), methodna(), cmethodna(), smethodna() all take same param list.
+ // Same for freenb() et al.
+ params = LLSDMap("a", LLSDArray("b")("i")("f")("d")("cp"))
+ ("b", LLSDArray("s")("uuid")("date")("uri")("bin"));
+ cout << "params:\n" << params << "\nparams[\"a\"]:\n" << params["a"] << "\nparams[\"b\"]:\n" << params["b"] << std::endl;
+ // default LLSD::Binary value
+ std::vector<U8> binary;
+ for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11)
+ {
+ binary.push_back(h);
+ }
+ // Full defaults arrays. We actually don't care what the LLUUID or
+ // LLDate values are, as long as they're different from the
+ // LLUUID() and LLDate() default values so inspect() will report
+ // them.
+ dft_array_full = LLSDMap("a", LLSDArray(true)(17)(3.14)(123456.78)("classic"))
+ ("b", LLSDArray("string")
+ (LLUUID::generateNewID())
+ (LLDate::now())
+ (LLURI("http://www.ietf.org/rfc/rfc3986.txt"))
+ (binary));
+ cout << "dft_array_full:\n" << dft_array_full << std::endl;
+ // Partial defaults arrays.
+ foreach(LLSD::String a, ab)
+ {
+ LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size()));
+ dft_array_partial[a] =
+ llsd_copy_array(dft_array_full[a].beginArray() + partition,
+ dft_array_full[a].endArray());
+ }
+ cout << "dft_array_partial:\n" << dft_array_partial << std::endl;
+
+ foreach(LLSD::String a, ab)
+ {
+ // Generate full defaults maps by zipping (params, dft_array_full).
+ dft_map_full[a] = zipmap(params[a], dft_array_full[a]);
+
+ // Generate partial defaults map by zipping alternate entries from
+ // (params, dft_array_full). Part of the point of using map-style
+ // defaults is to allow any subset of the target function's
+ // parameters to be optional, not just the rightmost.
+ for (LLSD::Integer ix = 0, ixend = params[a].size(); ix < ixend; ix += 2)
+ {
+ dft_map_partial[a][params[a][ix].asString()] = dft_array_full[a][ix];
+ }
+ }
+ cout << "dft_map_full:\n" << dft_map_full << "\ndft_map_partial:\n" << dft_map_partial << '\n';
+
+ // (Free function | static method) with (no | arbitrary) params,
+ // map style, no (empty array) defaults
+ addf("free0_map", "free0", &g);
+ work.add(name, desc, free0, LLSD::emptyArray());
+ addf("smethod0_map", "smethod0", &g);
+ work.add(name, desc, &Vars::smethod0, LLSD::emptyArray());
+ addf("freena_map_allreq", "freena", &g);
+ work.add(name, desc, freena, params["a"]);
+ addf("freenb_map_allreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"]);
+ addf("smethodna_map_allreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"]);
+ addf("smethodnb_map_allreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"]);
+ // Non-static method with (no | arbitrary) params, map style, no
+ // (empty array) defaults
+ addf("method0_map", "method0", &v);
+ work.add(name, desc, &Vars::method0, var(v), LLSD::emptyArray());
+ addf("methodna_map_allreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"]);
+ addf("methodnb_map_allreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"]);
+
+ // Except for the "more (array | map) defaults than params" error
+ // cases, tested separately below, the (partial | full)(array |
+ // map) defaults cases don't apply to no-params functions/methods.
+ // So eliminate free0, smethod0, method0 from the cases below.
+
+ // (Free function | static method) with arbitrary params, map
+ // style, partial (array | map) defaults
+ addf("freena_map_leftreq", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_array_partial["a"]);
+ addf("freenb_map_leftreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_array_partial["b"]);
+ addf("smethodna_map_leftreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_array_partial["a"]);
+ addf("smethodnb_map_leftreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_partial["b"]);
+ addf("freena_map_skipreq", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_map_partial["a"]);
+ addf("freenb_map_skipreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_map_partial["b"]);
+ addf("smethodna_map_skipreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_map_partial["a"]);
+ addf("smethodnb_map_skipreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_partial["b"]);
+ // Non-static method with arbitrary params, map style, partial
+ // (array | map) defaults
+ addf("methodna_map_leftreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_partial["a"]);
+ addf("methodnb_map_leftreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_partial["b"]);
+ addf("methodna_map_skipreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_partial["a"]);
+ addf("methodnb_map_skipreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_partial["b"]);
+
+ // (Free function | static method) with arbitrary params, map
+ // style, full (array | map) defaults
+ addf("freena_map_adft", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_array_full["a"]);
+ addf("freenb_map_adft", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_array_full["b"]);
+ addf("smethodna_map_adft", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_array_full["a"]);
+ addf("smethodnb_map_adft", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_full["b"]);
+ addf("freena_map_mdft", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_map_full["a"]);
+ addf("freenb_map_mdft", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_map_full["b"]);
+ addf("smethodna_map_mdft", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_map_full["a"]);
+ addf("smethodnb_map_mdft", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_full["b"]);
+ // Non-static method with arbitrary params, map style, full
+ // (array | map) defaults
+ addf("methodna_map_adft", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_full["a"]);
+ addf("methodnb_map_adft", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_full["b"]);
+ addf("methodna_map_mdft", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_full["a"]);
+ addf("methodnb_map_mdft", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_full["b"]);
+
+ // All the above are expected to succeed, and are setup for the
+ // tests to follow. Registration error cases are exercised as
+ // tests rather than as test setup.
+ }
+
+ void addf(const std::string& n, const std::string& d, Vars* v)
+ {
+ // This method is to capture in our own DescMap the name and
+ // description of every registered function, for metadata query
+ // testing.
+ descs[n] = d;
+ // Also capture the Vars instance on which each function should operate.
+ funcvars[n] = v;
+ // See constructor for rationale for setting these instance vars.
+ this->name = n;
+ this->desc = d;
+ }
+
+ void verify_descs()
+ {
+ // Copy descs to a temp map of same type.
+ DescMap forgotten(descs.begin(), descs.end());
+ // LLEventDispatcher intentionally provides only const_iterator:
+ // since dereferencing that iterator generates values on the fly,
+ // it's meaningless to have a modifiable iterator. But since our
+ // 'work' object isn't const, by default BOOST_FOREACH() wants to
+ // use non-const iterators. Persuade it to use the const_iterator.
+ foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work))
+ {
+ DescMap::iterator found = forgotten.find(nd.first);
+ ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first
+ << "' we didn't enter"),
+ found != forgotten.end());
+ ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second <<
+ "' doesn't match what we entered: '" << found->second << "'"),
+ nd.second, found->second);
+ // found in our map the name from LLEventDispatcher, good, erase
+ // our map entry
+ forgotten.erase(found);
+ }
+ if (! forgotten.empty())
+ {
+ std::ostringstream out;
+ out << "LLEventDispatcher failed to report";
+ const char* delim = ": ";
+ foreach(const DescMap::value_type& fme, forgotten)
+ {
+ out << delim << fme.first;
+ delim = ", ";
+ }
+ ensure(out.str(), false);
+ }
+ }
+
+ Vars* varsfor(const std::string& name)
+ {
+ VarsMap::const_iterator found = funcvars.find(name);
+ ensure(STRINGIZE("No Vars* for " << name), found != funcvars.end());
+ ensure(STRINGIZE("NULL Vars* for " << name), found->second);
+ return found->second;
+ }
+
+ void ensure_has(const std::string& outer, const std::string& inner)
+ {
+ ensure(STRINGIZE("'" << outer << "' does not contain '" << inner << "'").c_str(),
+ outer.find(inner) != std::string::npos);
+ }
+
+ void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
+ {
+ std::string threw;
+ try
+ {
+ work(func, args);
+ }
+ catch (const std::runtime_error& e)
+ {
+ cout << "*** " << e.what() << '\n';
+ threw = e.what();
+ }
+ ensure_has(threw, exc_frag);
+ }
+
+ LLSD getMetadata(const std::string& name)
+ {
+ LLSD meta(work.getMetadata(name));
+ ensure(STRINGIZE("No metadata for " << name), meta.isDefined());
+ return meta;
+ }
+
+ // From two related LLSD arrays, e.g. a param-names array and a values
+ // array, zip them together into an LLSD map.
+ LLSD zipmap(const LLSD& keys, const LLSD& values)
+ {
+ LLSD map;
+ for (LLSD::Integer i = 0, iend = keys.size(); i < iend; ++i)
+ {
+ // Have to select asString() since you can index an LLSD
+ // object with either String or Integer.
+ map[keys[i].asString()] = values[i];
+ }
+ return map;
+ }
+
+ // If I call this ensure_equals(), it blocks visibility of all other
+ // ensure_equals() overloads. Normally I could say 'using
+ // baseclass::ensure_equals;' and fix that, but I don't know what the
+ // base class is!
+ void ensure_llsd(const std::string& msg, const LLSD& actual, const LLSD& expected, U32 bits)
+ {
+ std::ostringstream out;
+ if (! msg.empty())
+ {
+ out << msg << ": ";
+ }
+ out << "expected " << expected << ", actual " << actual;
+ ensure(out.str(), llsd_equals(actual, expected, bits));
+ }
+
+ void ensure_llsd(const LLSD& actual, const LLSD& expected, U32 bits)
+ {
+ ensure_llsd("", actual, expected, bits);
+ }
+ };
+ typedef test_group<lleventdispatcher_data> lleventdispatcher_group;
+ typedef lleventdispatcher_group::object object;
+ lleventdispatcher_group lleventdispatchergrp("lleventdispatcher");
+
+ // Call cases:
+ // - (try_call | call) (explicit name | event key) (real | bogus) name
+ // - Callable with args that (do | do not) match required
+ // - (Free function | non-static method), no args, (array | map) style
+ // - (Free function | non-static method), arbitrary args,
+ // (array style with (scalar | map) | map style with scalar)
+ // - (Free function | non-static method), arbitrary args, array style with
+ // array (too short | too long | just right)
+ // [trap LL_WARNS for too-long case?]
+ // - (Free function | non-static method), arbitrary args, map style with
+ // (array | map) (all | too many | holes (with | without) defaults)
+ // - const char* param gets ("" | NULL)
+
+ // Query cases:
+ // - Iterate over all (with | without) remove()
+ // - getDispatchKey()
+ // - Callable style (with | without) required
+ // - (Free function | non-static method), array style, (no | arbitrary) params
+ // - (Free function | non-static method), map style, (no | arbitrary) params,
+ // (empty | full | partial (array | map)) defaults
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("map-style registration with non-array params");
+ // Pass "param names" as scalar or as map
+ LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2)));
+ foreach(LLSD ae, inArray(attempts))
+ {
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena, ae);
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "must be an array");
+ }
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("map-style registration with badly-formed defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17);
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "must be a map or an array");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("map-style registration with too many array defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDArray(17)(0.9)("gack"));
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "shorter than");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("map-style registration with too many map defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDMap("b", 17)("foo", 3.14)("bar", "sinister"));
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "nonexistent params");
+ ensure_has(threw, "foo");
+ ensure_has(threw, "bar");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("query all");
+ verify_descs();
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("query all with remove()");
+ ensure("remove('bogus') returned true", ! work.remove("bogus"));
+ ensure("remove('real') returned false", work.remove("free1"));
+ // Of course, remove that from 'descs' too...
+ descs.erase("free1");
+ verify_descs();
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("getDispatchKey()");
+ ensure_equals(work.getDispatchKey(), "op");
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("query Callables with/out required params");
+ LLSD names(LLSDArray("free1")("Dmethod1")("Dcmethod1")("method1"));
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure("should not have required structure", metadata["required"].isUndefined());
+ ensure("should not have optional", metadata["optional"].isUndefined());
+
+ std::string name_req(nm.asString() + "_req");
+ metadata = getMetadata(name_req);
+ ensure_equals(metadata["name"].asString(), name_req);
+ ensure_equals(metadata["desc"].asString(), descs[name_req]);
+ ensure_equals("required mismatch", required, metadata["required"]);
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("query array-style functions/methods");
+ // Associate each registered name with expected arity.
+ LLSD expected(LLSDArray
+ (LLSDArray
+ (0)(LLSDArray("free0_array")("smethod0_array")("method0_array")))
+ (LLSDArray
+ (5)(LLSDArray("freena_array")("smethodna_array")("methodna_array")))
+ (LLSDArray
+ (5)(LLSDArray("freenb_array")("smethodnb_array")("methodnb_array"))));
+ foreach(LLSD ae, inArray(expected))
+ {
+ LLSD::Integer arity(ae[0].asInteger());
+ LLSD names(ae[1]);
+ LLSD req(LLSD::emptyArray());
+ if (arity)
+ req[arity - 1] = LLSD();
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure_equals(STRINGIZE("mismatched required for " << nm.asString()),
+ metadata["required"], req);
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("query map-style no-params functions/methods");
+ // - (Free function | non-static method), map style, no params (ergo
+ // no defaults)
+ LLSD names(LLSDArray("free0_map")("smethod0_map")("method0_map"));
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure("should not have required",
+ (metadata["required"].isUndefined() || metadata["required"].size() == 0));
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ set_test_name("query map-style arbitrary-params functions/methods: "
+ "full array defaults vs. full map defaults");
+ // With functions registered with no defaults ("_allreq" suffixes),
+ // there is of course no difference between array defaults and map
+ // defaults. (We don't even bother registering with LLSD::emptyArray()
+ // vs. LLSD::emptyMap().) With functions registered with all defaults,
+ // there should (!) be no difference beween array defaults and map
+ // defaults. Verify, so we can ignore the distinction for all other
+ // tests.
+ LLSD equivalences(LLSDArray
+ (LLSDArray("freena_map_adft")("freena_map_mdft"))
+ (LLSDArray("freenb_map_adft")("freenb_map_mdft"))
+ (LLSDArray("smethodna_map_adft")("smethodna_map_mdft"))
+ (LLSDArray("smethodnb_map_adft")("smethodnb_map_mdft"))
+ (LLSDArray("methodna_map_adft")("methodna_map_mdft"))
+ (LLSDArray("methodnb_map_adft")("methodnb_map_mdft")));
+ foreach(LLSD eq, inArray(equivalences))
+ {
+ LLSD adft(eq[0]);
+ LLSD mdft(eq[1]);
+ // We can't just compare the results of the two getMetadata()
+ // calls, because they contain ["name"], which are different. So
+ // capture them, verify that each ["name"] is as expected, then
+ // remove for comparing the rest.
+ LLSD ameta(getMetadata(adft));
+ LLSD mmeta(getMetadata(mdft));
+ ensure_equals("adft name", adft, ameta["name"]);
+ ensure_equals("mdft name", mdft, mmeta["name"]);
+ ameta.erase("name");
+ mmeta.erase("name");
+ ensure_equals(STRINGIZE("metadata for " << adft.asString()
+ << " vs. " << mdft.asString()),
+ ameta, mmeta);
+ }
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ set_test_name("query map-style arbitrary-params functions/methods");
+ // - (Free function | non-static method), map style, arbitrary params,
+ // (empty | full | partial (array | map)) defaults
+
+ // Generate maps containing all parameter names for cases in which all
+ // params are required. Also maps containing left requirements for
+ // partial defaults arrays. Also defaults maps from defaults arrays.
+ LLSD allreq, leftreq, rightdft;
+ foreach(LLSD::String a, ab)
+ {
+ // The map in which all params are required uses params[a] as
+ // keys, with all isUndefined() as values. We can accomplish that
+ // by passing zipmap() an empty values array.
+ allreq[a] = zipmap(params[a], LLSD::emptyArray());
+ // Same for leftreq, save that we use the subset of the params not
+ // supplied by dft_array_partial[a].
+ LLSD::Integer partition(params[a].size() - dft_array_partial[a].size());
+ leftreq[a] = zipmap(llsd_copy_array(params[a].beginArray(),
+ params[a].beginArray() + partition),
+ LLSD::emptyArray());
+ // Generate map pairing dft_array_partial[a] values with their
+ // param names.
+ rightdft[a] = zipmap(llsd_copy_array(params[a].beginArray() + partition,
+ params[a].endArray()),
+ dft_array_partial[a]);
+ }
+ cout << "allreq:\n" << allreq << "\nleftreq:\n" << leftreq << "\nrightdft:\n" << rightdft << std::endl;
+
+ // Generate maps containing parameter names not provided by the
+ // dft_map_partial maps.
+ LLSD skipreq(allreq);
+ foreach(LLSD::String a, ab)
+ {
+ foreach(const MapEntry& me, inMap(dft_map_partial[a]))
+ {
+ skipreq[a].erase(me.first);
+ }
+ }
+ cout << "skipreq:\n" << skipreq << std::endl;
+
+ LLSD groups(LLSDArray // array of groups
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_allreq")("smethodna_map_allreq")("methodna_map_allreq"))
+ (LLSDArray(allreq["a"])(LLSD()))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_allreq")("smethodnb_map_allreq")("methodnb_map_allreq"))
+ (LLSDArray(allreq["b"])(LLSD()))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq"))
+ (LLSDArray(leftreq["a"])(rightdft["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq"))
+ (LLSDArray(leftreq["b"])(rightdft["b"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq"))
+ (LLSDArray(skipreq["a"])(dft_map_partial["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq"))
+ (LLSDArray(skipreq["b"])(dft_map_partial["b"]))) // required, optional
+
+ // We only need mention the full-map-defaults ("_mdft" suffix)
+ // registrations, having established their equivalence with the
+ // full-array-defaults ("_adft" suffix) registrations in another test.
+ (LLSDArray // group
+ (LLSDArray("freena_map_mdft")("smethodna_map_mdft")("methodna_map_mdft"))
+ (LLSDArray(LLSD::emptyMap())(dft_map_full["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_mdft")("smethodnb_map_mdft")("methodnb_map_mdft"))
+ (LLSDArray(LLSD::emptyMap())(dft_map_full["b"])))); // required, optional
+
+ foreach(LLSD grp, inArray(groups))
+ {
+ // Internal structure of each group in 'groups':
+ LLSD names(grp[0]);
+ LLSD required(grp[1][0]);
+ LLSD optional(grp[1][1]);
+ cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl;
+
+ // Loop through 'names'
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]);
+ ensure_equals(STRINGIZE(nm << " required mismatch"),
+ metadata["required"], required);
+ ensure_equals(STRINGIZE(nm << " optional mismatch"),
+ metadata["optional"], optional);
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<13>()
+ {
+ set_test_name("try_call()");
+ ensure("try_call(bogus name, LLSD()) returned true", ! work.try_call("freek", LLSD()));
+ ensure("try_call(bogus name) returned true", ! work.try_call(LLSDMap("op", "freek")));
+ ensure("try_call(real name, LLSD()) returned false", work.try_call("free0_array", LLSD()));
+ ensure("try_call(real name) returned false", work.try_call(LLSDMap("op", "free0_map")));
+ }
+
+ template<> template<>
+ void object::test<14>()
+ {
+ set_test_name("call with bad name");
+ call_exc("freek", LLSD(), "not found");
+ // We don't have a comparable helper function for the one-arg
+ // operator() method, and it's not worth building one just for this
+ // case. Write it out.
+ std::string threw;
+ try
+ {
+ work(LLSDMap("op", "freek"));
+ }
+ catch (const std::runtime_error& e)
+ {
+ cout << "*** " << e.what() << "\n";
+ threw = e.what();
+ }
+ ensure_has(threw, "bad");
+ ensure_has(threw, "op");
+ ensure_has(threw, "freek");
+ }
+
+ template<> template<>
+ void object::test<15>()
+ {
+ set_test_name("call with event key");
+ // We don't need a separate test for operator()(string, LLSD) with
+ // valid name, because all the rest of the tests exercise that case.
+ // The one we don't exercise elsewhere is operator()(LLSD) with valid
+ // name, so here it is.
+ work(LLSDMap("op", "free0_map"));
+ ensure_equals(g.i, 17);
+ }
+
+ // Cannot be defined inside function body... remind me again why we use C++... :-P
+ struct CallablesTriple
+ {
+ std::string name, name_req;
+ LLSD& llsd;
+ };
+
+ template<> template<>
+ void object::test<16>()
+ {
+ set_test_name("call Callables");
+ CallablesTriple tests[] =
+ {
+ { "free1", "free1_req", g.llsd },
+ { "Dmethod1", "Dmethod1_req", work.llsd },
+ { "Dcmethod1", "Dcmethod1_req", work.llsd },
+ { "method1", "method1_req", v.llsd }
+ };
+ // Arbitrary LLSD value that we should be able to pass to Callables
+ // without 'required', but should not be able to pass to Callables
+ // with 'required'.
+ LLSD answer(42);
+ // LLSD value matching 'required' according to llsd_matches() rules.
+ LLSD matching(LLSDMap("d", 3.14)("array", LLSDArray("answer")(true)(answer)));
+ // Okay, walk through 'tests'.
+ foreach(const CallablesTriple& tr, tests)
+ {
+ // Should be able to pass 'answer' to Callables registered
+ // without 'required'.
+ work(tr.name, answer);
+ ensure_equals("answer mismatch", tr.llsd, answer);
+ // Should NOT be able to pass 'answer' to Callables registered
+ // with 'required'.
+ call_exc(tr.name_req, answer, "bad request");
+ // But SHOULD be able to pass 'matching' to Callables registered
+ // with 'required'.
+ work(tr.name_req, matching);
+ ensure_equals("matching mismatch", tr.llsd, matching);
+ }
+ }
+
+ template<> template<>
+ void object::test<17>()
+ {
+ set_test_name("passing wrong args to (map | array)-style registrations");
+
+ // Pass scalar/map to array-style functions, scalar/array to map-style
+ // functions. As that validation happens well before we engage the
+ // argument magic, it seems pointless to repeat this with every
+ // variation: (free function | non-static method), (no | arbitrary)
+ // args. We should only need to engage it for one map-style
+ // registration and one array-style registration.
+ std::string array_exc("needs an args array");
+ call_exc("free0_array", 17, array_exc);
+ call_exc("free0_array", LLSDMap("pi", 3.14), array_exc);
+
+ std::string map_exc("needs a map");
+ call_exc("free0_map", 17, map_exc);
+ // Passing an array to a map-style function works now! No longer an
+ // error case!
+// call_exc("free0_map", LLSDArray("a")("b"), map_exc);
+ }
+
+ template<> template<>
+ void object::test<18>()
+ {
+ set_test_name("call no-args functions");
+ LLSD names(LLSDArray
+ ("free0_array")("free0_map")
+ ("smethod0_array")("smethod0_map")
+ ("method0_array")("method0_map"));
+ foreach(LLSD name, inArray(names))
+ {
+ // Look up the Vars instance for this function.
+ Vars* vars(varsfor(name));
+ // Both the global and stack Vars instances are automatically
+ // cleared at the start of each test<n> method. But since we're
+ // calling these things several different times in the same
+ // test<n> method, manually reset the Vars between each.
+ *vars = Vars();
+ ensure_equals(vars->i, 0);
+ // call function with empty array (or LLSD(), should be equivalent)
+ work(name, LLSD());
+ ensure_equals(vars->i, 17);
+ }
+ }
+
+ // Break out this data because we use it in a couple different tests.
+ LLSD array_funcs(LLSDArray
+ (LLSDMap("a", "freena_array") ("b", "freenb_array"))
+ (LLSDMap("a", "smethodna_array")("b", "smethodnb_array"))
+ (LLSDMap("a", "methodna_array") ("b", "methodnb_array")));
+
+ template<> template<>
+ void object::test<19>()
+ {
+ set_test_name("call array-style functions with too-short arrays");
+ // Could have two different too-short arrays, one for *na and one for
+ // *nb, but since they both take 5 params...
+ LLSD tooshort(LLSDArray("this")("array")("too")("short"));
+ foreach(const LLSD& funcsab, inArray(array_funcs))
+ {
+ foreach(const llsd::MapEntry& e, inMap(funcsab))
+ {
+ call_exc(e.second, tooshort, "requires more arguments");
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<20>()
+ {
+ set_test_name("call array-style functions with (just right | too long) arrays");
+ std::vector<U8> binary;
+ for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
+ {
+ binary.push_back(h);
+ }
+ LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*"))
+ ("b", LLSDArray("string")
+ (LLUUID("01234567-89ab-cdef-0123-456789abcdef"))
+ (LLDate("2011-02-03T15:07:00Z"))
+ (LLURI("http://secondlife.com"))
+ (binary)));
+ LLSD argsplus(args);
+ argsplus["a"].append("bogus");
+ argsplus["b"].append("bogus");
+ LLSD expect;
+ foreach(LLSD::String a, ab)
+ {
+ expect[a] = zipmap(params[a], args[a]);
+ }
+ // Adjust expect["a"]["cp"] for special Vars::cp treatment.
+ expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
+ cout << "expect: " << expect << '\n';
+
+ // Use substantially the same logic for args and argsplus
+ LLSD argsarrays(LLSDArray(args)(argsplus));
+ // So i==0 selects 'args', i==1 selects argsplus
+ for (LLSD::Integer i(0), iend(argsarrays.size()); i < iend; ++i)
+ {
+ foreach(const LLSD& funcsab, inArray(array_funcs))
+ {
+ foreach(LLSD::String a, ab)
+ {
+ // Reset the Vars instance before each call
+ Vars* vars(varsfor(funcsab[a]));
+ *vars = Vars();
+ work(funcsab[a], argsarrays[i][a]);
+ ensure_llsd(STRINGIZE(funcsab[a].asString() <<
+ ": expect[\"" << a << "\"] mismatch"),
+ vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
+
+ // TODO: in the i==1 or argsplus case, intercept LL_WARNS
+ // output? Even without that, using argsplus verifies that
+ // passing too many args isn't fatal; it works -- but
+ // would be nice to notice the warning too.
+ }
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<21>()
+ {
+ set_test_name("verify that passing LLSD() to const char* sends NULL");
+
+ ensure_equals("Vars::cp init", v.cp, "");
+ work("methodna_map_mdft", LLSDMap("cp", LLSD()));
+ ensure_equals("passing LLSD()", v.cp, "NULL");
+ work("methodna_map_mdft", LLSDMap("cp", ""));
+ ensure_equals("passing \"\"", v.cp, "''");
+ work("methodna_map_mdft", LLSDMap("cp", "non-NULL"));
+ ensure_equals("passing \"non-NULL\"", v.cp, "'non-NULL'");
+ }
+
+ template<> template<>
+ void object::test<22>()
+ {
+ set_test_name("call map-style functions with (full | oversized) (arrays | maps)");
+ const char binary[] = "\x99\x88\x77\x66\x55";
+ LLSD array_full(LLSDMap
+ ("a", LLSDArray(false)(255)(98.6)(1024.5)("pointer"))
+ ("b", LLSDArray("object")(LLUUID::generateNewID())(LLDate::now())(LLURI("http://wiki.lindenlab.com/wiki"))(LLSD::Binary(boost::begin(binary), boost::end(binary)))));
+ LLSD array_overfull(array_full);
+ foreach(LLSD::String a, ab)
+ {
+ array_overfull[a].append("bogus");
+ }
+ cout << "array_full: " << array_full << "\narray_overfull: " << array_overfull << std::endl;
+ // We rather hope that LLDate::now() will generate a timestamp
+ // distinct from the one it generated in the constructor, moments ago.
+ ensure_not_equals("Timestamps too close",
+ array_full["b"][2].asDate(), dft_array_full["b"][2].asDate());
+ // We /insist/ that LLUUID::generateNewID() do so.
+ ensure_not_equals("UUID collision",
+ array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID());
+ LLSD map_full, map_overfull;
+ foreach(LLSD::String a, ab)
+ {
+ map_full[a] = zipmap(params[a], array_full[a]);
+ map_overfull[a] = map_full[a];
+ map_overfull[a]["extra"] = "ignore";
+ }
+ cout << "map_full: " << map_full << "\nmap_overfull: " << map_overfull << std::endl;
+ LLSD expect(map_full);
+ // Twiddle the const char* param.
+ expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
+ // Another adjustment. For each data type, we're trying to distinguish
+ // three values: the Vars member's initial value (member wasn't
+ // stored; control never reached the set function), the registered
+ // default param value from dft_array_full, and the array_full value
+ // in this test. But bool can only distinguish two values. In this
+ // case, we want to differentiate the local array_full value from the
+ // dft_array_full value, so we use 'false'. However, that means
+ // Vars::inspect() doesn't differentiate it from the initial value,
+ // so won't bother returning it. Predict that behavior to match the
+ // LLSD values.
+ expect["a"].erase("b");
+ cout << "expect: " << expect << std::endl;
+ // For this test, calling functions registered with different sets of
+ // parameter defaults should make NO DIFFERENCE WHATSOEVER. Every call
+ // should pass all params.
+ LLSD names(LLSDMap
+ ("a", LLSDArray
+ ("freena_map_allreq") ("smethodna_map_allreq") ("methodna_map_allreq")
+ ("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq")
+ ("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq")
+ ("freena_map_adft") ("smethodna_map_adft") ("methodna_map_adft")
+ ("freena_map_mdft") ("smethodna_map_mdft") ("methodna_map_mdft"))
+ ("b", LLSDArray
+ ("freenb_map_allreq") ("smethodnb_map_allreq") ("methodnb_map_allreq")
+ ("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq")
+ ("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq")
+ ("freenb_map_adft") ("smethodnb_map_adft") ("methodnb_map_adft")
+ ("freenb_map_mdft") ("smethodnb_map_mdft") ("methodnb_map_mdft")));
+ // Treat (full | overfull) (array | map) the same.
+ LLSD argssets(LLSDArray(array_full)(array_overfull)(map_full)(map_overfull));
+ foreach(const LLSD& args, inArray(argssets))
+ {
+ foreach(LLSD::String a, ab)
+ {
+ foreach(LLSD::String name, inArray(names[a]))
+ {
+ // Reset the Vars instance
+ Vars* vars(varsfor(name));
+ *vars = Vars();
+ work(name, args[a]);
+ ensure_llsd(STRINGIZE(name << ": expect[\"" << a << "\"] mismatch"),
+ vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits
+ // intercept LL_WARNS for the two overfull cases?
+ }
+ }
+ }
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp
index 28b909298e..2cdfb52f2f 100644..100755
--- a/indra/llcommon/tests/lleventfilter_test.cpp
+++ b/indra/llcommon/tests/lleventfilter_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-03-06
* @brief Test for lleventfilter.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -227,7 +244,7 @@ namespace tut
void filter_object::test<4>()
{
set_test_name("LLEventTimeout::errorAfter()");
- WrapLL_ERRS capture;
+ WrapLLErrs capture;
LLEventPump& driver(pumps.obtain("driver"));
TestEventTimeout filter(driver);
listener0.reset(0);
@@ -257,7 +274,7 @@ namespace tut
{
mainloop.post(17);
}
- catch (const WrapLL_ERRS::FatalException& e)
+ catch (const WrapLLErrs::FatalException& e)
{
threw = e.what();
}
diff --git a/indra/llcommon/tests/llframetimer_test.cpp b/indra/llcommon/tests/llframetimer_test.cpp
index 1d047e41f8..be372bb855 100644..100755
--- a/indra/llcommon/tests/llframetimer_test.cpp
+++ b/indra/llcommon/tests/llframetimer_test.cpp
@@ -3,31 +3,25 @@
* @date 2006-07-23
* @brief Tests the timers.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * 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
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -49,7 +43,7 @@ namespace tut
};
typedef test_group<frametimer_test> frametimer_group_t;
typedef frametimer_group_t::object frametimer_object_t;
- tut::frametimer_group_t frametimer_instance("frametimer");
+ tut::frametimer_group_t frametimer_instance("LLFrameTimer");
template<> template<>
void frametimer_object_t::test<1>()
@@ -90,25 +84,34 @@ namespace tut
template<> template<>
void frametimer_object_t::test<3>()
{
+ clock_t t1 = clock();
+ ms_sleep(200);
+ clock_t t2 = clock();
+ clock_t elapsed = t2 - t1 + 1;
+ std::cout << "Note: using clock(), ms_sleep() actually took " << (long)elapsed << "ms" << std::endl;
+
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)
+ /*
+ * Note that the ms_sleep(200) below is only guaranteed to return
+ * in 200ms _or_more_, so it should be true that by the 10th
+ * iteration we've gotten to the 2 seconds requested above
+ * and the timer should expire, but it can expire in fewer iterations
+ * if one or more of the ms_sleep calls takes longer.
+ * (as it did when we moved to Mac OS X 10.10)
+ */
+ int iterations_until_expiration = 0;
+ while ( !timer.hasExpired() )
{
- 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();
+ ms_sleep(200);
+ LLFrameTimer::updateFrameTime();
+ iterations_until_expiration++;
}
- ensure("timer expired", timer.hasExpired());
+ ensure("timer took too long to expire", iterations_until_expiration <= 10);
}
+
/*
template<> template<>
void frametimer_object_t::test<4>()
diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp
index 4bb3ec2922..c7d4b8a06b 100644..100755
--- a/indra/llcommon/tests/llinstancetracker_test.cpp
+++ b/indra/llcommon/tests/llinstancetracker_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-11-10
* @brief Test for llinstancetracker.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -18,11 +35,18 @@
#include <vector>
#include <set>
#include <algorithm> // std::sort()
+#include <stdexcept>
// std headers
// external library headers
#include <boost/scoped_ptr.hpp>
// other Linden headers
#include "../test/lltut.h"
+#include "wrapllerrs.h"
+
+struct Badness: public std::runtime_error
+{
+ Badness(const std::string& what): std::runtime_error(what) {}
+};
struct Keyed: public LLInstanceTracker<Keyed, std::string>
{
@@ -35,6 +59,17 @@ struct Keyed: public LLInstanceTracker<Keyed, std::string>
struct Unkeyed: public LLInstanceTracker<Unkeyed>
{
+ Unkeyed(const std::string& thrw="")
+ {
+ // LLInstanceTracker should respond appropriately if a subclass
+ // constructor throws an exception. Specifically, it should run
+ // LLInstanceTracker's destructor and remove itself from the
+ // underlying container.
+ if (! thrw.empty())
+ {
+ throw Badness(thrw);
+ }
+ }
};
/*****************************************************************************
@@ -77,6 +112,7 @@ namespace tut
void object::test<2>()
{
ensure_equals(Unkeyed::instanceCount(), 0);
+ Unkeyed* dangling = NULL;
{
Unkeyed one;
ensure_equals(Unkeyed::instanceCount(), 1);
@@ -89,7 +125,11 @@ namespace tut
ensure_equals(found, two.get());
}
ensure_equals(Unkeyed::instanceCount(), 1);
- }
+ // store an unwise pointer to a temp Unkeyed instance
+ dangling = &one;
+ } // make that instance vanish
+ // check the now-invalid pointer to the destroyed instance
+ ensure("getInstance(T*) failed to track destruction", ! Unkeyed::getInstance(dangling));
ensure_equals(Unkeyed::instanceCount(), 0);
}
@@ -134,33 +174,125 @@ namespace tut
{
Unkeyed one, two, three;
typedef std::set<Unkeyed*> KeySet;
- KeySet keys;
- keys.insert(&one);
- keys.insert(&two);
- keys.insert(&three);
- {
- Unkeyed::LLInstanceTrackerScopedGuard guard;
- for (Unkeyed::key_iter ki(guard.beginKeys()), kend(guard.endKeys());
- ki != kend; ++ki)
- {
- ensure_equals("spurious key", keys.erase(*ki), 1);
- }
- }
- ensure_equals("unreported key", keys.size(), 0);
-
+
KeySet instances;
instances.insert(&one);
instances.insert(&two);
instances.insert(&three);
- {
- Unkeyed::LLInstanceTrackerScopedGuard guard;
- for (Unkeyed::instance_iter ii(guard.beginInstances()), iend(guard.endInstances());
- ii != iend; ++ii)
+
+ for (Unkeyed::instance_iter ii(Unkeyed::beginInstances()), iend(Unkeyed::endInstances()); ii != iend; ++ii)
{
Unkeyed& ref = *ii;
ensure_equals("spurious instance", instances.erase(&ref), 1);
}
- }
+
ensure_equals("unreported instance", instances.size(), 0);
}
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("delete Keyed with outstanding instance_iter");
+ std::string what;
+ Keyed* keyed = new Keyed("delete Keyed with outstanding instance_iter");
+ {
+ WrapLLErrs wrapper;
+ Keyed::instance_iter i(Keyed::beginInstances());
+ try
+ {
+ delete keyed;
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("delete Keyed with outstanding key_iter");
+ std::string what;
+ Keyed* keyed = new Keyed("delete Keyed with outstanding key_it");
+ {
+ WrapLLErrs wrapper;
+ Keyed::key_iter i(Keyed::beginKeys());
+ try
+ {
+ delete keyed;
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("delete Unkeyed with outstanding instance_iter");
+ std::string what;
+ Unkeyed* unkeyed = new Unkeyed;
+ {
+ WrapLLErrs wrapper;
+ Unkeyed::instance_iter i(Unkeyed::beginInstances());
+ try
+ {
+ delete unkeyed;
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("exception in subclass ctor");
+ typedef std::set<Unkeyed*> InstanceSet;
+ InstanceSet existing;
+ // We can't use the iterator-range InstanceSet constructor because
+ // beginInstances() returns an iterator that dereferences to an
+ // Unkeyed&, not an Unkeyed*.
+ for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+ ukend(Unkeyed::endInstances());
+ uki != ukend; ++uki)
+ {
+ existing.insert(&*uki);
+ }
+ try
+ {
+ // We don't expect the assignment to take place because we expect
+ // Unkeyed to respond to the non-empty string param by throwing.
+ // We know the LLInstanceTracker base-class constructor will have
+ // run before Unkeyed's constructor, therefore the new instance
+ // will have added itself to the underlying set. The whole
+ // question is, when Unkeyed's constructor throws, will
+ // LLInstanceTracker's destructor remove it from the set? I
+ // realize we're testing the C++ implementation more than
+ // Unkeyed's implementation, but this seems an important point to
+ // nail down.
+ new Unkeyed("throw");
+ }
+ catch (const Badness&)
+ {
+ }
+ // Ensure that every member of the new, updated set of Unkeyed
+ // instances was also present in the original set. If that's not true,
+ // it's because our new Unkeyed ended up in the updated set despite
+ // its constructor exception.
+ for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+ ukend(Unkeyed::endInstances());
+ uki != ukend; ++uki)
+ {
+ ensure("failed to remove instance", existing.find(&*uki) != existing.end());
+ }
+ }
} // namespace tut
diff --git a/indra/llcommon/tests/lllazy_test.cpp b/indra/llcommon/tests/lllazy_test.cpp
index db581d650f..32a717f4fc 100644..100755
--- a/indra/llcommon/tests/lllazy_test.cpp
+++ b/indra/llcommon/tests/lllazy_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-01-28
* @brief Tests of lllazy.h.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
new file mode 100755
index 0000000000..2d88e2c676
--- /dev/null
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -0,0 +1,671 @@
+/**
+ * @file llleap_test.cpp
+ * @author Nat Goodspeed
+ * @date 2012-02-21
+ * @brief Test for llleap.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llleap.h"
+// STL headers
+// std headers
+// external library headers
+#include <boost/assign/list_of.hpp>
+#include <boost/phoenix/core/argument.hpp>
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "../test/namedtempfile.h"
+#include "../test/catch_and_store_what_in.h"
+#include "wrapllerrs.h"
+#include "llevents.h"
+#include "llprocess.h"
+#include "stringize.h"
+#include "StringVec.h"
+#include <functional>
+
+using boost::assign::list_of;
+
+StringVec sv(const StringVec& listof) { return listof; }
+
+#if defined(LL_WINDOWS)
+#define sleep(secs) _sleep((secs) * 1000)
+#endif
+
+const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data
+
+void waitfor(const std::vector<LLLeap*>& instances, int timeout=60)
+{
+ int i;
+ for (i = 0; i < timeout; ++i)
+ {
+ // Every iteration, test whether any of the passed LLLeap instances
+ // still exist (are still running).
+ std::vector<LLLeap*>::const_iterator vli(instances.begin()), vlend(instances.end());
+ for ( ; vli != vlend; ++vli)
+ {
+ // getInstance() returns NULL if it's terminated/gone, non-NULL if
+ // it's still running
+ if (LLLeap::getInstance(*vli))
+ break;
+ }
+ // If we made it through all of 'instances' without finding one that's
+ // still running, we're done.
+ if (vli == vlend)
+ {
+/*==========================================================================*|
+ std::cout << instances.size() << " LLLeap instances terminated in "
+ << i << " seconds, proceeding" << std::endl;
+|*==========================================================================*/
+ return;
+ }
+ // Found an instance that's still running. Wait and pump LLProcess.
+ sleep(1);
+ LLEventPumps::instance().obtain("mainloop").post(LLSD());
+ }
+ tut::ensure(STRINGIZE("at least 1 of " << instances.size()
+ << " LLLeap instances timed out ("
+ << timeout << " seconds) without terminating"),
+ i < timeout);
+}
+
+void waitfor(LLLeap* instance, int timeout=60)
+{
+ std::vector<LLLeap*> instances;
+ instances.push_back(instance);
+ waitfor(instances, timeout);
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llleap_data
+ {
+ llleap_data():
+ reader(".py",
+ // This logic is adapted from vita.viewerclient.receiveEvent()
+ boost::phoenix::placeholders::arg1 <<
+ "import re\n"
+ "import os\n"
+ "import sys\n"
+ "\n"
+ // Don't forget that this Python script is written to some
+ // temp directory somewhere! Its __file__ is useless in
+ // finding indra/lib/python. Use our __FILE__, with
+ // raw-string syntax to deal with Windows pathnames.
+ "mydir = os.path.dirname(r'" << __FILE__ << "')\n"
+ // We expect mydir to be .../indra/llcommon/tests.
+ "sys.path.insert(0,\n"
+ " os.path.join(mydir, os.pardir, os.pardir, 'lib', 'python'))\n"
+ "from indra.base import llsd\n"
+ "\n"
+ "class ProtocolError(Exception):\n"
+ " def __init__(self, msg, data):\n"
+ " Exception.__init__(self, msg)\n"
+ " self.data = data\n"
+ "\n"
+ "class ParseError(ProtocolError):\n"
+ " pass\n"
+ "\n"
+ "def get():\n"
+ " hdr = ''\n"
+ " while ':' not in hdr and len(hdr) < 20:\n"
+ " hdr += sys.stdin.read(1)\n"
+ " if not hdr:\n"
+ " sys.exit(0)\n"
+ " if not hdr.endswith(':'):\n"
+ " raise ProtocolError('Expected len:data, got %r' % hdr, hdr)\n"
+ " try:\n"
+ " length = int(hdr[:-1])\n"
+ " except ValueError:\n"
+ " raise ProtocolError('Non-numeric len %r' % hdr[:-1], hdr[:-1])\n"
+ " parts = []\n"
+ " received = 0\n"
+ " while received < length:\n"
+ " parts.append(sys.stdin.read(length - received))\n"
+ " received += len(parts[-1])\n"
+ " data = ''.join(parts)\n"
+ " assert len(data) == length\n"
+ " try:\n"
+ " return llsd.parse(data)\n"
+ // Seems the old indra.base.llsd module didn't properly
+ // convert IndexError (from running off end of string) to
+ // LLSDParseError.
+ " except (IndexError, llsd.LLSDParseError), e:\n"
+ " msg = 'Bad received packet (%s)' % e\n"
+ " print >>sys.stderr, '%s, %s bytes:' % (msg, len(data))\n"
+ " showmax = 40\n"
+ // We've observed failures with very large packets;
+ // dumping the entire packet wastes time and space.
+ // But if the error states a particular byte offset,
+ // truncate to (near) that offset when dumping data.
+ " location = re.search(r' at (byte|index) ([0-9]+)', str(e))\n"
+ " if not location:\n"
+ " # didn't find offset, dump whole thing, no ellipsis\n"
+ " ellipsis = ''\n"
+ " else:\n"
+ " # found offset within error message\n"
+ " trunc = int(location.group(2)) + showmax\n"
+ " data = data[:trunc]\n"
+ " ellipsis = '... (%s more)' % (length - trunc)\n"
+ " offset = -showmax\n"
+ " for offset in xrange(0, len(data)-showmax, showmax):\n"
+ " print >>sys.stderr, '%04d: %r +' % \\\n"
+ " (offset, data[offset:offset+showmax])\n"
+ " offset += showmax\n"
+ " print >>sys.stderr, '%04d: %r%s' % \\\n"
+ " (offset, data[offset:], ellipsis)\n"
+ " raise ParseError(msg, data)\n"
+ "\n"
+ "# deal with initial stdin message\n"
+ // this will throw if the initial write to stdin doesn't
+ // follow len:data protocol, or if we couldn't find 'pump'
+ // in the dict
+ "_reply = get()['pump']\n"
+ "\n"
+ "def replypump():\n"
+ " return _reply\n"
+ "\n"
+ "def put(req):\n"
+ " sys.stdout.write(':'.join((str(len(req)), req)))\n"
+ " sys.stdout.flush()\n"
+ "\n"
+ "def send(pump, data):\n"
+ " put(llsd.format_notation(dict(pump=pump, data=data)))\n"
+ "\n"
+ "def request(pump, data):\n"
+ " # we expect 'data' is a dict\n"
+ " data['reply'] = _reply\n"
+ " send(pump, data)\n"),
+ // Get the actual pathname of the NamedExtTempFile and trim off
+ // the ".py" extension. (We could cache reader.getName() in a
+ // separate member variable, but I happen to know getName() just
+ // returns a NamedExtTempFile member rather than performing any
+ // computation, so I don't mind calling it twice.) Then take the
+ // basename.
+ reader_module(LLProcess::basename(
+ reader.getName().substr(0, reader.getName().length()-3))),
+ pPYTHON(getenv("PYTHON")),
+ PYTHON(pPYTHON? pPYTHON : "")
+ {
+ ensure("Set PYTHON to interpreter pathname", pPYTHON);
+ }
+ NamedExtTempFile reader;
+ const std::string reader_module;
+ const char* pPYTHON;
+ const std::string PYTHON;
+ };
+ typedef test_group<llleap_data> llleap_group;
+ typedef llleap_group::object object;
+ llleap_group llleapgrp("llleap");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("multiple LLLeap instances");
+ NamedTempFile script("py",
+ "import time\n"
+ "time.sleep(1)\n");
+ std::vector<LLLeap*> instances;
+ instances.push_back(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ instances.push_back(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ // In this case we're simply establishing that two LLLeap instances
+ // can coexist without throwing exceptions or bombing in any other
+ // way. Wait for them to terminate.
+ waitfor(instances);
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("stderr to log");
+ NamedTempFile script("py",
+ "import sys\n"
+ "sys.stderr.write('''Hello from Python!\n"
+ "note partial line''')\n");
+ CaptureLog log(LLError::LEVEL_INFO);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ log.messageWith("Hello from Python!");
+ log.messageWith("note partial line");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("bad stdout protocol");
+ NamedTempFile script("py",
+ "print 'Hello from Python!'\n");
+ CaptureLog log(LLError::LEVEL_WARN);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ ensure_contains("error log line",
+ log.messageWith("invalid protocol"), "Hello from Python!");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("leftover stdout");
+ NamedTempFile script("py",
+ "import sys\n"
+ // note lack of newline
+ "sys.stdout.write('Hello from Python!')\n");
+ CaptureLog log(LLError::LEVEL_WARN);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ ensure_contains("error log line",
+ log.messageWith("Discarding"), "Hello from Python!");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("bad stdout len prefix");
+ NamedTempFile script("py",
+ "import sys\n"
+ "sys.stdout.write('5a2:something')\n");
+ CaptureLog log(LLError::LEVEL_WARN);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ ensure_contains("error log line",
+ log.messageWith("invalid protocol"), "5a2:");
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("empty plugin vector");
+ std::string threw;
+ try
+ {
+ LLLeap::create("empty", StringVec());
+ }
+ CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+ ensure_contains("LLLeap::Error", threw, "no plugin");
+ // try the suppress-exception variant
+ ensure("bad launch returned non-NULL", ! LLLeap::create("empty", StringVec(), false));
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("bad launch");
+ // Synthesize bogus executable name
+ std::string BADPYTHON(PYTHON.substr(0, PYTHON.length()-1) + "x");
+ CaptureLog log;
+ std::string threw;
+ try
+ {
+ LLLeap::create("bad exe", BADPYTHON);
+ }
+ CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+ ensure_contains("LLLeap::create() didn't throw", threw, "failed");
+ log.messageWith("failed");
+ log.messageWith(BADPYTHON);
+ // try the suppress-exception variant
+ ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false));
+ }
+
+ // Generic self-contained listener: derive from this and override its
+ // call() method, then tell somebody to post on the pump named getName().
+ // Control will reach your call() override.
+ struct ListenerBase
+ {
+ // Pass the pump name you want; will tweak for uniqueness.
+ ListenerBase(const std::string& name):
+ mPump(name, true)
+ {
+ mPump.listen(name, boost::bind(&ListenerBase::call, this, _1));
+ }
+
+ virtual ~ListenerBase() {} // pacify MSVC
+
+ virtual bool call(const LLSD& request)
+ {
+ return false;
+ }
+
+ LLEventPump& getPump() { return mPump; }
+ const LLEventPump& getPump() const { return mPump; }
+
+ std::string getName() const { return mPump.getName(); }
+ void post(const LLSD& data) { mPump.post(data); }
+
+ LLEventStream mPump;
+ };
+
+ // Mimic a dummy little LLEventAPI that merely sends a reply back to its
+ // requester on the "reply" pump.
+ struct AckAPI: public ListenerBase
+ {
+ AckAPI(): ListenerBase("AckAPI") {}
+
+ virtual bool call(const LLSD& request)
+ {
+ LLEventPumps::instance().obtain(request["reply"]).post("ack");
+ return false;
+ }
+ };
+
+ // Give LLLeap script a way to post success/failure.
+ struct Result: public ListenerBase
+ {
+ Result(): ListenerBase("Result") {}
+
+ virtual bool call(const LLSD& request)
+ {
+ mData = request;
+ return false;
+ }
+
+ void ensure() const
+ {
+ tut::ensure(std::string("never posted to ") + getName(), mData.isDefined());
+ // Post an empty string for success, non-empty string is failure message.
+ tut::ensure(mData, mData.asString().empty());
+ }
+
+ LLSD mData;
+ };
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("round trip");
+ AckAPI api;
+ Result result;
+ NamedTempFile script("py",
+ boost::phoenix::placeholders::arg1 <<
+ "from " << reader_module << " import *\n"
+ // make a request on our little API
+ "request(pump='" << api.getName() << "', data={})\n"
+ // wait for its response
+ "resp = get()\n"
+ "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
+ " else 'bad: ' + str(resp)\n"
+ "send(pump='" << result.getName() << "', data=result)\n");
+ waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
+ result.ensure();
+ }
+
+ struct ReqIDAPI: public ListenerBase
+ {
+ ReqIDAPI(): ListenerBase("ReqIDAPI") {}
+
+ virtual bool call(const LLSD& request)
+ {
+ // free function from llevents.h
+ sendReply(LLSD(), request);
+ return false;
+ }
+ };
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("many small messages");
+ // It's not clear to me whether there's value in iterating many times
+ // over a send/receive loop -- I don't think that will exercise any
+ // interesting corner cases. This test first sends a large number of
+ // messages, then receives all the responses. The intent is to ensure
+ // that some of that data stream crosses buffer boundaries, loop
+ // iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
+ ReqIDAPI api;
+ Result result;
+ NamedTempFile script("py",
+ boost::phoenix::placeholders::arg1 <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Note that since reader imports llsd, this
+ // 'import *' gets us llsd too.
+ "sample = llsd.format_notation(dict(pump='" <<
+ api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
+ // The whole packet has length prefix too: "len:data"
+ "samplen = len(str(len(sample))) + 1 + len(sample)\n"
+ // guess how many messages it will take to
+ // accumulate BUFFERED_LENGTH
+ "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
+ "print >>sys.stderr, 'Sending %s requests' % count\n"
+ "for i in xrange(count):\n"
+ " request('" << api.getName() << "', dict(reqid=i))\n"
+ // The assumption in this specific test that
+ // replies will arrive in the same order as
+ // requests is ONLY valid because the API we're
+ // invoking sends replies instantly. If the API
+ // had to wait for some external event before
+ // sending its reply, replies could arrive in
+ // arbitrary order, and we'd have to tick them
+ // off from a set.
+ "result = ''\n"
+ "for i in xrange(count):\n"
+ " resp = get()\n"
+ " if resp['data']['reqid'] != i:\n"
+ " result = 'expected reqid=%s in %s' % (i, resp)\n"
+ " break\n"
+ "send(pump='" << result.getName() << "', data=result)\n");
+ waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
+ 300); // needs more realtime than most tests
+ result.ensure();
+ }
+
+ // This is the body of test<10>, extracted so we can run it over a number
+ // of large-message sizes.
+ void test_large_message(const std::string& PYTHON, const std::string& reader_module,
+ const std::string& test_name, size_t size)
+ {
+ ReqIDAPI api;
+ Result result;
+ NamedTempFile script("py",
+ boost::phoenix::placeholders::arg1 <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Generate a very large string value.
+ "desired = int(sys.argv[1])\n"
+ // 7 chars per item: 6 digits, 1 comma
+ "count = int((desired - 50)/7)\n"
+ "large = ''.join('%06d,' % i for i in xrange(count))\n"
+ // Pass 'large' as reqid because we know the API
+ // will echo reqid, and we want to receive it back.
+ "request('" << api.getName() << "', dict(reqid=large))\n"
+ "try:\n"
+ " resp = get()\n"
+ "except ParseError, e:\n"
+ " # try to find where e.data diverges from expectation\n"
+ // Normally we'd expect a 'pump' key in there,
+ // too, with value replypump(). But Python
+ // serializes keys in a different order than C++,
+ // so incoming data start with 'data'.
+ // Truthfully, though, if we get as far as 'pump'
+ // before we find a difference, something's very
+ // strange.
+ " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
+ " chunk = 40\n"
+ " for offset in xrange(0, max(len(e.data), len(expect)), chunk):\n"
+ " if e.data[offset:offset+chunk] != \\\n"
+ " expect[offset:offset+chunk]:\n"
+ " print >>sys.stderr, 'Offset %06d: expect %r,\\n'\\\n"
+ " ' get %r' %\\\n"
+ " (offset,\n"
+ " expect[offset:offset+chunk],\n"
+ " e.data[offset:offset+chunk])\n"
+ " break\n"
+ " else:\n"
+ " print >>sys.stderr, 'incoming data matches expect?!'\n"
+ " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
+ " sys.exit(1)\n"
+ "\n"
+ "echoed = resp['data']['reqid']\n"
+ "if echoed == large:\n"
+ " send('" << result.getName() << "', '')\n"
+ " sys.exit(0)\n"
+ // Here we know echoed did NOT match; try to find where
+ "for i in xrange(count):\n"
+ " start = 7*i\n"
+ " end = 7*(i+1)\n"
+ " if end > len(echoed)\\\n"
+ " or echoed[start:end] != large[start:end]:\n"
+ " send('" << result.getName() << "',\n"
+ " 'at offset %s, expected %r but got %r' %\n"
+ " (start, large[start:end], echoed[start:end]))\n"
+ "sys.exit(1)\n");
+ waitfor(LLLeap::create(test_name,
+ sv(list_of
+ (PYTHON)
+ (script.getName())
+ (stringize(size)))),
+ 180); // try a longer timeout
+ result.ensure();
+ }
+
+ struct TestLargeMessage: public std::binary_function<size_t, size_t, bool>
+ {
+ TestLargeMessage(const std::string& PYTHON_, const std::string& reader_module_,
+ const std::string& test_name_):
+ PYTHON(PYTHON_),
+ reader_module(reader_module_),
+ test_name(test_name_)
+ {}
+
+ bool operator()(size_t left, size_t right) const
+ {
+ // We don't know whether upper_bound is going to pass the "sought
+ // value" as the left or the right operand. We pass 0 as the
+ // "sought value" so we can distinguish it. Of course that means
+ // the sequence we're searching must not itself contain 0!
+ size_t size;
+ bool success;
+ if (left)
+ {
+ size = left;
+ // Consider our return value carefully. Normal binary_search
+ // (or, in our case, upper_bound) expects a container sorted
+ // in ascending order, and defaults to the std::less
+ // comparator. Our container is in fact in ascending order, so
+ // return consistently with std::less. Here we were called as
+ // compare(item, sought). If std::less were called that way,
+ // 'true' would mean to move right (to higher numbers) within
+ // the sequence: the item being considered is less than the
+ // sought value. For us, that means that test_large_message()
+ // success should return 'true'.
+ success = true;
+ }
+ else
+ {
+ size = right;
+ // Here we were called as compare(sought, item). If std::less
+ // were called that way, 'true' would mean to move left (to
+ // lower numbers) within the sequence: the sought value is
+ // less than the item being considered. For us, that means
+ // test_large_message() FAILURE should return 'true', hence
+ // test_large_message() success should return 'false'.
+ success = false;
+ }
+
+ try
+ {
+ test_large_message(PYTHON, reader_module, test_name, size);
+ std::cout << "test_large_message(" << size << ") succeeded" << std::endl;
+ return success;
+ }
+ catch (const failure& e)
+ {
+ std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
+ return ! success;
+ }
+ }
+
+ const std::string PYTHON, reader_module, test_name;
+ };
+
+ // The point of this function is to try to find a size at which
+ // test_large_message() can succeed. We still want the overall test to
+ // fail; otherwise we won't get the coder's attention -- but if
+ // test_large_message() fails, try to find a plausible size at which it
+ // DOES work.
+ void test_or_split(const std::string& PYTHON, const std::string& reader_module,
+ const std::string& test_name, size_t size)
+ {
+ try
+ {
+ test_large_message(PYTHON, reader_module, test_name, size);
+ }
+ catch (const failure& e)
+ {
+ std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
+ // If it still fails below 4K, give up: subdividing any further is
+ // pointless.
+ if (size >= 4096)
+ {
+ try
+ {
+ // Recur with half the size
+ size_t smaller(size/2);
+ test_or_split(PYTHON, reader_module, test_name, smaller);
+ // Recursive call will throw if test_large_message()
+ // failed, therefore we only reach the line below if it
+ // succeeded.
+ std::cout << "but test_large_message(" << smaller << ") succeeded" << std::endl;
+
+ // Binary search for largest size that works. But since
+ // std::binary_search() only returns bool, actually use
+ // std::upper_bound(), consistent with our desire to find
+ // the LARGEST size that works. First generate a sorted
+ // container of all the sizes we intend to try, from
+ // 'smaller' (known to work) to 'size' (known to fail). We
+ // could whomp up magic iterators to do this dynamically,
+ // without actually instantiating a vector, but for a test
+ // program this will do. At least preallocate the vector.
+ // Per TestLargeMessage comments, it's important that this
+ // vector not contain 0.
+ std::vector<size_t> sizes;
+ sizes.reserve((size - smaller)/4096 + 1);
+ for (size_t sz(smaller), szend(size); sz < szend; sz += 4096)
+ sizes.push_back(sz);
+ // our comparator
+ TestLargeMessage tester(PYTHON, reader_module, test_name);
+ // Per TestLargeMessage comments, pass 0 as the sought value.
+ std::vector<size_t>::const_iterator found =
+ std::upper_bound(sizes.begin(), sizes.end(), 0, tester);
+ if (found != sizes.end() && found != sizes.begin())
+ {
+ std::cout << "test_large_message(" << *(found - 1)
+ << ") is largest that succeeds" << std::endl;
+ }
+ else
+ {
+ std::cout << "cannot determine largest test_large_message(size) "
+ << "that succeeds" << std::endl;
+ }
+ }
+ catch (const failure&)
+ {
+ // The recursive test_or_split() call above has already
+ // handled the exception. We don't want our caller to see
+ // innermost exception; propagate outermost (below).
+ }
+ }
+ // In any case, because we reached here through failure of
+ // our original test_large_message(size) call, ensure failure
+ // propagates.
+ throw e;
+ }
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("very large message");
+ test_or_split(PYTHON, reader_module, get_test_name(), BUFFERED_LENGTH);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llmemtype_test.cpp b/indra/llcommon/tests/llmemtype_test.cpp
index 6cc5ce01ce..1f050d6dc7 100644..100755
--- a/indra/llcommon/tests/llmemtype_test.cpp
+++ b/indra/llcommon/tests/llmemtype_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-03-
* @brief Test for llmemtype.cpp.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* 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.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
new file mode 100755
index 0000000000..5ba343b183
--- /dev/null
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -0,0 +1,1262 @@
+/**
+ * @file llprocess_test.cpp
+ * @author Nat Goodspeed
+ * @date 2011-12-19
+ * @brief Test for llprocess.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llprocess.h"
+// STL headers
+#include <vector>
+#include <list>
+// std headers
+#include <fstream>
+// external library headers
+#include "llapr.h"
+#include "apr_thread_proc.h"
+#include <boost/foreach.hpp>
+#include <boost/function.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
+//#include <boost/lambda/lambda.hpp>
+//#include <boost/lambda/bind.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "../test/namedtempfile.h"
+#include "../test/catch_and_store_what_in.h"
+#include "stringize.h"
+#include "llsdutil.h"
+#include "llevents.h"
+#include "wrapllerrs.h"
+
+#if defined(LL_WINDOWS)
+#define sleep(secs) _sleep((secs) * 1000)
+#define EOL "\r\n"
+#else
+#define EOL "\n"
+#include <sys/wait.h>
+#endif
+
+//namespace lambda = boost::lambda;
+ std::string apr_strerror_helper(apr_status_t rv)
+{
+ char errbuf[256];
+ apr_strerror(rv, errbuf, sizeof(errbuf));
+ return errbuf;
+}
+
+/*****************************************************************************
+* Helpers
+*****************************************************************************/
+
+#define ensure_equals_(left, right) \
+ ensure_equals(STRINGIZE(#left << " != " << #right), (left), (right))
+
+#define aprchk(expr) aprchk_(#expr, (expr))
+static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS)
+{
+ tut::ensure_equals(STRINGIZE(call << " => " << rv << ": " << apr_strerror_helper
+ (rv)),
+ rv, expected);
+}
+
+/**
+ * Read specified file using std::getline(). It is assumed to be an error if
+ * the file is empty: don't use this function if that's an acceptable case.
+ * Last line will not end with '\n'; this is to facilitate the usual case of
+ * string compares with a single line of output.
+ * @param pathname The file to read.
+ * @param desc Optional description of the file for error message;
+ * defaults to "in <pathname>"
+ */
+static std::string readfile(const std::string& pathname, const std::string& desc="")
+{
+ std::string use_desc(desc);
+ if (use_desc.empty())
+ {
+ use_desc = STRINGIZE("in " << pathname);
+ }
+ std::ifstream inf(pathname.c_str());
+ std::string output;
+ tut::ensure(STRINGIZE("No output " << use_desc), bool(std::getline(inf, output)));
+ std::string more;
+ while (std::getline(inf, more))
+ {
+ output += '\n' + more;
+ }
+ return output;
+}
+
+/// Looping on LLProcess::isRunning() must now be accompanied by pumping
+/// "mainloop" -- otherwise the status won't update and you get an infinite
+/// loop.
+void yield(int seconds=1)
+{
+ // This function simulates waiting for another viewer frame
+ sleep(seconds);
+ LLEventPumps::instance().obtain("mainloop").post(LLSD());
+}
+
+void waitfor(LLProcess& proc, int timeout=60)
+{
+ int i = 0;
+ for ( ; i < timeout && proc.isRunning(); ++i)
+ {
+ yield();
+ }
+ tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
+ i < timeout);
+}
+
+void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
+{
+ int i = 0;
+ for ( ; i < timeout && LLProcess::isRunning(h, desc); ++i)
+ {
+ yield();
+ }
+ tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
+ i < timeout);
+}
+
+/**
+ * Construct an LLProcess to run a Python script.
+ */
+struct PythonProcessLauncher
+{
+ /**
+ * @param desc Arbitrary description for error messages
+ * @param script Python script, any form acceptable to NamedTempFile,
+ * typically either a std::string or an expression of the form
+ * (lambda::_1 << "script content with " << variable_data)
+ */
+ template <typename CONTENT>
+ PythonProcessLauncher(const std::string& desc, const CONTENT& script):
+ mDesc(desc),
+ mScript("py", script)
+ {
+ const char* PYTHON(getenv("PYTHON"));
+ tut::ensure("Set $PYTHON to the Python interpreter", PYTHON);
+
+ mParams.desc = desc + " script";
+ mParams.executable = PYTHON;
+ mParams.args.add(mScript.getName());
+ }
+
+ /// Launch Python script; verify that it launched
+ void launch()
+ {
+ mPy = LLProcess::create(mParams);
+ tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
+ }
+
+ /// Run Python script and wait for it to complete.
+ void run()
+ {
+ launch();
+ // One of the irritating things about LLProcess is that
+ // there's no API to wait for the child to terminate -- but given
+ // its use in our graphics-intensive interactive viewer, it's
+ // understandable.
+ waitfor(*mPy);
+ }
+
+ /**
+ * Run a Python script using LLProcess, expecting that it will
+ * write to the file passed as its sys.argv[1]. Retrieve that output.
+ *
+ * Until January 2012, LLProcess provided distressingly few
+ * mechanisms for a child process to communicate back to its caller --
+ * not even its return code. We've introduced a convention by which we
+ * create an empty temp file, pass the name of that file to our child
+ * as sys.argv[1] and expect the script to write its output to that
+ * file. This function implements the C++ (parent process) side of
+ * that convention.
+ */
+ std::string run_read()
+ {
+ NamedTempFile out("out", ""); // placeholder
+ // pass name of this temporary file to the script
+ mParams.args.add(out.getName());
+ run();
+ // assuming the script wrote to that file, read it
+ return readfile(out.getName(), STRINGIZE("from " << mDesc << " script"));
+ }
+
+ LLProcess::Params mParams;
+ LLProcessPtr mPy;
+ std::string mDesc;
+ NamedTempFile mScript;
+};
+
+/// convenience function for PythonProcessLauncher::run()
+template <typename CONTENT>
+static void python(const std::string& desc, const CONTENT& script)
+{
+ PythonProcessLauncher py(desc, script);
+ py.run();
+}
+
+/// convenience function for PythonProcessLauncher::run_read()
+template <typename CONTENT>
+static std::string python_out(const std::string& desc, const CONTENT& script)
+{
+ PythonProcessLauncher py(desc, script);
+ return py.run_read();
+}
+
+/// Create a temporary directory and clean it up later.
+class NamedTempDir: public boost::noncopyable
+{
+public:
+ // Use python() function to create a temp directory: I've found
+ // nothing in either Boost.Filesystem or APR quite like Python's
+ // tempfile.mkdtemp().
+ // Special extra bonus: on Mac, mkdtemp() reports a pathname
+ // starting with /var/folders/something, whereas that's really a
+ // symlink to /private/var/folders/something. Have to use
+ // realpath() to compare properly.
+ NamedTempDir():
+ mPath(python_out("mkdtemp()",
+ "from __future__ import with_statement\n"
+ "import os.path, sys, tempfile\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write(os.path.normcase(os.path.normpath(os.path.realpath(tempfile.mkdtemp()))))\n"))
+ {}
+
+ ~NamedTempDir()
+ {
+ aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp));
+ }
+
+ std::string getName() const { return mPath; }
+
+private:
+ std::string mPath;
+};
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llprocess_data
+ {
+ LLAPRPool pool;
+ };
+ typedef test_group<llprocess_data> llprocess_group;
+ typedef llprocess_group::object object;
+ llprocess_group llprocessgrp("llprocess");
+
+ struct Item
+ {
+ Item(): tries(0) {}
+ unsigned tries;
+ std::string which;
+ std::string what;
+ };
+
+/*==========================================================================*|
+#define tabent(symbol) { symbol, #symbol }
+ static struct ReasonCode
+ {
+ int code;
+ const char* name;
+ } reasons[] =
+ {
+ tabent(APR_OC_REASON_DEATH),
+ tabent(APR_OC_REASON_UNWRITABLE),
+ tabent(APR_OC_REASON_RESTART),
+ tabent(APR_OC_REASON_UNREGISTER),
+ tabent(APR_OC_REASON_LOST),
+ tabent(APR_OC_REASON_RUNNING)
+ };
+#undef tabent
+|*==========================================================================*/
+
+ struct WaitInfo
+ {
+ WaitInfo(apr_proc_t* child_):
+ child(child_),
+ rv(-1), // we haven't yet called apr_proc_wait()
+ rc(0),
+ why(apr_exit_why_e(0))
+ {}
+ apr_proc_t* child; // which subprocess
+ apr_status_t rv; // return from apr_proc_wait()
+ int rc; // child's exit code
+ apr_exit_why_e why; // APR_PROC_EXIT, APR_PROC_SIGNAL, APR_PROC_SIGNAL_CORE
+ };
+
+ void child_status_callback(int reason, void* data, int status)
+ {
+/*==========================================================================*|
+ std::string reason_str;
+ BOOST_FOREACH(const ReasonCode& rcp, reasons)
+ {
+ if (reason == rcp.code)
+ {
+ reason_str = rcp.name;
+ break;
+ }
+ }
+ if (reason_str.empty())
+ {
+ reason_str = STRINGIZE("unknown reason " << reason);
+ }
+ std::cout << "child_status_callback(" << reason_str << ")\n";
+|*==========================================================================*/
+
+ if (reason == APR_OC_REASON_DEATH || reason == APR_OC_REASON_LOST)
+ {
+ // Somewhat oddly, APR requires that you explicitly unregister
+ // even when it already knows the child has terminated.
+ apr_proc_other_child_unregister(data);
+
+ WaitInfo* wi(static_cast<WaitInfo*>(data));
+ // It's just wrong to call apr_proc_wait() here. The only way APR
+ // knows to call us with APR_OC_REASON_DEATH is that it's already
+ // reaped this child process, so calling wait() will only produce
+ // "huh?" from the OS. We must rely on the status param passed in,
+ // which unfortunately comes straight from the OS wait() call.
+// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
+ wi->rv = APR_CHILD_DONE; // fake apr_proc_wait() results
+#if defined(LL_WINDOWS)
+ wi->why = APR_PROC_EXIT;
+ wi->rc = status; // no encoding on Windows (no signals)
+#else // Posix
+ if (WIFEXITED(status))
+ {
+ wi->why = APR_PROC_EXIT;
+ wi->rc = WEXITSTATUS(status);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ wi->why = APR_PROC_SIGNAL;
+ wi->rc = WTERMSIG(status);
+ }
+ else // uh, shouldn't happen?
+ {
+ wi->why = APR_PROC_EXIT;
+ wi->rc = status; // someone else will have to decode
+ }
+#endif // Posix
+ }
+ }
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("raw APR nonblocking I/O");
+
+ // Create a script file in a temporary place.
+ NamedTempFile script("py",
+ "import sys" EOL
+ "import time" EOL
+ EOL
+ "time.sleep(2)" EOL
+ "print >>sys.stdout, 'stdout after wait'" EOL
+ "sys.stdout.flush()" EOL
+ "time.sleep(2)" EOL
+ "print >>sys.stderr, 'stderr after wait'" EOL
+ "sys.stderr.flush()" EOL
+ );
+
+ // Arrange to track the history of our interaction with child: what we
+ // fetched, which pipe it came from, how many tries it took before we
+ // got it.
+ std::vector<Item> history;
+ history.push_back(Item());
+
+ // Run the child process.
+ apr_procattr_t *procattr = NULL;
+ aprchk(apr_procattr_create(&procattr, pool.getAPRPool()));
+ aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK));
+ aprchk(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));
+
+ std::vector<const char*> argv;
+ apr_proc_t child;
+ argv.push_back("python");
+ // Have to have a named copy of this std::string so its c_str() value
+ // will persist.
+ std::string scriptname(script.getName());
+ argv.push_back(scriptname.c_str());
+ argv.push_back(NULL);
+
+ aprchk(apr_proc_create(&child, argv[0],
+ &argv[0],
+ NULL, // if we wanted to pass explicit environment
+ procattr,
+ pool.getAPRPool()));
+
+ // We do not want this child process to outlive our APR pool. On
+ // destruction of the pool, forcibly kill the process. Tell APR to try
+ // SIGTERM and wait 3 seconds. If that didn't work, use SIGKILL.
+ apr_pool_note_subprocess(pool.getAPRPool(), &child, APR_KILL_AFTER_TIMEOUT);
+
+ // arrange to call child_status_callback()
+ WaitInfo wi(&child);
+ apr_proc_other_child_register(&child, child_status_callback, &wi, child.in, pool.getAPRPool());
+
+ // TODO:
+ // Stuff child.in until it (would) block to verify EWOULDBLOCK/EAGAIN.
+ // Have child script clear it later, then write one more line to prove
+ // that it gets through.
+
+ // Monitor two different output pipes. Because one will be closed
+ // before the other, keep them in a list so we can drop whichever of
+ // them is closed first.
+ typedef std::pair<std::string, apr_file_t*> DescFile;
+ typedef std::list<DescFile> DescFileList;
+ DescFileList outfiles;
+ outfiles.push_back(DescFile("out", child.out));
+ outfiles.push_back(DescFile("err", child.err));
+
+ while (! outfiles.empty())
+ {
+ // This peculiar for loop is designed to let us erase(dfli). With
+ // a list, that invalidates only dfli itself -- but even so, we
+ // lose the ability to increment it for the next item. So at the
+ // top of every loop, while dfli is still valid, increment
+ // dflnext. Then before the next iteration, set dfli to dflnext.
+ for (DescFileList::iterator
+ dfli(outfiles.begin()), dflnext(outfiles.begin()), dflend(outfiles.end());
+ dfli != dflend; dfli = dflnext)
+ {
+ // Only valid to increment dflnext once we're sure it's not
+ // already at dflend.
+ ++dflnext;
+
+ char buf[4096];
+
+ apr_status_t rv = apr_file_gets(buf, sizeof(buf), dfli->second);
+ if (APR_STATUS_IS_EOF(rv))
+ {
+// std::cout << "(EOF on " << dfli->first << ")\n";
+// history.back().which = dfli->first;
+// history.back().what = "*eof*";
+// history.push_back(Item());
+ outfiles.erase(dfli);
+ continue;
+ }
+ if (rv == EWOULDBLOCK || rv == EAGAIN)
+ {
+// std::cout << "(waiting; apr_file_gets(" << dfli->first << ") => " << rv << ": " << manager.strerror(rv) << ")\n";
+ ++history.back().tries;
+ continue;
+ }
+ aprchk_("apr_file_gets(buf, sizeof(buf), dfli->second)", rv);
+ // Is it even possible to get APR_SUCCESS but read 0 bytes?
+ // Hope not, but defend against that anyway.
+ if (buf[0])
+ {
+// std::cout << dfli->first << ": " << buf;
+ history.back().which = dfli->first;
+ history.back().what.append(buf);
+ if (buf[strlen(buf) - 1] == '\n')
+ history.push_back(Item());
+ else
+ {
+ // Just for pretty output... if we only read a partial
+ // line, terminate it.
+// std::cout << "...\n";
+ }
+ }
+ }
+ // Do this once per tick, as we expect the viewer will
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+ sleep(1);
+ }
+ apr_file_close(child.in);
+ apr_file_close(child.out);
+ apr_file_close(child.err);
+
+ // Okay, we've broken the loop because our pipes are all closed. If we
+ // haven't yet called wait, give the callback one more chance. This
+ // models the fact that unlike this small test program, the viewer
+ // will still be running.
+ if (wi.rv == -1)
+ {
+ std::cout << "last gasp apr_proc_other_child_refresh_all()\n";
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+ }
+
+ if (wi.rv == -1)
+ {
+ std::cout << "child_status_callback(APR_OC_REASON_DEATH) wasn't called" << std::endl;
+ wi.rv = apr_proc_wait(wi.child, &wi.rc, &wi.why, APR_NOWAIT);
+ }
+// std::cout << "child done: rv = " << rv << " (" << manager.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
+ aprchk_("apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT)", wi.rv, APR_CHILD_DONE);
+ ensure_equals_(wi.why, APR_PROC_EXIT);
+ ensure_equals_(wi.rc, 0);
+
+ // Beyond merely executing all the above successfully, verify that we
+ // obtained expected output -- and that we duly got control while
+ // waiting, proving the non-blocking nature of these pipes.
+ try
+ {
+ unsigned i = 0;
+ ensure("blocking I/O on child pipe (0)", history[i].tries);
+ ensure_equals_(history[i].which, "out");
+ ensure_equals_(history[i].what, "stdout after wait" EOL);
+// ++i;
+// ensure_equals_(history[i].which, "out");
+// ensure_equals_(history[i].what, "*eof*");
+ ++i;
+ ensure("blocking I/O on child pipe (1)", history[i].tries);
+ ensure_equals_(history[i].which, "err");
+ ensure_equals_(history[i].what, "stderr after wait" EOL);
+// ++i;
+// ensure_equals_(history[i].which, "err");
+// ensure_equals_(history[i].what, "*eof*");
+ }
+ catch (const failure&)
+ {
+ std::cout << "History:\n";
+ BOOST_FOREACH(const Item& item, history)
+ {
+ std::string what(item.what);
+ if ((! what.empty()) && what[what.length() - 1] == '\n')
+ {
+ what.erase(what.length() - 1);
+ if ((! what.empty()) && what[what.length() - 1] == '\r')
+ {
+ what.erase(what.length() - 1);
+ what.append("\\r");
+ }
+ what.append("\\n");
+ }
+ std::cout << " " << item.which << ": '" << what << "' ("
+ << item.tries << " tries)\n";
+ }
+ std::cout << std::flush;
+ // re-raise same error; just want to enrich the output
+ throw;
+ }
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("setWorkingDirectory()");
+ // We want to test setWorkingDirectory(). But what directory is
+ // guaranteed to exist on every machine, under every OS? Have to
+ // create one. Naturally, ensure we clean it up when done.
+ NamedTempDir tempdir;
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import os, sys\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n");
+ // Before running, call setWorkingDirectory()
+ py.mParams.cwd = tempdir.getName();
+ ensure_equals("os.getcwd()", py.run_read(), tempdir.getName());
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("arguments");
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys\n"
+ // note nonstandard output-file arg!
+ "with open(sys.argv[3], 'w') as f:\n"
+ " for arg in sys.argv[1:]:\n"
+ " print >>f, arg\n");
+ // We expect that PythonProcessLauncher has already appended
+ // its own NamedTempFile to mParams.args (sys.argv[0]).
+ py.mParams.args.add("first arg"); // sys.argv[1]
+ py.mParams.args.add("second arg"); // sys.argv[2]
+ // run_read() appends() one more argument, hence [3]
+ std::string output(py.run_read());
+ boost::split_iterator<std::string::const_iterator>
+ li(output, boost::first_finder("\n")), lend;
+ ensure("didn't get first arg", li != lend);
+ std::string arg(li->begin(), li->end());
+ ensure_equals(arg, "first arg");
+ ++li;
+ ensure("didn't get second arg", li != lend);
+ arg.assign(li->begin(), li->end());
+ ensure_equals(arg, "second arg");
+ ++li;
+ ensure("didn't get output filename?!", li != lend);
+ arg.assign(li->begin(), li->end());
+ ensure("output filename empty?!", ! arg.empty());
+ ++li;
+ ensure("too many args", li == lend);
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("exit(0)");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(0)\n");
+ py.run();
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 0);
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("exit(2)");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(2)\n");
+ py.run();
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 2);
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("syntax_error");
+ PythonProcessLauncher py(get_test_name(),
+ "syntax_error:\n");
+ py.mParams.files.add(LLProcess::FileParam()); // inherit stdin
+ py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
+ py.mParams.files.add(LLProcess::FileParam().type("pipe")); // pipe for stderr
+ py.run();
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 1);
+ std::istream& rpipe(py.mPy->getReadPipe(LLProcess::STDERR).get_istream());
+ std::vector<char> buffer(4096);
+ rpipe.read(&buffer[0], buffer.size());
+ std::streamsize got(rpipe.gcount());
+ ensure("Nothing read from stderr pipe", got);
+ std::string data(&buffer[0], got);
+ ensure("Didn't find 'SyntaxError:'", data.find("\nSyntaxError:") != std::string::npos);
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("explicit kill()");
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys, time\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ok')\n"
+ "# now sleep; expect caller to kill\n"
+ "time.sleep(120)\n"
+ "# if caller hasn't managed to kill by now, bad\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('bad')\n");
+ NamedTempFile out("out", "not started");
+ py.mParams.args.add(out.getName());
+ py.launch();
+ // Wait for the script to wake up and do its first write
+ int i = 0, timeout = 60;
+ for ( ; i < timeout; ++i)
+ {
+ yield();
+ if (readfile(out.getName(), "from kill() script") == "ok")
+ break;
+ }
+ // If we broke this loop because of the counter, something's wrong
+ ensure("script never started", i < timeout);
+ // script has performed its first write and should now be sleeping.
+ py.mPy->kill();
+ // wait for the script to terminate... one way or another.
+ waitfor(*py.mPy);
+#if LL_WINDOWS
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, -1);
+#else
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::KILLED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, SIGTERM);
+#endif
+ // If kill() failed, the script would have woken up on its own and
+ // overwritten the file with 'bad'. But if kill() succeeded, it should
+ // not have had that chance.
+ ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("implicit kill()");
+ NamedTempFile out("out", "not started");
+ LLProcess::handle phandle(0);
+ {
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys, time\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ok')\n"
+ "# now sleep; expect caller to kill\n"
+ "time.sleep(120)\n"
+ "# if caller hasn't managed to kill by now, bad\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('bad')\n");
+ py.mParams.args.add(out.getName());
+ py.launch();
+ // Capture handle for later
+ phandle = py.mPy->getProcessHandle();
+ // Wait for the script to wake up and do its first write
+ int i = 0, timeout = 60;
+ for ( ; i < timeout; ++i)
+ {
+ yield();
+ if (readfile(out.getName(), "from kill() script") == "ok")
+ break;
+ }
+ // If we broke this loop because of the counter, something's wrong
+ ensure("script never started", i < timeout);
+ // Script has performed its first write and should now be sleeping.
+ // Destroy the LLProcess, which should kill the child.
+ }
+ // wait for the script to terminate... one way or another.
+ waitfor(phandle, "kill() script");
+ // If kill() failed, the script would have woken up on its own and
+ // overwritten the file with 'bad'. But if kill() succeeded, it should
+ // not have had that chance.
+ ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("autokill=false");
+ NamedTempFile from("from", "not started");
+ NamedTempFile to("to", "");
+ LLProcess::handle phandle(0);
+ {
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys, time\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ok')\n"
+ "# wait for 'go' from test program\n"
+ "for i in xrange(60):\n"
+ " time.sleep(1)\n"
+ " with open(sys.argv[2]) as f:\n"
+ " go = f.read()\n"
+ " if go == 'go':\n"
+ " break\n"
+ "else:\n"
+ " with open(sys.argv[1], 'w') as f:\n"
+ " f.write('never saw go')\n"
+ " sys.exit(1)\n"
+ "# okay, saw 'go', write 'ack'\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ack')\n");
+ py.mParams.args.add(from.getName());
+ py.mParams.args.add(to.getName());
+ py.mParams.autokill = false;
+ py.launch();
+ // Capture handle for later
+ phandle = py.mPy->getProcessHandle();
+ // Wait for the script to wake up and do its first write
+ int i = 0, timeout = 60;
+ for ( ; i < timeout; ++i)
+ {
+ yield();
+ if (readfile(from.getName(), "from autokill script") == "ok")
+ break;
+ }
+ // If we broke this loop because of the counter, something's wrong
+ ensure("script never started", i < timeout);
+ // Now destroy the LLProcess, which should NOT kill the child!
+ }
+ // If the destructor killed the child anyway, give it time to die
+ yield(2);
+ // How do we know it's not terminated? By making it respond to
+ // a specific stimulus in a specific way.
+ {
+ std::ofstream outf(to.getName().c_str());
+ outf << "go";
+ } // flush and close.
+ // now wait for the script to terminate... one way or another.
+ waitfor(phandle, "autokill script");
+ // If the LLProcess destructor implicitly called kill(), the
+ // script could not have written 'ack' as we expect.
+ ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack");
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("'bogus' test");
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam("bogus"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'bogus'", ! py.mPy);
+ std::string message(recorder.messageWith("bogus"));
+ ensure_contains("did not name 'stdin'", message, "stdin");
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ set_test_name("'file' test");
+ // Replace this test with one or more real 'file' tests when we
+ // implement 'file' support
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam("file"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'file'", ! py.mPy);
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ set_test_name("'tpipe' test");
+ // Replace this test with one or more real 'tpipe' tests when we
+ // implement 'tpipe' support
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam("tpipe"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'tpipe'", ! py.mPy);
+ std::string message(recorder.messageWith("tpipe"));
+ ensure_contains("did not name 'stdout'", message, "stdout");
+ }
+
+ template<> template<>
+ void object::test<13>()
+ {
+ set_test_name("'npipe' test");
+ // Replace this test with one or more real 'npipe' tests when we
+ // implement 'npipe' support
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam("npipe"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'npipe'", ! py.mPy);
+ std::string message(recorder.messageWith("npipe"));
+ ensure_contains("did not name 'stderr'", message, "stderr");
+ }
+
+ template<> template<>
+ void object::test<14>()
+ {
+ set_test_name("internal pipe name warning");
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(7)\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe", "somename"));
+ py.run(); // verify that it did launch anyway
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 7);
+ std::string message(recorder.messageWith("not yet supported"));
+ ensure_contains("log message did not mention internal pipe name",
+ message, "somename");
+ }
+
+ /*-------------- support for "get*Pipe() validation" test --------------*/
+#define TEST_getPipe(PROCESS, GETPIPE, GETOPTPIPE, VALID, NOPIPE, BADPIPE) \
+ do \
+ { \
+ std::string threw; \
+ /* Both the following calls should work. */ \
+ (PROCESS).GETPIPE(VALID); \
+ ensure(#GETOPTPIPE "(" #VALID ") failed", bool((PROCESS).GETOPTPIPE(VALID))); \
+ /* pass obviously bogus PIPESLOT */ \
+ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(LLProcess::FILESLOT(4))); \
+ ensure_contains("didn't reject bad slot", threw, "no slot"); \
+ ensure_contains("didn't mention bad slot num", threw, "4"); \
+ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(LLProcess::FILESLOT(4))); \
+ /* pass NOPIPE */ \
+ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(NOPIPE)); \
+ ensure_contains("didn't reject non-pipe", threw, "not a monitored"); \
+ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(NOPIPE)); \
+ /* pass BADPIPE: FILESLOT isn't empty but wrong direction */ \
+ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(BADPIPE)); \
+ /* sneaky: GETPIPE is getReadPipe or getWritePipe */ \
+ /* so skip "get" to obtain ReadPipe or WritePipe :-P */ \
+ ensure_contains("didn't reject wrong pipe", threw, (#GETPIPE)+3); \
+ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(BADPIPE)); \
+ } while (0)
+
+/// For expecting exceptions. Execute CODE, catch EXCEPTION, store its what()
+/// in std::string THREW, ensure it's not empty (i.e. EXCEPTION did happen).
+#define CATCH_IN(THREW, EXCEPTION, CODE) \
+ do \
+ { \
+ (THREW).clear(); \
+ try \
+ { \
+ CODE; \
+ } \
+ CATCH_AND_STORE_WHAT_IN(THREW, EXCEPTION) \
+ ensure("failed to throw " #EXCEPTION ": " #CODE, ! (THREW).empty()); \
+ } while (0)
+
+#define EXPECT_FAIL_WITH_LOG(EXPECT, CODE) \
+ do \
+ { \
+ CaptureLog recorder; \
+ ensure(#CODE " succeeded", ! (CODE)); \
+ recorder.messageWith(EXPECT); \
+ } while (0)
+
+ template<> template<>
+ void object::test<15>()
+ {
+ set_test_name("get*Pipe() validation");
+ PythonProcessLauncher py(get_test_name(),
+ "print 'this output is expected'\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stdin
+ py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr
+ py.run();
+ TEST_getPipe(*py.mPy, getWritePipe, getOptWritePipe,
+ LLProcess::STDIN, // VALID
+ LLProcess::STDOUT, // NOPIPE
+ LLProcess::STDERR); // BADPIPE
+ TEST_getPipe(*py.mPy, getReadPipe, getOptReadPipe,
+ LLProcess::STDERR, // VALID
+ LLProcess::STDOUT, // NOPIPE
+ LLProcess::STDIN); // BADPIPE
+ }
+
+ template<> template<>
+ void object::test<16>()
+ {
+ set_test_name("talk to stdin/stdout");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys, time\n"
+ "print 'ok'\n"
+ "sys.stdout.flush()\n"
+ "# wait for 'go' from test program\n"
+ "go = sys.stdin.readline()\n"
+ "if go != 'go\\n':\n"
+ " sys.exit('expected \"go\", saw %r' % go)\n"
+ "print 'ack'\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ int i, timeout = 60;
+ for (i = 0; i < timeout && py.mPy->isRunning() && childout.size() < 3; ++i)
+ {
+ yield();
+ }
+ ensure("script never started", i < timeout);
+ ensure_equals("bad wakeup from stdin/stdout script",
+ childout.getline(), "ok");
+ // important to get the implicit flush from std::endl
+ py.mPy->getWritePipe().get_ostream() << "go" << std::endl;
+ waitfor(*py.mPy);
+ ensure("script never replied", childout.contains("\n"));
+ ensure_equals("child didn't ack", childout.getline(), "ack");
+ ensure_equals("bad child termination", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("bad child exit code", py.mPy->getStatus().mData, 0);
+ }
+
+ struct EventListener: public boost::noncopyable
+ {
+ EventListener(LLEventPump& pump)
+ {
+ mConnection =
+ pump.listen("EventListener", boost::bind(&EventListener::tick, this, _1));
+ }
+
+ bool tick(const LLSD& data)
+ {
+ mHistory.push_back(data);
+ return false;
+ }
+
+ std::list<LLSD> mHistory;
+ LLTempBoundListener mConnection;
+ };
+
+ static bool ack(std::ostream& out, const LLSD& data)
+ {
+ out << "continue" << std::endl;
+ return false;
+ }
+
+ template<> template<>
+ void object::test<17>()
+ {
+ set_test_name("listen for ReadPipe events");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.stdout.write('abc')\n"
+ "sys.stdout.flush()\n"
+ "sys.stdin.readline()\n"
+ "sys.stdout.write('def')\n"
+ "sys.stdout.flush()\n"
+ "sys.stdin.readline()\n"
+ "sys.stdout.write('ghi\\n')\n"
+ "sys.stdout.flush()\n"
+ "sys.stdin.readline()\n"
+ "sys.stdout.write('second line\\n')\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ std::ostream& childin(py.mPy->getWritePipe(LLProcess::STDIN).get_ostream());
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ // lift the default limit; allow event to carry (some of) the actual data
+ childout.setLimit(20);
+ // listen for incoming data on childout
+ EventListener listener(childout.getPump());
+ // also listen with a function that prompts the child to continue
+ // every time we see output
+ LLTempBoundListener connection(
+ childout.getPump().listen("ack", boost::bind(ack, boost::ref(childin), _1)));
+ int i, timeout = 60;
+ // wait through stuttering first line
+ for (i = 0; i < timeout && py.mPy->isRunning() && ! childout.contains("\n"); ++i)
+ {
+ yield();
+ }
+ ensure("couldn't get first line", i < timeout);
+ // disconnect from listener
+ listener.mConnection.disconnect();
+ // finish out the run
+ waitfor(*py.mPy);
+ // now verify history
+ std::list<LLSD>::const_iterator li(listener.mHistory.begin()),
+ lend(listener.mHistory.end());
+ ensure("no events", li != lend);
+ ensure_equals("history[0]", (*li)["data"].asString(), "abc");
+ ensure_equals("history[0] len", (*li)["len"].asInteger(), 3);
+ ++li;
+ ensure("only 1 event", li != lend);
+ ensure_equals("history[1]", (*li)["data"].asString(), "abcdef");
+ ensure_equals("history[0] len", (*li)["len"].asInteger(), 6);
+ ++li;
+ ensure("only 2 events", li != lend);
+ ensure_equals("history[2]", (*li)["data"].asString(), "abcdefghi" EOL);
+ ensure_equals("history[0] len", (*li)["len"].asInteger(), 9 + sizeof(EOL) - 1);
+ ++li;
+ // We DO NOT expect a whole new event for the second line because we
+ // disconnected.
+ ensure("more than 3 events", li == lend);
+ }
+
+ template<> template<>
+ void object::test<18>()
+ {
+ set_test_name("ReadPipe \"eof\" event");
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello from Python!'\n");
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ EventListener listener(childout.getPump());
+ waitfor(*py.mPy);
+ // We can't be positive there will only be a single event, if the OS
+ // (or any other intervening layer) does crazy buffering. What we want
+ // to ensure is that there was exactly ONE event with "eof" true, and
+ // that it was the LAST event.
+ std::list<LLSD>::const_reverse_iterator rli(listener.mHistory.rbegin()),
+ rlend(listener.mHistory.rend());
+ ensure("no events", rli != rlend);
+ ensure("last event not \"eof\"", (*rli)["eof"].asBoolean());
+ while (++rli != rlend)
+ {
+ ensure("\"eof\" event not last", ! (*rli)["eof"].asBoolean());
+ }
+ }
+
+ template<> template<>
+ void object::test<19>()
+ {
+ set_test_name("setLimit()");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.stdout.write(sys.argv[1])\n");
+ std::string abc("abcdefghijklmnopqrstuvwxyz");
+ py.mParams.args.add(abc);
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ // listen for incoming data on childout
+ EventListener listener(childout.getPump());
+ // but set limit
+ childout.setLimit(10);
+ ensure_equals("getLimit() after setlimit(10)", childout.getLimit(), 10);
+ // okay, pump I/O to pick up output from child
+ waitfor(*py.mPy);
+ ensure("no events", ! listener.mHistory.empty());
+ // For all we know, that data could have arrived in several different
+ // bursts... probably not, but anyway, only check the last one.
+ ensure_equals("event[\"len\"]",
+ listener.mHistory.back()["len"].asInteger(), abc.length());
+ ensure_equals("length of setLimit(10) data",
+ listener.mHistory.back()["data"].asString().length(), 10);
+ }
+
+ template<> template<>
+ void object::test<20>()
+ {
+ set_test_name("peek() ReadPipe data");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.stdout.write(sys.argv[1])\n");
+ std::string abc("abcdefghijklmnopqrstuvwxyz");
+ py.mParams.args.add(abc);
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ // okay, pump I/O to pick up output from child
+ waitfor(*py.mPy);
+ // peek() with substr args
+ ensure_equals("peek()", childout.peek(), abc);
+ ensure_equals("peek(23)", childout.peek(23), abc.substr(23));
+ ensure_equals("peek(5, 3)", childout.peek(5, 3), abc.substr(5, 3));
+ ensure_equals("peek(27, 2)", childout.peek(27, 2), "");
+ ensure_equals("peek(23, 5)", childout.peek(23, 5), "xyz");
+ // contains() -- we don't exercise as thoroughly as find() because the
+ // contains() implementation is trivially (and visibly) based on find()
+ ensure("contains(\":\")", ! childout.contains(":"));
+ ensure("contains(':')", ! childout.contains(':'));
+ ensure("contains(\"d\")", childout.contains("d"));
+ ensure("contains('d')", childout.contains('d'));
+ ensure("contains(\"klm\")", childout.contains("klm"));
+ ensure("contains(\"klx\")", ! childout.contains("klx"));
+ // find()
+ ensure("find(\":\")", childout.find(":") == LLProcess::ReadPipe::npos);
+ ensure("find(':')", childout.find(':') == LLProcess::ReadPipe::npos);
+ ensure_equals("find(\"d\")", childout.find("d"), 3);
+ ensure_equals("find('d')", childout.find('d'), 3);
+ ensure_equals("find(\"d\", 3)", childout.find("d", 3), 3);
+ ensure_equals("find('d', 3)", childout.find('d', 3), 3);
+ ensure("find(\"d\", 4)", childout.find("d", 4) == LLProcess::ReadPipe::npos);
+ ensure("find('d', 4)", childout.find('d', 4) == LLProcess::ReadPipe::npos);
+ // The case of offset == end and offset > end are different. In the
+ // first case, we can form a valid (albeit empty) iterator range and
+ // search that. In the second, guard logic in the implementation must
+ // realize we can't form a valid iterator range.
+ ensure("find(\"d\", 26)", childout.find("d", 26) == LLProcess::ReadPipe::npos);
+ ensure("find('d', 26)", childout.find('d', 26) == LLProcess::ReadPipe::npos);
+ ensure("find(\"d\", 27)", childout.find("d", 27) == LLProcess::ReadPipe::npos);
+ ensure("find('d', 27)", childout.find('d', 27) == LLProcess::ReadPipe::npos);
+ ensure_equals("find(\"ghi\")", childout.find("ghi"), 6);
+ ensure_equals("find(\"ghi\", 6)", childout.find("ghi"), 6);
+ ensure("find(\"ghi\", 7)", childout.find("ghi", 7) == LLProcess::ReadPipe::npos);
+ ensure("find(\"ghi\", 26)", childout.find("ghi", 26) == LLProcess::ReadPipe::npos);
+ ensure("find(\"ghi\", 27)", childout.find("ghi", 27) == LLProcess::ReadPipe::npos);
+ }
+
+ template<> template<>
+ void object::test<21>()
+ {
+ set_test_name("bad postend");
+ std::string pumpname("postend");
+ EventListener listener(LLEventPumps::instance().obtain(pumpname));
+ LLProcess::Params params;
+ params.desc = get_test_name();
+ params.postend = pumpname;
+ LLProcessPtr child = LLProcess::create(params);
+ ensure("shouldn't have launched", ! child);
+ ensure_equals("number of postend events", listener.mHistory.size(), 1);
+ LLSD postend(listener.mHistory.front());
+ ensure("has id", ! postend.has("id"));
+ ensure_equals("desc", postend["desc"].asString(), std::string(params.desc));
+ ensure_equals("state", postend["state"].asInteger(), LLProcess::UNSTARTED);
+ ensure("has data", ! postend.has("data"));
+ std::string error(postend["string"]);
+ // All we get from canned parameter validation is a bool, so the
+ // "validation failed" message we ourselves generate can't mention
+ // "executable" by name. Just check that it's nonempty.
+ //ensure_contains("error", error, "executable");
+ ensure("string", ! error.empty());
+ }
+
+ template<> template<>
+ void object::test<22>()
+ {
+ set_test_name("good postend");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(35)\n");
+ std::string pumpname("postend");
+ EventListener listener(LLEventPumps::instance().obtain(pumpname));
+ py.mParams.postend = pumpname;
+ py.launch();
+ LLProcess::id childid(py.mPy->getProcessID());
+ // Don't use waitfor(), which calls isRunning(); instead wait for an
+ // event on pumpname.
+ int i, timeout = 60;
+ for (i = 0; i < timeout && listener.mHistory.empty(); ++i)
+ {
+ yield();
+ }
+ ensure("no postend event", i < timeout);
+ ensure_equals("number of postend events", listener.mHistory.size(), 1);
+ LLSD postend(listener.mHistory.front());
+ ensure_equals("id", postend["id"].asInteger(), childid);
+ ensure("desc empty", ! postend["desc"].asString().empty());
+ ensure_equals("state", postend["state"].asInteger(), LLProcess::EXITED);
+ ensure_equals("data", postend["data"].asInteger(), 35);
+ std::string str(postend["string"]);
+ ensure_contains("string", str, "exited");
+ ensure_contains("string", str, "35");
+ }
+
+ struct PostendListener
+ {
+ PostendListener(LLProcess::ReadPipe& rpipe,
+ const std::string& pumpname,
+ const std::string& expect):
+ mReadPipe(rpipe),
+ mExpect(expect),
+ mTriggered(false)
+ {
+ LLEventPumps::instance().obtain(pumpname)
+ .listen("PostendListener", boost::bind(&PostendListener::postend, this, _1));
+ }
+
+ bool postend(const LLSD&)
+ {
+ mTriggered = true;
+ ensure_equals("postend listener", mReadPipe.read(mReadPipe.size()), mExpect);
+ return false;
+ }
+
+ LLProcess::ReadPipe& mReadPipe;
+ std::string mExpect;
+ bool mTriggered;
+ };
+
+ template<> template<>
+ void object::test<23>()
+ {
+ set_test_name("all data visible at postend");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ // note, no '\n' in written data
+ "sys.stdout.write('partial line')\n");
+ std::string pumpname("postend");
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.mParams.postend = pumpname;
+ py.launch();
+ PostendListener listener(py.mPy->getReadPipe(LLProcess::STDOUT),
+ pumpname,
+ "partial line");
+ waitfor(*py.mPy);
+ ensure("postend never triggered", listener.mTriggered);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llprocessor_test.cpp b/indra/llcommon/tests/llprocessor_test.cpp
new file mode 100755
index 0000000000..884e1b5e5b
--- /dev/null
+++ b/indra/llcommon/tests/llprocessor_test.cpp
@@ -0,0 +1,61 @@
+/**
+ * @file llprocessor_test.cpp
+ * @date 2010-06-01
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "../test/lltut.h"
+
+#include "../llprocessor.h"
+
+
+namespace tut
+{
+ struct processor
+ {
+ };
+
+ typedef test_group<processor> processor_t;
+ typedef processor_t::object processor_object_t;
+ tut::processor_t tut_processor("LLProcessor");
+
+ template<> template<>
+ void processor_object_t::test<1>()
+ {
+ set_test_name("LLProcessorInfo regression test");
+
+ LLProcessorInfo pi;
+ F64 freq = pi.getCPUFrequency();
+ //bool sse = pi.hasSSE();
+ //bool sse2 = pi.hasSSE2();
+ //bool alitvec = pi.hasAltivec();
+ std::string family = pi.getCPUFamilyName();
+ std::string brand = pi.getCPUBrandName();
+ //std::string steam = pi.getCPUFeatureDescription();
+
+ ensure_not_equals("Unknown Brand name", brand, "Unknown");
+ ensure_not_equals("Unknown Family name", family, "Unknown");
+ ensure("Reasonable CPU Frequency > 100 && < 10000", freq > 100 && freq < 10000);
+ }
+}
diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp
new file mode 100644
index 0000000000..12d5a695ee
--- /dev/null
+++ b/indra/llcommon/tests/llprocinfo_test.cpp
@@ -0,0 +1,91 @@
+/**
+ * @file llprocinfo_test.cpp
+ * @brief Tests for the LLProcInfo class.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../llprocinfo.h"
+
+#include "../test/lltut.h"
+#include "../lltimer.h"
+
+
+static const LLProcInfo::time_type bad_user(289375U), bad_system(275U);
+
+
+namespace tut
+{
+
+struct procinfo_test
+{
+ procinfo_test()
+ {
+ }
+};
+
+typedef test_group<procinfo_test> procinfo_group_t;
+typedef procinfo_group_t::object procinfo_object_t;
+tut::procinfo_group_t procinfo_instance("LLProcInfo");
+
+
+// Basic invocation works
+template<> template<>
+void procinfo_object_t::test<1>()
+{
+ LLProcInfo::time_type user(bad_user), system(bad_system);
+
+ set_test_name("getCPUUsage() basic function");
+
+ LLProcInfo::getCPUUsage(user, system);
+
+ ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user);
+ ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system);
+}
+
+
+// Time increases
+template<> template<>
+void procinfo_object_t::test<2>()
+{
+ LLProcInfo::time_type user(bad_user), system(bad_system);
+ LLProcInfo::time_type user2(bad_user), system2(bad_system);
+
+ set_test_name("getCPUUsage() increases over time");
+
+ LLProcInfo::getCPUUsage(user, system);
+
+ for (int i(0); i < 100000; ++i)
+ {
+ ms_sleep(0);
+ }
+
+ LLProcInfo::getCPUUsage(user2, system2);
+
+ ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true);
+ ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true);
+}
+
+
+} // end namespace tut
diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp
index 1f178d6fc9..383e6f9e0a 100644..100755
--- a/indra/llcommon/tests/llrand_test.cpp
+++ b/indra/llcommon/tests/llrand_test.cpp
@@ -3,31 +3,25 @@
* @author Phoenix
* @date 2007-01-25
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -45,7 +39,7 @@ namespace tut
typedef test_group<random> random_t;
typedef random_t::object random_object_t;
- tut::random_t tut_random("random");
+ tut::random_t tut_random("LLSeedRand");
template<> template<>
void random_object_t::test<1>()
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 6ab48ec34a..6fbb9abfc0 100644..100755
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -3,61 +3,66 @@
* @date 2006-04
* @brief LLSDSerialize unit tests
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-#if !LL_WINDOWS
+
+#include "linden_common.h"
+
+#if LL_WINDOWS
+#include <winsock2.h>
+typedef U32 uint32_t;
+#include <process.h>
+#include <io.h>
+#else
+#include <unistd.h>
#include <netinet/in.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include "llprocess.h"
#endif
-#include "linden_common.h"
+#include "boost/range.hpp"
+#include "boost/foreach.hpp"
+#include "boost/function.hpp"
+#include "boost/bind.hpp"
+#include "boost/phoenix/bind/bind_function.hpp"
+#include "boost/phoenix/core/argument.hpp"
+using namespace boost::phoenix;
+
#include "../llsd.h"
#include "../llsdserialize.h"
+#include "llsdutil.h"
#include "../llformat.h"
#include "../test/lltut.h"
+#include "../test/namedtempfile.h"
+#include "stringize.h"
-
-#if LL_WINDOWS
-#include <winsock2.h>
-typedef U32 uint32_t;
-#endif
-
-std::vector<U8> string_to_vector(std::string str)
+std::vector<U8> string_to_vector(const 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;
+ return std::vector<U8>(str.begin(), str.end());
}
namespace tut
@@ -80,7 +85,7 @@ namespace tut
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");
+ tut::sd_xml_test sd_xml_stream("LLSDXMLFormatter");
template<> template<>
void sd_xml_object::test<1>()
@@ -261,7 +266,7 @@ namespace tut
{
std::stringstream stream;
mFormatter->format(v, stream);
- //llinfos << "checkRoundTrip: length " << stream.str().length() << llendl;
+ //LL_INFOS() << "checkRoundTrip: length " << stream.str().length() << LL_ENDL;
LLSD w;
mParser->reset(); // reset() call is needed since test code re-uses mParser
mParser->parse(stream, w, stream.str().size());
@@ -458,7 +463,7 @@ namespace tut
checkRoundTrip(msg + " nested arrays", v);
v = LLSD::emptyMap();
- fillmap(v, 10, 6); // 10^6 maps
+ fillmap(v, 10, 3); // 10^6 maps
checkRoundTrip(msg + " many nested maps", v);
}
@@ -1500,5 +1505,226 @@ namespace tut
ensureBinaryAndNotation("map", test);
ensureBinaryAndXML("map", test);
}
-}
+ struct TestPythonCompatible
+ {
+ TestPythonCompatible():
+ // Note the peculiar insertion of __FILE__ into this string. Since
+ // this script is being written into a platform-dependent temp
+ // directory, we can't locate indra/lib/python relative to
+ // Python's __file__. Use __FILE__ instead, navigating relative
+ // to this C++ source file. Use Python raw-string syntax so
+ // Windows pathname backslashes won't mislead Python's string
+ // scanner.
+ import_llsd("import os.path\n"
+ "import sys\n"
+ "sys.path.insert(0,\n"
+ " os.path.join(os.path.dirname(r'" __FILE__ "'),\n"
+ " os.pardir, os.pardir, 'lib', 'python'))\n"
+ "from indra.base import llsd\n")
+ {}
+ ~TestPythonCompatible() {}
+
+ std::string import_llsd;
+
+ template <typename CONTENT>
+ void python(const std::string& desc, const CONTENT& script, int expect=0)
+ {
+ const char* PYTHON(getenv("PYTHON"));
+ ensure("Set $PYTHON to the Python interpreter", PYTHON);
+
+ NamedTempFile scriptfile("py", script);
+
+#if LL_WINDOWS
+ std::string q("\"");
+ std::string qPYTHON(q + PYTHON + q);
+ std::string qscript(q + scriptfile.getName() + q);
+ int rc = _spawnl(_P_WAIT, PYTHON, qPYTHON.c_str(), qscript.c_str(), NULL);
+ if (rc == -1)
+ {
+ char buffer[256];
+ strerror_s(buffer, errno); // C++ can infer the buffer size! :-O
+ ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false);
+ }
+ else
+ {
+ ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect);
+ }
+
+#else // LL_DARWIN, LL_LINUX
+ LLProcess::Params params;
+ params.executable = PYTHON;
+ params.args.add(scriptfile.getName());
+ LLProcessPtr py(LLProcess::create(params));
+ ensure(STRINGIZE("Couldn't launch " << desc << " script"), py);
+ // Implementing timeout would mean messing with alarm() and
+ // catching SIGALRM... later maybe...
+ int status(0);
+ if (waitpid(py->getProcessID(), &status, 0) == -1)
+ {
+ int waitpid_errno(errno);
+ ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
+ "waitpid() errno " << waitpid_errno),
+ waitpid_errno, ECHILD);
+ }
+ else
+ {
+ if (WIFEXITED(status))
+ {
+ int rc(WEXITSTATUS(status));
+ ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc),
+ rc, expect);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)),
+ false);
+ }
+ else
+ {
+ ensure(STRINGIZE(desc << " script produced impossible status " << status),
+ false);
+ }
+ }
+#endif
+ }
+ };
+
+ typedef tut::test_group<TestPythonCompatible> TestPythonCompatibleGroup;
+ typedef TestPythonCompatibleGroup::object TestPythonCompatibleObject;
+ TestPythonCompatibleGroup pycompat("LLSD serialize Python compatibility");
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<1>()
+ {
+ set_test_name("verify python()");
+ python("hello",
+ "import sys\n"
+ "sys.exit(17)\n",
+ 17); // expect nonzero rc
+ }
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<2>()
+ {
+ set_test_name("verify NamedTempFile");
+ python("platform",
+ "import sys\n"
+ "print 'Running on', sys.platform\n");
+ }
+
+ // helper for test<3>
+ static void writeLLSDArray(std::ostream& out, const LLSD& array)
+ {
+ BOOST_FOREACH(LLSD item, llsd::inArray(array))
+ {
+ LLSDSerialize::toNotation(item, out);
+ // It's important to separate with newlines because Python's llsd
+ // module doesn't support parsing from a file stream, only from a
+ // string, so we have to know how much of the file to read into a
+ // string.
+ out << '\n';
+ }
+ }
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<3>()
+ {
+ set_test_name("verify sequence to Python");
+
+ LLSD cdata(LLSDArray(17)(3.14)
+ ("This string\n"
+ "has several\n"
+ "lines."));
+
+ const char pydata[] =
+ "def verify(iterable):\n"
+ " it = iter(iterable)\n"
+ " assert it.next() == 17\n"
+ " assert abs(it.next() - 3.14) < 0.01\n"
+ " assert it.next() == '''\\\n"
+ "This string\n"
+ "has several\n"
+ "lines.'''\n"
+ " try:\n"
+ " it.next()\n"
+ " except StopIteration:\n"
+ " pass\n"
+ " else:\n"
+ " assert False, 'Too many data items'\n";
+
+ // Create an llsdXXXXXX file containing 'data' serialized to
+ // notation.
+ NamedTempFile file("llsd",
+ // NamedTempFile's boost::function constructor
+ // takes a callable. To this callable it passes the
+ // std::ostream with which it's writing the
+ // NamedTempFile.
+ boost::bind(writeLLSDArray, _1, cdata));
+
+ python("read C++ notation",
+ placeholders::arg1 <<
+ import_llsd <<
+ "def parse_each(iterable):\n"
+ " for item in iterable:\n"
+ " yield llsd.parse(item)\n" <<
+ pydata <<
+ // Don't forget raw-string syntax for Windows pathnames.
+ "verify(parse_each(open(r'" << file.getName() << "')))\n");
+ }
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<4>()
+ {
+ set_test_name("verify sequence from Python");
+
+ // Create an empty data file. This is just a placeholder for our
+ // script to write into. Create it to establish a unique name that
+ // we know.
+ NamedTempFile file("llsd", "");
+
+ python("write Python notation",
+ placeholders::arg1 <<
+ "from __future__ import with_statement\n" <<
+ import_llsd <<
+ "DATA = [\n"
+ " 17,\n"
+ " 3.14,\n"
+ " '''\\\n"
+ "This string\n"
+ "has several\n"
+ "lines.''',\n"
+ "]\n"
+ // Don't forget raw-string syntax for Windows pathnames.
+ // N.B. Using 'print' implicitly adds newlines.
+ "with open(r'" << file.getName() << "', 'w') as f:\n"
+ " for item in DATA:\n"
+ " print >>f, llsd.format_notation(item)\n");
+
+ std::ifstream inf(file.getName().c_str());
+ LLSD item;
+ // Notice that we're not doing anything special to parse out the
+ // newlines: LLSDSerialize::fromNotation ignores them. While it would
+ // seem they're not strictly necessary, going in this direction, we
+ // want to ensure that notation-separated-by-newlines works in both
+ // directions -- since in practice, a given file might be read by
+ // either language.
+ ensure_equals("Failed to read LLSD::Integer from Python",
+ LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+ 1);
+ ensure_equals(item.asInteger(), 17);
+ ensure_equals("Failed to read LLSD::Real from Python",
+ LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+ 1);
+ ensure_approximately_equals("Bad LLSD::Real value from Python",
+ item.asReal(), 3.14, 7); // 7 bits ~= 0.01
+ ensure_equals("Failed to read LLSD::String from Python",
+ LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+ 1);
+ ensure_equals(item.asString(),
+ "This string\n"
+ "has several\n"
+ "lines.");
+
+ }
+}
diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp
new file mode 100755
index 0000000000..385289aefe
--- /dev/null
+++ b/indra/llcommon/tests/llsingleton_test.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llsingleton.h"
+#include "../test/lltut.h"
+
+namespace tut
+{
+ struct singleton
+ {
+ // We need a class created with the LLSingleton template to test with.
+ class LLSingletonTest: public LLSingleton<LLSingletonTest>
+ {
+
+ };
+ };
+
+ typedef test_group<singleton> singleton_t;
+ typedef singleton_t::object singleton_object_t;
+ tut::singleton_t tut_singleton("LLSingleton");
+
+ template<> template<>
+ void singleton_object_t::test<1>()
+ {
+
+ }
+ template<> template<>
+ void singleton_object_t::test<2>()
+ {
+ LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
+ ensure(singleton_test);
+ }
+ template<> template<>
+ void singleton_object_t::test<3>()
+ {
+ //Construct the instance
+ LLSingletonTest::getInstance();
+ ensure(LLSingletonTest::instanceExists());
+
+ //Delete the instance
+ LLSingletonTest::deleteSingleton();
+ ensure(LLSingletonTest::destroyed());
+ ensure(!LLSingletonTest::instanceExists());
+
+ //Construct it again.
+ LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
+ ensure(singleton_test);
+ ensure(LLSingletonTest::instanceExists());
+ }
+}
diff --git a/indra/llcommon/tests/llstreamqueue_test.cpp b/indra/llcommon/tests/llstreamqueue_test.cpp
new file mode 100755
index 0000000000..050ad5c5bf
--- /dev/null
+++ b/indra/llcommon/tests/llstreamqueue_test.cpp
@@ -0,0 +1,197 @@
+/**
+ * @file llstreamqueue_test.cpp
+ * @author Nat Goodspeed
+ * @date 2012-01-05
+ * @brief Test for llstreamqueue.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llstreamqueue.h"
+// STL headers
+#include <vector>
+// std headers
+// external library headers
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "stringize.h"
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llstreamqueue_data
+ {
+ llstreamqueue_data():
+ // we want a buffer with actual bytes in it, not an empty vector
+ buffer(10)
+ {}
+ // As LLStreamQueue is merely a typedef for
+ // LLGenericStreamQueue<char>, and no logic in LLGenericStreamQueue is
+ // specific to the <char> instantiation, we're comfortable for now
+ // testing only the narrow-char version.
+ LLStreamQueue strq;
+ // buffer for use in multiple tests
+ std::vector<char> buffer;
+ };
+ typedef test_group<llstreamqueue_data> llstreamqueue_group;
+ typedef llstreamqueue_group::object object;
+ llstreamqueue_group llstreamqueuegrp("llstreamqueue");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("empty LLStreamQueue");
+ ensure_equals("brand-new LLStreamQueue isn't empty",
+ strq.size(), 0);
+ ensure_equals("brand-new LLStreamQueue returns data",
+ strq.asSource().read(&buffer[0], buffer.size()), 0);
+ strq.asSink().close();
+ ensure_equals("closed empty LLStreamQueue not at EOF",
+ strq.asSource().read(&buffer[0], buffer.size()), -1);
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("one internal block, one buffer");
+ LLStreamQueue::Sink sink(strq.asSink());
+ ensure_equals("write(\"\")", sink.write("", 0), 0);
+ ensure_equals("0 write should leave LLStreamQueue empty (size())",
+ strq.size(), 0);
+ ensure_equals("0 write should leave LLStreamQueue empty (peek())",
+ strq.peek(&buffer[0], buffer.size()), 0);
+ // The meaning of "atomic" is that it must be smaller than our buffer.
+ std::string atomic("atomic");
+ ensure("test data exceeds buffer", atomic.length() < buffer.size());
+ ensure_equals(STRINGIZE("write(\"" << atomic << "\")"),
+ sink.write(&atomic[0], atomic.length()), atomic.length());
+ ensure_equals("size() after write()", strq.size(), atomic.length());
+ size_t peeklen(strq.peek(&buffer[0], buffer.size()));
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\")"),
+ peeklen, atomic.length());
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\") result"),
+ std::string(buffer.begin(), buffer.begin() + peeklen), atomic);
+ ensure_equals("size() after peek()", strq.size(), atomic.length());
+ // peek() should not consume. Use a different buffer to prove it isn't
+ // just leftover data from the first peek().
+ std::vector<char> again(buffer.size());
+ peeklen = size_t(strq.peek(&again[0], again.size()));
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again"),
+ peeklen, atomic.length());
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again result"),
+ std::string(again.begin(), again.begin() + peeklen), atomic);
+ // now consume.
+ std::vector<char> third(buffer.size());
+ size_t readlen(strq.read(&third[0], third.size()));
+ ensure_equals(STRINGIZE("read(\"" << atomic << "\")"),
+ readlen, atomic.length());
+ ensure_equals(STRINGIZE("read(\"" << atomic << "\") result"),
+ std::string(third.begin(), third.begin() + readlen), atomic);
+ ensure_equals("peek() after read()", strq.peek(&buffer[0], buffer.size()), 0);
+ ensure_equals("size() after read()", strq.size(), 0);
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("basic skip()");
+ std::string lovecraft("lovecraft");
+ ensure("test data exceeds buffer", lovecraft.length() < buffer.size());
+ ensure_equals(STRINGIZE("write(\"" << lovecraft << "\")"),
+ strq.write(&lovecraft[0], lovecraft.length()), lovecraft.length());
+ size_t peeklen(strq.peek(&buffer[0], buffer.size()));
+ ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\")"),
+ peeklen, lovecraft.length());
+ ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\") result"),
+ std::string(buffer.begin(), buffer.begin() + peeklen), lovecraft);
+ std::streamsize skip1(4);
+ ensure_equals(STRINGIZE("skip(" << skip1 << ")"), strq.skip(skip1), skip1);
+ ensure_equals("size() after skip()", strq.size(), lovecraft.length() - skip1);
+ size_t readlen(strq.read(&buffer[0], buffer.size()));
+ ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\")"),
+ readlen, lovecraft.length() - skip1);
+ ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\") result"),
+ std::string(buffer.begin(), buffer.begin() + readlen),
+ lovecraft.substr(skip1));
+ ensure_equals("unconsumed", strq.read(&buffer[0], buffer.size()), 0);
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("skip() multiple blocks");
+ std::string blocks[] = { "books of ", "H.P. ", "Lovecraft" };
+ std::streamsize total(blocks[0].length() + blocks[1].length() + blocks[2].length());
+ std::streamsize leave(5); // len("craft") above
+ std::streamsize skip(total - leave);
+ std::streamsize written(0);
+ BOOST_FOREACH(const std::string& block, blocks)
+ {
+ written += strq.write(&block[0], block.length());
+ ensure_equals("size() after write()", strq.size(), written);
+ }
+ std::streamsize skiplen(strq.skip(skip));
+ ensure_equals(STRINGIZE("skip(" << skip << ")"), skiplen, skip);
+ ensure_equals("size() after skip()", strq.size(), leave);
+ size_t readlen(strq.read(&buffer[0], buffer.size()));
+ ensure_equals("read(\"craft\")", readlen, leave);
+ ensure_equals("read(\"craft\") result",
+ std::string(buffer.begin(), buffer.begin() + readlen), "craft");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("concatenate blocks");
+ std::string blocks[] = { "abcd", "efghij", "klmnopqrs" };
+ BOOST_FOREACH(const std::string& block, blocks)
+ {
+ strq.write(&block[0], block.length());
+ }
+ std::vector<char> longbuffer(30);
+ std::streamsize readlen(strq.read(&longbuffer[0], longbuffer.size()));
+ ensure_equals("read() multiple blocks",
+ readlen, blocks[0].length() + blocks[1].length() + blocks[2].length());
+ ensure_equals("read() multiple blocks result",
+ std::string(longbuffer.begin(), longbuffer.begin() + readlen),
+ blocks[0] + blocks[1] + blocks[2]);
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("split blocks");
+ std::string blocks[] = { "abcdefghijklm", "nopqrstuvwxyz" };
+ BOOST_FOREACH(const std::string& block, blocks)
+ {
+ strq.write(&block[0], block.length());
+ }
+ strq.close();
+ // We've already verified what strq.size() should be at this point;
+ // see above test named "skip() multiple blocks"
+ std::streamsize chksize(strq.size());
+ std::streamsize readlen(strq.read(&buffer[0], buffer.size()));
+ ensure_equals("read() 0", readlen, buffer.size());
+ ensure_equals("read() 0 result", std::string(buffer.begin(), buffer.end()), "abcdefghij");
+ chksize -= readlen;
+ ensure_equals("size() after read() 0", strq.size(), chksize);
+ readlen = strq.read(&buffer[0], buffer.size());
+ ensure_equals("read() 1", readlen, buffer.size());
+ ensure_equals("read() 1 result", std::string(buffer.begin(), buffer.end()), "klmnopqrst");
+ chksize -= readlen;
+ ensure_equals("size() after read() 1", strq.size(), chksize);
+ readlen = strq.read(&buffer[0], buffer.size());
+ ensure_equals("read() 2", readlen, chksize);
+ ensure_equals("read() 2 result",
+ std::string(buffer.begin(), buffer.begin() + readlen), "uvwxyz");
+ ensure_equals("read() 3", strq.read(&buffer[0], buffer.size()), -1);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index beba55416a..a7aa347222 100644..100755
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -4,38 +4,36 @@
* @date 2006-12-24
* @brief Test cases of llstring.cpp
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * 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
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
-#include "../test/lltut.h"
+#include <boost/assign/list_of.hpp>
#include "../llstring.h"
+#include "StringVec.h" // must come BEFORE lltut.h
+#include "../test/lltut.h"
+
+using boost::assign::list_of;
namespace tut
{
@@ -44,7 +42,7 @@ namespace tut
};
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");
+ tut::string_index_t tut_string_index("LLString");
template<> template<>
void string_index_object_t::test<1>()
@@ -630,6 +628,14 @@ namespace tut
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 on nested brackets
+ std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]");
+ ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
// Test an assorted substitution
std::string srcs8 = "foo[DELETE]bar?";
@@ -748,4 +754,118 @@ namespace tut
ensure("empty substr.", !LLStringUtil::endsWith(empty, value));
ensure("empty everything.", !LLStringUtil::endsWith(empty, empty));
}
+
+ template<> template<>
+ void string_index_object_t::test<41>()
+ {
+ set_test_name("getTokens(\"delims\")");
+ ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec());
+ ensure_equals("only delims",
+ LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec());
+ ensure_equals("sequence of delims",
+ LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one"));
+ // nat considers this a dubious implementation side effect, but I'd
+ // hate to change it now...
+ ensure_equals("noncontiguous tokens",
+ LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one"));
+ ensure_equals("space-padded tokens",
+ LLStringUtil::getTokens(", one , two ,", ","), list_of("one")("two"));
+ ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one"));
+ }
+
+ // Shorthand for verifying that getTokens() behaves the same when you
+ // don't pass a string of escape characters, when you pass an empty string
+ // (different overloads), and when you pass a string of characters that
+ // aren't actually present.
+ void ensure_getTokens(const std::string& desc,
+ const std::string& string,
+ const std::string& drop_delims,
+ const std::string& keep_delims,
+ const std::string& quotes,
+ const std::vector<std::string>& expect)
+ {
+ ensure_equals(desc + " - no esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes),
+ expect);
+ ensure_equals(desc + " - empty esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""),
+ expect);
+ ensure_equals(desc + " - unused esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"),
+ expect);
+ }
+
+ void ensure_getTokens(const std::string& desc,
+ const std::string& string,
+ const std::string& drop_delims,
+ const std::string& keep_delims,
+ const std::vector<std::string>& expect)
+ {
+ ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<42>()
+ {
+ set_test_name("getTokens(\"delims\", etc.)");
+ // Signatures to test in this method:
+ // getTokens(string, drop_delims, keep_delims [, quotes [, escapes]])
+ // If you omit keep_delims, you get the older function (test above).
+
+ // cases like the getTokens(string, delims) tests above
+ ensure_getTokens("empty string", "", " ", "", StringVec());
+ ensure_getTokens("only delims",
+ " \r\n ", " \r\n", "", StringVec());
+ ensure_getTokens("sequence of delims",
+ ",,, one ,,,", ", ", "", list_of("one"));
+ // Note contrast with the case in the previous method
+ ensure_getTokens("noncontiguous tokens",
+ ", ,, , one ,,,", ", ", "", list_of("one"));
+ ensure_getTokens("space-padded tokens",
+ ", one , two ,", ", ", "",
+ list_of("one")("two"));
+ ensure_getTokens("no delims", "one", ",", "", list_of("one"));
+
+ // drop_delims vs. keep_delims
+ ensure_getTokens("arithmetic",
+ " ab+def / xx* yy ", " ", "+-*/",
+ list_of("ab")("+")("def")("/")("xx")("*")("yy"));
+
+ // quotes
+ ensure_getTokens("no quotes",
+ "She said, \"Don't go.\"", " ", ",", "",
+ list_of("She")("said")(",")("\"Don't")("go.\""));
+ ensure_getTokens("quotes",
+ "She said, \"Don't go.\"", " ", ",", "\"",
+ list_of("She")("said")(",")("Don't go."));
+ ensure_getTokens("quotes and delims",
+ "run c:/'Documents and Settings'/someone", " ", "", "'",
+ list_of("run")("c:/Documents and Settings/someone"));
+ ensure_getTokens("unmatched quote",
+ "baby don't leave", " ", "", "'",
+ list_of("baby")("don't")("leave"));
+ ensure_getTokens("adjacent quoted",
+ "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'",
+ list_of("abcdef \"ghijkl' mnopqr"));
+ ensure_getTokens("quoted empty string",
+ "--set SomeVar ''", " ", "", "'",
+ list_of("--set")("SomeVar")(""));
+
+ // escapes
+ // Don't use backslash as an escape for these tests -- you'll go nuts
+ // between the C++ string scanner and getTokens() escapes. Test with
+ // something else!
+ ensure_equals("escaped delims",
+ LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"),
+ list_of(" a")("-")("dog-gone phrase"));
+ ensure_equals("escaped quotes",
+ LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"),
+ list_of("say:")("this isn't working."));
+ ensure_equals("escaped escape",
+ LLStringUtil::getTokens("want x^^2", " ", "", "", "^"),
+ list_of("want")("x^2"));
+ ensure_equals("escape at end",
+ LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"),
+ list_of("it's up")("there^"));
+ }
}
diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp
new file mode 100644
index 0000000000..0a9d85ad00
--- /dev/null
+++ b/indra/llcommon/tests/lltrace_test.cpp
@@ -0,0 +1,142 @@
+/**
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
+#include "lltracerecording.h"
+#include "../test/lltut.h"
+
+namespace LLUnits
+{
+ // using powers of 2 to allow strict floating point equality
+ LL_DECLARE_BASE_UNIT(Ounces, "oz");
+ LL_DECLARE_DERIVED_UNIT(TallCup, "", Ounces, / 12);
+ LL_DECLARE_DERIVED_UNIT(GrandeCup, "", Ounces, / 16);
+ LL_DECLARE_DERIVED_UNIT(VentiCup, "", Ounces, / 20);
+
+ LL_DECLARE_BASE_UNIT(Grams, "g");
+ LL_DECLARE_DERIVED_UNIT(Milligrams, "mg", Grams, * 1000);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Ounces);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, TallCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, GrandeCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, VentiCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Grams);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milligrams);
+
+
+namespace tut
+{
+ using namespace LLTrace;
+ struct trace
+ {
+ ThreadRecorder mRecorder;
+ };
+
+ typedef test_group<trace> trace_t;
+ typedef trace_t::object trace_object_t;
+ tut::trace_t tut_singleton("LLTrace");
+
+ static CountStatHandle<S32> sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast.");
+ static SampleStatHandle<F32Milligrams> sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient");
+ static EventStatHandle<S32Ounces> sOuncesPerCup("cupsize", "Large, huge, or ginormous");
+
+ static F32 sCaffeineLevel(0.f);
+ const F32Milligrams sCaffeinePerOz(18.f);
+
+ void drink_coffee(S32 num_cups, S32Ounces cup_size)
+ {
+ add(sCupsOfCoffeeConsumed, num_cups);
+ for (S32 i = 0; i < num_cups; i++)
+ {
+ record(sOuncesPerCup, cup_size);
+ }
+
+ sCaffeineLevel += F32Ounces(num_cups * cup_size).value() * sCaffeinePerOz.value();
+ sample(sCaffeineLevelStat, sCaffeineLevel);
+ }
+
+ // basic data collection
+ template<> template<>
+ void trace_object_t::test<1>()
+ {
+ sample(sCaffeineLevelStat, sCaffeineLevel);
+
+ Recording all_day;
+ Recording at_work;
+ Recording after_3pm;
+
+ all_day.start();
+ {
+ // warm up with one grande cup
+ drink_coffee(1, S32TallCup(1));
+
+ // go to work
+ at_work.start();
+ {
+ // drink 3 tall cups, 1 after 3 pm
+ drink_coffee(2, S32GrandeCup(1));
+ after_3pm.start();
+ drink_coffee(1, S32GrandeCup(1));
+ }
+ at_work.stop();
+ drink_coffee(1, S32VentiCup(1));
+ }
+ // don't need to stop recordings to get accurate values out of them
+ //after_3pm.stop();
+ //all_day.stop();
+
+ ensure("count stats are counted when recording is active",
+ at_work.getSum(sCupsOfCoffeeConsumed) == 3
+ && all_day.getSum(sCupsOfCoffeeConsumed) == 5
+ && after_3pm.getSum(sCupsOfCoffeeConsumed) == 2);
+ ensure("measurement sums are counted when recording is active",
+ at_work.getSum(sOuncesPerCup) == S32Ounces(48)
+ && all_day.getSum(sOuncesPerCup) == S32Ounces(80)
+ && after_3pm.getSum(sOuncesPerCup) == S32Ounces(36));
+ ensure("measurement min is specific to when recording is active",
+ at_work.getMin(sOuncesPerCup) == S32GrandeCup(1)
+ && all_day.getMin(sOuncesPerCup) == S32TallCup(1)
+ && after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1));
+ ensure("measurement max is specific to when recording is active",
+ at_work.getMax(sOuncesPerCup) == S32GrandeCup(1)
+ && all_day.getMax(sOuncesPerCup) == S32VentiCup(1)
+ && after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1));
+ ensure("sample min is specific to when recording is active",
+ at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value()
+ && all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f)
+ && after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value());
+ ensure("sample max is specific to when recording is active",
+ at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value()
+ && all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()
+ && after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value());
+ }
+
+}
diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp
index 31c70b4daa..1d619867d4 100644..100755
--- a/indra/llcommon/tests/lltreeiterators_test.cpp
+++ b/indra/llcommon/tests/lltreeiterators_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-08-20
* @brief Test of lltreeiterators.h
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -62,7 +56,7 @@ namespace tut
};
typedef test_group<iter_data> iter_group;
typedef iter_group::object iter_object;
- tut::iter_group ig("lltreeiterators");
+ tut::iter_group ig("LLTreeIterators");
} // namespace tut
/*****************************************************************************
diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp
new file mode 100644
index 0000000000..57cf9810af
--- /dev/null
+++ b/indra/llcommon/tests/llunits_test.cpp
@@ -0,0 +1,388 @@
+/**
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llunits.h"
+#include "../test/lltut.h"
+
+namespace LLUnits
+{
+ // using powers of 2 to allow strict floating point equality
+ LL_DECLARE_BASE_UNIT(Quatloos, "Quat");
+ LL_DECLARE_DERIVED_UNIT(Latinum, "Lat", Quatloos, / 4);
+ LL_DECLARE_DERIVED_UNIT(Solari, "Sol", Latinum, * 16);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Quatloos);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Latinum);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Solari);
+
+namespace LLUnits
+{
+ LL_DECLARE_BASE_UNIT(Celcius, "c");
+ LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32);
+ LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Celcius);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Fahrenheit);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kelvin);
+
+
+namespace tut
+{
+ using namespace LLUnits;
+ struct units
+ {
+ };
+
+ typedef test_group<units> units_t;
+ typedef units_t::object units_object_t;
+ tut::units_t tut_singleton("LLUnit");
+
+ // storage type conversions
+ template<> template<>
+ void units_object_t::test<1>()
+ {
+ LLUnit<F32, Quatloos> float_quatloos;
+ ensure("default float unit is zero", float_quatloos == F32Quatloos(0.f));
+
+ LLUnit<F32, Quatloos> float_initialize_quatloos(1);
+ ensure("non-zero initialized unit", float_initialize_quatloos == F32Quatloos(1.f));
+
+ LLUnit<S32, Quatloos> int_quatloos;
+ ensure("default int unit is zero", int_quatloos == S32Quatloos(0));
+
+ int_quatloos = S32Quatloos(42);
+ ensure("int assignment is preserved", int_quatloos == S32Quatloos(42));
+ float_quatloos = int_quatloos;
+ ensure("float assignment from int preserves value", float_quatloos == F32Quatloos(42.f));
+
+ int_quatloos = float_quatloos;
+ ensure("int assignment from float preserves value", int_quatloos == S32Quatloos(42));
+
+ float_quatloos = F32Quatloos(42.1f);
+ int_quatloos = float_quatloos;
+ ensure("int units truncate float units on assignment", int_quatloos == S32Quatloos(42));
+
+ LLUnit<U32, Quatloos> unsigned_int_quatloos(float_quatloos);
+ ensure("unsigned int can be initialized from signed int", unsigned_int_quatloos == S32Quatloos(42));
+
+ S32Solari int_solari(1);
+
+ float_quatloos = int_solari;
+ ensure("fractional units are preserved in conversion from integer to float type", float_quatloos == F32Quatloos(0.25f));
+
+ int_quatloos = S32Quatloos(1);
+ F32Solari float_solari = int_quatloos;
+ ensure("can convert with fractional intermediates from integer to float type", float_solari == F32Solari(4.f));
+ }
+
+ // conversions to/from base unit
+ template<> template<>
+ void units_object_t::test<2>()
+ {
+ LLUnit<F32, Quatloos> quatloos(1.f);
+ LLUnit<F32, Latinum> latinum_bars(quatloos);
+ ensure("conversion between units is automatic via initialization", latinum_bars == F32Latinum(1.f / 4.f));
+
+ latinum_bars = S32Latinum(256);
+ quatloos = latinum_bars;
+ ensure("conversion between units is automatic via assignment, and bidirectional", quatloos == S32Quatloos(1024));
+
+ LLUnit<S32, Quatloos> single_quatloo(1);
+ LLUnit<F32, Latinum> quarter_latinum = single_quatloo;
+ ensure("division of integer unit preserves fractional values when converted to float unit", quarter_latinum == F32Latinum(0.25f));
+ }
+
+ // conversions across non-base units
+ template<> template<>
+ void units_object_t::test<3>()
+ {
+ LLUnit<F32, Quatloos> quatloos(1024);
+ LLUnit<F32, Solari> solari(quatloos);
+ ensure("conversions can work between indirectly related units: Quatloos -> Latinum -> Solari", solari == S32Solari(4096));
+
+ LLUnit<F32, Latinum> latinum_bars = solari;
+ ensure("Non base units can be converted between each other", latinum_bars == S32Latinum(256));
+ }
+
+ // math operations
+ template<> template<>
+ void units_object_t::test<4>()
+ {
+ // exercise math operations
+ LLUnit<F32, Quatloos> quatloos(1.f);
+ quatloos *= 4.f;
+ ensure(quatloos == S32Quatloos(4));
+ quatloos = quatloos * 2;
+ ensure(quatloos == S32Quatloos(8));
+ quatloos = 2.f * quatloos;
+ ensure(quatloos == S32Quatloos(16));
+
+ quatloos += F32Quatloos(4.f);
+ ensure(quatloos == S32Quatloos(20));
+ quatloos += S32Quatloos(4);
+ ensure(quatloos == S32Quatloos(24));
+ quatloos = quatloos + S32Quatloos(4);
+ ensure(quatloos == S32Quatloos(28));
+ quatloos = S32Quatloos(4) + quatloos;
+ ensure(quatloos == S32Quatloos(32));
+ quatloos += quatloos * 3;
+ ensure(quatloos == S32Quatloos(128));
+
+ quatloos -= quatloos / 4 * 3;
+ ensure(quatloos == S32Quatloos(32));
+ quatloos = quatloos - S32Quatloos(8);
+ ensure(quatloos == S32Quatloos(24));
+ quatloos -= S32Quatloos(4);
+ ensure(quatloos == S32Quatloos(20));
+ quatloos -= F32Quatloos(4.f);
+ ensure(quatloos == S32Quatloos(16));
+
+ quatloos /= 2.f;
+ ensure(quatloos == S32Quatloos(8));
+ quatloos = quatloos / 4;
+ ensure(quatloos == S32Quatloos(2));
+
+ F32 ratio = quatloos / LLUnit<F32, Quatloos>(2.f);
+ ensure(ratio == 1);
+ ratio = quatloos / LLUnit<F32, Solari>(8.f);
+ ensure(ratio == 1);
+
+ quatloos += LLUnit<F32, Solari>(8.f);
+ ensure(quatloos == S32Quatloos(4));
+ quatloos -= LLUnit<F32, Latinum>(1.f);
+ ensure(quatloos == S32Quatloos(0));
+ }
+
+ // comparison operators
+ template<> template<>
+ void units_object_t::test<5>()
+ {
+ LLUnit<S32, Quatloos> quatloos(1);
+ ensure("can perform less than comparison against same type", quatloos < S32Quatloos(2));
+ ensure("can perform less than comparison against different storage type", quatloos < F32Quatloos(2.f));
+ ensure("can perform less than comparison against different units", quatloos < S32Latinum(5));
+ ensure("can perform less than comparison against different storage type and units", quatloos < F32Latinum(5.f));
+
+ ensure("can perform greater than comparison against same type", quatloos > S32Quatloos(0));
+ ensure("can perform greater than comparison against different storage type", quatloos > F32Quatloos(0.f));
+ ensure("can perform greater than comparison against different units", quatloos > S32Latinum(0));
+ ensure("can perform greater than comparison against different storage type and units", quatloos > F32Latinum(0.f));
+
+ }
+
+ bool accept_explicit_quatloos(S32Quatloos q)
+ {
+ return true;
+ }
+
+ bool accept_implicit_quatloos(S32Quatloos q)
+ {
+ return true;
+ }
+
+ // signature compatibility
+ template<> template<>
+ void units_object_t::test<6>()
+ {
+ S32Quatloos quatloos(1);
+ ensure("can pass unit values as argument", accept_explicit_quatloos(S32Quatloos(1)));
+ ensure("can pass unit values as argument", accept_explicit_quatloos(quatloos));
+ }
+
+ // implicit units
+ template<> template<>
+ void units_object_t::test<7>()
+ {
+ LLUnit<F32, Quatloos> quatloos;
+ LLUnitImplicit<F32, Quatloos> quatloos_implicit = quatloos + S32Quatloos(1);
+ ensure("can initialize implicit unit from explicit", quatloos_implicit == 1);
+
+ quatloos = quatloos_implicit;
+ ensure("can assign implicit unit to explicit unit", quatloos == S32Quatloos(1));
+ quatloos += quatloos_implicit;
+ ensure("can perform math operation using mixture of implicit and explicit units", quatloos == S32Quatloos(2));
+
+ // math operations on implicits
+ quatloos_implicit = 1;
+ ensure(quatloos_implicit == 1);
+
+ quatloos_implicit += 2;
+ ensure(quatloos_implicit == 3);
+
+ quatloos_implicit *= 2;
+ ensure(quatloos_implicit == 6);
+
+ quatloos_implicit -= 1;
+ ensure(quatloos_implicit == 5);
+
+ quatloos_implicit /= 5;
+ ensure(quatloos_implicit == 1);
+
+ quatloos_implicit = quatloos_implicit + 3 + quatloos_implicit;
+ ensure(quatloos_implicit == 5);
+
+ quatloos_implicit = 10 - quatloos_implicit - 1;
+ ensure(quatloos_implicit == 4);
+
+ quatloos_implicit = 2 * quatloos_implicit * 2;
+ ensure(quatloos_implicit == 16);
+
+ F32 one_half = quatloos_implicit / (quatloos_implicit * 2);
+ ensure(one_half == 0.5f);
+
+ // implicit conversion to POD
+ F32 float_val = quatloos_implicit;
+ ensure("implicit units convert implicitly to regular values", float_val == 16);
+
+ S32 int_val = quatloos_implicit;
+ ensure("implicit units convert implicitly to regular values", int_val == 16);
+
+ // conversion of implicits
+ LLUnitImplicit<F32, Latinum> latinum_implicit(2);
+ ensure("implicit units of different types are comparable", latinum_implicit * 2 == quatloos_implicit);
+
+ quatloos_implicit += F32Quatloos(10);
+ ensure("can add-assign explicit units", quatloos_implicit == 26);
+
+ quatloos_implicit -= F32Quatloos(10);
+ ensure("can subtract-assign explicit units", quatloos_implicit == 16);
+
+ // comparisons
+ ensure("can compare greater than implicit unit", quatloos_implicit > F32QuatloosImplicit(0.f));
+ ensure("can compare greater than non-implicit unit", quatloos_implicit > F32Quatloos(0.f));
+ ensure("can compare greater than or equal to implicit unit", quatloos_implicit >= F32QuatloosImplicit(0.f));
+ ensure("can compare greater than or equal to non-implicit unit", quatloos_implicit >= F32Quatloos(0.f));
+ ensure("can compare less than implicit unit", quatloos_implicit < F32QuatloosImplicit(20.f));
+ ensure("can compare less than non-implicit unit", quatloos_implicit < F32Quatloos(20.f));
+ ensure("can compare less than or equal to implicit unit", quatloos_implicit <= F32QuatloosImplicit(20.f));
+ ensure("can compare less than or equal to non-implicit unit", quatloos_implicit <= F32Quatloos(20.f));
+ }
+
+ // precision tests
+ template<> template<>
+ void units_object_t::test<8>()
+ {
+ U32Bytes max_bytes(U32_MAX);
+ S32Megabytes mega_bytes = max_bytes;
+ ensure("max available precision is used when converting units", mega_bytes == (S32Megabytes)4095);
+
+ mega_bytes = (S32Megabytes)-5 + (U32Megabytes)1;
+ ensure("can mix signed and unsigned in units addition", mega_bytes == (S32Megabytes)-4);
+
+ mega_bytes = (U32Megabytes)5 + (S32Megabytes)-1;
+ ensure("can mix unsigned and signed in units addition", mega_bytes == (S32Megabytes)4);
+ }
+
+ // default units
+ template<> template<>
+ void units_object_t::test<9>()
+ {
+ U32Gigabytes GB(1);
+ U32Megabytes MB(GB);
+ U32Kilobytes KB(GB);
+ U32Bytes B(GB);
+
+ ensure("GB -> MB conversion", MB.value() == 1024);
+ ensure("GB -> KB conversion", KB.value() == 1024 * 1024);
+ ensure("GB -> B conversion", B.value() == 1024 * 1024 * 1024);
+
+ KB = U32Kilobytes(1);
+ U32Kilobits Kb(KB);
+ U32Bits b(KB);
+ ensure("KB -> Kb conversion", Kb.value() == 8);
+ ensure("KB -> b conversion", b.value() == 8 * 1024);
+
+ U32Days days(1);
+ U32Hours hours(days);
+ U32Minutes minutes(days);
+ U32Seconds seconds(days);
+ U32Milliseconds ms(days);
+
+ ensure("days -> hours conversion", hours.value() == 24);
+ ensure("days -> minutes conversion", minutes.value() == 24 * 60);
+ ensure("days -> seconds conversion", seconds.value() == 24 * 60 * 60);
+ ensure("days -> ms conversion", ms.value() == 24 * 60 * 60 * 1000);
+
+ U32Kilometers km(1);
+ U32Meters m(km);
+ U32Centimeters cm(km);
+ U32Millimeters mm(km);
+
+ ensure("km -> m conversion", m.value() == 1000);
+ ensure("km -> cm conversion", cm.value() == 1000 * 100);
+ ensure("km -> mm conversion", mm.value() == 1000 * 1000);
+
+ U32Gigahertz GHz(1);
+ U32Megahertz MHz(GHz);
+ U32Kilohertz KHz(GHz);
+ U32Hertz Hz(GHz);
+
+ ensure("GHz -> MHz conversion", MHz.value() == 1000);
+ ensure("GHz -> KHz conversion", KHz.value() == 1000 * 1000);
+ ensure("GHz -> Hz conversion", Hz.value() == 1000 * 1000 * 1000);
+
+ F32Radians rad(6.2831853071795f);
+ S32Degrees deg(rad);
+ ensure("radians -> degrees conversion", deg.value() == 360);
+
+ F32Percent percent(50);
+ F32Ratio ratio(percent);
+ ensure("percent -> ratio conversion", ratio.value() == 0.5f);
+
+ U32Kilotriangles ktris(1);
+ U32Triangles tris(ktris);
+ ensure("kilotriangles -> triangles conversion", tris.value() == 1000);
+ }
+
+ bool value_near(F32 value, F32 target, F32 threshold)
+ {
+ return fabsf(value - target) < threshold;
+ }
+
+ // linear transforms
+ template<> template<>
+ void units_object_t::test<10>()
+ {
+ F32Celcius float_celcius(100);
+ F32Fahrenheit float_fahrenheit(float_celcius);
+ ensure("floating point celcius -> fahrenheit conversion using linear transform", value_near(float_fahrenheit.value(), 212, 0.1f) );
+
+ float_celcius = float_fahrenheit;
+ ensure("floating point fahrenheit -> celcius conversion using linear transform (round trip)", value_near(float_celcius.value(), 100.f, 0.1f) );
+
+ S32Celcius int_celcius(100);
+ S32Fahrenheit int_fahrenheit(int_celcius);
+ ensure("integer celcius -> fahrenheit conversion using linear transform", int_fahrenheit.value() == 212);
+
+ int_celcius = int_fahrenheit;
+ ensure("integer fahrenheit -> celcius conversion using linear transform (round trip)", int_celcius.value() == 100);
+ }
+}
diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp
index 0a7c37d4b9..4c64f15ca7 100644..100755
--- a/indra/llcommon/tests/lluri_test.cpp
+++ b/indra/llcommon/tests/lluri_test.cpp
@@ -3,31 +3,25 @@
* @brief LLURI unit tests
* @date September 2006
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * 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.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -64,12 +58,12 @@ namespace tut
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>()
{
@@ -95,14 +89,14 @@ namespace tut
template<> template<>
void URITestObject::test<2>()
{
- // empty string
+ set_test_name("empty string");
checkParts(LLURI(""), "", "", "", "");
}
-
+
template<> template<>
void URITestObject::test<3>()
{
- // no scheme
+ set_test_name("no scheme");
checkParts(LLURI("foo"), "", "foo", "", "");
checkParts(LLURI("foo%3A"), "", "foo:", "", "");
}
@@ -110,7 +104,7 @@ namespace tut
template<> template<>
void URITestObject::test<4>()
{
- // scheme w/o paths
+ set_test_name("scheme w/o paths");
checkParts(LLURI("mailto:zero@ll.com"),
"mailto", "zero@ll.com", "", "");
checkParts(LLURI("silly://abc/def?foo"),
@@ -120,16 +114,16 @@ namespace tut
template<> template<>
void URITestObject::test<5>()
{
- // authority section
+ set_test_name("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", "");
}
@@ -137,13 +131,13 @@ namespace tut
template<> template<>
void URITestObject::test<6>()
{
- // path section
+ set_test_name("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/");
}
@@ -151,16 +145,16 @@ namespace tut
template<> template<>
void URITestObject::test<7>()
{
- // query string
+ set_test_name("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", "/", "?");
}
@@ -173,19 +167,44 @@ namespace tut
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");
+
+ ensure_equals(LLURI::buildHTTP("host", "").asString(),
+ "http://host");
+ ensure_equals(LLURI::buildHTTP("host", "/").asString(),
+ "http://host/");
+ ensure_equals(LLURI::buildHTTP("host", "//").asString(),
+ "http://host/");
+ ensure_equals(LLURI::buildHTTP("host", "dir name").asString(),
+ "http://host/dir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(),
+ "http://host/dir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(),
+ "http://host/dir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(),
+ "http://host/dir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(),
+ "http://host/dir%20name/subdir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(),
+ "http://host/dir%20name/subdir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(),
+ "http://host/dir%20name/subdir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(),
+ "http://host/dir%20name/subdir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(),
+ "http://host/dir%20name/subdir%20name/");
}
template<> template<>
void URITestObject::test<9>()
{
- // test unescaped path components
+ set_test_name("test unescaped path components");
LLSD path;
path.append("x@*//*$&^");
path.append("123");
@@ -196,7 +215,7 @@ namespace tut
template<> template<>
void URITestObject::test<10>()
{
- // test unescaped query components
+ set_test_name("test unescaped query components");
LLSD path;
path.append("x");
path.append("123");
@@ -211,7 +230,7 @@ namespace tut
template<> template<>
void URITestObject::test<11>()
{
- // test unescaped host components
+ set_test_name("test unescaped host components");
LLSD path;
path.append("x");
path.append("123");
@@ -222,16 +241,16 @@ namespace tut
"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
-
+ set_test_name("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", "/");
@@ -248,7 +267,7 @@ namespace tut
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789"
"-._~";
- // test escape
+ set_test_name("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",
@@ -265,13 +284,12 @@ namespace tut
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.
+ set_test_name("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(""));
@@ -281,7 +299,7 @@ namespace tut
template<> template<>
void URITestObject::test<15>()
{
- // do some round-trip tests
+ set_test_name("do some round-trip tests");
escapeRoundTrip("http://secondlife.com");
escapeRoundTrip("http://secondlife.com/url with spaces");
escapeRoundTrip("http://bad[domain]name.com/");
@@ -292,7 +310,7 @@ namespace tut
template<> template<>
void URITestObject::test<16>()
{
- // Test the default escaping
+ set_test_name("Test the default escaping");
// yes -- this mangles the url. This is expected behavior
std::string simple("http://secondlife.com");
ensure_equals(
@@ -308,7 +326,7 @@ namespace tut
template<> template<>
void URITestObject::test<17>()
{
- // do some round-trip tests with very long strings.
+ set_test_name("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"
@@ -328,7 +346,7 @@ namespace tut
"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>()
{
@@ -341,7 +359,7 @@ namespace tut
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");
@@ -356,7 +374,7 @@ namespace tut
template<> template<>
void URITestObject::test<19>()
{
- // Parse about: schemes
+ set_test_name("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(), "");
diff --git a/indra/llcommon/tests/reflection_test.cpp b/indra/llcommon/tests/reflection_test.cpp
deleted file mode 100644
index 5263e7fa64..0000000000
--- a/indra/llcommon/tests/reflection_test.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/**
- * @file reflection_test.cpp
- * @date May 2006
- * @brief Reflection 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 "../linden_common.h"
-#include "../reflective.h"
-#include "../metaclasst.h"
-#include "../metapropertyt.h"
-#include "../stdtypes.h"
-
-#include "../test/lltut.h"
-
-namespace tut
-{
- class TestAggregatedData : public LLReflective
- {
- public:
- TestAggregatedData() {;}
- virtual const LLMetaClass& getMetaClass() const;
-
- private:
- };
-
- class TestReflectionData : public LLReflective
- {
- public:
- TestReflectionData() : mInt(42), mString("foo"), mNullPtr(NULL), mPtr(new TestAggregatedData()), mRef(*(new TestAggregatedData)) {;}
- virtual ~TestReflectionData() {delete mPtr;}
- virtual const LLMetaClass& getMetaClass() const;
-
- static U32 getPropertyCount() {return 5;}
-
- private:
-
- friend class LLMetaClassT<TestReflectionData>;
- S32 mInt;
- std::string mString;
- TestAggregatedData* mNullPtr;
- TestAggregatedData* mPtr;
- TestAggregatedData mObj;
- TestAggregatedData& mRef;
- };
-}
-
-template <>
-void LLMetaClassT<tut::TestReflectionData>::reflectProperties(LLMetaClass& meta_class)
-{
- reflectProperty(meta_class, "mInt", &tut::TestReflectionData::mInt);
- reflectProperty(meta_class, "mString", &tut::TestReflectionData::mString);
- reflectPtrProperty(meta_class, "mNullPtr", &tut::TestReflectionData::mNullPtr);
- reflectPtrProperty(meta_class, "mPtr", &tut::TestReflectionData::mPtr);
- reflectProperty(meta_class, "mObj", &tut::TestReflectionData::mObj);
- //reflectProperty(meta_class, "mRef", &tut::TestReflectionData::mRef); // AARGH!
-}
-
-namespace tut
-{
- // virtual
- const LLMetaClass& TestReflectionData::getMetaClass() const
- {
- return LLMetaClassT<TestReflectionData>::instance();
- }
-
- const LLMetaClass& TestAggregatedData::getMetaClass() const
- {
- return LLMetaClassT<TestAggregatedData>::instance();
- }
-}
-
-namespace tut
-{
- typedef tut::test_group<TestReflectionData> TestReflectionGroup;
- typedef TestReflectionGroup::object TestReflectionObject;
- TestReflectionGroup gTestReflectionGroup("reflection");
-
- template<> template<>
- void TestReflectionObject::test<1>()
- {
- // Check properties can be found.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLMetaProperty* null = NULL;
- ensure_not_equals(meta_class.findProperty("mInt"), null);
- ensure_not_equals(meta_class.findProperty("mString"), null);
- }
-
- template<> template<>
- void TestReflectionObject::test<2>()
- {
- // Check non-existent property cannot be found.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLMetaProperty* null = NULL;
- ensure_equals(meta_class.findProperty("foo"), null);
- }
-
- template<> template<>
- void TestReflectionObject::test<3>()
- {
- // Check integer property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- ensure_equals(meta_class.findProperty("mInt")->getLLSD(this).asInteger(), 42);
- }
-
- template<> template<>
- void TestReflectionObject::test<4>()
- {
- // Check string property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- ensure_equals(meta_class.findProperty("mString")->getLLSD(this).asString(), std::string("foo"));
- }
-
- template<> template<>
- void TestReflectionObject::test<5>()
- {
- // Check NULL reference property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLReflective* null = NULL;
- ensure_equals(meta_class.findProperty("mNullPtr")->get(this), null);
- }
-
- template<> template<>
- void TestReflectionObject::test<6>()
- {
- // Check reference property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLReflective* null = NULL;
- const LLReflective* ref = meta_class.findProperty("mPtr")->get(this);
- ensure_not_equals(ref, null);
- }
-
- template<> template<>
- void TestReflectionObject::test<7>()
- {
- // Check reflective property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLReflective* null = NULL;
- const LLReflective* ref = meta_class.findProperty("mObj")->get(this);
- ensure_not_equals(ref, null);
- }
-
- template<> template<>
- void TestReflectionObject::test<8>()
- {
- // Check property count.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- ensure_equals(meta_class.getPropertyCount(), TestReflectionData::getPropertyCount());
- }
-
- template<> template<>
- void TestReflectionObject::test<9>()
- {
- // Check property iteration.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- U32 count = 0;
- LLMetaClass::PropertyIterator iter;
- for(iter = meta_class.beginProperties(); iter != meta_class.endProperties(); ++iter)
- {
- ++count;
- }
- ensure_equals(count, TestReflectionData::getPropertyCount());
- }
-
- template<> template<>
- void TestReflectionObject::test<10>()
- {
- // Check meta classes of different types do not compare equal.
- const LLMetaClass* reflection_data_meta_class = &(LLMetaClassT<TestReflectionData>::instance());
- const LLMetaClass* aggregated_data_meta_class = &(LLMetaClassT<TestAggregatedData>::instance());
- ensure_not_equals(reflection_data_meta_class, aggregated_data_meta_class);
- }
-
- template<> template<>
- void TestReflectionObject::test<11>()
- {
- // Check class cast checks.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- TestAggregatedData* aggregated_data = new TestAggregatedData();
- LLMetaClass::PropertyIterator iter;
- U32 exception_count = 0;
- for(iter = meta_class.beginProperties(); iter != meta_class.endProperties(); ++iter)
- {
- try
- {
- const LLMetaProperty* property = (*iter).second;
- const LLReflective* reflective = property->get(aggregated_data); // Wrong reflective type, should throw exception.
-
- // useless op to get rid of compiler warning.
- reflective = NULL;
- }
- catch(...)
- {
- ++exception_count;
- }
- }
- ensure_equals(exception_count, getPropertyCount());
-
- }
-}
diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp
index dd69787a1c..2a4ed44a67 100644..100755
--- a/indra/llcommon/tests/stringize_test.cpp
+++ b/indra/llcommon/tests/stringize_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-09-12
* @brief Test of stringize.h
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* 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
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * 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
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * 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.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -73,6 +67,8 @@ namespace tut
llsd["i"] = i;
llsd["d"] = d;
llsd["abc"] = abc;
+ def = L"def ghi";
+
}
char c;
@@ -82,11 +78,12 @@ namespace tut
float f;
double d;
std::string abc;
+ std::wstring def;
LLSD llsd;
};
typedef test_group<stringize_data> stringize_group;
typedef stringize_group::object stringize_object;
- tut::stringize_group strzgrp("stringize");
+ tut::stringize_group strzgrp("stringize_h");
template<> template<>
void stringize_object::test<1>()
@@ -98,6 +95,7 @@ namespace tut
ensure_equals(stringize(f), "3.14159");
ensure_equals(stringize(d), "3.14159");
ensure_equals(stringize(abc), "abc def");
+ ensure_equals(stringize(def), "def ghi"); //Will generate LL_WARNS() due to narrowing.
ensure_equals(stringize(llsd), "{'abc':'abc def','d':r3.14159,'i':i34}");
}
@@ -107,4 +105,20 @@ namespace tut
ensure_equals(STRINGIZE("c is " << c), "c is c");
ensure_equals(STRINGIZE(std::setprecision(4) << d), "3.142");
}
+
+ template<> template<>
+ void stringize_object::test<3>()
+ {
+ //Tests rely on validity of wstring_to_utf8str()
+ ensure_equals(wstring_to_utf8str(wstringize(c)), wstring_to_utf8str(L"c"));
+ ensure_equals(wstring_to_utf8str(wstringize(s)), wstring_to_utf8str(L"17"));
+ ensure_equals(wstring_to_utf8str(wstringize(i)), wstring_to_utf8str(L"34"));
+ ensure_equals(wstring_to_utf8str(wstringize(l)), wstring_to_utf8str(L"68"));
+ ensure_equals(wstring_to_utf8str(wstringize(f)), wstring_to_utf8str(L"3.14159"));
+ ensure_equals(wstring_to_utf8str(wstringize(d)), wstring_to_utf8str(L"3.14159"));
+ ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(L"abc def"));
+ ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(wstringize(abc.c_str())));
+ ensure_equals(wstring_to_utf8str(wstringize(def)), wstring_to_utf8str(L"def ghi"));
+ // ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}"));
+ }
} // namespace tut
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index 1001ebc466..785197ba11 100644..100755
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -4,19 +4,52 @@
* @date 2009-03-11
* @brief Define a class useful for unit tests that engage llerrs (LL_ERRS) functionality
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_WRAPLLERRS_H)
#define LL_WRAPLLERRS_H
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+#include <tut/tut.hpp>
#include "llerrorcontrol.h"
+#include "stringize.h"
+#include <boost/bind.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <string>
+#include <stdexcept>
+
+// statically reference the function in test.cpp... it's short, we could
+// replicate, but better to reuse
+extern void wouldHaveCrashed(const std::string& message);
-struct WrapLL_ERRS
+struct WrapLLErrs
{
- WrapLL_ERRS():
+ WrapLLErrs():
// Resetting Settings discards the default Recorder that writes to
// stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the
// console output of successful tests, potentially confusing things.
@@ -25,10 +58,10 @@ struct WrapLL_ERRS
mPriorFatal(LLError::getFatalFunction())
{
// Make LL_ERRS call our own operator() method
- LLError::setFatalFunction(boost::bind(&WrapLL_ERRS::operator(), this, _1));
+ LLError::setFatalFunction(boost::bind(&WrapLLErrs::operator(), this, _1));
}
- ~WrapLL_ERRS()
+ ~WrapLLErrs()
{
LLError::setFatalFunction(mPriorFatal);
LLError::restoreSettings(mPriorErrorSettings);
@@ -49,8 +82,128 @@ struct WrapLL_ERRS
}
std::string error;
- LLError::Settings* mPriorErrorSettings;
+ LLError::SettingsStoragePtr mPriorErrorSettings;
LLError::FatalFunction mPriorFatal;
};
+/**
+ * Capture log messages. This is adapted (simplified) from the one in
+ * llerror_test.cpp.
+ */
+class CaptureLogRecorder : public LLError::Recorder, public boost::noncopyable
+{
+public:
+ CaptureLogRecorder()
+ : LLError::Recorder(),
+ boost::noncopyable(),
+ mMessages()
+ {
+ }
+
+ virtual ~CaptureLogRecorder()
+ {
+ }
+
+ virtual void recordMessage(LLError::ELevel level, const std::string& message)
+ {
+ mMessages.push_back(message);
+ }
+
+ /// Don't assume the message we want is necessarily the LAST log message
+ /// emitted by the underlying code; search backwards through all messages
+ /// for the sought string.
+ std::string messageWith(const std::string& search, bool required)
+ {
+ for (MessageList::const_reverse_iterator rmi(mMessages.rbegin()), rmend(mMessages.rend());
+ rmi != rmend; ++rmi)
+ {
+ if (rmi->find(search) != std::string::npos)
+ return *rmi;
+ }
+ // failed to find any such message
+ if (! required)
+ return std::string();
+
+ throw tut::failure(STRINGIZE("failed to find '" << search
+ << "' in captured log messages:\n"
+ << boost::ref(*this)));
+ }
+
+ std::ostream& streamto(std::ostream& out) const
+ {
+ MessageList::const_iterator mi(mMessages.begin()), mend(mMessages.end());
+ if (mi != mend)
+ {
+ // handle first message separately: it doesn't get a newline
+ out << *mi++;
+ for ( ; mi != mend; ++mi)
+ {
+ // every subsequent message gets a newline
+ out << '\n' << *mi;
+ }
+ }
+ return out;
+ }
+
+private:
+ typedef std::list<std::string> MessageList;
+ MessageList mMessages;
+};
+
+/**
+ * Capture log messages. This is adapted (simplified) from the one in
+ * llerror_test.cpp.
+ */
+class CaptureLog : public boost::noncopyable
+{
+public:
+ CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG)
+ // Mostly what we're trying to accomplish by saving and resetting
+ // LLError::Settings is to bypass the default RecordToStderr and
+ // RecordToWinDebug Recorders. As these are visible only inside
+ // llerror.cpp, we can't just call LLError::removeRecorder() with
+ // each. For certain tests we need to produce, capture and examine
+ // DEBUG log messages -- but we don't want to spam the user's console
+ // with that output. If it turns out that saveAndResetSettings() has
+ // some bad effect, give up and just let the DEBUG level log messages
+ // display.
+ : boost::noncopyable(),
+ mOldSettings(LLError::saveAndResetSettings()),
+ mRecorder(new CaptureLogRecorder())
+ {
+ LLError::setFatalFunction(wouldHaveCrashed);
+ LLError::setDefaultLevel(level);
+ LLError::addRecorder(mRecorder);
+ }
+
+ ~CaptureLog()
+ {
+ LLError::removeRecorder(mRecorder);
+ LLError::restoreSettings(mOldSettings);
+ }
+
+ /// Don't assume the message we want is necessarily the LAST log message
+ /// emitted by the underlying code; search backwards through all messages
+ /// for the sought string.
+ std::string messageWith(const std::string& search, bool required=true)
+ {
+ return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required);
+ }
+
+ std::ostream& streamto(std::ostream& out) const
+ {
+ return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
+ }
+
+private:
+ LLError::SettingsStoragePtr mOldSettings;
+ LLError::RecorderPtr mRecorder;
+};
+
+inline
+std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log)
+{
+ return log.streamto(out);
+}
+
#endif /* ! defined(LL_WRAPLLERRS_H) */