summaryrefslogtreecommitdiff
path: root/indra/llcommon/tests
diff options
context:
space:
mode:
authorJonathan "Geenz" Goodman <geenz@geenzo.com>2026-08-11 21:39:19 -0400
committerJonathan "Geenz" Goodman <geenz@geenzo.com>2026-08-11 21:39:19 -0400
commit8cabc33e960ca93fdc1a32c77cf611269d95d51b (patch)
tree931fcecac10bf73214efa07cc3d704ba2bd01272 /indra/llcommon/tests
parent9b7ab49575b54523128952af6686d17eb6696725 (diff)
parentd045f9aa32789a35628aced166145c182751acbf (diff)
Merge remote-tracking branch 'origin/develop-linux' into geenz/linux-to-develop
Diffstat (limited to 'indra/llcommon/tests')
-rw-r--r--indra/llcommon/tests/commonmisc_test.cpp4
-rw-r--r--indra/llcommon/tests/llallocator_heap_profile_test.cpp144
-rw-r--r--indra/llcommon/tests/llallocator_test.cpp80
-rw-r--r--indra/llcommon/tests/llapp_test.cpp162
-rw-r--r--indra/llcommon/tests/lldependencies_test.cpp12
-rw-r--r--indra/llcommon/tests/lldoubledispatch_test.cpp240
-rw-r--r--indra/llcommon/tests/llerror_test.cpp4
-rw-r--r--indra/llcommon/tests/llevents_test.cpp506
-rw-r--r--indra/llcommon/tests/llfile_test.cpp299
-rw-r--r--indra/llcommon/tests/llhttpdate_test.cpp162
-rw-r--r--indra/llcommon/tests/lllazy_test.cpp239
-rw-r--r--indra/llcommon/tests/llleap_test.cpp20
-rw-r--r--indra/llcommon/tests/llmemtype_test.cpp117
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp56
-rw-r--r--indra/llcommon/tests/llsd_test.cpp830
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp18
-rw-r--r--indra/llcommon/tests/llsdutil_test.cpp434
-rw-r--r--indra/llcommon/tests/llstreamqueue_test.cpp194
-rw-r--r--indra/llcommon/tests/llstreamtools_test.cpp883
-rw-r--r--indra/llcommon/tests/llstring_test.cpp4
20 files changed, 3570 insertions, 838 deletions
diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp
index b1a284225e..80be26dfeb 100644
--- a/indra/llcommon/tests/commonmisc_test.cpp
+++ b/indra/llcommon/tests/commonmisc_test.cpp
@@ -503,7 +503,7 @@ namespace tut
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.4", val_str, result_str);
- val = U64L(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615
+ val = U64(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615
val_str = "18446744073709551615";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
@@ -619,7 +619,7 @@ namespace tut
val = U64_to_F64(U64L(2));
ensure_equals("U64_to_F64 converted 3.6", val, result);
- result = U64L(0x7FFFFFFFFFFFFFFF) * 1.0L; // 0x7FFFFFFFFFFFFFFF
+ result = F64(0x7FFFFFFFFFFFFFFF) * 1.0; // 0x7FFFFFFFFFFFFFFF
val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF));
ensure_equals("U64_to_F64 converted 3.7", val, result);
}
diff --git a/indra/llcommon/tests/llallocator_heap_profile_test.cpp b/indra/llcommon/tests/llallocator_heap_profile_test.cpp
deleted file mode 100644
index da8b54ffc0..0000000000
--- a/indra/llcommon/tests/llallocator_heap_profile_test.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * @file llallocator_heap_profile_test.cpp
- * @author Brad Kittenbrink
- * @date 2008-02-
- * @brief Test for llallocator_heap_profile.cpp.
- *
- * $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$
- */
-
-#include "../llallocator_heap_profile.h"
-#include "../test/lltut.h"
-
-namespace tut
-{
- struct llallocator_heap_profile_data
- {
- LLAllocatorHeapProfile prof;
-
- static char const * const sample_win_profile;
- // *TODO - get test output from mac/linux tcmalloc
- static char const * const sample_mac_profile;
- static char const * const sample_lin_profile;
-
- static char const * const crash_testcase;
- };
- typedef test_group<llallocator_heap_profile_data> factory;
- typedef factory::object object;
-}
-namespace
-{
- tut::factory llallocator_heap_profile_test_factory("LLAllocatorHeapProfile");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- prof.parse(sample_win_profile);
-
- ensure_equals("count lines", prof.mLines.size() , 5);
- ensure_equals("alloc counts", prof.mLines[0].mLiveCount, 2131854U);
- ensure_equals("alloc counts", prof.mLines[0].mLiveSize, 2245710106ULL);
- ensure_equals("alloc counts", prof.mLines[0].mTotalCount, 14069198U);
- ensure_equals("alloc counts", prof.mLines[0].mTotalSize, 4295177308ULL);
- ensure_equals("count markers", prof.mLines[0].mTrace.size(), 0);
- ensure_equals("count markers", prof.mLines[1].mTrace.size(), 0);
- ensure_equals("count markers", prof.mLines[2].mTrace.size(), 4);
- ensure_equals("count markers", prof.mLines[3].mTrace.size(), 6);
- ensure_equals("count markers", prof.mLines[4].mTrace.size(), 7);
-
- //prof.dump(std::cout);
- }
-
- template<> template<>
- void object::test<2>()
- {
- prof.parse(crash_testcase);
-
- ensure_equals("count lines", prof.mLines.size(), 2);
- ensure_equals("alloc counts", prof.mLines[0].mLiveCount, 3U);
- ensure_equals("alloc counts", prof.mLines[0].mLiveSize, 1049652ULL);
- ensure_equals("alloc counts", prof.mLines[0].mTotalCount, 8U);
- ensure_equals("alloc counts", prof.mLines[0].mTotalSize, 1049748ULL);
- ensure_equals("count markers", prof.mLines[0].mTrace.size(), 0);
- ensure_equals("count markers", prof.mLines[1].mTrace.size(), 0);
-
- //prof.dump(std::cout);
- }
-
- template<> template<>
- void object::test<3>()
- {
- // test that we don't crash on edge case data
- prof.parse("");
- ensure("emtpy on error", prof.mLines.empty());
-
- prof.parse("heap profile:");
- ensure("emtpy on error", prof.mLines.empty());
- }
-
-char const * const llallocator_heap_profile_data::sample_win_profile =
-"heap profile: 2131854: 2245710106 [14069198: 4295177308] @\n"
-"308592: 1073398388 [966564: 1280998739] @\n"
-"462651: 375969538 [1177377: 753561247] @ 2 3 6 1\n"
-"314744: 206611283 [2008722: 570934755] @ 2 3 3 7 21 32\n"
-"277152: 82862770 [621961: 168503640] @ 2 3 3 7 21 32 87\n"
-"\n"
-"MAPPED_LIBRARIES:\n"
-"00400000-02681000 r-xp 00000000 00:00 0 c:\\proj\\tcmalloc-eval-9\\indra\\build-vc80\\newview\\RelWithDebInfo\\secondlife-bin.exe\n"
-"77280000-773a7000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\ntdll.dll\n"
-"76df0000-76ecb000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\kernel32.dll\n"
-"76000000-76073000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\comdlg32.dll\n"
-"75ee0000-75f8a000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\msvcrt.dll\n"
-"76c30000-76c88000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\SHLWAPI.dll\n"
-"75f90000-75fdb000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\GDI32.dll\n"
-"77420000-774bd000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\USER32.dll\n"
-"75e10000-75ed6000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\ADVAPI32.dll\n"
-"75b00000-75bc2000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\RPCRT4.dll\n"
-"72ca0000-72d25000 r-xp 00000000 00:00 0 C:\\Windows\\WinSxS\\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.6001.18000_none_886786f450a74a05\\COMCTL32.dll\n"
-"76120000-76c30000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\SHELL32.dll\n"
-"71ce0000-71d13000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\DINPUT8.dll\n";
-
-
-char const * const llallocator_heap_profile_data::crash_testcase =
-"heap profile: 3: 1049652 [ 8: 1049748] @\n"
-" 3: 1049652 [ 8: 1049748] @\n"
-"\n"
-"MAPPED_LIBRARIES:\n"
-"00400000-004d5000 r-xp 00000000 00:00 0 c:\\code\\linden\\tcmalloc\\indra\\build-vc80\\llcommon\\RelWithDebInfo\\llallocator_test.exe\n"
-"7c900000-7c9af000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\ntdll.dll\n"
-"7c800000-7c8f6000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\kernel32.dll\n"
-"77dd0000-77e6b000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\ADVAPI32.dll\n"
-"77e70000-77f02000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\RPCRT4.dll\n"
-"77fe0000-77ff1000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\Secur32.dll\n"
-"71ab0000-71ac7000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\WS2_32.dll\n"
-"77c10000-77c68000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\msvcrt.dll\n"
-"71aa0000-71aa8000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\WS2HELP.dll\n"
-"76bf0000-76bfb000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\PSAPI.DLL\n"
-"5b860000-5b8b5000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\NETAPI32.dll\n"
-"10000000-10041000 r-xp 00000000 00:00 0 c:\\code\\linden\\tcmalloc\\indra\\build-vc80\\llcommon\\RelWithDebInfo\\libtcmalloc_minimal.dll\n"
-"7c420000-7c4a7000 r-xp 00000000 00:00 0 C:\\WINDOWS\\WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\MSVCP80.dll\n"
-"78130000-781cb000 r-xp 00000000 00:00 0 C:\\WINDOWS\\WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\MSVCR80.dll\n";
-
-}
diff --git a/indra/llcommon/tests/llallocator_test.cpp b/indra/llcommon/tests/llallocator_test.cpp
deleted file mode 100644
index a7573641c7..0000000000
--- a/indra/llcommon/tests/llallocator_test.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * @file llallocator_test.cpp
- * @author Brad Kittenbrink
- * @date 2008-02-
- * @brief Test for llallocator.cpp.
- *
- * $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$
- */
-
-#include "../llallocator.h"
-#include "../test/lltut.h"
-
-namespace tut
-{
- struct llallocator_data
- {
- LLAllocator llallocator;
- };
- typedef test_group<llallocator_data> factory;
- typedef factory::object object;
-}
-namespace
-{
- tut::factory llallocator_test_factory("LLAllocator");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- llallocator.setProfilingEnabled(false);
- ensure("Profiler disable", !llallocator.isProfiling());
- }
-
-#if LL_USE_TCMALLOC
- template<> template<>
- void object::test<2>()
- {
- llallocator.setProfilingEnabled(true);
- ensure("Profiler enable", llallocator.isProfiling());
- }
-
- template <> template <>
- void object::test<3>()
- {
- llallocator.setProfilingEnabled(true);
-
- char * test_alloc = new char[1024];
-
- llallocator.getProfile();
-
- delete [] test_alloc;
-
- llallocator.getProfile();
-
- // *NOTE - this test isn't ensuring anything right now other than no
- // exceptions are thrown.
- }
-#endif // LL_USE_TCMALLOC
-};
diff --git a/indra/llcommon/tests/llapp_test.cpp b/indra/llcommon/tests/llapp_test.cpp
new file mode 100644
index 0000000000..114af6edbf
--- /dev/null
+++ b/indra/llcommon/tests/llapp_test.cpp
@@ -0,0 +1,162 @@
+/**
+ * @file llapp_tut.cpp
+ * @author Phoenix
+ * @date 2006-09-12
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2006-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 <tut/tut.hpp>
+
+#include "linden_common.h"
+#include "llapp.h"
+#include "lltut.h"
+
+
+namespace tut
+{
+ struct application
+ {
+ class LLTestApp : public LLApp
+ {
+ public:
+ virtual bool init() { return true; }
+ virtual bool cleanup() { return true; }
+ virtual bool frame() { return true; }
+ };
+ LLTestApp* mApp;
+ application()
+ {
+ mApp = new LLTestApp;
+ }
+ ~application()
+ {
+ delete mApp;
+ }
+ };
+
+ typedef test_group<application> application_t;
+ typedef application_t::object application_object_t;
+ tut::application_t tut_application("application");
+
+ template<> template<>
+ void application_object_t::test<1>()
+ {
+ LLSD defaults;
+ defaults["template"] = "../../../scripts/messages/message_template.msg";
+ defaults["configdir"] = ".";
+ defaults["datadir"] = "data";
+ mApp->setOptionData(LLApp::PRIORITY_DEFAULT, defaults);
+
+ LLSD datadir_sd = mApp->getOption("datadir");
+ ensure_equals("data type", datadir_sd.type(), LLSD::TypeString);
+ ensure_equals(
+ "data value", datadir_sd.asString(), std::string("data"));
+ }
+
+ template<> template<>
+ void application_object_t::test<2>()
+ {
+ const int ARGC = 13;
+ const char* ARGV[ARGC] =
+ {
+ "", // argv[0] is usually the application name
+ "-crashcount",
+ "2",
+ "-space",
+ "spaceserver.grid.lindenlab.com",
+ "-db_host",
+ "localhost",
+ "--allowlslhttprequests",
+ "-asset-uri",
+ "http://test.lindenlab.com/assets",
+ "-data",
+ "127.0.0.1",
+ "--smtp"
+ };
+ bool ok = mApp->parseCommandOptions(ARGC, const_cast<char**>(ARGV));
+ ensure("command line parsed", ok);
+ ensure_equals(
+ "crashcount", mApp->getOption("crashcount").asInteger(), 2);
+ ensure_equals(
+ "space",
+ mApp->getOption("space").asString(),
+ std::string("spaceserver.grid.lindenlab.com"));
+ ensure_equals(
+ "db_host",
+ mApp->getOption("db_host").asString(),
+ std::string("localhost"));
+ ensure("allowlshlttprequests", mApp->getOption("smtp"));
+ ensure_equals(
+ "asset-uri",
+ mApp->getOption("asset-uri").asString(),
+ std::string("http://test.lindenlab.com/assets"));
+ ensure_equals(
+ "data",
+ mApp->getOption("data").asString(),
+ std::string("127.0.0.1"));
+ ensure("smtp", mApp->getOption("smtp"));
+ }
+
+ template<> template<>
+ void application_object_t::test<3>()
+ {
+ const int ARGC = 4;
+ const char* ARGV[ARGC] =
+ {
+ "", // argv[0] is usually the application name
+ "crashcount",
+ "2",
+ "--space"
+ };
+ bool ok = mApp->parseCommandOptions(ARGC, const_cast<char**>(ARGV));
+ ensure("command line parse failure", !ok);
+ }
+
+ template<> template<>
+ void application_object_t::test<4>()
+ {
+ const int ARGC = 4;
+ const char* ARGV[ARGC] =
+ {
+ "", // argv[0] is usually the application name
+ "--crashcount",
+ "2",
+ "space"
+ };
+ bool ok = mApp->parseCommandOptions(ARGC, const_cast<char**>(ARGV));
+ ensure("command line parse failure", !ok);
+ }
+
+
+ template<> template<>
+ void application_object_t::test<5>()
+ {
+ LLSD options;
+ options["boolean-test"] = true;
+ mApp->setOptionData(LLApp::PRIORITY_GENERAL_CONFIGURATION, options);
+ ensure("bool set", mApp->getOption("boolean-test").asBoolean());
+ options["boolean-test"] = false;
+ mApp->setOptionData(LLApp::PRIORITY_RUNTIME_OVERRIDE, options);
+ ensure("bool unset", !mApp->getOption("boolean-test").asBoolean());
+ }
+}
diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp
index 2fea92e3b7..fd554005a3 100644
--- a/indra/llcommon/tests/lldependencies_test.cpp
+++ b/indra/llcommon/tests/lldependencies_test.cpp
@@ -228,19 +228,7 @@ namespace tut
StringList keys(StringList{ "The", "brown", "dog.", "fox", "jumps", "lazy", "over", "quick", "the", "yellow" });
ensure_equals(instance_from_range<StringList>(deps.get_key_range()), keys);
-#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
- // This is the succinct way, works on modern compilers
ensure_equals(instance_from_range<StringList>(make_transform_range(deps.get_range(), extract_key)), keys);
-#else // gcc 3.3
- StringDeps::range got_range(deps.get_range());
- StringDeps::iterator kni = got_range.begin(), knend = got_range.end();
- StringList::iterator ki = keys.begin(), kend = keys.end();
- for ( ; kni != knend && ki != kend; ++kni, ++ki)
- {
- ensure_equals(kni->first, *ki);
- }
- ensure("get_range() returns proper length", kni == knend && ki == kend);
-#endif // gcc 3.3
// blow off get_node_range() because they're all LLDependenciesEmpty instances
}
diff --git a/indra/llcommon/tests/lldoubledispatch_test.cpp b/indra/llcommon/tests/lldoubledispatch_test.cpp
new file mode 100644
index 0000000000..79c4edc058
--- /dev/null
+++ b/indra/llcommon/tests/lldoubledispatch_test.cpp
@@ -0,0 +1,240 @@
+/**
+ * @file lldoubledispatch_tut.cpp
+ * @author Nat Goodspeed
+ * @date 2008-11-13
+ * @brief Test for lldoubledispatch.h
+ *
+ * This program tests the DoubleDispatch class, using a variation on the example
+ * from Scott Meyers' "More Effective C++", Item 31.
+ *
+ * $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$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lldoubledispatch.h"
+// STL headers
+// std headers
+#include <iostream>
+#include <memory>
+#include <string>
+#include <typeinfo>
+// external library headers
+// other Linden headers
+#include "lltut.h"
+
+
+/*---------------------------- Class hierarchy -----------------------------*/
+// All objects are GameObjects.
+class GameObject
+{
+public:
+ GameObject(const std::string& name): mName(name) {}
+ virtual ~GameObject() {}
+ virtual std::string stringize() { return std::string(typeid(*this).name()) + " " + mName; }
+
+protected:
+ std::string mName;
+};
+
+// SpaceStation, Asteroid and SpaceShip are peer GameObjects.
+struct SpaceStation: public GameObject
+{
+ SpaceStation(const std::string& name): GameObject(name) {}
+ // Only a dummy SpaceStation is constructed without a name
+ SpaceStation(): GameObject("dummy") {}
+};
+
+struct Asteroid: public GameObject
+{
+ Asteroid(const std::string& name): GameObject(name) {}
+ Asteroid(): GameObject("dummy") {}
+};
+
+struct SpaceShip: public GameObject
+{
+ SpaceShip(const std::string& name): GameObject(name) {}
+ SpaceShip(): GameObject("dummy") {}
+};
+
+// SpaceShip is specialized further into CommercialShip and MilitaryShip.
+struct CommercialShip: public SpaceShip
+{
+ CommercialShip(const std::string& name): SpaceShip(name) {}
+ CommercialShip(): SpaceShip("dummy") {}
+};
+
+struct MilitaryShip: public SpaceShip
+{
+ MilitaryShip(const std::string& name): SpaceShip(name) {}
+ MilitaryShip(): SpaceShip("dummy") {}
+};
+
+/*-------------------------- Collision functions ---------------------------*/
+// This mechanism permits us to overcome a limitation of Meyers' approach: we
+// can declare the parameter types exactly as we want, rather than having to
+// make them all GameObject& parameters.
+std::string shipAsteroid(SpaceShip& ship, Asteroid& rock)
+{
+// std::cout << rock.stringize() << " has pulverized " << ship.stringize() << std::endl;
+ return "shipAsteroid";
+}
+
+std::string militaryShipAsteroid(MilitaryShip& ship, Asteroid& rock)
+{
+// std::cout << rock.stringize() << " has severely damaged " << ship.stringize() << std::endl;
+ return "militaryShipAsteroid";
+}
+
+std::string shipStation(SpaceShip& ship, SpaceStation& dock)
+{
+// std::cout << ship.stringize() << " has docked at " << dock.stringize() << std::endl;
+ return "shipStation";
+}
+
+std::string asteroidStation(Asteroid& rock, SpaceStation& dock)
+{
+// std::cout << rock.stringize() << " has damaged " << dock.stringize() << std::endl;
+ return "asteroidStation";
+}
+
+/*------------------------------- Test code --------------------------------*/
+namespace tut
+{
+ struct dispatch_data
+ {
+ dispatch_data():
+ home(new SpaceStation("Terra Station")),
+ obstacle(new Asteroid("Ganymede")),
+ tug(new CommercialShip("Pilotfish")),
+ patrol(new MilitaryShip("Enterprise"))
+ {}
+
+ // Instantiate and populate the DoubleDispatch object.
+ typedef LLDoubleDispatch<std::string, GameObject> DD;
+ DD dispatcher;
+
+ // Instantiate a few GameObjects. Make sure we refer to them
+ // polymorphically, and don't let them leak.
+ std::unique_ptr<GameObject> home;
+ std::unique_ptr<GameObject> obstacle;
+ std::unique_ptr<GameObject> tug;
+ std::unique_ptr<GameObject> patrol;
+
+ // prototype objects
+ Asteroid dummyAsteroid;
+ SpaceShip dummyShip;
+ MilitaryShip dummyMilitary;
+ CommercialShip dummyCommercial;
+ SpaceStation dummyStation;
+ };
+ typedef test_group<dispatch_data> dispatch_group;
+ typedef dispatch_group::object dispatch_object;
+ tut::dispatch_group ddgr("double dispatch");
+
+ template<> template<>
+ void dispatch_object::test<1>()
+ {
+ // Describe param types using explicit DD::Type objects
+ // (order-sensitive add() variant)
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true);
+ // naive adding, won't work
+ dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true);
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true);
+ dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true);
+
+ // Try colliding them.
+ ensure_equals(dispatcher(*home, *tug), // reverse params, SpaceShip subclass
+ "shipStation");
+ ensure_equals(dispatcher(*patrol, *home), // forward params, SpaceShip subclass
+ "shipStation");
+ ensure_equals(dispatcher(*obstacle, *home), // forward params
+ "asteroidStation");
+ ensure_equals(dispatcher(*home, *obstacle), // reverse params
+ "asteroidStation");
+ ensure_equals(dispatcher(*tug, *obstacle), // forward params, SpaceShip subclass
+ "shipAsteroid");
+ ensure_equals(dispatcher(*obstacle, *patrol), // reverse params, SpaceShip subclass
+ // won't use militaryShipAsteroid() because it was added
+ // in wrong order
+ "shipAsteroid");
+ }
+
+ template<> template<>
+ void dispatch_object::test<2>()
+ {
+ // Describe param types using explicit DD::Type objects
+ // (order-sensitive add() variant)
+ // adding in correct order
+ dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true);
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true);
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true);
+ dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ }
+
+ template<> template<>
+ void dispatch_object::test<3>()
+ {
+ // Describe param types with actual prototype instances
+ // (order-insensitive add() variant)
+ dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid);
+ dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid);
+ dispatcher.add(dummyShip, dummyStation, shipStation);
+ dispatcher.add(dummyAsteroid, dummyStation, asteroidStation);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ ensure_equals(dispatcher(*obstacle, *patrol), "");
+ }
+
+ template<> template<>
+ void dispatch_object::test<4>()
+ {
+ // Describe param types with actual prototype instances
+ // (order-insensitive add() variant)
+ dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid);
+ // Even if we add the militaryShipAsteroid in the wrong order, it
+ // should still work.
+ dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid);
+ dispatcher.add(dummyShip, dummyStation, shipStation);
+ dispatcher.add(dummyAsteroid, dummyStation, asteroidStation);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ }
+
+ template<> template<>
+ void dispatch_object::test<5>()
+ {
+ dispatcher.add<SpaceShip, Asteroid>(shipAsteroid);
+ dispatcher.add<MilitaryShip, Asteroid>(militaryShipAsteroid);
+ dispatcher.add<SpaceShip, SpaceStation>(shipStation);
+ dispatcher.add<Asteroid, SpaceStation>(asteroidStation);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index 3ec429530c..653f64f1b6 100644
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -59,8 +59,10 @@ static const char* FieldName[] =
namespace
{
-#ifdef __clang__
+#if defined(LL_CLANG)
# pragma clang diagnostic ignored "-Wunused-function"
+#elif defined(LL_GNUC)
+# pragma GCC diagnostic ignored "-Wunused-function"
#endif
void test_that_error_h_includes_enough_things_to_compile_a_message()
{
diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp
new file mode 100644
index 0000000000..c5d5dcb9d4
--- /dev/null
+++ b/indra/llcommon/tests/llevents_test.cpp
@@ -0,0 +1,506 @@
+/**
+ * @file llevents_tut.cpp
+ * @author Nat Goodspeed
+ * @date 2008-09-12
+ * @brief Test of llevents.h
+ *
+ * $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$
+ */
+
+#if LL_WINDOWS
+#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
+#endif
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+// UGLY HACK! We want to verify state internal to the classes without
+// providing public accessors.
+#define testable public
+#include "llevents.h"
+#undef testable
+// STL headers
+// std headers
+#include <iostream>
+#include <typeinfo>
+// external library headers
+#include <boost/bind.hpp>
+// other Linden headers
+#include "tests/listener.h" // must PRECEDE lltut.h
+#include "lltut.h"
+#include "catch_and_store_what_in.h"
+#include "stringize.h"
+
+template<typename T>
+T make(const T& value)
+{
+ return value;
+}
+
+/*****************************************************************************
+ * tut test group
+ *****************************************************************************/
+namespace tut
+{
+struct events_data
+{
+ events_data() :
+ pumps(LLEventPumps::instance()),
+ listener0("first"),
+ listener1("second")
+ {
+ }
+ LLEventPumps& pumps;
+ Listener listener0;
+ Listener listener1;
+
+ void check_listener(const std::string& desc, const Listener& listener, LLSD::Integer got)
+ {
+ ensure_equals(STRINGIZE(listener << ' ' << desc),
+ listener.getLastEvent().asInteger(), got);
+ }
+};
+typedef test_group<events_data> events_group;
+typedef events_group::object events_object;
+tut::events_group evgr("events");
+
+template<> template<>
+void events_object::test<1>()
+{
+ set_test_name("basic operations");
+ // Having to modify this to track the statically-
+ // constructed pumps in other TUT modules in this giant monolithic test
+ // executable isn't such a hot idea.
+ // ensure_equals("initial pump", pumps.mPumpMap.size(), 1);
+ size_t initial_pumps(pumps.mPumpMap.size());
+ LLEventPump& per_frame(pumps.obtain("per-frame"));
+ ensure_equals("first explicit pump", pumps.mPumpMap.size(), initial_pumps + 1);
+ // Verify that per_frame was instantiated as an LLEventStream.
+ ensure("LLEventStream leaf class", dynamic_cast<LLEventStream*> (&per_frame));
+ ensure("enabled", per_frame.enabled());
+ // Trivial test, but posting an event to an EventPump with no
+ // listeners should not blow up. The test is relevant because defining
+ // a boost::signal with a non-void return signature, using the default
+ // combiner, blows up if there are no listeners. This is because the
+ // default combiner is defined to return the value returned by the
+ // last listener, which is meaningless if there were no listeners.
+ per_frame.post(0);
+ LLBoundListener connection = listener0.listenTo(per_frame);
+ ensure("connected", connection.connected());
+ ensure("not blocked", !connection.blocked());
+ per_frame.post(1);
+ check_listener("received", listener0, 1);
+ { // block the connection
+ LLEventPump::Blocker block(connection);
+ ensure("blocked", connection.blocked());
+ per_frame.post(2);
+ check_listener("not updated", listener0, 1);
+ } // unblock
+ ensure("unblocked", !connection.blocked());
+ per_frame.post(3);
+ check_listener("unblocked", listener0, 3);
+ LLBoundListener sameConnection = per_frame.getListener(listener0.getName());
+ ensure("still connected", sameConnection.connected());
+ ensure("still not blocked", !sameConnection.blocked());
+ { // block it again
+ LLEventPump::Blocker block(sameConnection);
+ ensure("re-blocked", sameConnection.blocked());
+ per_frame.post(4);
+ check_listener("re-blocked", listener0, 3);
+ } // unblock
+ std::string threw = catch_what<LLEventPump::DupListenerName>(
+ [&per_frame, this](){
+ // NOTE: boost::bind() saves its arguments by VALUE! If you pass
+ // an object instance rather than a pointer, you'll end up binding
+ // to an internal copy of that instance! Use boost::ref() to
+ // capture a reference instead.
+ per_frame.listen(listener0.getName(), // note bug, dup name
+ boost::bind(&Listener::call, boost::ref(listener1), _1));
+ });
+ ensure_equals(threw,
+ std::string("DupListenerName: "
+ "Attempt to register duplicate listener name '") +
+ listener0.getName() + "' on " + typeid(per_frame).name() +
+ " '" + per_frame.getName() + "'");
+ // do it right this time
+ listener1.listenTo(per_frame);
+ per_frame.post(5);
+ check_listener("got", listener0, 5);
+ check_listener("got", listener1, 5);
+ per_frame.enable(false);
+ per_frame.post(6);
+ check_listener("didn't get", listener0, 5);
+ check_listener("didn't get", listener1, 5);
+ per_frame.enable();
+ per_frame.post(7);
+ check_listener("got", listener0, 7);
+ check_listener("got", listener1, 7);
+ per_frame.stopListening(listener0.getName());
+ ensure("disconnected 0", ! connection.connected());
+ ensure("disconnected 1", ! sameConnection.connected());
+ per_frame.post(8);
+ check_listener("disconnected", listener0, 7);
+ check_listener("still connected", listener1, 8);
+ per_frame.stopListening(listener1.getName());
+ per_frame.post(9);
+ check_listener("disconnected", listener1, 8);
+}
+
+template<> template<>
+void events_object::test<2>()
+{
+ set_test_name("callstop() returning true");
+ LLEventPump& per_frame(pumps.obtain("per-frame"));
+ listener0.reset(0);
+ listener1.reset(0);
+ LLBoundListener bound0 = listener0.listenTo(per_frame, &Listener::callstop);
+ LLBoundListener bound1 = listener1.listenTo(per_frame, &Listener::call,
+ // after listener0
+ make<LLEventPump::NameList>(LLEventPump::NameList{ listener0.getName() }));
+ ensure("enabled", per_frame.enabled());
+ ensure("connected 0", bound0.connected());
+ ensure("unblocked 0", !bound0.blocked());
+ ensure("connected 1", bound1.connected());
+ ensure("unblocked 1", !bound1.blocked());
+ per_frame.post(1);
+ check_listener("got", listener0, 1);
+ // Because listener0.callstop() returns true, control never reaches listener1.call().
+ check_listener("got", listener1, 0);
+}
+
+bool chainEvents(Listener& someListener, const LLSD& event)
+{
+ // Make this call so we can watch for side effects for test purposes.
+ someListener.call(event);
+ // This function represents a recursive event chain -- or some other
+ // scenario in which an event handler raises additional events.
+ int value = event.asInteger();
+ if (value)
+ {
+ LLEventPumps::instance().obtain("login").post(value - 1);
+ }
+ return false;
+}
+
+template<> template<>
+void events_object::test<3>()
+{
+ set_test_name("explicitly-instantiated LLEventStream");
+ // Explicitly instantiate an LLEventStream, and verify that it
+ // self-registers with LLEventPumps
+ size_t registered = pumps.mPumpMap.size();
+ size_t owned = pumps.mOurPumps.size();
+ LLEventPump* localInstance;
+ {
+ LLEventStream myEventStream("stream");
+ localInstance = &myEventStream;
+ LLEventPump& stream(pumps.obtain("stream"));
+ ensure("found named LLEventStream instance", &stream == localInstance);
+ ensure_equals("registered new instance", pumps.mPumpMap.size(), registered + 1);
+ ensure_equals("explicit instance not owned", pumps.mOurPumps.size(), owned);
+ } // destroy myEventStream -- should unregister
+ ensure_equals("destroyed instance unregistered", pumps.mPumpMap.size(), registered);
+ ensure_equals("destroyed instance not owned", pumps.mOurPumps.size(), owned);
+ LLEventPump& stream(pumps.obtain("stream"));
+ ensure("new LLEventStream instance", &stream != localInstance);
+ ensure_equals("obtain()ed instance registered", pumps.mPumpMap.size(), registered + 1);
+ ensure_equals("obtain()ed instance owned", pumps.mOurPumps.size(), owned + 1);
+}
+
+template<> template<>
+void events_object::test<4>()
+{
+ set_test_name("stopListening()");
+ LLEventPump& login(pumps.obtain("login"));
+ listener0.listenTo(login);
+ login.stopListening(listener0.getName());
+ // should not throw because stopListening() should have removed name
+ listener0.listenTo(login, &Listener::callstop);
+ LLBoundListener wrong = login.getListener("bogus");
+ ensure("bogus connection disconnected", !wrong.connected());
+ ensure("bogus connection blocked", wrong.blocked());
+}
+
+template<> template<>
+void events_object::test<5>()
+{
+ set_test_name("chaining LLEventPump instances");
+ LLEventPump& upstream(pumps.obtain("upstream"));
+ // One potentially-useful construct is to chain LLEventPumps together.
+ // Among other things, this allows you to turn subsets of listeners on
+ // and off in groups.
+ LLEventPump& filter0(pumps.obtain("filter0"));
+ LLEventPump& filter1(pumps.obtain("filter1"));
+ upstream.listen(filter0.getName(), boost::bind(&LLEventPump::post, boost::ref(filter0), _1));
+ upstream.listen(filter1.getName(), boost::bind(&LLEventPump::post, boost::ref(filter1), _1));
+ listener0.listenTo(filter0);
+ listener1.listenTo(filter1);
+ listener0.reset(0);
+ listener1.reset(0);
+ upstream.post(1);
+ check_listener("got unfiltered", listener0, 1);
+ check_listener("got unfiltered", listener1, 1);
+ filter0.enable(false);
+ upstream.post(2);
+ check_listener("didn't get filtered", listener0, 1);
+ check_listener("got filtered", listener1, 2);
+}
+
+template<> template<>
+void events_object::test<6>()
+{
+ set_test_name("listener dependency order");
+ typedef LLEventPump::NameList NameList;
+ LLEventPump& button(pumps.obtain("button"));
+ Collect collector;
+ button.listen("Mary",
+ boost::bind(&Collect::add, boost::ref(collector), "Mary", _1),
+ // state that "Mary" must come after "checked"
+ make<NameList>(NameList{ "checked" }));
+ button.listen("checked",
+ boost::bind(&Collect::add, boost::ref(collector), "checked", _1),
+ // "checked" must come after "spot"
+ make<NameList>(NameList{ "spot" }));
+ button.listen("spot",
+ boost::bind(&Collect::add, boost::ref(collector), "spot", _1));
+ button.post(1);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "spot", "checked", "Mary" }));
+ collector.clear();
+ button.stopListening("Mary");
+ button.listen("Mary",
+ boost::bind(&Collect::add, boost::ref(collector), "Mary", _1),
+ LLEventPump::empty, // no after dependencies
+ // now "Mary" must come before "spot"
+ make<NameList>(NameList{ "spot" }));
+ button.post(2);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "Mary", "spot", "checked" }));
+ collector.clear();
+ button.stopListening("spot");
+ std::string threw = catch_what<LLEventPump::Cycle>(
+ [&button, &collector](){
+ button.listen("spot",
+ boost::bind(&Collect::add, boost::ref(collector), "spot", _1),
+ // after "Mary" and "checked" -- whoops!
+ make<NameList>(NameList{ "Mary", "checked" }));
+ });
+ // Obviously the specific wording of the exception text can
+ // change; go ahead and change the test to match.
+ // Establish that it contains:
+ // - the name and runtime type of the LLEventPump
+ ensure_contains("LLEventPump type", threw, typeid(button).name());
+ ensure_contains("LLEventPump name", threw, "'button'");
+ // - the name of the new listener that caused the problem
+ ensure_contains("new listener name", threw, "'spot'");
+ // - a synopsis of the problematic dependencies.
+ ensure_contains("cyclic dependencies", threw,
+ "\"Mary\" -> before (\"spot\")");
+ ensure_contains("cyclic dependencies", threw,
+ "after (\"spot\") -> \"checked\"");
+ ensure_contains("cyclic dependencies", threw,
+ "after (\"Mary\", \"checked\") -> \"spot\"");
+ button.listen("yellow",
+ boost::bind(&Collect::add, boost::ref(collector), "yellow", _1), make<NameList>(NameList{ "checked" }));
+ button.listen("shoelaces",
+ boost::bind(&Collect::add, boost::ref(collector), "shoelaces", _1), make<NameList>(NameList{ "checked" }));
+ button.post(3);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "Mary", "checked", "yellow", "shoelaces" }));
+ collector.clear();
+ threw = catch_what<LLEventPump::OrderChange>(
+ [&button, &collector](){
+ button.listen("of",
+ boost::bind(&Collect::add, boost::ref(collector), "of", _1),
+ make<NameList>(NameList{ "shoelaces" }),
+ make<NameList>(NameList{ "yellow" }));
+ });
+ // Same remarks about the specific wording of the exception. Just
+ // ensure that it contains enough information to clarify the
+ // problem and what must be done to resolve it.
+ ensure_contains("LLEventPump type", threw, typeid(button).name());
+ ensure_contains("LLEventPump name", threw, "'button'");
+ ensure_contains("new listener name", threw, "'of'");
+ ensure_contains("prev listener name", threw, "'yellow'");
+ // std::cout << "Thrown Exception: " << threw << std::endl;
+ ensure_contains("old order", threw, "was: Mary, checked, yellow, shoelaces");
+ ensure_contains("new order", threw, "now: Mary, checked, shoelaces, of, yellow");
+ button.post(4);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "Mary", "checked", "yellow", "shoelaces" }));
+}
+
+template<> template<>
+void events_object::test<7>()
+{
+ set_test_name("tweaked and untweaked LLEventPump instance names");
+ { // nested scope
+ // Hand-instantiate an LLEventStream...
+ LLEventStream bob("bob");
+ std::string threw = catch_what<LLEventPump::DupPumpName>(
+ [](){
+ // then another with a duplicate name.
+ LLEventStream bob2("bob");
+ });
+ ensure("Caught DupPumpName", !threw.empty());
+ } // delete first 'bob'
+ LLEventStream bob("bob"); // should work, previous one unregistered
+ LLEventStream bob1("bob", true);// allowed to tweak name
+ ensure_equals("tweaked LLEventStream name", bob1.getName(), "bob1");
+ std::vector<std::shared_ptr<LLEventStream> > streams;
+ for (int i = 2; i <= 10; ++i)
+ {
+ streams.push_back(std::shared_ptr<LLEventStream>(new LLEventStream("bob", true)));
+ }
+ ensure_equals("last tweaked LLEventStream name", streams.back()->getName(), "bob10");
+}
+
+// Define a function that accepts an LLListenerOrPumpName
+void eventSource(const LLListenerOrPumpName& listener)
+{
+ // Pretend that some time has elapsed. Call listener immediately.
+ listener(17);
+}
+
+template<> template<>
+void events_object::test<8>()
+{
+ set_test_name("LLListenerOrPumpName");
+ // Passing a boost::bind() expression to LLListenerOrPumpName
+ listener0.reset(0);
+ eventSource(boost::bind(&Listener::call, boost::ref(listener0), _1));
+ check_listener("got by listener", listener0, 17);
+ // Passing a string LLEventPump name to LLListenerOrPumpName
+ listener0.reset(0);
+ LLEventStream random("random");
+ listener0.listenTo(random);
+ eventSource("random");
+ check_listener("got by pump name", listener0, 17);
+ std::string threw = catch_what<LLListenerOrPumpName::Empty>(
+ [](){
+ LLListenerOrPumpName empty;
+ empty(17);
+ });
+
+ ensure("threw Empty", !threw.empty());
+}
+
+class TempListener: public Listener
+{
+public:
+ TempListener(const std::string& name, bool& liveFlag) :
+ Listener(name), mLiveFlag(liveFlag)
+ {
+ mLiveFlag = true;
+ }
+
+ virtual ~TempListener()
+ {
+ mLiveFlag = false;
+ }
+
+private:
+ bool& mLiveFlag;
+};
+
+template<> template<>
+void events_object::test<9>()
+{
+ set_test_name("listen(boost::bind(...TempListener...))");
+ // listen() can't do anything about a plain TempListener instance:
+ // it's not managed with shared_ptr, nor is it an LLEventTrackable subclass
+ bool live = false;
+ LLEventPump& heaptest(pumps.obtain("heaptest"));
+ LLBoundListener connection;
+ {
+ TempListener tempListener("temp", live);
+ ensure("TempListener constructed", live);
+ connection = heaptest.listen(tempListener.getName(),
+ boost::bind(&Listener::call,
+ boost::ref(tempListener),
+ _1));
+ heaptest.post(1);
+ check_listener("received", tempListener, 1);
+ } // presumably this will make newListener go away?
+ // verify that
+ ensure("TempListener destroyed", !live);
+ // This is the case against which we can't defend. Don't even try to
+ // post to heaptest -- that would engage Undefined Behavior.
+ // Cautiously inspect connection...
+ ensure("misleadingly connected", connection.connected());
+ // then disconnect by hand.
+ heaptest.stopListening("temp");
+}
+
+class TempTrackableListener: public TempListener, public LLEventTrackable
+{
+public:
+ TempTrackableListener(const std::string& name, bool& liveFlag):
+ TempListener(name, liveFlag)
+ {}
+};
+
+template<> template<>
+void events_object::test<10>()
+{
+ set_test_name("listen(boost::bind(...TempTrackableListener ref...))");
+ bool live = false;
+ LLEventPump& heaptest(pumps.obtain("heaptest"));
+ LLBoundListener connection;
+ {
+ TempTrackableListener tempListener("temp", live);
+ ensure("TempTrackableListener constructed", live);
+ connection = heaptest.listen(tempListener.getName(),
+ boost::bind(&TempTrackableListener::call,
+ boost::ref(tempListener), _1));
+ heaptest.post(1);
+ check_listener("received", tempListener, 1);
+ } // presumably this will make tempListener go away?
+ // verify that
+ ensure("TempTrackableListener destroyed", ! live);
+ ensure("implicit disconnect", ! connection.connected());
+ // now just make sure we don't blow up trying to access a freed object!
+ heaptest.post(2);
+}
+
+template<> template<>
+void events_object::test<11>()
+{
+ set_test_name("listen(boost::bind(...TempTrackableListener pointer...))");
+ bool live = false;
+ LLEventPump& heaptest(pumps.obtain("heaptest"));
+ LLBoundListener connection;
+ {
+ TempTrackableListener* newListener(new TempTrackableListener("temp", live));
+ ensure("TempTrackableListener constructed", live);
+ connection = heaptest.listen(newListener->getName(),
+ boost::bind(&TempTrackableListener::call,
+ newListener, _1));
+ heaptest.post(1);
+ check_listener("received", *newListener, 1);
+ // explicitly destroy newListener
+ delete newListener;
+ }
+ // verify that
+ ensure("TempTrackableListener destroyed", ! live);
+ ensure("implicit disconnect", ! connection.connected());
+ // now just make sure we don't blow up trying to access a freed object!
+ heaptest.post(2);
+}
+
+} // namespace tut
diff --git a/indra/llcommon/tests/llfile_test.cpp b/indra/llcommon/tests/llfile_test.cpp
new file mode 100644
index 0000000000..ab4616cfb2
--- /dev/null
+++ b/indra/llcommon/tests/llfile_test.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file llfile_test.cpp
+ * @author Frederick Martian
+ * @date 2025-11
+ * @brief LLFile test cases.
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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 <tut/tut.hpp>
+#include "lltut.h"
+#include "linden_common.h"
+#include "llfile.h"
+
+namespace tut
+{
+ static void clear_entire_dir(const std::filesystem::path& dir_path)
+ {
+ std::error_code ec;
+ std::filesystem::remove_all(dir_path, ec);
+ }
+
+ static std::filesystem::path append_filename(const std::filesystem::path& dir, const std::u8string& element)
+ {
+ std::filesystem::path path = dir;
+ return path.append(element);
+ }
+
+ static std::filesystem::path get_testdir(const std::filesystem::path& tempdir)
+ {
+ return append_filename(tempdir, std::u8string(u8"test_dir"));
+ }
+
+ static std::filesystem::path get_testdir_unicode(const std::filesystem::path& tempdir)
+ {
+ // Example Unicode directory name: "test_ユニコード_dir"
+ return append_filename(tempdir, std::u8string(u8"test_\xE3\x83\xA6\xE3\x83\x8B\xE3\x82\xB3\xE3\x83\xBC\xE3\x83\x89_dir"));
+ }
+
+ struct llfile_test
+ {
+ std::filesystem::path tempdir = LLFile::tmpdir();
+ std::filesystem::path testdir = get_testdir(tempdir);
+ };
+ typedef test_group<llfile_test> llfile_test_t;
+ typedef llfile_test_t::object llfile_test_object_t;
+ tut::llfile_test_t tut_llfile_test("llfile_test");
+
+ template<> template<>
+ void llfile_test_object_t::test<1>()
+ {
+ // Test creating directories and files and deleting them and checking if the
+ // relevant status functions work as expected
+ ensure("LLFile::tmpdir() empty", !tempdir.empty());
+ ensure("LLFile::tmpdir() doesn't exist", LLFile::exists(tempdir));
+ ensure("LLFile::tmpdir() is not a directory", LLFile::isdir(tempdir));
+ ensure("LLFile::tmpdir() should not be a file", !LLFile::isfile(tempdir));
+
+ // Make sure there is nothing left from a previous test run
+ clear_entire_dir(testdir);
+ ensure("llfile_test should not exist anymore", !LLFile::exists(testdir));
+
+ int rc = LLFile::mkdir(testdir);
+ ensure("LLFile::mkdir() failed", rc == 0);
+ ensure("llfile_test should be a directory", LLFile::isdir(testdir));
+ rc = LLFile::mkdir(testdir);
+ ensure("LLFile::mkdir() should not fail when the directory already exists", rc == 0);
+
+ std::filesystem::path testfile1 = testdir;
+ testfile1.append("llfile_test.dat");
+ ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1));
+
+ const char* testdata = "testdata";
+ S64 bytes = LLFile::write(testfile1, testdata, 0, sizeof(testdata));
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata));
+
+ rc = LLFile::remove(testfile1);
+ ensure("LLFile::remove() for file test_file.dat", rc == 0);
+ ensure("llfile_test.dat should not exist anymore", !LLFile::exists(testfile1));
+ ensure("llfile_test.dat should not be a file", !LLFile::isfile(testfile1));
+ ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1));
+ ensure("llfile_test.dat should not be a symlink", !LLFile::islink(testfile1));
+
+ rc = LLFile::remove(testdir);
+ ensure("LLFile::remove() for directory llfile_test failed", rc == 0);
+ ensure("llfile_test should not exist anymore", !LLFile::exists(testdir));
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<2>()
+ {
+ // High level static file IO functions to read and write data files
+ LLFile::mkdir(testdir);
+ ensure("llfile_test should exist", LLFile::isdir(testdir));
+
+ std::filesystem::path testfile1 = testdir;
+ testfile1.append("llfile_test.dat");
+
+ std::string testdata1("testdata");
+ std::string testdata2("datateststuff");
+ std::time_t current = time(nullptr);
+ S64 bytes = LLFile::write(testfile1, testdata1.c_str(), 0, testdata1.length());
+ ensure("LLFile::write() did not write correctly", bytes == testdata1.length());
+ ensure("llfile_test.dat should exist", LLFile::exists(testfile1));
+ ensure("llfile_test.dat should be a file", LLFile::isfile(testfile1));
+ ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1));
+
+ bytes = LLFile::size(testfile1);
+ ensure("LLFile::size() did not return the correct size", bytes == testdata1.length());
+
+ std::string data = LLFile::getContents(testfile1);
+ ensure("LLFile::getContents() did not return the correct size data", data.length() == testdata1.length());
+ ensure_memory_matches("LLFile::getContents() did not read correct data", testdata1.c_str(), (U32)testdata1.length(), data.c_str(), (U32)data.length());
+
+ char buffer[1024];
+ bytes = LLFile::read(testfile1, buffer, 0, testdata1.length());
+ ensure("LLFile:read() did not return the correct size", bytes == testdata1.length());
+ ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
+
+ // What if we try to read more data than there is in the file?
+ bytes = LLFile::read(testfile1, buffer, 0, bytes + 10);
+ ensure("LLFile:read() did not correctly stop on eof", bytes == testdata1.length());
+ ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
+
+ // Let's append more data
+ bytes = LLFile::write(testfile1, testdata2.c_str(), -1, testdata2.length());
+ ensure("LLFile::write() did not write correctly", bytes == testdata2.length());
+
+ bytes = LLFile::size(testfile1);
+ ensure("LLFile::size() did not return the correct size", bytes == testdata1.length() + testdata2.length());
+ bytes = LLFile::read(testfile1, buffer, 0, bytes);
+ ensure("LLFile:read() did not read correct number of bytes", bytes == testdata1.length() + testdata2.length());
+ ensure_memory_matches("LLFile:read() did not read correct testdata1", testdata1.c_str(), (U32)testdata1.length(), buffer, (U32)testdata1.length());
+ ensure_memory_matches("LLFile:read() did not read correct testdata2", testdata2.c_str(), (U32)testdata2.length(), buffer + testdata1.length(), (U32)testdata2.length());
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<3>()
+ {
+ const size_t numints = 1024;
+
+ // Testing the LLFile class implementation
+ std::filesystem::path testfile = testdir;
+ testfile.append("llfile_test.bin");
+
+ int data[numints];
+ for (int &t : data)
+ {
+ t = rand();
+ }
+
+ std::error_code ec;
+ LLFile fileout(testfile, LLFile::out, ec);
+ ensure("LLFile constructor did not open correctly", (bool)fileout);
+ ensure("error_code from LLFile constructor should not indicate an error", !ec);
+ if (fileout)
+ {
+ S64 length = fileout.size(ec);
+ ensure("freshly created file should be empty", length == 0);
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ S64 bytes = fileout.write(data, sizeof(data), ec);
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
+ ensure("error_code from LLFile::write() should not indicate an error", !ec);
+ bytes = fileout.write(data, sizeof(data), ec);
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
+ ensure("error_code from LLFile::write() should not indicate an error", !ec);
+ bytes = fileout.size(ec);
+ ensure("LLFile::size() returned wrong size", bytes == 2 * sizeof(data));
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ fileout.close();
+ }
+
+ LLFile filein(testfile, LLFile::in, ec);
+ ensure("LLFile constructor did not open correctly", (bool)filein);
+ ensure("error_code from LLFile constructor should not indicate an error", !ec);
+ if (filein)
+ {
+ S64 length = filein.size(ec);
+ ensure("LLFile::size() returned wrong size", length == 2 * sizeof(data));
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ char* buffer = (char*)malloc(length);
+ S64 bytes = filein.read(buffer, length, ec);
+ ensure("LLFile::read() did not read correctly", bytes == length);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ ensure_memory_matches("LLFile:read() did not read correct data1", data, (U32)sizeof(data), buffer, (U32)sizeof(data));
+ ensure_memory_matches("LLFile:read() did not read correct data2", data, (U32)sizeof(data), buffer + sizeof(data), (U32)sizeof(data));
+ S64 offset = filein.tell(ec);
+ ensure("LLFile::tell() returned a bad offset", offset == length);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ offset = sizeof(data) / 2;
+ int rc = filein.seek(offset, ec);
+ ensure("LLFile::seek() indicated an error", rc == 0);
+ ensure("error_code from LLFile::seek() should not indicate an error", !ec);
+ bytes = filein.read(buffer, 2 * sizeof(data), ec);
+ ensure("LLFile::read() did not read correctly", bytes == sizeof(data) + offset);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ ensure_memory_matches("LLFile:read() did not read correct data3", (char*)data + offset, (U32)offset, buffer, (U32)offset);
+ ensure_memory_matches("LLFile:read() did not read correct data4", (char*)data, (U32)sizeof(data), buffer + offset, (U32)sizeof(data));
+ filein.close();
+
+ free(buffer);
+ }
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<4>()
+ {
+ // Testing the LLFile class implementation with wrong paths and parameters
+ std::filesystem::path testfile = testdir;
+ testfile.append("llfile_test.bin");
+
+ std::error_code ec;
+ LLFile file(testfile, LLFile::out | LLFile::noreplace, ec);
+ ensure("LLFile constructor should not have opened the already existing file", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ LLFile::remove(testfile);
+ file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::trunc, ec);
+ ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::noreplace, ec);
+ ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ testfile = testdir;
+ testfile.append("llfile_test");
+ testfile.append("llfile_test.bin");
+
+ file = LLFile(testfile, LLFile::in, ec);
+ ensure("LLFile constructor should not have been able to open the file in the non-existing directory", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<5>()
+ {
+ // Test file and directory operations with Unicode paths and filenames
+ std::filesystem::path testdir_unicode = get_testdir_unicode(tempdir);
+
+ // Unicode filename: "ファイル_テスト.bin" (means "file_test.bin" in Japanese)
+ std::string unicode_filename = "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB_\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88.bin";
+ std::filesystem::path testfile_unicode = testdir_unicode;
+ testfile_unicode.append(unicode_filename);
+
+ // Clean up any previous test artifacts
+ clear_entire_dir(testdir_unicode);
+ ensure("Unicode test directory should not exist", !LLFile::exists(testdir_unicode));
+
+ // Create the Unicode directory
+ int rc = LLFile::mkdir(testdir_unicode);
+ ensure("LLFile::mkdir() failed for Unicode directory", rc == 0);
+ ensure("Unicode test directory should exist", LLFile::isdir(testdir_unicode));
+
+
+ ensure("Unicode test file should not exist", !LLFile::exists(testfile_unicode));
+
+ // Write to the Unicode file
+ const char* testdata = "unicode_testdata";
+ S64 bytes = LLFile::write(testfile_unicode, testdata, 0, strlen(testdata));
+ ensure("Unicode test file should exist", LLFile::exists(testfile_unicode));
+ ensure("Unicode test file should be a file", LLFile::isfile(testfile_unicode));
+ ensure("LLFile::write() did not write correctly to Unicode file", bytes == (S64)strlen(testdata));
+
+ // Read back the data
+ char buffer[64] = {};
+ bytes = LLFile::read(testfile_unicode, buffer, 0, sizeof(buffer));
+ ensure("LLFile::read() did not read correctly from Unicode file", bytes == (S64)strlen(testdata));
+ ensure_memory_matches("LLFile::read() did not read correct Unicode data", testdata, (U32)bytes, buffer, (U32)bytes);
+
+ // Remove the file and directory
+ rc = LLFile::remove(testfile_unicode);
+ ensure("LLFile::remove() failed for Unicode file", rc == 0);
+ ensure("Unicode test file should not exist after removal", !LLFile::exists(testfile_unicode));
+
+ rc = LLFile::remove(testdir_unicode);
+ ensure("LLFile::remove() failed for Unicode directory", rc == 0);
+ ensure("Unicode test directory should not exist after removal", !LLFile::exists(testdir_unicode));
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llhttpdate_test.cpp b/indra/llcommon/tests/llhttpdate_test.cpp
new file mode 100644
index 0000000000..b580b09a9f
--- /dev/null
+++ b/indra/llcommon/tests/llhttpdate_test.cpp
@@ -0,0 +1,162 @@
+/**
+ * @file llhttpdate_tut.cpp
+ * @author Kartic Krishnamurthy
+ * @date Wednesday, 18 Jul 2007 17:00:00 GMT :)
+ *
+ * $LicenseInfo:firstyear=2007&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 "lltut.h"
+
+#include "lldate.h"
+#include "llcommon.h"
+#include "llframetimer.h"
+
+#include <time.h>
+#include <locale.h>
+
+namespace tut
+{
+ struct httpdate_data
+ {
+ httpdate_data()
+ {
+ }
+ ~httpdate_data()
+ {
+ }
+ LLDate some_date;
+ };
+ typedef test_group<httpdate_data> httpdate_test;
+ typedef httpdate_test::object httpdate_object;
+ tut::httpdate_test httpdate("httpdate");
+
+ template<> template<>
+ void httpdate_object::test<1>()
+ {
+
+ static std::string epoch_expected = "Thursday, 01 Jan 1970 00:00:00 GMT" ;
+ ensure("Check Epoch in RFC 1123", ( epoch_expected == some_date.asRFC1123()));
+ }
+
+ template<> template<>
+ void httpdate_object::test<2>()
+ {
+
+ static std::string expected = "Wednesday, 18 Jul 2007 22:17:24 GMT" ;
+ some_date = LLDate(1184797044.037586);
+ ensure("Check some timestamp in RFC 1123", ( expected == some_date.asRFC1123()));
+ }
+
+ // This test of course most generic.. runs off current time
+ template<> template<>
+ void httpdate_object::test<3>()
+ {
+
+ //F64 sometime = LLFrameTimer::getTotalSeconds();
+ time_t sometime;
+ time(&sometime);
+ some_date = LLDate((F64) sometime);
+ struct tm *result;
+ char expected[255];
+ std::string actual;
+
+ result = gmtime(&sometime);
+ /*
+ std::cout << " seconds: "<< result->tm_sec
+ << ", minutes: " << result->tm_min
+ << ", hours: " << result->tm_hour
+ << ", day of the month: " << result->tm_mday
+ << ", month: " << result->tm_mon
+ << ", year: " << result->tm_year
+ << ", day of the week: " << result->tm_wday
+ << ", day in the year: " << result->tm_yday
+ << ", DST: " << result->tm_isdst << std::endl;
+ */
+ strftime(expected, 255, "%A, %d %b %Y %H:%M:%S GMT", result);
+ actual = some_date.asRFC1123();
+ // probably not a good idea to use strcmp but this is just a unit test
+ ensure("Current time in RFC 1123", (strcmp(expected, actual.c_str()) == 0));
+ }
+
+ void test_date_string(const std::string &locale, struct tm *t,
+ const std::string &fmt, const std::string &expected)
+ {
+ std::string result = LLDate::toHTTPDateString(t, fmt);
+ LLStringUtil::toLower(result);
+ std::string label = std::string("toHTTPDateString - ") + locale;
+ ensure_equals(label.c_str(), result, expected);
+ }
+
+ template<> template<>
+ void httpdate_object::test<4>()
+ {
+ // test localization of http dates
+ const char *en_locale = "en_US.UTF-8";
+ const char *fr_locale = "fr_FR.UTF-8";
+
+ std::string prev_locale = LLStringUtil::getLocale();
+ std::string prev_clocale = std::string(setlocale(LC_TIME, NULL));
+ time_t test_time = 1252374030; // 8 Sep 2009 01:40:01
+ struct tm *t = gmtime(&test_time);
+
+ setlocale(LC_TIME, en_locale);
+ if (strcmp(setlocale(LC_TIME, NULL), en_locale) != 0)
+ {
+ setlocale(LC_TIME, prev_clocale.c_str());
+ skip("Cannot set English locale");
+ }
+
+ LLStringUtil::setLocale(en_locale);
+ test_date_string(en_locale, t, "%d %B %Y - %H:%M", "08 september 2009 - 01:40");
+ test_date_string(en_locale, t, "%H", "01");
+ test_date_string(en_locale, t, "%M", "40");
+ test_date_string(en_locale, t, "%I", "01");
+ test_date_string(en_locale, t, "%d", "08");
+ test_date_string(en_locale, t, "%Y", "2009");
+ test_date_string(en_locale, t, "%p", "am");
+ test_date_string(en_locale, t, "%A", "tuesday");
+ test_date_string(en_locale, t, "%B", "september");
+
+ setlocale(LC_TIME, fr_locale);
+ if (strcmp(setlocale(LC_TIME, NULL), fr_locale) != 0)
+ {
+ LLStringUtil::setLocale(prev_locale);
+ setlocale(LC_TIME, prev_clocale.c_str());
+ skip("Cannot set French locale");
+ }
+
+ LLStringUtil::setLocale(fr_locale);
+ test_date_string(fr_locale, t, "%d %B %Y - %H:%M", "08 septembre 2009 - 01:40");
+ test_date_string(fr_locale, t, "%H", "01");
+ test_date_string(fr_locale, t, "%M", "40");
+ test_date_string(fr_locale, t, "%I", "01");
+ test_date_string(fr_locale, t, "%d", "08");
+ test_date_string(fr_locale, t, "%Y", "2009");
+ test_date_string(fr_locale, t, "%A", "mardi");
+ test_date_string(fr_locale, t, "%B", "septembre");
+
+ LLStringUtil::setLocale(prev_locale);
+ setlocale(LC_TIME, prev_clocale.c_str());
+ }
+}
diff --git a/indra/llcommon/tests/lllazy_test.cpp b/indra/llcommon/tests/lllazy_test.cpp
deleted file mode 100644
index 923fe952a9..0000000000
--- a/indra/llcommon/tests/lllazy_test.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * @file lllazy_test.cpp
- * @author Nat Goodspeed
- * @date 2009-01-28
- * @brief Tests of lllazy.h.
- *
- * $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$
- */
-
-// Precompiled header
-#include "linden_common.h"
-// associated header
-#include "lllazy.h"
-// STL headers
-#include <iostream>
-// std headers
-// external library headers
-#include <boost/lambda/construct.hpp>
-#include <boost/lambda/bind.hpp>
-// other Linden headers
-#include "../test/lltut.h"
-#include "../test/catch_and_store_what_in.h"
-
-namespace bll = boost::lambda;
-
-/*****************************************************************************
-* Test classes
-*****************************************************************************/
-
-// Let's say that because of its many external dependencies, YuckyFoo is very
-// hard to instantiate in a test harness.
-class YuckyFoo
-{
-public:
- virtual ~YuckyFoo() {}
- virtual std::string whoami() const { return "YuckyFoo"; }
-};
-
-// Let's further suppose that YuckyBar is another hard-to-instantiate class.
-class YuckyBar
-{
-public:
- YuckyBar(const std::string& which):
- mWhich(which)
- {}
- virtual ~YuckyBar() {}
-
- virtual std::string identity() const { return std::string("YuckyBar(") + mWhich + ")"; }
-
-private:
- const std::string mWhich;
-};
-
-// Pretend that this class would be tough to test because, up until we started
-// trying to test it, it contained instances of both YuckyFoo and YuckyBar.
-// Now we've refactored so it contains LLLazy<YuckyFoo> and LLLazy<YuckyBar>.
-// More than that, it contains them by virtue of deriving from
-// LLLazyBase<YuckyFoo> and LLLazyBase<YuckyBar>.
-// We postulate two different LLLazyBases because, with only one, you need not
-// specify *which* get()/set() method you're talking about. That's a simpler
-// case.
-class NeedsTesting: public LLLazyBase<YuckyFoo>, public LLLazyBase<YuckyBar>
-{
-public:
- NeedsTesting():
- // mYuckyBar("RealYuckyBar")
- LLLazyBase<YuckyBar>(bll::bind(bll::new_ptr<YuckyBar>(), "RealYuckyBar"))
- {}
- virtual ~NeedsTesting() {}
-
- virtual std::string describe() const
- {
- return std::string("NeedsTesting(") + getLazy<YuckyFoo>(this).whoami() + ", " +
- getLazy<YuckyBar>(this).identity() + ")";
- }
-
-private:
- // These instance members were moved to LLLazyBases:
- // YuckyFoo mYuckyFoo;
- // YuckyBar mYuckyBar;
-};
-
-// Fake up a test YuckyFoo class
-class TestFoo: public YuckyFoo
-{
-public:
- virtual std::string whoami() const { return "TestFoo"; }
-};
-
-// and a test YuckyBar
-class TestBar: public YuckyBar
-{
-public:
- TestBar(const std::string& which): YuckyBar(which) {}
- virtual std::string identity() const
- {
- return std::string("TestBar(") + YuckyBar::identity() + ")";
- }
-};
-
-// So here's a test subclass of NeedsTesting that uses TestFoo and TestBar
-// instead of YuckyFoo and YuckyBar.
-class TestNeedsTesting: public NeedsTesting
-{
-public:
- TestNeedsTesting()
- {
- // Exercise setLazy(T*)
- setLazy<YuckyFoo>(this, new TestFoo());
- // Exercise setLazy(Factory)
- setLazy<YuckyBar>(this, bll::bind(bll::new_ptr<TestBar>(), "TestYuckyBar"));
- }
-
- virtual std::string describe() const
- {
- return std::string("TestNeedsTesting(") + NeedsTesting::describe() + ")";
- }
-
- void toolate()
- {
- setLazy<YuckyFoo>(this, new TestFoo());
- }
-};
-
-// This class tests having an explicit LLLazy<T> instance as a named member,
-// rather than deriving from LLLazyBase<T>.
-class LazyMember
-{
-public:
- YuckyFoo& getYuckyFoo() { return *mYuckyFoo; }
- std::string whoisit() const { return mYuckyFoo->whoami(); }
-
-protected:
- LLLazy<YuckyFoo> mYuckyFoo;
-};
-
-// This is a test subclass of the above, dynamically replacing the
-// LLLazy<YuckyFoo> member.
-class TestLazyMember: public LazyMember
-{
-public:
- // use factory setter
- TestLazyMember()
- {
- mYuckyFoo.set(bll::new_ptr<TestFoo>());
- }
-
- // use instance setter
- TestLazyMember(YuckyFoo* instance)
- {
- mYuckyFoo.set(instance);
- }
-};
-
-/*****************************************************************************
-* TUT
-*****************************************************************************/
-namespace tut
-{
- struct lllazy_data
- {
- };
- typedef test_group<lllazy_data> lllazy_group;
- typedef lllazy_group::object lllazy_object;
- lllazy_group lllazygrp("lllazy");
-
- template<> template<>
- void lllazy_object::test<1>()
- {
- // Instantiate an official one, just because we can
- NeedsTesting nt;
- // and a test one
- TestNeedsTesting tnt;
-// std::cout << nt.describe() << '\n';
- ensure_equals(nt.describe(), "NeedsTesting(YuckyFoo, YuckyBar(RealYuckyBar))");
-// std::cout << tnt.describe() << '\n';
- ensure_equals(tnt.describe(),
- "TestNeedsTesting(NeedsTesting(TestFoo, TestBar(YuckyBar(TestYuckyBar))))");
- }
-
- template<> template<>
- void lllazy_object::test<2>()
- {
- TestNeedsTesting tnt;
- std::string threw = catch_what<LLLazyCommon::InstanceChange>([&tnt](){
- tnt.toolate();
- });
- ensure_contains("InstanceChange exception", threw, "replace LLLazy instance");
- }
-
- template<> template<>
- void lllazy_object::test<3>()
- {
- {
- LazyMember lm;
- // operator*() on-demand instantiation
- ensure_equals(lm.getYuckyFoo().whoami(), "YuckyFoo");
- }
- {
- LazyMember lm;
- // operator->() on-demand instantiation
- ensure_equals(lm.whoisit(), "YuckyFoo");
- }
- }
-
- template<> template<>
- void lllazy_object::test<4>()
- {
- {
- // factory setter
- TestLazyMember tlm;
- ensure_equals(tlm.whoisit(), "TestFoo");
- }
- {
- // instance setter
- TestLazyMember tlm(new TestFoo());
- ensure_equals(tlm.whoisit(), "TestFoo");
- }
- }
-} // namespace tut
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index fa48bcdefd..ae20e7ed27 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -190,7 +190,7 @@ namespace tut
// 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))),
+ reader.getPath().string().substr(0, reader.getPath().string().length()-3))),
PYTHON(LLStringUtil::getenv("PYTHON"))
{
ensure("Set PYTHON to interpreter pathname", !PYTHON.empty());
@@ -212,9 +212,9 @@ namespace tut
"time.sleep(1)\n");
LLLeapVector instances;
instances.push_back(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()})->getWeak());
+ StringVec{PYTHON, script.getPath().string()})->getWeak());
instances.push_back(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()})->getWeak());
+ StringVec{PYTHON, script.getPath().string()})->getWeak());
// 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.
@@ -229,7 +229,7 @@ namespace tut
"import sys\n"
"sys.stderr.write('''Hello from Python!\n"
"note partial line''')\n");
- StringVec vcommand{ PYTHON, script.getName() };
+ StringVec vcommand{ PYTHON, script.getPath().string() };
CaptureLog log(LLError::LEVEL_INFO);
waitfor(LLLeap::create(get_test_name(), vcommand));
log.messageWith("Hello from Python!");
@@ -244,7 +244,7 @@ namespace tut
"print('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "Hello from Python!");
}
@@ -259,7 +259,7 @@ namespace tut
"sys.stdout.write('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("Discarding"), "Hello from Python!");
}
@@ -273,7 +273,7 @@ namespace tut
"sys.stdout.write('5a2:something')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "5a2:");
}
@@ -386,7 +386,7 @@ namespace tut
" else 'bad: ' + str(resp)\n"
"send(pump='" << result.getName() << "', data=result)\n";});
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
result.ensure();
}
@@ -445,7 +445,7 @@ namespace tut
" result = 'expected reqid=%s in %s' % (i, resp)\n"
" break\n"
"send(pump='" << result.getName() << "', data=result)\n";});
- waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}),
+ waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getPath().string()}),
300); // needs more realtime than most tests
result.ensure();
}
@@ -512,7 +512,7 @@ namespace tut
" (start, large[start:end], echoed[start:end]))\n"
"sys.exit(1)\n";});
waitfor(LLLeap::create(test_name,
- StringVec{PYTHON, script.getName(), stringize(size)}),
+ StringVec{PYTHON, script.getPath().string(), stringize(size)}),
180); // try a longer timeout
result.ensure();
}
diff --git a/indra/llcommon/tests/llmemtype_test.cpp b/indra/llcommon/tests/llmemtype_test.cpp
deleted file mode 100644
index 2d64d342ae..0000000000
--- a/indra/llcommon/tests/llmemtype_test.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * @file llmemtype_test.cpp
- * @author Palmer Truelson
- * @date 2008-03-
- * @brief Test for llmemtype.cpp.
- *
- * $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$
- */
-
-#include "../llmemtype.h"
-#include "../test/lltut.h"
-#include "../llallocator.h"
-
-
-#include <stack>
-
-std::stack<S32> memTypeStack;
-
-void LLAllocator::pushMemType(S32 i)
-{
- memTypeStack.push(i);
-}
-
-S32 LLAllocator::popMemType(void)
-{
- S32 ret = memTypeStack.top();
- memTypeStack.pop();
- return ret;
-}
-
-namespace tut
-{
- struct llmemtype_data
- {
- };
-
- typedef test_group<llmemtype_data> factory;
- typedef factory::object object;
-}
-namespace
-{
- tut::factory llmemtype_test_factory("LLMemType");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- ensure("Simplest test ever", true);
- }
-
- // test with no scripts
- template<> template<>
- void object::test<2>()
- {
- {
- LLMemType m1(LLMemType::MTYPE_INIT);
- }
- ensure("Test that you can construct and destruct the mem type");
- }
-
- // test creation and stack testing
- template<> template<>
- void object::test<3>()
- {
- {
- ensure("Test that creation and destruction properly inc/dec the stack");
- ensure_equals(memTypeStack.size(), 0);
- {
- LLMemType m1(LLMemType::MTYPE_INIT);
- ensure_equals(memTypeStack.size(), 1);
- LLMemType m2(LLMemType::MTYPE_STARTUP);
- ensure_equals(memTypeStack.size(), 2);
- }
- ensure_equals(memTypeStack.size(), 0);
- }
- }
-
- // test with no scripts
- template<> template<>
- void object::test<4>()
- {
- // catch the begining and end
- std::string test_name = LLMemType::getNameFromID(LLMemType::MTYPE_INIT.mID);
- ensure_equals("Init name", test_name, "Init");
-
- std::string test_name2 = LLMemType::getNameFromID(LLMemType::MTYPE_VOLUME.mID);
- ensure_equals("Volume name", test_name2, "Volume");
-
- std::string test_name3 = LLMemType::getNameFromID(LLMemType::MTYPE_OTHER.mID);
- ensure_equals("Other name", test_name3, "Other");
-
- std::string test_name4 = LLMemType::getNameFromID(-1);
- ensure_equals("Invalid name", test_name4, "INVALID");
- }
-
-};
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 00dcd52d5d..5df43e497f 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -89,14 +89,14 @@ static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR
* @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="")
+static std::string readfile(const std::filesystem::path& pathname, const std::string& desc="")
{
std::string use_desc(desc);
if (use_desc.empty())
{
- use_desc = "in " + pathname;
+ use_desc = "in " + pathname.string();
}
- std::ifstream inf(pathname.c_str());
+ llifstream inf(pathname.c_str());
std::string output;
if (!std::getline(inf, output))
{
@@ -183,7 +183,7 @@ struct PythonProcessLauncher
mParams.desc = desc + " script";
mParams.executable = PYTHON;
- mParams.args.add(mScript.getName());
+ mParams.args.add(mScript.getPath().string());
}
/// Launch Python script; verify that it launched
@@ -203,7 +203,7 @@ struct PythonProcessLauncher
const char* APR_LOG = getenv("APR_LOG");
if (APR_LOG && *APR_LOG)
{
- std::ifstream inf(APR_LOG);
+ llifstream inf(APR_LOG);
if (! inf.is_open())
{
LL_WARNS() << "Couldn't open '" << APR_LOG << "'" << LL_ENDL;
@@ -251,11 +251,11 @@ struct PythonProcessLauncher
{
NamedTempFile out("out", ""); // placeholder
// pass name of this temporary file to the script
- mParams.args.add(out.getName());
+ mParams.args.add(out.getPath().string());
run();
// assuming the script wrote to that file, read it
std::string desc = "from " + mDesc + " script";
- return readfile(out.getName(), desc);
+ return readfile(out.getPath(), desc);
}
LLProcess::Params mParams;
@@ -498,23 +498,23 @@ public:
NamedTempDir():
mPath(NamedTempFile::temp_path()),
- mCreated(boost::filesystem::create_directories(mPath))
+ mCreated(std::filesystem::create_directories(mPath))
{
- mPath = boost::filesystem::canonical(mPath);
+ mPath = std::filesystem::canonical(mPath);
}
~NamedTempDir()
{
if (mCreated)
{
- boost::filesystem::remove_all(mPath);
+ std::filesystem::remove_all(mPath);
}
}
std::string getName() const { return mPath.string(); }
private:
- boost::filesystem::path mPath;
+ std::filesystem::path mPath;
bool mCreated;
};
@@ -674,7 +674,7 @@ namespace tut
#endif
// Have to have a named copy of this std::string so its c_str() value
// will persist.
- std::string scriptname(script.getName());
+ std::string scriptname(script.getPath().string());
argv.push_back(scriptname.c_str());
argv.push_back(NULL);
@@ -953,14 +953,14 @@ namespace tut
"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.mParams.args.add(out.getPath().string());
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")
+ if (readfile(out.getPath(), "from kill() script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -979,7 +979,7 @@ namespace tut
// 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");
+ ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok");
}
template<> template<>
@@ -999,7 +999,7 @@ namespace tut
"# 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.mParams.args.add(out.getPath().string());
py.launch();
// Capture handle for later
phandle = py.mPy->getProcessHandle();
@@ -1008,7 +1008,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(out.getName(), "from kill() script") == "ok")
+ if (readfile(out.getPath(), "from kill() script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -1021,7 +1021,7 @@ namespace tut
// 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");
+ ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok");
}
template<> template<>
@@ -1051,8 +1051,8 @@ namespace tut
"# 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.args.add(from.getPath().string());
+ py.mParams.args.add(to.getPath().string());
py.mParams.autokill = false;
py.launch();
// Capture handle for later
@@ -1062,7 +1062,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(from.getName(), "from autokill script") == "ok")
+ if (readfile(from.getPath(), "from autokill script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -1074,14 +1074,14 @@ namespace tut
// 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());
+ llofstream outf(to.getPath());
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");
+ ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack");
}
template<> template<>
@@ -1113,8 +1113,8 @@ namespace tut
"# 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.args.add(from.getPath().string());
+ py.mParams.args.add(to.getPath().string());
py.mParams.autokill = true;
py.mParams.attached = false;
py.launch();
@@ -1125,7 +1125,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(from.getName(), "from autokill script") == "ok")
+ if (readfile(from.getPath(), "from autokill script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -1137,14 +1137,14 @@ namespace tut
// 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());
+ llofstream outf(to.getPath());
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");
+ ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack");
}
template<> template<>
diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp
new file mode 100644
index 0000000000..9cc93bcd14
--- /dev/null
+++ b/indra/llcommon/tests/llsd_test.cpp
@@ -0,0 +1,830 @@
+/**
+ * @file llsd_new_tut.cpp
+ * @date February 2006
+ * @brief LLSD unit tests
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2006-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$
+ */
+
+#define LLSD_DEBUG_INFO
+#include "linden_common.h"
+#include "lltut.h"
+#include "llsd.h"
+
+#include "llsdtraits.h"
+#include "llstring.h"
+
+using std::fpclassify;
+
+namespace tut
+{
+ class SDCleanupCheck
+ {
+ private:
+ U32 mOutstandingAtStart;
+ public:
+ SDCleanupCheck() : mOutstandingAtStart(llsd::outstandingCount()) { }
+ ~SDCleanupCheck()
+ {
+ ensure_equals("SDCleanupCheck",
+ llsd::outstandingCount(), mOutstandingAtStart);
+ }
+ };
+
+ class SDAllocationCheck : public SDCleanupCheck
+ {
+ private:
+ std::string mMessage;
+ U32 mExpectedAllocations;
+ U32 mAllocationAtStart;
+ public:
+ SDAllocationCheck(const std::string& message, int expectedAllocations)
+ : mMessage(message),
+ mExpectedAllocations(expectedAllocations),
+ mAllocationAtStart(llsd::allocationCount())
+ { }
+ ~SDAllocationCheck()
+ {
+ ensure_equals(mMessage + " SDAllocationCheck",
+ llsd::allocationCount() - mAllocationAtStart,
+ mExpectedAllocations);
+ }
+ };
+
+ struct SDTestData {
+ template<class T>
+ static void ensureTypeAndValue(const char* msg, const LLSD& actual,
+ T expectedValue)
+ {
+ LLSDTraits<T> traits;
+
+ std::string s(msg);
+
+ ensure( s + " type", traits.checkType(actual));
+ ensure_equals( s + " value", traits.get(actual), expectedValue);
+ }
+
+ template<class T>
+ static void ensureTypeAndRefValue(const char* msg, const LLSD& actual,
+ const T& expectedValue)
+ {
+ LLSDTraits<const T&> traits;
+
+ std::string s(msg);
+
+ ensure( s + " type", traits.checkType(actual));
+ ensure_equals( s + " value", traits.get(actual), expectedValue);
+ }
+ };
+
+ typedef test_group<SDTestData> SDTestGroup;
+ typedef SDTestGroup::object SDTestObject;
+
+ SDTestGroup sdTestGroup("LLSD(new)");
+
+ // template<> template<>
+ // void SDTestObject::test<1>()
+ // // construction and test of undefined
+ // {
+ // SDCleanupCheck check;
+
+ // LLSD u;
+ // ensure("is undefined", u.isUndefined());
+ // }
+
+ template<> template<>
+ void SDTestObject::test<2>()
+ // setting and fetching scalar types
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ v = true; ensureTypeAndValue("set true", v, true);
+ v = false; ensureTypeAndValue("set false", v, false);
+ v = true; ensureTypeAndValue("set true again", v, true);
+
+ v = 42; ensureTypeAndValue("set to 42", v, 42);
+ v = 0; ensureTypeAndValue("set to zero", v, 0);
+ v = -12345; ensureTypeAndValue("set to neg", v, -12345);
+ v = 2000000000; ensureTypeAndValue("set to big", v, 2000000000);
+
+ v = 3.14159265359;
+ ensureTypeAndValue("set to pi", v, 3.14159265359);
+ ensure_not_equals("isn't float", v.asReal(),
+ (float)3.14159265359);
+ v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256);
+
+ LLUUID nullUUID;
+ LLUUID newUUID;
+ newUUID.generate();
+
+ v = nullUUID; ensureTypeAndValue("set to null UUID", v, nullUUID);
+ v = newUUID; ensureTypeAndValue("set to new UUID", v, newUUID);
+ v = nullUUID; ensureTypeAndValue("set to null again", v, nullUUID);
+
+ // strings must be tested with two types of string objects
+ std::string s = "now is the time";
+ const char* cs = "for all good zorks";
+
+ v = s; ensureTypeAndValue("set to std::string", v, s);
+ v = cs; ensureTypeAndValue("set to const char*", v, cs);
+
+ LLDate epoch;
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+
+ v = epoch; ensureTypeAndValue("set to epoch", v, epoch);
+ v = aDay; ensureTypeAndValue("set to a day", v, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+
+ v = path; ensureTypeAndValue("set to a uri", v, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+
+ v = data; ensureTypeAndRefValue("set to data", v, data);
+
+ v.clear();
+ ensure("reset to undefined", v.type() == LLSD::TypeUndefined);
+ }
+
+ template<> template<>
+ void SDTestObject::test<3>()
+ // construction via scalar values
+ // tests both constructor and initialize forms
+ {
+ SDCleanupCheck check;
+
+ LLSD b1(true); ensureTypeAndValue("construct boolean", b1, true);
+ LLSD b2 = true; ensureTypeAndValue("initialize boolean", b2, true);
+ LLSD i1(42); ensureTypeAndValue("construct int", i1, 42);
+ LLSD i2 =42; ensureTypeAndValue("initialize int", i2, 42);
+ LLSD d1(1.2); ensureTypeAndValue("construct double", d1, 1.2);
+ LLSD d2 = 1.2; ensureTypeAndValue("initialize double", d2, 1.2);
+
+ LLUUID newUUID;
+ newUUID.generate();
+ LLSD u1(newUUID);
+ ensureTypeAndValue("construct UUID", u1, newUUID);
+ LLSD u2 = newUUID;
+ ensureTypeAndValue("initialize UUID", u2, newUUID);
+
+ LLSD ss1(std::string("abc"));
+ ensureTypeAndValue("construct std::string", ss1, "abc");
+ LLSD ss2 = std::string("abc");
+ ensureTypeAndValue("initialize std::string",ss2, "abc");
+ LLSD sl1(std::string("def"));
+ ensureTypeAndValue("construct std::string", sl1, "def");
+ LLSD sl2 = std::string("def");
+ ensureTypeAndValue("initialize std::string", sl2, "def");
+ LLSD sc1("ghi");
+ ensureTypeAndValue("construct const char*", sc1, "ghi");
+ LLSD sc2 = "ghi";
+ ensureTypeAndValue("initialize const char*",sc2, "ghi");
+
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+ LLSD t1(aDay); ensureTypeAndValue("construct LLDate", t1, aDay);
+ LLSD t2 = aDay; ensureTypeAndValue("initialize LLDate", t2, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLSD p1(path); ensureTypeAndValue("construct LLURI", p1, path);
+ LLSD p2 = path; ensureTypeAndValue("initialize LLURI", p2, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+ LLSD x1(data); ensureTypeAndRefValue("construct vector<U8>", x1, data);
+ LLSD x2 = data; ensureTypeAndRefValue("initialize vector<U8>", x2, data);
+ }
+
+ void checkConversions(const char* msg, const LLSD& v,
+ LLSD::Boolean eBoolean, LLSD::Integer eInteger,
+ LLSD::Real eReal, const LLSD::String& eString)
+ {
+ std::string s(msg);
+
+ ensure_equals(s+" to bool", v.asBoolean(), eBoolean);
+ ensure_equals(s+" to int", v.asInteger(), eInteger);
+ if (eReal == eReal)
+ {
+ ensure_equals(s+" to real", v.asReal(), eReal);
+ ensure_equals(s+" to string", v.asString(), eString);
+ }
+ else
+ {
+ int left = fpclassify(v.asReal());
+ int right = fpclassify(eReal);
+
+ ensure_equals(s+" to real", left, right);
+ // ensure_equals(s+" to string", v.asString(), eString);
+ // I've commented this check out, since there doesn't
+ // seem to be uniform string representation for NaN on
+ // all platforms. For example, on my Ubuntu 8.10 laptop
+ // with libc 2.11.1, sqrt(-1.0) will return '-nan', not
+ // 'nan'.
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<4>()
+ // conversion between undefined and basic scalar types:
+ // boolean, integer, real and string
+ {
+ SDCleanupCheck check;
+
+ LLSD v; checkConversions("untitled", v, false, 0, 0.0, "");
+
+ v = false; checkConversions("false", v, false, 0, 0.0, "");
+ v = true; checkConversions("true", v, true, 1, 1.0, "true");
+
+ v = 0; checkConversions("zero", v, false, 0, 0.0, "0");
+ v = 1; checkConversions("one", v, true, 1, 1.0, "1");
+ v = -33; checkConversions("neg33", v, true, -33, -33.0, "-33");
+
+ v = 0.0; checkConversions("0.0", v, false, 0, 0.0, "0");
+ v = 0.5; checkConversions("point5", v, true, 0, 0.5, "0.5");
+ v = 0.9; checkConversions("point9", v, true, 0, 0.9, "0.9");
+ v = -3.9; checkConversions("neg3dot9", v, true, -3, -3.9, "-3.9");
+ // Get rid of NaN test. First, some libraries don't reliably return
+ // NaN for sqrt(-1.0) -- meaning that I don't even know how to
+ // portably, reliably produce a NaN value. Second, we observe failures
+ // on different platforms in the asString() test. But LLSD's
+ // ImplReal::asString() does not itself recognize NaN! It merely
+ // passes the value through to llformat(), which passes it through to
+ // the library vsnprintf(). That is, even when we do produce NaN,
+ // we're not testing any LLSD code: we're testing the local library's
+ // vsnprintf() function, which (empirically) produces idiosyncratic
+ // results. This is just not a good test case.
+// v = sqrt(-1.0); checkConversions("NaN", v, false, 0, sqrt(-1.0), "nan");
+
+ v = ""; checkConversions("empty", v, false, 0, 0.0, "");
+ v = "0"; checkConversions("digit0", v, true, 0, 0.0, "0");
+ v = "10"; checkConversions("digit10", v, true, 10, 10.0, "10");
+ v = "-2.345"; checkConversions("decdigits", v,
+ true, -2, -2.345, "-2.345");
+ v = "apple"; checkConversions("apple", v, true, 0, 0.0, "apple");
+ v = "33bob"; checkConversions("digialpha", v, true, 0, 0.0, "33bob");
+ v = " "; checkConversions("space", v, true, 0, 0.0, " ");
+ v = "\n"; checkConversions("newline", v, true, 0, 0.0, "\n");
+ }
+
+ template<class T>
+ void checkRoundTrip(const std::string& msg, const LLSD& actual,
+ const char* sExpected, T vExpected)
+ {
+ std::string str = actual.asString();
+
+ if (sExpected) {
+ ensure_equals(msg + " string", str, sExpected);
+ }
+
+ LLSD u(str);
+ LLSDTraits<T> traits;
+
+ ensure_equals(msg + " value", traits.get(u), vExpected);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<5>()
+ // conversion of String to and from UUID, Date and URI.
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ LLUUID nullUUID;
+ LLUUID someUUID;
+ someUUID.generate();
+
+ v = nullUUID; checkRoundTrip("null uuid", v,
+ "00000000-0000-0000-0000-000000000000", nullUUID);
+ v = someUUID; checkRoundTrip("random uuid", v, 0, someUUID);
+
+ LLDate epoch;
+ LLDate beta("2003-04-30T04:00:00Z");
+ LLDate oneOh("2003-06-23T04:00:00Z");
+
+ v = epoch; checkRoundTrip("epoch date", v, 0, epoch);
+ v = beta; checkRoundTrip("beta date", v,
+ "2003-04-30T04:00:00Z", beta);
+ v = oneOh; checkRoundTrip("1.0 date", v,
+ "2003-06-23T04:00:00Z", oneOh);
+
+ LLURI empty;
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLURI mail("mailto:zero.linden@secondlife.com");
+
+ v = empty; checkRoundTrip("empty URI", v, 0, empty);
+ v = path; checkRoundTrip("path URI", v,
+ "http://slurl.com/secondlife/Ambleside/57/104/26/",
+ path);
+ v = mail; checkRoundTrip("mail URI", v,
+ "mailto:zero.linden@secondlife.com", mail);
+ }
+
+ template<> template<>
+ void SDTestObject::test<6>()
+ // copy construction and assignment
+ // checking for shared values after constr. or assignment
+ // checking in both the same type and change of type case
+ {
+ SDCleanupCheck check;
+
+ {
+ LLSD v = 42;
+
+ LLSD w0(v);
+ ensureTypeAndValue("int constr.", w0, 42);
+
+ LLSD w1(v);
+ w1 = 13;
+ ensureTypeAndValue("int constr. change case 1", w1, 13);
+ ensureTypeAndValue("int constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = 7;
+ ensureTypeAndValue("int constr. change case 3", w2, 42);
+ ensureTypeAndValue("int constr. change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1(v);
+ w1 = "bob";
+ ensureTypeAndValue("string constr. change case 1", w1, "bob");
+ ensureTypeAndValue("string constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = "amy";
+ ensureTypeAndValue("string constr. change case 3", w2, 42);
+ ensureTypeAndValue("string constr. change case 4", v, "amy");
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w0;
+ w0 = v;
+ ensureTypeAndValue("int assign", w0, 42);
+
+ LLSD w1;
+ w1 = v;
+ w1 = 13;
+ ensureTypeAndValue("int assign change case 1", w1, 13);
+ ensureTypeAndValue("int assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = 7;
+ ensureTypeAndValue("int assign change case 3", w2, 42);
+ ensureTypeAndValue("int assign change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1;
+ w1 = v;
+ w1 = "bob";
+ ensureTypeAndValue("string assign change case 1", w1, "bob");
+ ensureTypeAndValue("string assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = "amy";
+ ensureTypeAndValue("string assign change case 3", w2, 42);
+ ensureTypeAndValue("string assign change case 4", v, "amy");
+ }
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<7>()
+ // Test assignment and casting to various scalar types. These
+ // assignments should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b = false;
+ b = v; ensure_equals("assign to bool", b, true);
+ b = (bool)v; ensure_equals("cast to bool", b, true);
+
+ int i = 99;
+ i = v; ensure_equals("assign to int", i, 42);
+ i = (int)v; ensure_equals("cast to int", i, 42);
+
+ double d = 3.14159;
+ d = v; ensure_equals("assign to double", d, 42.375);
+ d = (double)v; ensure_equals("cast to double", d, 42.375);
+
+ std::string s = "yo";
+// SAD s = v; ensure_equals("assign to string", s, " 42.375");
+ s = (std::string)v; ensure_equals("cast to string", s, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID u;
+ u = v;
+ ensure_equals("assign to LLUUID", u, LLUUID(uuidStr));
+// SAD u = (LLUUID)v;
+// ensure_equals("cast to LLUUID", u, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date;
+ date = v;
+ ensure_equals("assign to LLDate", date.asString(), dateStr);
+// SAD date = (LLDate)v;
+// ensure_equals("cast to LLDate", date.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri;
+ uri = v;
+ ensure_equals("assign to LLURI", uri.asString(), uriStr);
+// SAD uri = (LLURI)v;
+// ensure_equals("cast to LLURI", uri.asString(), uriStr);
+ }
+
+ template<> template<>
+ void SDTestObject::test<8>()
+ // Test construction of various scalar types from LLSD.
+ // Test both construction and initialization forms.
+ // These should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b1(v); ensure_equals("contruct bool", b1, true);
+ bool b2 = v; ensure_equals("initialize bool", b2, true);
+
+ int i1(v); ensure_equals("contruct int", i1, 42);
+ int i2 = v; ensure_equals("initialize int", i2, 42);
+
+ double d1(v); ensure_equals("contruct double", d1, 42.375);
+ double d2 = v; ensure_equals("initialize double", d2, 42.375);
+
+ std::string s1(v);
+ std::string s2 = v;
+ ensure_equals("contruct string", s1, " 42.375");
+ ensure_equals("initialize string", s2, " 42.375");
+
+ std::string t1(v);
+ std::string t2 = v.asString(); // SAD
+ ensure_equals("contruct std::string", t1, " 42.375");
+ ensure_equals("initialize std::string", t2, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID uuid1(v.asUUID()); // SAD
+ LLUUID uuid2 = v;
+ ensure_equals("contruct LLUUID", uuid1, LLUUID(uuidStr));
+ ensure_equals("initialize LLUUID", uuid2, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date1(v.asDate()); // SAD
+ LLDate date2 = v;
+ ensure_equals("contruct LLDate", date1.asString(), dateStr);
+ ensure_equals("initialize LLDate", date2.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri1(v.asURI()); // SAD
+ LLURI uri2 = v;
+ ensure_equals("contruct LLURI", uri1.asString(), uriStr);
+ ensure_equals("initialize LLURI", uri2.asString(), uriStr);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<9>()
+ // test to make sure v is interpreted as a bool in a various
+ // scenarios.
+ {
+ SDCleanupCheck check;
+
+ LLSD v = "0";
+ // magic value that is interpreted as boolean true, but integer false!
+
+ ensure_equals("trinary operator bool", (v ? true : false), true);
+ ensure_equals("convert to int, then bool",
+ ((int)v ? true : false), false);
+
+ if(v)
+ {
+ ensure("if converted to bool", true);
+ }
+ else
+ {
+ fail("bool did not convert to a bool in if statement.");
+ }
+
+ if(!v)
+ {
+ fail("bool did not convert to a bool in negated if statement.");
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<10>()
+ // map operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure("undefined has no members", !v.has("amy"));
+ ensure("undefined get() is undefined", v.get("bob").isUndefined());
+
+ v = LLSD::emptyMap();
+ ensure("empty map is a map", v.isMap());
+ ensure("empty map has no members", !v.has("cam"));
+ ensure("empty map get() is undefined", v.get("don").isUndefined());
+
+ v.clear();
+ v.insert("eli", 43);
+ ensure("insert converts to map", v.isMap());
+ ensure("inserted key is present", v.has("eli"));
+ ensureTypeAndValue("inserted value", v.get("eli"), 43);
+
+ v.insert("fra", false);
+ ensure("first key still present", v.has("eli"));
+ ensure("second key is present", v.has("fra"));
+ ensureTypeAndValue("first value", v.get("eli"), 43);
+ ensureTypeAndValue("second value", v.get("fra"), false);
+
+ v.erase("eli");
+ ensure("first key now gone", !v.has("eli"));
+ ensure("second key still present", v.has("fra"));
+ ensure("first value gone", v.get("eli").isUndefined());
+ ensureTypeAndValue("second value sill there", v.get("fra"), false);
+
+ v.erase("fra");
+ ensure("second key now gone", !v.has("fra"));
+ ensure("second value gone", v.get("fra").isUndefined());
+
+ v["gil"] = (std::string)"good morning";
+ ensure("third key present", v.has("gil"));
+ ensureTypeAndValue("third key value", v.get("gil"), "good morning");
+
+ const LLSD& cv = v; // FIX ME IF POSSIBLE
+ ensure("missing key", cv["ham"].isUndefined());
+ ensure("key not present", !v.has("ham"));
+
+ LLSD w = 43;
+ const LLSD& cw = w; // FIX ME IF POSSIBLE
+ int i = cw["ian"];
+ ensureTypeAndValue("other missing value", i, 0);
+ ensure("other missing key", !w.has("ian"));
+ ensure("no conversion", w.isInteger());
+
+ LLSD x;
+ x = v;
+ ensure("copy map type", x.isMap());
+ ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning");
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<11>()
+ // array operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure_equals("undefined has no size", v.size(), 0);
+ ensure("undefined get() is undefined", v.get(0).isUndefined());
+
+ v = LLSD::emptyArray();
+ ensure("empty array is an array", v.isArray());
+ ensure_equals("empty array has no size", v.size(), 0);
+ ensure("empty map get() is undefined", v.get(0).isUndefined());
+
+ v.clear();
+ v.append(88);
+ v.append("noodle");
+ v.append(true);
+ ensure_equals("appened array size", v.size(), 3);
+ ensure("append array is an array", v.isArray());
+ ensureTypeAndValue("append 0", v[0], 88);
+ ensureTypeAndValue("append 1", v[1], "noodle");
+ ensureTypeAndValue("append 2", v[2], true);
+
+ v.insert(0, 77);
+ v.insert(2, "soba");
+ v.insert(4, false);
+ ensure_equals("inserted array size", v.size(), 6);
+ ensureTypeAndValue("post insert 0", v[0], 77);
+ ensureTypeAndValue("post insert 1", v[1], 88);
+ ensureTypeAndValue("post insert 2", v[2], "soba");
+ ensureTypeAndValue("post insert 3", v[3], "noodle");
+ ensureTypeAndValue("post insert 4", v[4], false);
+ ensureTypeAndValue("post insert 5", v[5], true);
+
+ ensureTypeAndValue("get 1", v.get(1), 88);
+ v.set(1, "hot");
+ ensureTypeAndValue("set 1", v.get(1), "hot");
+
+ v.erase(3);
+ ensure_equals("post erase array size", v.size(), 5);
+ ensureTypeAndValue("post erase 0", v[0], 77);
+ ensureTypeAndValue("post erase 1", v[1], "hot");
+ ensureTypeAndValue("post erase 2", v[2], "soba");
+ ensureTypeAndValue("post erase 3", v[3], false);
+ ensureTypeAndValue("post erase 4", v[4], true);
+
+ v.append(34);
+ ensure_equals("size after append", v.size(), 6);
+ ensureTypeAndValue("post append 5", v[5], 34);
+
+ LLSD w;
+ w = v;
+ ensure("copy array type", w.isArray());
+ ensure_equals("copy array size", w.size(), 6);
+ ensureTypeAndValue("copy array 0", w[0], 77);
+ ensureTypeAndValue("copy array 1", w[1], "hot");
+ ensureTypeAndValue("copy array 2", w[2], "soba");
+ ensureTypeAndValue("copy array 3", w[3], false);
+ ensureTypeAndValue("copy array 4", w[4], true);
+ ensureTypeAndValue("copy array 5", w[5], 34);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<12>()
+ // no sharing
+ {
+ SDCleanupCheck check;
+
+ LLSD a = 99;
+ LLSD b = a;
+ a = 34;
+ ensureTypeAndValue("top level original changed", a, 34);
+ ensureTypeAndValue("top level copy unaltered", b, 99);
+ b = a;
+ b = 66;
+ ensureTypeAndValue("top level original unaltered", a, 34);
+ ensureTypeAndValue("top level copy changed", b, 66);
+
+ a[0] = "uno";
+ a[1] = 99;
+ a[2] = 1.414;
+ b = a;
+ a[1] = 34;
+ ensureTypeAndValue("array member original changed", a[1], 34);
+ ensureTypeAndValue("array member copy unaltered", b[1], 99);
+ b = a;
+ b[1] = 66;
+ ensureTypeAndValue("array member original unaltered", a[1], 34);
+ ensureTypeAndValue("array member copy changed", b[1], 66);
+
+ a["alpha"] = "uno";
+ a["beta"] = 99;
+ a["gamma"] = 1.414;
+ b = a;
+ a["beta"] = 34;
+ ensureTypeAndValue("map member original changed", a["beta"], 34);
+ ensureTypeAndValue("map member copy unaltered", b["beta"], 99);
+ b = a;
+ b["beta"] = 66;
+ ensureTypeAndValue("map member original unaltered", a["beta"], 34);
+ ensureTypeAndValue("map member copy changed", b["beta"], 66);
+ }
+
+ template<> template<>
+ void SDTestObject::test<13>()
+ // sharing implementation
+ {
+ SDCleanupCheck check;
+
+ {
+ SDAllocationCheck check("copy construct undefinded", 0);
+ LLSD v;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign undefined", 0);
+ LLSD v;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer value", 1);
+ LLSD v = 45;
+ v = 33;
+ v = 0;
+ }
+
+ {
+ SDAllocationCheck check("copy construct integer", 1);
+ LLSD v = 45;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer", 1);
+ LLSD v = 45;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("avoids extra clone", 2);
+ LLSD v = 45;
+ LLSD w = v;
+ w = "nice day";
+ }
+
+ {
+ SDAllocationCheck check("shared values test for threaded work", 9);
+
+ //U32 start_llsd_count = LLSD::outstandingCount();
+
+ LLSD m = LLSD::emptyMap();
+
+ m["one"] = 1;
+ m["two"] = 2;
+ m["one_copy"] = m["one"]; // 3 (m, "one" and "two")
+
+ m["undef_one"] = LLSD();
+ m["undef_two"] = LLSD();
+ m["undef_one_copy"] = m["undef_one"];
+
+ { // Ensure first_array gets freed to avoid counting it
+ LLSD first_array = LLSD::emptyArray();
+ first_array.append(1.0f);
+ first_array.append(2.0f);
+ first_array.append(3.0f); // 7
+
+ m["array"] = first_array;
+ m["array_clone"] = first_array;
+ m["array_copy"] = m["array"]; // 7
+ }
+
+ m["string_one"] = "string one value";
+ m["string_two"] = "string two value";
+ m["string_one_copy"] = m["string_one"]; // 9
+
+ //U32 llsd_object_count = LLSD::outstandingCount();
+ //std::cout << "Using " << (llsd_object_count - start_llsd_count) << " LLSD objects" << std::endl;
+
+ //m.dumpStats();
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<14>()
+ // make sure that assignment of char* NULL in a string does not crash.
+ {
+ LLSD v;
+ v = (const char*)NULL;
+ ensure("type is a string", v.isString());
+ }
+
+ /* TO DO:
+ conversion of undefined to UUID, Date, URI and Binary
+ conversion of undefined to map and array
+ test map operations
+ test array operations
+ test array extension
+
+ test copying and assign maps and arrays (clone)
+ test iteration over map
+ test iteration over array
+ test iteration over scalar
+
+ test empty map and empty array are indeed shared
+ test serializations
+ */
+}
+
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index f52b3fec71..71096523a9 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -52,7 +52,7 @@ typedef U32 uint32_t;
#include "llformat.h"
#include "llmemorystream.h"
-#include "../test/hexdump.h"
+#include "hexdump.h"
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
#include "stringize.h"
@@ -1860,7 +1860,7 @@ namespace tut
#if LL_WINDOWS
std::string q("\"");
std::string qPYTHON(q + PYTHON + q);
- std::string qscript(q + scriptfile.getName() + q);
+ std::string qscript(q + scriptfile.getPath().string() + q);
int rc = (int)_spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(),
std::forward<ARGS>(args)..., NULL);
if (rc == -1)
@@ -1877,7 +1877,7 @@ namespace tut
#else // LL_DARWIN, LL_LINUX
LLProcess::Params params;
params.executable = PYTHON;
- params.args.add(scriptfile.getName());
+ params.args.add(scriptfile.getPath().string());
for (const std::string& arg : StringVec{ std::forward<ARGS>(args)... })
{
params.args.add(arg);
@@ -1973,12 +1973,12 @@ namespace tut
int bufflen{ static_cast<int>(buffstr.length()) };
out.write(reinterpret_cast<const char*>(&bufflen), sizeof(bufflen));
LL_DEBUGS() << "Wrote length: "
- << hexdump(reinterpret_cast<const char*>(&bufflen),
+ << LL::hexdump(reinterpret_cast<const char*>(&bufflen),
sizeof(bufflen))
<< LL_ENDL;
out.write(buffstr.c_str(), buffstr.length());
LL_DEBUGS() << "Wrote data: "
- << hexmix(buffstr.c_str(), buffstr.length())
+ << LL::hexmix(buffstr.c_str(), buffstr.length())
<< LL_ENDL;
}
}
@@ -2054,8 +2054,8 @@ namespace tut
" yield frombytes\n"
<< pydata <<
// Don't forget raw-string syntax for Windows pathnames.
- "debug = open(r'" << debug.getName() << "', 'w')\n"
- "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n";});
+ "debug = open(r'" << debug.getPath().string() << "', 'w')\n"
+ "verify(parse_each(open(r'" << file.getPath().string() << "', 'rb')))\n";});
}
catch (const failure&)
{
@@ -2163,13 +2163,13 @@ namespace tut
"]\n"
// Don't forget raw-string syntax for Windows pathnames.
// N.B. Using 'print' implicitly adds newlines.
- "with open(r'" << file.getName() << "', 'wb') as f:\n"
+ "with open(r'" << (const char*)file.getPath().u8string().c_str() << "', 'wb') as f:\n"
" for item in DATA:\n"
" serialized = llsd." << pyformatter << "(item)\n"
" f.write(lenformat.pack(len(serialized)))\n"
" f.write(serialized)\n";});
- std::ifstream inf(file.getName().c_str());
+ llifstream inf(file.getPath());
LLSD item;
try
{
diff --git a/indra/llcommon/tests/llsdutil_test.cpp b/indra/llcommon/tests/llsdutil_test.cpp
new file mode 100644
index 0000000000..4b25a02f30
--- /dev/null
+++ b/indra/llcommon/tests/llsdutil_test.cpp
@@ -0,0 +1,434 @@
+/**
+ * @file llsdutil_tut.cpp
+ * @author Adroit
+ * @date 2007-02
+ * @brief LLSD conversion routines test cases.
+ *
+ * $LicenseInfo:firstyear=2007&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 "lltut.h"
+#include "m4math.h"
+#include "v2math.h"
+#include "v2math.h"
+#include "v3color.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4coloru.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "llsdutil.h"
+#include "llsdutil_math.h"
+#include "stringize.h"
+#include <set>
+#include <boost/range.hpp>
+
+namespace tut
+{
+ struct llsdutil_data
+ {
+ void test_matches(const std::string& proto_key, const LLSD& possibles,
+ const char** begin, const char** end)
+ {
+ std::set<std::string> succeed(begin, end);
+ LLSD prototype(possibles[proto_key]);
+ for (LLSD::map_const_iterator pi(possibles.beginMap()), pend(possibles.endMap());
+ pi != pend; ++pi)
+ {
+ std::string match(llsd_matches(prototype, pi->second));
+ std::set<std::string>::const_iterator found = succeed.find(pi->first);
+ if (found != succeed.end())
+ {
+ // This test is supposed to succeed. Comparing to the
+ // empty string ensures that if the test fails, it will
+ // display the string received so we can tell what failed.
+ ensure_equals("match", match, "");
+ }
+ else
+ {
+ // This test is supposed to fail. If we get a false match,
+ // the string 'match' will be empty, which doesn't tell us
+ // much about which case went awry. So construct a more
+ // detailed description string.
+ ensure(proto_key + " shouldn't match " + pi->first, ! match.empty());
+ }
+ }
+ }
+ };
+ typedef test_group<llsdutil_data> llsdutil_test;;
+ typedef llsdutil_test::object llsdutil_object;
+ tut::llsdutil_test tutil("llsdutil");
+
+ template<> template<>
+ void llsdutil_object::test<1>()
+ {
+ LLSD sd;
+ U64 valueIn , valueOut;
+ valueIn = U64L(0xFEDCBA9876543210);
+ sd = ll_sd_from_U64(valueIn);
+ valueOut = ll_U64_from_sd(sd);
+ ensure_equals("U64 valueIn->sd->valueOut", valueIn, valueOut);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<2>()
+ {
+ LLSD sd;
+ U32 valueIn, valueOut;
+ valueIn = 0x87654321;
+ sd = ll_sd_from_U32(valueIn);
+ valueOut = ll_U32_from_sd(sd);
+ ensure_equals("U32 valueIn->sd->valueOut", valueIn, valueOut);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<3>()
+ {
+ U32 valueIn, valueOut;
+ valueIn = 0x87654321;
+ LLSD sd;
+ sd = ll_sd_from_ipaddr(valueIn);
+ valueOut = ll_ipaddr_from_sd(sd);
+ ensure_equals("valueIn->sd->valueOut", valueIn, valueOut);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<4>()
+ {
+ LLSD sd;
+ LLVector3 vec1(-1.0, 2.0, -3.0);
+ sd = ll_sd_from_vector3(vec1);
+ LLVector3 vec2 = ll_vector3_from_sd(sd);
+ ensure_equals("vector3 -> sd -> vector3: 1", vec1, vec2);
+
+ LLVector3 vec3(sd);
+ ensure_equals("vector3 -> sd -> vector3: 2", vec1, vec3);
+
+ sd.clear();
+ vec1.setVec(0., 0., 0.);
+ sd = ll_sd_from_vector3(vec1);
+ vec2 = ll_vector3_from_sd(sd);
+ ensure_equals("vector3 -> sd -> vector3: 3", vec1, vec2);
+ sd.clear();
+ }
+
+ template<> template<>
+ void llsdutil_object::test<5>()
+ {
+ LLSD sd;
+ LLVector3d vec1((F64)(U64L(0xFEDCBA9876543210) << 2), -1., 0);
+ sd = ll_sd_from_vector3d(vec1);
+ LLVector3d vec2 = ll_vector3d_from_sd(sd);
+ ensure_equals("vector3d -> sd -> vector3d: 1", vec1, vec2);
+
+ LLVector3d vec3(sd);
+ ensure_equals("vector3d -> sd -> vector3d : 2", vec1, vec3);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<6>()
+ {
+ LLSD sd;
+ LLVector2 vec((F32) -3., (F32) 4.2);
+ sd = ll_sd_from_vector2(vec);
+ LLVector2 vec1 = ll_vector2_from_sd(sd);
+ ensure_equals("vector2 -> sd -> vector2", vec, vec1);
+
+ LLSD sd2 = ll_sd_from_vector2(vec1);
+ ensure_equals("sd -> vector2 -> sd: 2", sd, sd2);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<7>()
+ {
+ LLSD sd;
+ LLQuaternion quat((F32) 1., (F32) -0.98, (F32) 2.3, (F32) 0xffff);
+ sd = ll_sd_from_quaternion(quat);
+ LLQuaternion quat1 = ll_quaternion_from_sd(sd);
+ ensure_equals("LLQuaternion -> sd -> LLQuaternion", quat, quat1);
+
+ LLSD sd2 = ll_sd_from_quaternion(quat1);
+ ensure_equals("sd -> LLQuaternion -> sd ", sd, sd2);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<8>()
+ {
+ LLSD sd;
+ LLColor4 c(1.0f, 2.2f, 4.0f, 7.f);
+ sd = ll_sd_from_color4(c);
+ LLColor4 c1 =ll_color4_from_sd(sd);
+ ensure_equals("LLColor4 -> sd -> LLColor4", c, c1);
+
+ LLSD sd1 = ll_sd_from_color4(c1);
+ ensure_equals("sd -> LLColor4 -> sd", sd, sd1);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<9>()
+ {
+ set_test_name("llsd_matches");
+
+ // for this test, construct a map of all possible LLSD types
+ LLSD map;
+ map.insert("empty", LLSD());
+ map.insert("Boolean", LLSD::Boolean());
+ map.insert("Integer", LLSD::Integer(0));
+ map.insert("Real", LLSD::Real(0.0));
+ map.insert("String", LLSD::String("bah"));
+ map.insert("NumString", LLSD::String("1"));
+ map.insert("UUID", LLSD::UUID());
+ map.insert("Date", LLSD::Date());
+ map.insert("URI", LLSD::URI());
+ map.insert("Binary", LLSD::Binary());
+ map.insert("Map", LLSD().with("foo", LLSD()));
+ // Only an empty array can be constructed on the fly
+ LLSD array;
+ array.append(LLSD());
+ map.insert("Array", array);
+
+ // These iterators are declared outside our various for loops to avoid
+ // fatal MSVC warning: "I used to be broken, but I'm all better now!"
+ LLSD::map_const_iterator mi, mend(map.endMap());
+
+ /*-------------------------- llsd_matches --------------------------*/
+
+ // empty prototype matches anything
+ for (mi = map.beginMap(); mi != mend; ++mi)
+ {
+ ensure_equals(std::string("empty matches ") + mi->first, llsd_matches(LLSD(), mi->second), "");
+ }
+
+ LLSD proto_array, data_array;
+ for (int i = 0; i < 3; ++i)
+ {
+ proto_array.append(LLSD());
+ data_array.append(LLSD());
+ }
+
+ // prototype array matches only array
+ for (mi = map.beginMap(); mi != mend; ++mi)
+ {
+ ensure(std::string("array doesn't match ") + mi->first,
+ ! llsd_matches(proto_array, mi->second).empty());
+ }
+
+ // data array must be at least as long as prototype array
+ proto_array.append(LLSD());
+ ensure_equals("data array too short", llsd_matches(proto_array, data_array),
+ "Array size 4 required instead of Array size 3");
+ data_array.append(LLSD());
+ ensure_equals("data array just right", llsd_matches(proto_array, data_array), "");
+ data_array.append(LLSD());
+ ensure_equals("data array longer", llsd_matches(proto_array, data_array), "");
+
+ // array element matching
+ data_array[0] = LLSD::String();
+ ensure_equals("undefined prototype array entry", llsd_matches(proto_array, data_array), "");
+ proto_array[0] = LLSD::Binary();
+ ensure_equals("scalar prototype array entry", llsd_matches(proto_array, data_array),
+ "[0]: Binary required instead of String");
+ data_array[0] = LLSD::Binary();
+ ensure_equals("matching prototype array entry", llsd_matches(proto_array, data_array), "");
+
+ // build a coupla maps
+ LLSD proto_map, data_map;
+ data_map["got"] = LLSD();
+ data_map["found"] = LLSD();
+ for (LLSD::map_const_iterator dmi(data_map.beginMap()), dmend(data_map.endMap());
+ dmi != dmend; ++dmi)
+ {
+ proto_map[dmi->first] = dmi->second;
+ }
+ proto_map["foo"] = LLSD();
+ proto_map["bar"] = LLSD();
+
+ // prototype map matches only map
+ for (mi = map.beginMap(); mi != mend; ++mi)
+ {
+ ensure(std::string("map doesn't match ") + mi->first,
+ ! llsd_matches(proto_map, mi->second).empty());
+ }
+
+ // data map must contain all keys in prototype map
+ std::string error(llsd_matches(proto_map, data_map));
+ ensure_contains("missing keys", error, "missing keys");
+ ensure_contains("missing foo", error, "foo");
+ ensure_contains("missing bar", error, "bar");
+ ensure_does_not_contain("found found", error, "found");
+ ensure_does_not_contain("got got", error, "got");
+ data_map["bar"] = LLSD();
+ error = llsd_matches(proto_map, data_map);
+ ensure_contains("missing foo", error, "foo");
+ ensure_does_not_contain("got bar", error, "bar");
+ data_map["foo"] = LLSD();
+ ensure_equals("data map just right", llsd_matches(proto_map, data_map), "");
+ data_map["extra"] = LLSD();
+ ensure_equals("data map with extra", llsd_matches(proto_map, data_map), "");
+
+ // map element matching
+ data_map["foo"] = LLSD::String();
+ ensure_equals("undefined prototype map entry", llsd_matches(proto_map, data_map), "");
+ proto_map["foo"] = LLSD::Binary();
+ ensure_equals("scalar prototype map entry", llsd_matches(proto_map, data_map),
+ "['foo']: Binary required instead of String");
+ data_map["foo"] = LLSD::Binary();
+ ensure_equals("matching prototype map entry", llsd_matches(proto_map, data_map), "");
+
+ // String
+ {
+ static const char* matches[] = { "String", "NumString", "Boolean", "Integer",
+ "Real", "UUID", "Date", "URI" };
+ test_matches("String", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // Boolean, Integer, Real
+ static const char* numerics[] = { "Boolean", "Integer", "Real" };
+ for (const char **ni = boost::begin(numerics), **nend = boost::end(numerics);
+ ni != nend; ++ni)
+ {
+ static const char* matches[] = { "Boolean", "Integer", "Real", "String", "NumString" };
+ test_matches(*ni, map, boost::begin(matches), boost::end(matches));
+ }
+
+ // UUID
+ {
+ static const char* matches[] = { "UUID", "String", "NumString" };
+ test_matches("UUID", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // Date
+ {
+ static const char* matches[] = { "Date", "String", "NumString" };
+ test_matches("Date", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // URI
+ {
+ static const char* matches[] = { "URI", "String", "NumString" };
+ test_matches("URI", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // Binary
+ {
+ static const char* matches[] = { "Binary" };
+ test_matches("Binary", map, boost::begin(matches), boost::end(matches));
+ }
+
+ /*-------------------------- llsd_equals ---------------------------*/
+
+ // Cross-product of each LLSD type with every other
+ for (LLSD::map_const_iterator lmi(map.beginMap()), lmend(map.endMap());
+ lmi != lmend; ++lmi)
+ {
+ for (LLSD::map_const_iterator rmi(map.beginMap()), rmend(map.endMap());
+ rmi != rmend; ++rmi)
+ {
+ // Name this test based on the map keys naming the types of
+ // interest, e.g "String::Integer".
+ // We expect the values (xmi->second) to be equal if and only
+ // if the type names (xmi->first) are equal.
+ ensure(STRINGIZE(lmi->first << "::" << rmi->first),
+ bool(lmi->first == rmi->first) ==
+ bool(llsd_equals(lmi->second, rmi->second)));
+ }
+ }
+
+ // Array cases
+ LLSD rarray;
+ rarray.append(1.0);
+ rarray.append(2);
+ rarray.append("3");
+ LLSD larray(rarray);
+ ensure("llsd_equals(equal arrays)", llsd_equals(larray, rarray));
+ rarray[2] = "4";
+ ensure("llsd_equals(different [2])", ! llsd_equals(larray, rarray));
+ rarray = larray;
+ rarray.append(LLSD::Date());
+ ensure("llsd_equals(longer right array)", ! llsd_equals(larray, rarray));
+ rarray = larray;
+ rarray.erase(2);
+ ensure("llsd_equals(shorter right array)", ! llsd_equals(larray, rarray));
+
+ // Map cases
+ LLSD rmap;
+ rmap["San Francisco"] = 65;
+ rmap["Phoenix"] = 92;
+ rmap["Boston"] = 77;
+ LLSD lmap(rmap);
+ ensure("llsd_equals(equal maps)", llsd_equals(lmap, rmap));
+ rmap["Boston"] = 80;
+ ensure("llsd_equals(different [\"Boston\"])", ! llsd_equals(lmap, rmap));
+ rmap = lmap;
+ rmap["Atlanta"] = 95;
+ ensure("llsd_equals(superset right map)", ! llsd_equals(lmap, rmap));
+ rmap = lmap;
+ lmap["Seattle"] = 72;
+ ensure("llsd_equals(superset left map)", ! llsd_equals(lmap, rmap));
+ }
+
+ template<> template<>
+ void llsdutil_object::test<10>()
+ {
+ set_test_name("llsd_hashing");
+
+ {
+ LLSD data_s1 = LLSD::String("The quick brown aardvark jumped over the lazy lemming.");
+ LLSD data_s2 = LLSD::String("The quick brown aardvark jumped over the lazy lemming.");
+
+ ensure("hash: Identical string hashes match.", boost::hash<LLSD>{}(data_s1) == boost::hash<LLSD>{}(data_s2));
+ }
+ {
+ LLSD data_r1 = LLSD::Real(3.0f);
+ LLSD data_i1 = LLSD::Integer(3);
+ ensure("hash: equivalent values but different types do not match.", boost::hash<LLSD>{}(data_r1) != boost::hash<LLSD>{}(data_i1));
+ }
+ {
+ LLSD data_a1 = llsd::array("A", "B", "C");
+ LLSD data_a2 = llsd::array("A", "B", "C");
+
+ ensure("hash: identical arrays produce identical results", boost::hash<LLSD>{}(data_a1) == boost::hash<LLSD>{}(data_a2));
+
+ data_a2.append(llsd::array(1, 2));
+
+ ensure("hash: changing the array changes the hash.", boost::hash<LLSD>{}(data_a1) != boost::hash<LLSD>{}(data_a2));
+
+ data_a1.append(llsd::array(1, 2));
+ ensure("hash: identical arrays produce identical results with nested arrays", boost::hash<LLSD>{}(data_a1) == boost::hash<LLSD>{}(data_a2));
+ }
+ {
+ LLSD data_m1 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+ LLSD data_m2 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+
+ ensure("hash: identical maps produce identical results", boost::hash<LLSD>{}(data_m1) == boost::hash<LLSD>{}(data_m2));
+
+ LLSD data_m3 = LLSDMap("key1", LLSD::Real(5.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+ ensure("hash: Different values in the map produce different hashes.", boost::hash<LLSD>{}(data_m1) != boost::hash<LLSD>{}(data_m3));
+
+ LLSD data_m4 = LLSDMap("keyA", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+ ensure("hash: Different keys in the map produce different hashes.", boost::hash<LLSD>{}(data_m1) != boost::hash<LLSD>{}(data_m4));
+
+ }
+ }
+
+}
diff --git a/indra/llcommon/tests/llstreamqueue_test.cpp b/indra/llcommon/tests/llstreamqueue_test.cpp
deleted file mode 100644
index 82b451119e..0000000000
--- a/indra/llcommon/tests/llstreamqueue_test.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
- * @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>
-// 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);
- for (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" };
- for (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" };
- for (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/llstreamtools_test.cpp b/indra/llcommon/tests/llstreamtools_test.cpp
new file mode 100644
index 0000000000..970afb3b02
--- /dev/null
+++ b/indra/llcommon/tests/llstreamtools_test.cpp
@@ -0,0 +1,883 @@
+/**
+ * @file llstreamtools_tut.cpp
+ * @author Adroit
+ * @date February 2007
+ * @brief llstreamtools test cases.
+ *
+ * $LicenseInfo:firstyear=2007&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 <tut/tut.hpp>
+
+#include "linden_common.h"
+#include "llstreamtools.h"
+#include "lltut.h"
+
+
+namespace tut
+{
+ struct streamtools_data
+ {
+ };
+ typedef test_group<streamtools_data> streamtools_test;
+ typedef streamtools_test::object streamtools_object;
+ tut::streamtools_test streamtools_testcase("streamtools");
+
+ //test cases for skip_whitespace()
+ template<> template<>
+ void streamtools_object::test<1>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.str(str = "");
+ ensure("skip_whitespace: empty string", (false == skip_whitespace(is)));
+
+ is.clear();
+ is.str(str = " SecondLife is a 3D World");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("skip_whitespace: space", arr, expected_result);
+
+ is.clear();
+ is.str(str = "\t \tSecondLife is a 3D World");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("skip_whitespace: space and tabs", arr, expected_result);
+
+ is.clear();
+ is.str(str = "\t \tSecondLife is a 3D World ");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World ";
+ ensure_equals("skip_whitespace: space at end", arr, expected_result);
+
+ is.clear();
+ is.str(str = "\t \r\nSecondLife is a 3D World");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "\r\nSecondLife is a 3D World";
+ ensure_equals("skip_whitespace: space at end", arr, expected_result);
+ }
+
+ //testcases for skip_emptyspaces()
+ template<> template<>
+ void streamtools_object::test<2>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " \tSecondLife is a 3D World.\n");
+ skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.\n";
+ ensure_equals("skip_emptyspace: space and tabs", arr, expected_result);
+
+ is.clear();
+ is.str(str = " \t\r\n \r SecondLife is a 3D World.\n");
+ skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.\n";
+ ensure_equals("skip_emptyspace: space, tabs, carriage return, newline", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ ensure("skip_emptyspace: empty string", ret == false);
+
+ is.clear();
+ is.str(str = " \r\n \t ");
+ ret = skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ ensure("skip_emptyspace: space newline empty", ret == false);
+ }
+
+ //testcases for skip_comments_and_emptyspace()
+ template<> template<>
+ void streamtools_object::test<3>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " \t\r\n \r SecondLife is a 3D World.\n");
+ skip_comments_and_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.\n";
+ ensure_equals("skip_comments_and_emptyspace: space, tabs, carriage return, newline", arr, expected_result);
+
+ is.clear();
+ is.str(str = "# \r\n SecondLife is a 3D World.");
+ skip_comments_and_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.";
+ ensure_equals("skip_comments_and_emptyspace: skip comment - 1", arr, expected_result);
+
+ is.clear();
+ is.str(str = "# \r\n # SecondLife is a 3D World. ##");
+ ensure("should not be good()", ! skip_comments_and_emptyspace(is));
+ ensure("should be at eof()", is.eof());
+ // don't get(): given bad state, we can't rely on results
+
+ is.clear();
+ is.str(str = " \r\n SecondLife is a 3D World. ##");
+ skip_comments_and_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World. ##";
+ ensure_equals("skip_comments_and_emptyspace: skip comment - 3", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_comments_and_emptyspace(is);
+ ensure("skip_comments_and_emptyspace: empty string", ! ret);
+
+ is.clear();
+ is.str(str = " \r\n \t # SecondLife is a 3D World");
+ ret = skip_comments_and_emptyspace(is);
+ ensure("skip_comments_and_emptyspace: space newline comment empty", ! ret);
+ }
+
+ //testcases for skip_line()
+ template<> template<>
+ void streamtools_object::test<4>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "SecondLife is a 3D World.\n\n It provides an opportunity to the site \nuser to perform real life activities in virtual world.");
+ skip_line(is);
+ is.get(arr, 255, '\0');
+ expected_result = "\n It provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_line: 1 newline", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_line(is);
+ is.get(arr, 255, '\0');
+ expected_result = " It provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_line: 2 newline", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_line(is);
+ is.get(arr, 255, '\0');
+ expected_result = "user to perform real life activities in virtual world.";
+ ensure_equals("skip_line: 3 newline", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_line(is);
+ ensure("skip_line: empty string", ret == false);
+ }
+
+
+ // testcases for skip_to_next_word()
+ template<> template<>
+ void streamtools_object::test<5>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "SecondLife is a 3D_World.\n\n It-provides an opportunity to the site \nuser to perform real life activities in virtual world.");
+ skip_to_next_word(is); // get past SecondLife
+ is.get(arr, 255, '\0');
+ expected_result = "is a 3D_World.\n\n It-provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_to_next_word: 1", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_to_next_word(is); // get past is
+ skip_to_next_word(is); // get past a
+ skip_to_next_word(is); // get past 3D_World.\n\n
+ is.get(arr, 255, '\0');
+ expected_result = "It-provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_to_next_word: get past .\n\n 2", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_to_next_word(is); // get past It-
+ expected_result = "provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ is.get(arr, 255, '\0');
+ ensure_equals("skip_to_next_word: get past -", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_to_next_word(is);
+ ensure("skip_line: empty string", ret == false);
+
+ is.clear();
+ is.str(str = " \r\n\r\n");
+ ret = skip_to_next_word(is);
+ ensure("skip_line: space new lines", ret == false);
+ }
+
+
+ //testcases for skip_to_end_of_next_keyword()
+ template<> template<>
+ void streamtools_object::test<6>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "FIRSTKEY followed by second delimiter\nSECONDKEY\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld.");
+ ret = skip_to_end_of_next_keyword("FIRSTKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = " followed by second delimiter\nSECONDKEY\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 1", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ ret = skip_to_end_of_next_keyword("SECONDKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = "\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 2", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ ret = skip_to_end_of_next_keyword("SECONDKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = "\nFOURTHKEY FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 3", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ ret = skip_to_end_of_next_keyword("FOURTHKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = " FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 4", arr, expected_result);
+
+ is.clear();
+ is.str(str = "{should be skipped as newline/space/tab does not follow but this one should be picked\n { Does it?\n");
+ ret = skip_to_end_of_next_keyword("{", is);
+ is.get(arr, 255, '\0');
+ expected_result = " Does it?\n";
+ ensure_equals("skip_to_end_of_next_keyword: multiple delim matches on same line", arr, expected_result);
+
+ is.clear();
+ is.str(str = "Delim { could not be found at start");
+ ret = skip_to_end_of_next_keyword("{", is);
+ ensure("skip_to_end_of_next_keyword: delim should not be present", ret == false);
+
+ is.clear();
+ is.str(str = "Empty Delim");
+ ret = skip_to_end_of_next_keyword("", is);
+ ensure("skip_to_end_of_next_keyword: empty delim should not be valid", ret == false);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_to_end_of_next_keyword("}", is);
+ ensure("skip_to_end_of_next_keyword: empty string", ret == false);
+ }
+
+ //testcases for get_word(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<7>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth-ShouldThisBePartOfFourth Fifth\n");
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ expected_result = "First";
+ ensure_equals("get_word: 1", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ expected_result = "Second";
+ ensure_equals("get_word: 2", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ expected_result = "Third";
+ ensure_equals("get_word: 3", actual_result, expected_result);
+
+ // the current implementation of get_word seems inconsistent with
+ // skip_to_next_word. skip_to_next_word treats any character other
+ // than alhpa-numeric and '_' as a delimter, while get_word()
+ // treats only isspace() (i.e. space, form-feed('\f'), newline ('\n'),
+ // carriage return ('\r'), horizontal tab ('\t'), and vertical tab ('\v')
+ // as delimiters
+ actual_result = "";
+ ret = get_word(actual_result, is); // will copy Fourth-ShouldThisBePartOfFourth
+ expected_result = "Fourth-ShouldThisBePartOfFourth"; // as per current impl.
+ ensure_equals("get_word: 4", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is); // will copy Fifth
+ expected_result = "Fifth"; // as per current impl.
+ ensure_equals("get_word: 5", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = " \t \r \n ");
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ ensure("get_word: empty all spaces, newline tabs", ret == false);
+
+ is.clear();
+ is.str(str = "");
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ ensure("get_word: empty string", ret == false);
+ }
+
+ // testcase for get_word and skip_to_next_word compatibility
+ template<> template<>
+ void streamtools_object::test<8>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth-ShouldThisBePartOfFourth Fifth\n");
+ actual_result = "";
+ get_word(actual_result, is); // First
+ actual_result = "";
+ get_word(actual_result, is); // Second
+ actual_result = "";
+ get_word(actual_result, is); // Third
+
+ // the current implementation of get_word seems inconsistent with
+ // skip_to_next_word. skip_to_next_word treats any character other
+ // than alhpa-numeric and '_' as a delimter, while get_word()
+ // treats only isspace() (i.e. space, form-feed('\f'), newline ('\n'),
+ // carriage return ('\r'), horizontal tab ('\t'), and vertical tab ('\v')
+ // as delimiters
+ actual_result = "";
+ get_word(actual_result, is); // will copy Fourth-ShouldThisBePartOfFourth
+
+ actual_result = "";
+ get_word(actual_result, is); // will copy Fifth
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth_ShouldThisBePartOfFourth Fifth\n");
+ skip_to_next_word(is); // should now point to First
+ skip_to_next_word(is); // should now point to Second
+ skip_to_next_word(is); // should now point to Third
+ skip_to_next_word(is); // should now point to Fourth
+ skip_to_next_word(is); // should now point to ShouldThisBePartOfFourth
+ expected_result = "";
+ // will copy ShouldThisBePartOfFourth, the fifth word,
+ // while using get_word above five times result in getting "Fifth"
+ get_word(expected_result, is);
+ ensure_equals("get_word: skip_to_next_word compatibility", actual_result, expected_result);
+ }
+
+ //testcases for get_word(std::string& output_string, std::istream& input_stream, int n)
+ template<> template<>
+ void streamtools_object::test<9>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth-ShouldThisBePartOfFourth Fifth\n");
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ expected_result = "First";
+ ensure_equals("get_word: 1", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 4);
+ expected_result = "Seco"; // should be cut short
+ ensure_equals("get_word: 2", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ expected_result = "nd"; // get remainder of Second
+ ensure_equals("get_word: 3", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 0); // 0 size string
+ expected_result = ""; // get remainder of Second
+ ensure_equals("get_word: 0 sized output", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ expected_result = "Third";
+ ensure_equals("get_word: 4", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = " \t \r \n ");
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ ensure("get_word: empty all spaces, newline tabs", ret == false);
+
+ is.clear();
+ is.str(str = "");
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ ensure("get_word: empty string", ret == false);
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<10>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = "First Second \t \r\n Third Fourth-ShouldThisBePartOfFourth IsThisFifth\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "First Second \t \r\n";
+ ensure_equals("get_line: 1", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = " Third Fourth-ShouldThisBePartOfFourth IsThisFifth\n";
+ ensure_equals("get_line: 2", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = "\nFirst Line.\n\nSecond Line.\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "\n";
+ ensure_equals("get_line: First char as newline", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "First Line.\n";
+ ensure_equals("get_line: 3", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "\n";
+ ensure_equals("get_line: 4", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "Second Line.\n";
+ ensure_equals("get_line: 5", actual_result, expected_result);
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<11>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "One Line only with no newline");
+ actual_result = "";
+ ret = get_line(actual_result, is);
+ expected_result = "One Line only with no newline";
+ ensure_equals("get_line: No newline", actual_result, expected_result);
+ ensure_equals("return value is good state of stream", ret, is.good());
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<12>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ // need to be check if this test case is wrong or the implementation is wrong.
+ is.clear();
+ is.str(str = "Should not skip lone \r.\r\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "Should not skip lone \r.\r\n";
+ ensure_equals("get_line: carriage return skipped even though not followed by newline", actual_result, expected_result);
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<13>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = "\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "\n";
+ ensure_equals("get_line: Just newline", actual_result, expected_result);
+ }
+
+
+ //testcases for get_line(std::string& output_string, std::istream& input_stream, int n)
+ template<> template<>
+ void streamtools_object::test<14>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = "First Line.\nSecond Line.\n");
+ actual_result = "";
+ get_line(actual_result, is, 255);
+ expected_result = "First Line.\n";
+ ensure_equals("get_line: Basic Operation", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is, sizeof("Second")-1);
+ expected_result = "Second\n";
+ ensure_equals("get_line: Insufficient length 1", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is, 255);
+ expected_result = " Line.\n";
+ ensure_equals("get_line: Remainder after earlier insufficient length", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = "One Line only with no newline with limited length");
+ actual_result = "";
+ get_line(actual_result, is, sizeof("One Line only with no newline with limited length")-1);
+ expected_result = "One Line only with no newline with limited length\n";
+ ensure_equals("get_line: No newline with limited length", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = "One Line only with no newline");
+ actual_result = "";
+ get_line(actual_result, is, 255);
+ expected_result = "One Line only with no newline";
+ ensure_equals("get_line: No newline", actual_result, expected_result);
+ }
+
+ //testcases for get_line(std::string& output_string, std::istream& input_stream, int n)
+ template<> template<>
+ void streamtools_object::test<15>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "One Line only with no newline");
+ actual_result = "";
+ ret = get_line(actual_result, is, 255);
+ expected_result = "One Line only with no newline";
+ ensure_equals("get_line: No newline", actual_result, expected_result);
+ ensure_equals("return value is good state of stream", ret, is.good());
+ }
+
+ //testcases for remove_last_char()
+ template<> template<>
+ void streamtools_object::test<16>()
+ {
+ std::string str;
+ std::string expected_result;
+ bool ret;
+
+ str = "SecondLife is a 3D World";
+
+ ret = remove_last_char('d',str);
+ expected_result = "SecondLife is a 3D Worl";
+ ensure_equals("remove_last_char: should remove last char", str, expected_result);
+
+ str = "SecondLife is a 3D World";
+ ret = remove_last_char('W',str);
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("remove_last_char: should not remove as it is not last char", str, expected_result);
+ ensure("remove_last_char: should return false", ret == false);
+
+ str = "SecondLife is a 3D World\n";
+ ret = remove_last_char('\n',str);
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("remove_last_char: should remove last newline", str, expected_result);
+ ensure("remove_last_char: should remove newline and return true", ret == true);
+ }
+
+
+ //testcases for unescaped_string()
+ template<> template<>
+ void streamtools_object::test<17>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a 3D world \\n";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \n";
+ ensure_equals("unescape_string: newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \\\\t \\n";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \\t \n";
+ ensure_equals("unescape_string: backslash and newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \\ ";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \\ ";
+ ensure_equals("unescape_string: insufficient to unescape", str, expected_result);
+
+ str = "SecondLife is a 3D world \\n \\n \\n \\\\\\n";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \n \n \n \\\n";
+ ensure_equals("unescape_string: multipel newline and backslash", str, expected_result);
+
+ str = "SecondLife is a 3D world \\t";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \\t";
+ ensure_equals("unescape_string: leaves tab as is", str, expected_result);
+
+ str = "\\n";
+ unescape_string(str);
+ expected_result = "\n";
+ ensure_equals("unescape_string: only a newline", str, expected_result);
+ }
+
+ //testcases for escape_string()
+ template<> template<>
+ void streamtools_object::test<18>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a 3D world \n";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\n";
+ ensure_equals("escape_string: newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \\t \n";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\\\t \\n";
+ ensure_equals("escape_string: backslash and newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \n \n \n \\\n";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\\\n";
+ ensure_equals("escape_string: multipel newline and backslash", str, expected_result);
+
+ str = "SecondLife is a 3D world \t";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \t";
+ ensure_equals("unescape_string: leaves tab as is", str, expected_result);
+
+ str = "\n";
+ escape_string(str);
+ expected_result = "\\n";
+ ensure_equals("unescape_string: only a newline", str, expected_result);
+
+ // serialization/deserialization escape->unescape
+ str = "SecondLife is a 3D world \n \n \n \\\n";
+ escape_string(str);
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \n \n \n \\\n";
+ ensure_equals("escape_string: should preserve with escape/unescape", str, expected_result);
+
+ // serialization/deserialization unescape->escape
+ str = "SecondLife is a 3D world \\n \\n \\n \\\\";
+ unescape_string(str);
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\";
+ ensure_equals("escape_string: should preserve with unescape/escape", str, expected_result);
+ }
+
+ // testcases for replace_newlines_with_whitespace()
+ template<> template<>
+ void streamtools_object::test<19>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a 3D \n\nworld\n";
+ replace_newlines_with_whitespace(str);
+ expected_result = "SecondLife is a 3D world ";
+ ensure_equals("replace_newlines_with_whitespace: replace all newline", str, expected_result);
+
+ str = "\nSecondLife is a 3D world\n";
+ replace_newlines_with_whitespace(str);
+ expected_result = " SecondLife is a 3D world ";
+ ensure_equals("replace_newlines_with_whitespace: begin and newline", str, expected_result);
+
+ str = "SecondLife is a 3D world\r\t";
+ replace_newlines_with_whitespace(str);
+ expected_result = "SecondLife is a 3D world\r\t";
+ ensure_equals("replace_newlines_with_whitespace: should only replace newline", str, expected_result);
+
+ str = "";
+ replace_newlines_with_whitespace(str);
+ expected_result = "";
+ ensure_equals("replace_newlines_with_whitespace: empty string", str, expected_result);
+ }
+
+ //testcases for remove_double_quotes()
+ template<> template<>
+ void streamtools_object::test<20>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a \"\"3D world";
+ remove_double_quotes(str);
+ expected_result = "SecondLife is a 3D world";
+ ensure_equals("remove_double_quotes: replace empty doube quotes", str, expected_result);
+
+ str = "SecondLife is a \"3D world";
+ remove_double_quotes(str);
+ expected_result = "SecondLife is a 3D world";
+ ensure_equals("remove_double_quotes: keep as is it matching quote not found", str, expected_result);
+ }
+
+ // testcases for get_brace_count()
+ template<> template<>
+ void streamtools_object::test<21>()
+ {
+ }
+
+ //testcases for get_keyword_and_value()
+ template<> template<>
+ void streamtools_object::test<22>()
+ {
+ std::string s = "SecondLife is a 3D World";
+ std::string keyword;
+ std::string value;
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: Unable to get Keyword and Value", ((keyword == "SecondLife") && (value == "is a 3D World")));
+
+ s = "SecondLife";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: value should be empty", ((keyword == "SecondLife") && (value == "")));
+
+ s = "SecondLife \t is cool! \n";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: remove space before value but not after", ((keyword == "SecondLife") && (value == "is cool! ")));
+ }
+
+ //testcases for get_keyword_and_value()
+ template<> template<>
+ void streamtools_object::test<23>()
+ {
+ std::string s;
+ std::string keyword = "SOME PRIOR KEYWORD";
+ std::string value = "SOME PRIOR VALUE";
+
+ s = "SecondLife\n";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: terminated with newline. value should be empty", ((keyword == "SecondLife") && (value == "")));
+ }
+
+ //testcases for get_keyword_and_value()
+ template<> template<>
+ void streamtools_object::test<24>()
+ {
+ std::string s;
+ std::string keyword = "SOME PRIOR KEYWORD";
+ std::string value = "SOME PRIOR VALUE";
+
+ s = "";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: empty string. keyword value should empty", ((keyword == "") && (value == "")));
+ }
+
+ //testcase for fullread()
+ template<> template<>
+ void streamtools_object::test<25>()
+ {
+ std::string str = "First Line.\nSecond Line\n";
+ std::istringstream is(str);
+ char buf[255] = {0};
+
+ fullread(is, buf, 255);
+ ensure_memory_matches("fullread: read with newlines", (void*) buf, static_cast<U32>(str.size())-1, (void*) str.c_str(), static_cast<U32>(str.size())-1);
+
+ is.clear();
+ is.str(str = "First Line.\nSecond Line\n");
+ memset(buf, 0, 255);
+
+ char expected_string[] = "First Line.\nSecond";
+ int len = sizeof(expected_string)-1;
+ fullread(is, buf, len);
+ ensure_memory_matches("fullread: read with newlines", (void*) buf, len, (void*) &expected_string, len);
+ }
+
+
+// testcases for operator >>
+
+ template<> template<>
+ void streamtools_object::test<26>()
+ {
+ char arr[255];
+ std::string str;
+ std::string toCheck = "SecondLife" ;
+ std::string expected_result;
+ std::istringstream stream("SecondLife is a 3D World");
+ stream >> toCheck.c_str();
+ stream.get(arr, 255, '\0');
+ expected_result = " is a 3D World";
+ ensure_equals("istream << operator", arr, expected_result);
+
+ stream.clear();
+ stream.str(str = "SecondLife is a 3D World");
+ toCheck = "is";
+ stream >> toCheck.c_str();
+ ensure("istream << operator should have failed", stream.good() == false);
+ }
+}
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index 7393e0087c..3c9d8071ac 100644
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -374,7 +374,7 @@ namespace tut
{
F32 value;
std::string str_val("2147483647"); //0x7FFFFFFF
- ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647);
+ ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647.f);
str_val = "0";
ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0);
@@ -396,7 +396,7 @@ namespace tut
{
F64 value;
std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF
- ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL);
+ ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807.0);
str_val = "0";
ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);