Apertura del repositorio

This commit is contained in:
2025-05-24 18:09:39 -03:00
parent 76e6359dad
commit d883ddd0d0
35253 changed files with 2891973 additions and 2 deletions
+37
View File
@@ -0,0 +1,37 @@
/**
* @file StringVec.h
* @author Nat Goodspeed
* @date 2012-02-24
* @brief Extend TUT ensure_equals() to handle std::vector<std::string>
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
#if ! defined(LL_STRINGVEC_H)
#define LL_STRINGVEC_H
#include <vector>
#include <string>
#include <iostream>
typedef std::vector<std::string> StringVec;
std::ostream& operator<<(std::ostream& out, const StringVec& strings)
{
out << '(';
StringVec::const_iterator begin(strings.begin()), end(strings.end());
if (begin != end)
{
out << '"' << *begin << '"';
while (++begin != end)
{
out << ", \"" << *begin << '"';
}
}
out << ')';
return out;
}
#endif /* ! defined(LL_STRINGVEC_H) */
+240
View File
@@ -0,0 +1,240 @@
/**
* @file apply_test.cpp
* @author Nat Goodspeed
* @date 2022-12-19
* @brief Test for apply.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "apply.h"
// STL headers
// std headers
#include <iomanip>
// external library headers
// other Linden headers
#include "llsd.h"
#include "llsdutil.h"
#include <array>
#include <string>
#include <vector>
// for ensure_equals
std::ostream& operator<<(std::ostream& out, const std::vector<std::string>& stringvec)
{
const char* delim = "[";
for (const auto& str : stringvec)
{
out << delim << std::quoted(str);
delim = ", ";
}
return out << ']';
}
// the above must be declared BEFORE ensure_equals(std::vector<std::string>)
#include "../test/lltut.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
namespace statics
{
/*------------------------------ data ------------------------------*/
// Although we're using types from the LLSD namespace, we're not
// constructing LLSD values, but rather instances of the C++ types
// supported by LLSD.
static LLSD::Boolean b{true};
static LLSD::Integer i{17};
static LLSD::Real f{3.14};
static LLSD::String s{ "hello" };
static LLSD::UUID uu{ "baadf00d-dead-beef-baad-feedb0ef" };
static LLSD::Date dt{ "2022-12-19" };
static LLSD::URI uri{ "http://secondlife.com" };
static LLSD::Binary bin{ 0x01, 0x02, 0x03, 0x04, 0x05 };
static std::vector<LLSD::String> quick
{
"The", "quick", "brown", "fox", "etc."
};
static std::array<int, 5> fibs
{
0, 1, 1, 2, 3
};
// ensure that apply() actually reaches the target method --
// lack of ensure_equals() failure could be due to no-op apply()
bool called{ false };
// capture calls from collect()
std::vector<std::string> collected;
/*------------------------- test functions -------------------------*/
void various(LLSD::Boolean b, LLSD::Integer i, LLSD::Real f, const LLSD::String& s,
const LLSD::UUID& uu, const LLSD::Date& dt,
const LLSD::URI& uri, const LLSD::Binary& bin)
{
called = true;
ensure_equals( "b mismatch", b, statics::b);
ensure_equals( "i mismatch", i, statics::i);
ensure_equals( "f mismatch", f, statics::f);
ensure_equals( "s mismatch", s, statics::s);
ensure_equals( "uu mismatch", uu, statics::uu);
ensure_equals( "dt mismatch", dt, statics::dt);
ensure_equals("uri mismatch", uri, statics::uri);
ensure_equals("bin mismatch", bin, statics::bin);
}
void strings(std::string s0, std::string s1, std::string s2, std::string s3, std::string s4)
{
called = true;
ensure_equals("s0 mismatch", s0, statics::quick[0]);
ensure_equals("s1 mismatch", s1, statics::quick[1]);
ensure_equals("s2 mismatch", s2, statics::quick[2]);
ensure_equals("s3 mismatch", s3, statics::quick[3]);
ensure_equals("s4 mismatch", s4, statics::quick[4]);
}
void ints(int i0, int i1, int i2, int i3, int i4)
{
called = true;
ensure_equals("i0 mismatch", i0, statics::fibs[0]);
ensure_equals("i1 mismatch", i1, statics::fibs[1]);
ensure_equals("i2 mismatch", i2, statics::fibs[2]);
ensure_equals("i3 mismatch", i3, statics::fibs[3]);
ensure_equals("i4 mismatch", i4, statics::fibs[4]);
}
void sdfunc(const LLSD& sd)
{
called = true;
ensure_equals("sd mismatch", sd.asInteger(), statics::i);
}
void intfunc(int i)
{
called = true;
ensure_equals("i mismatch", i, statics::i);
}
void voidfunc()
{
called = true;
}
// recursion tail
void collect()
{
called = true;
}
// collect(arbitrary)
template <typename... ARGS>
void collect(const std::string& first, ARGS&&... rest)
{
statics::collected.push_back(first);
collect(std::forward<ARGS>(rest)...);
}
} // namespace statics
struct apply_data
{
apply_data()
{
// reset called before each test
statics::called = false;
statics::collected.clear();
}
};
typedef test_group<apply_data> apply_group;
typedef apply_group::object object;
apply_group applygrp("apply");
template<> template<>
void object::test<1>()
{
set_test_name("apply(tuple)");
LL::apply(statics::various,
std::make_tuple(statics::b, statics::i, statics::f, statics::s,
statics::uu, statics::dt, statics::uri, statics::bin));
ensure("apply(tuple) failed", statics::called);
}
template<> template<>
void object::test<2>()
{
set_test_name("apply(array)");
LL::apply(statics::ints, statics::fibs);
ensure("apply(array) failed", statics::called);
}
template<> template<>
void object::test<3>()
{
set_test_name("apply(vector)");
LL::apply(statics::strings, statics::quick);
ensure("apply(vector) failed", statics::called);
}
// The various apply(LLSD) tests exercise only the success cases because
// the failure cases trigger assert() fail, which is hard to catch.
template<> template<>
void object::test<4>()
{
set_test_name("apply(LLSD())");
LL::apply(statics::voidfunc, LLSD());
ensure("apply(LLSD()) failed", statics::called);
}
template<> template<>
void object::test<5>()
{
set_test_name("apply(fn(int), LLSD scalar)");
LL::apply(statics::intfunc, LLSD(statics::i));
ensure("apply(fn(int), LLSD scalar) failed", statics::called);
}
template<> template<>
void object::test<6>()
{
set_test_name("apply(fn(LLSD), LLSD scalar)");
// This test verifies that LLSDParam<LLSD> doesn't send the compiler
// into infinite recursion when the target is itself LLSD.
LL::apply(statics::sdfunc, LLSD(statics::i));
ensure("apply(fn(LLSD), LLSD scalar) failed", statics::called);
}
template<> template<>
void object::test<7>()
{
set_test_name("apply(LLSD array)");
LL::apply(statics::various,
llsd::array(statics::b, statics::i, statics::f, statics::s,
statics::uu, statics::dt, statics::uri, statics::bin));
ensure("apply(LLSD array) failed", statics::called);
}
template<> template<>
void object::test<8>()
{
set_test_name("VAPPLY()");
// Make a std::array<std::string> from statics::quick. We can't call a
// variadic function with a data structure of dynamic length.
std::array<std::string, 5> strray;
for (size_t i = 0; i < strray.size(); ++i)
strray[i] = statics::quick[i];
// This doesn't work: the compiler doesn't know which overload of
// collect() to pass to LL::apply().
// LL::apply(statics::collect, strray);
// That's what VAPPLY() is for.
VAPPLY(statics::collect, strray);
ensure("VAPPLY() failed", statics::called);
ensure_equals("collected mismatch", statics::collected, statics::quick);
}
} // namespace tut
+118
View File
@@ -0,0 +1,118 @@
/**
* @file bitpack_test.cpp
* @author Adroit
* @date 2007-02
* @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 "linden_common.h"
#include "../llbitpack.h"
#include "../test/lltut.h"
namespace tut
{
struct bit_pack
{
};
typedef test_group<bit_pack> bit_pack_t;
typedef bit_pack_t::object bit_pack_object_t;
tut::bit_pack_t tut_bit_pack("LLBitPack");
// pack -> unpack
template<> template<>
void bit_pack_object_t::test<1>()
{
U8 packbuffer[255];
U8 unpackbuffer[255];
int pack_bufsize = 0;
int unpack_bufsize = 0;
LLBitPack bitpack(packbuffer, 255);
char str[] = "SecondLife is a 3D virtual world";
int len = sizeof(str);
pack_bufsize = bitpack.bitPack((U8*) str, len*8);
pack_bufsize = bitpack.flushBitPack();
LLBitPack bitunpack(packbuffer, pack_bufsize*8);
unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, len*8);
ensure("bitPack: unpack size should be same as string size prior to pack", len == unpack_bufsize);
ensure_memory_matches("str->bitPack->bitUnpack should be equal to string", str, len, unpackbuffer, unpack_bufsize);
}
// pack large, unpack in individual bytes
template<> template<>
void bit_pack_object_t::test<2>()
{
U8 packbuffer[255];
U8 unpackbuffer[255];
int pack_bufsize = 0;
LLBitPack bitpack(packbuffer, 255);
char str[] = "SecondLife";
int len = sizeof(str);
pack_bufsize = bitpack.bitPack((U8*) str, len*8);
pack_bufsize = bitpack.flushBitPack();
LLBitPack bitunpack(packbuffer, pack_bufsize*8);
bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 0", unpackbuffer[0] == (U8) str[0]);
bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 1", unpackbuffer[0] == (U8) str[1]);
bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 2", unpackbuffer[0] == (U8) str[2]);
bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 3", unpackbuffer[0] == (U8) str[3]);
bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 4", unpackbuffer[0] == (U8) str[4]);
bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]);
bitunpack.bitUnpack(unpackbuffer, 8*4); // Life
ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4);
}
// U32 packing
template<> template<>
void bit_pack_object_t::test<3>()
{
U8 packbuffer[255];
int pack_bufsize = 0;
LLBitPack bitpack(packbuffer, 255);
U32 num = 0x41fab67a;
pack_bufsize = bitpack.bitPack((U8*)&num, 8*sizeof(U32));
pack_bufsize = bitpack.flushBitPack();
LLBitPack bitunpack(packbuffer, pack_bufsize*8);
U32 res = 0;
// since packing and unpacking is done on same machine in the unit test run,
// endianness should not matter
bitunpack.bitUnpack((U8*) &res, sizeof(res)*8);
ensure("U32->bitPack->bitUnpack->U32 should be equal", num == res);
}
}
@@ -0,0 +1,144 @@
/**
* @file classic_callback_test.cpp
* @author Nat Goodspeed
* @date 2021-09-22
* @brief Test ClassicCallback and HeapClassicCallback.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "classic_callback.h"
// STL headers
#include <iostream>
#include <string>
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
/*****************************************************************************
* example callback
*****************************************************************************/
// callback_t is part of the specification of someAPI()
typedef void (*callback_t)(const char*, void*);
void someAPI(callback_t callback, void* userdata)
{
callback("called", userdata);
}
// C++ callable I want as the actual callback
struct MyCallback
{
void operator()(const char* msg, void*)
{
mMsg = msg;
}
void callback_with_extra(const std::string& extra, const char* msg)
{
mMsg = extra + ' ' + msg;
}
std::string mMsg;
};
/*****************************************************************************
* example callback accepting several params, and void* userdata isn't first
*****************************************************************************/
typedef std::string (*complex_callback)(int, const char*, void*, double);
std::string otherAPI(complex_callback callback, void* userdata)
{
return callback(17, "hello world", userdata, 3.0);
}
// struct into which we can capture complex_callback params
static struct Data
{
void set(int i, const char* s, double f)
{
mi = i;
ms = s;
mf = f;
}
void clear() { set(0, "", 0.0); }
int mi;
std::string ms;
double mf;
} sData;
// C++ callable I want to pass
struct OtherCallback
{
std::string operator()(int num, const char* str, void*, double approx)
{
sData.set(num, str, approx);
return "hello back!";
}
};
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct classic_callback_data
{
};
typedef test_group<classic_callback_data> classic_callback_group;
typedef classic_callback_group::object object;
classic_callback_group classic_callbackgrp("classic_callback");
template<> template<>
void object::test<1>()
{
set_test_name("ClassicCallback");
// engage someAPI(MyCallback())
auto ccb{ makeClassicCallback<callback_t>(MyCallback()) };
someAPI(ccb.get_callback(), ccb.get_userdata());
// Unfortunately, with the side effect confined to the bound
// MyCallback instance, that call was invisible. Bind a reference to a
// named instance by specifying a ref type.
MyCallback mcb;
ClassicCallback<callback_t, void*, MyCallback&> ccb2(mcb);
someAPI(ccb2.get_callback(), ccb2.get_userdata());
ensure_equals("failed to call through ClassicCallback", mcb.mMsg, "called");
// try with HeapClassicCallback
mcb.mMsg.clear();
auto hcbp{ makeHeapClassicCallback<callback_t>(mcb) };
someAPI(hcbp->get_callback(), hcbp->get_userdata());
ensure_equals("failed to call through HeapClassicCallback", mcb.mMsg, "called");
// lambda
// The tricky thing here is that a lambda is an unspecified type, so
// you can't declare a ClassicCallback<signature, void*, that type>.
mcb.mMsg.clear();
auto xcb(
makeClassicCallback<callback_t>(
[&mcb](const char* msg, void*)
{ mcb.callback_with_extra("extra", msg); }));
someAPI(xcb.get_callback(), xcb.get_userdata());
ensure_equals("failed to call lambda", mcb.mMsg, "extra called");
// engage otherAPI(OtherCallback())
OtherCallback ocb;
// Instead of specifying a reference type for the bound CALLBACK, as
// with ccb2 above, you can alternatively move the callable object
// into the ClassicCallback (of course AFTER any other reference).
// That's why OtherCallback uses external data for its observable side
// effect.
auto occb{ makeClassicCallback<complex_callback>(std::move(ocb)) };
std::string result{ otherAPI(occb.get_callback(), occb.get_userdata()) };
ensure_equals("failed to return callback result", result, "hello back!");
ensure_equals("failed to set int", sData.mi, 17);
ensure_equals("failed to set string", sData.ms, "hello world");
ensure_equals("failed to set double", sData.mf, 3.0);
}
} // namespace tut
+665
View File
@@ -0,0 +1,665 @@
/**
* @file common.cpp
* @author Phoenix
* @date 2005-10-12
* @brief Common templates for test framework
*
* $LicenseInfo:firstyear=2005&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$
*/
/**
*
* THOROUGH_DESCRIPTION of common.cpp
*
*/
#include <algorithm>
#include <iomanip>
#include <iterator>
#include "linden_common.h"
#include "../llmemorystream.h"
#include "../llsd.h"
#include "../llsdserialize.h"
#include "../u64.h"
#include "../llhash.h"
#include "../test/lltut.h"
namespace tut
{
struct sd_data
{
};
typedef test_group<sd_data> sd_test;
typedef sd_test::object sd_object;
tut::sd_test sd("LLSD");
template<> template<>
void sd_object::test<1>()
{
std::ostringstream resp;
resp << "{'connect':true, 'position':[r128,r128,r128], 'look_at':[r0,r1,r0], 'agent_access':'M', 'region_x':i8192, 'region_y':i8192}";
std::string str = resp.str();
LLMemoryStream mstr((U8*)str.c_str(), static_cast<S32>(str.size()));
LLSD response;
S32 count = LLSDSerialize::fromNotation(response, mstr, str.size());
ensure("stream parsed", response.isDefined());
ensure_equals("stream parse count", count, 13);
ensure_equals("sd type", response.type(), LLSD::TypeMap);
ensure_equals("map element count", response.size(), 6);
ensure_equals("value connect", response["connect"].asBoolean(), true);
ensure_equals("value region_x", response["region_x"].asInteger(),8192);
ensure_equals("value region_y", response["region_y"].asInteger(),8192);
}
template<> template<>
void sd_object::test<2>()
{
const std::string decoded("random");
//const std::string encoded("cmFuZG9t\n");
const std::string streamed("b(6)\"random\"");
typedef std::vector<U8> buf_t;
buf_t buf;
std::copy(
decoded.begin(),
decoded.end(),
std::back_insert_iterator<buf_t>(buf));
LLSD sd;
sd = buf;
std::stringstream str;
S32 count = LLSDSerialize::toNotation(sd, str);
ensure_equals("output count", count, 1);
std::string actual(str.str());
ensure_equals("formatted binary encoding", actual, streamed);
sd.clear();
LLSDSerialize::fromNotation(sd, str, str.str().size());
std::vector<U8> after;
after = sd.asBinary();
ensure_equals("binary decoded size", after.size(), decoded.size());
ensure("binary decoding", (0 == memcmp(
&after[0],
decoded.c_str(),
decoded.size())));
}
template<> template<>
void sd_object::test<3>()
{
for(S32 i = 0; i < 100; ++i)
{
// gen up a starting point
typedef std::vector<U8> buf_t;
buf_t source;
srand(i); /* Flawfinder: ignore */
S32 size = rand() % 1000 + 10;
std::generate_n(
std::back_insert_iterator<buf_t>(source),
size,
rand);
LLSD sd(source);
std::stringstream str;
S32 count = LLSDSerialize::toNotation(sd, str);
sd.clear();
ensure_equals("format count", count, 1);
LLSD sd2;
count = LLSDSerialize::fromNotation(sd2, str, str.str().size());
ensure_equals("parse count", count, 1);
buf_t dest = sd2.asBinary();
str.str("");
str << "binary encoding size " << i;
ensure_equals(str.str().c_str(), dest.size(), source.size());
str.str("");
str << "binary encoding " << i;
ensure(str.str().c_str(), (source == dest));
}
}
template<> template<>
void sd_object::test<4>()
{
std::ostringstream ostr;
ostr << "{'task_id':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n"
<< "{\n\tname\tObject|\n}\n";
std::string expected = ostr.str();
std::stringstream serialized;
serialized << "'" << LLSDNotationFormatter::escapeString(expected)
<< "'";
LLSD sd;
S32 count = LLSDSerialize::fromNotation(
sd,
serialized,
serialized.str().size());
ensure_equals("parse count", count, 1);
ensure_equals("String streaming", sd.asString(), expected);
}
template<> template<>
void sd_object::test<5>()
{
for(S32 i = 0; i < 100; ++i)
{
// gen up a starting point
typedef std::vector<U8> buf_t;
buf_t source;
srand(666 + i); /* Flawfinder: ignore */
S32 size = rand() % 1000 + 10;
std::generate_n(
std::back_insert_iterator<buf_t>(source),
size,
rand);
std::stringstream str;
str << "b(" << size << ")\"";
str.write((const char*)&source[0], size);
str << "\"";
LLSD sd;
S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
ensure_equals("binary parse", count, 1);
buf_t actual = sd.asBinary();
ensure_equals("binary size", actual.size(), (size_t)size);
ensure("binary data", (0 == memcmp(&source[0], &actual[0], size)));
}
}
template<> template<>
void sd_object::test<6>()
{
std::string expected("'{\"task_id\":u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}'\t\n\t\t");
std::stringstream str;
str << "s(" << expected.size() << ")'";
str.write(expected.c_str(), expected.size());
str << "'";
LLSD sd;
S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
ensure_equals("parse count", count, 1);
std::string actual = sd.asString();
ensure_equals("string sizes", actual.size(), expected.size());
ensure_equals("string content", actual, expected);
}
template<> template<>
void sd_object::test<7>()
{
std::string msg("come on in");
std::stringstream stream;
stream << "{'connect':1, 'message':'" << msg << "',"
<< " 'position':[r45.65,r100.1,r25.5],"
<< " 'look_at':[r0,r1,r0],"
<< " 'agent_access':'PG'}";
LLSD sd;
S32 count = LLSDSerialize::fromNotation(
sd,
stream,
stream.str().size());
ensure_equals("parse count", count, 12);
ensure_equals("bool value", sd["connect"].asBoolean(), true);
ensure_equals("message value", sd["message"].asString(), msg);
ensure_equals("pos x", sd["position"][0].asReal(), 45.65);
ensure_equals("pos y", sd["position"][1].asReal(), 100.1);
ensure_equals("pos z", sd["position"][2].asReal(), 25.5);
ensure_equals("look x", sd["look_at"][0].asReal(), 0.0);
ensure_equals("look y", sd["look_at"][1].asReal(), 1.0);
ensure_equals("look z", sd["look_at"][2].asReal(), 0.0);
}
template<> template<>
void sd_object::test<8>()
{
std::stringstream resp;
resp << "{'label':'short string test', 'singlechar':'a', 'empty':'', 'endoftest':'end' }";
LLSD response;
S32 count = LLSDSerialize::fromNotation(
response,
resp,
resp.str().size());
ensure_equals("parse count", count, 5);
ensure_equals("sd type", response.type(), LLSD::TypeMap);
ensure_equals("map element count", response.size(), 4);
ensure_equals("singlechar", response["singlechar"].asString(), "a");
ensure_equals("empty", response["empty"].asString(), "");
}
template<> template<>
void sd_object::test<9>()
{
std::ostringstream resp;
resp << "{'label':'short binary test', 'singlebinary':b(1)\"A\", 'singlerawstring':s(1)\"A\", 'endoftest':'end' }";
std::string str = resp.str();
LLSD sd;
LLMemoryStream mstr((U8*)str.c_str(), static_cast<S32>(str.size()));
S32 count = LLSDSerialize::fromNotation(sd, mstr, str.size());
ensure_equals("parse count", count, 5);
ensure("sd created", sd.isDefined());
ensure_equals("sd type", sd.type(), LLSD::TypeMap);
ensure_equals("map element count", sd.size(), 4);
ensure_equals(
"label",
sd["label"].asString(),
"short binary test");
std::vector<U8> bin = sd["singlebinary"].asBinary();
std::vector<U8> expected;
expected.resize(1);
expected[0] = 'A';
ensure("single binary", (0 == memcmp(&bin[0], &expected[0], 1)));
ensure_equals(
"single string",
sd["singlerawstring"].asString(),
std::string("A"));
ensure_equals("end", sd["endoftest"].asString(), "end");
}
template<> template<>
void sd_object::test<10>()
{
std::string message("parcel '' is naughty.");
std::stringstream str;
str << "{'message':'" << LLSDNotationFormatter::escapeString(message)
<< "'}";
std::string expected_str("{'message':'parcel \\'\\' is naughty.'}");
std::string actual_str = str.str();
ensure_equals("stream contents", actual_str, expected_str);
LLSD sd;
S32 count = LLSDSerialize::fromNotation(sd, str, actual_str.size());
ensure_equals("parse count", count, 2);
ensure("valid parse", sd.isDefined());
std::string actual = sd["message"].asString();
ensure_equals("message contents", actual, message);
}
template<> template<>
void sd_object::test<11>()
{
std::string expected("\"\"\"\"''''''\"");
std::stringstream str;
str << "'" << LLSDNotationFormatter::escapeString(expected) << "'";
LLSD sd;
S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
ensure_equals("parse count", count, 1);
ensure_equals("string value", sd.asString(), expected);
}
template<> template<>
void sd_object::test<12>()
{
std::string expected("mytest\\");
std::stringstream str;
str << "'" << LLSDNotationFormatter::escapeString(expected) << "'";
LLSD sd;
S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size());
ensure_equals("parse count", count, 1);
ensure_equals("string value", sd.asString(), expected);
}
template<> template<>
void sd_object::test<13>()
{
for(S32 i = 0; i < 1000; ++i)
{
// gen up a starting point
std::string expected;
srand(1337 + i); /* Flawfinder: ignore */
S32 size = rand() % 30 + 5;
std::generate_n(
std::back_insert_iterator<std::string>(expected),
size,
rand);
std::stringstream str;
str << "'" << LLSDNotationFormatter::escapeString(expected) << "'";
LLSD sd;
S32 count = LLSDSerialize::fromNotation(sd, str, expected.size());
ensure_equals("parse count", count, 1);
std::string actual = sd.asString();
/*
if(actual != expected)
{
LL_WARNS() << "iteration " << i << LL_ENDL;
std::ostringstream e_str;
std::string::iterator iter = expected.begin();
std::string::iterator end = expected.end();
for(; iter != end; ++iter)
{
e_str << (S32)((U8)(*iter)) << " ";
}
e_str << std::endl;
llsd_serialize_string(e_str, expected);
LL_WARNS() << "expected size: " << expected.size() << LL_ENDL;
LL_WARNS() << "expected: " << e_str.str() << LL_ENDL;
std::ostringstream a_str;
iter = actual.begin();
end = actual.end();
for(; iter != end; ++iter)
{
a_str << (S32)((U8)(*iter)) << " ";
}
a_str << std::endl;
llsd_serialize_string(a_str, actual);
LL_WARNS() << "actual size: " << actual.size() << LL_ENDL;
LL_WARNS() << "actual: " << a_str.str() << LL_ENDL;
}
*/
ensure_equals("string value", actual, expected);
}
}
template<> template<>
void sd_object::test<14>()
{
//#if LL_WINDOWS && _MSC_VER >= 1400
// skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser.");
//#endif
std::string param = "[{'version':i1},{'data':{'binary_bucket':b(0)\"\"},'from_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'from_name':'Phoenix Linden','id':u004e45e5-5576-277a-fba7-859d6a4cb5c8,'message':'hey','offline':i0,'timestamp':i0,'to_id':u3c5f1bb4-5182-7546-6401-1d329b4ff2f8,'type':i0},{'agent_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'god_level':i0,'limited_to_estate':i1}]";
std::istringstream istr;
istr.str(param);
LLSD param_sd;
LLSDSerialize::fromNotation(param_sd, istr, param.size());
ensure_equals("parsed type", param_sd.type(), LLSD::TypeArray);
LLSD version_sd = param_sd[0];
ensure_equals("version type", version_sd.type(), LLSD::TypeMap);
ensure("has version", version_sd.has("version"));
ensure_equals("version number", version_sd["version"].asInteger(), 1);
LLSD src_sd = param_sd[1];
ensure_equals("src type", src_sd.type(), LLSD::TypeMap);
LLSD dst_sd = param_sd[2];
ensure_equals("dst type", dst_sd.type(), LLSD::TypeMap);
}
template<> template<>
void sd_object::test<15>()
{
std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]";
std::istringstream istr;
istr.str(val);
LLSD sd;
LLSDSerialize::fromNotation(sd, istr, val.size());
ensure_equals("parsed type", sd.type(), LLSD::TypeArray);
ensure_equals("parsed size", sd.size(), 1);
LLSD failures = sd[0]["failures"];
ensure("no failures.", failures.isUndefined());
LLSD success = sd[0]["successfuls"];
ensure_equals("success type", success.type(), LLSD::TypeArray);
ensure_equals("success size", success.size(), 1);
ensure_equals("success instance type", success[0].type(), LLSD::TypeUUID);
}
template<> template<>
void sd_object::test<16>()
{
std::string val = "[f,t,0,1,{'foo':t,'bar':f}]";
std::istringstream istr;
istr.str(val);
LLSD sd;
LLSDSerialize::fromNotation(sd, istr, val.size());
ensure_equals("parsed type", sd.type(), LLSD::TypeArray);
ensure_equals("parsed size", sd.size(), 5);
ensure_equals("element 0 false", sd[0].asBoolean(), false);
ensure_equals("element 1 true", sd[1].asBoolean(), true);
ensure_equals("element 2 false", sd[2].asBoolean(), false);
ensure_equals("element 3 true", sd[3].asBoolean(), true);
LLSD map = sd[4];
ensure_equals("element 4 type", map.type(), LLSD::TypeMap);
ensure_equals("map foo type", map["foo"].type(), LLSD::TypeBoolean);
ensure_equals("map foo value", map["foo"].asBoolean(), true);
ensure_equals("map bar type", map["bar"].type(), LLSD::TypeBoolean);
ensure_equals("map bar value", map["bar"].asBoolean(), false);
}
/*
template<> template<>
void sd_object::test<16>()
{
}
*/
}
#if 0
'{\'task_id\':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t00082000\n\t\tcreator_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t00000000-0000-0000-0000-000000000000\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t10284\n\ttotal_crc\t35\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t21\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t0\t0\t0\n\toldpos\t0\t0\t0\n\trotation\t4.371139183945160766597837e-08\t1\t4.371139183945160766597837e-08\t0\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.2816932\t0.2816932\t0.2816932\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t80\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\t89556747-24cb-43ed-920b-47caed15465f\n\t\tcolors\t1 1 1 1\n\t\tscales\t0.56\n\t\tscalet\t0.56\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t1132625972249870\n\tbirthtime\t1132625953120694\n\treztime\t1132625953120694\n\tparceltime\t1132625953120694\n\ttax_rate\t1.01615\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.141593, 0.000000, -3.141593\n\tnamevalue\tAttachmentOffset VEC3 RW DS 0.000000, 0.000000, 0.000000\n\tnamevalue\tAttachPt U32 RW S 5\n\tnamevalue\tAttachItemID STRING RW SV 1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t52019cdd-b464-ba19-e66d-3da751fef9da\n\torig_item_id\t1f9975c0-2951-1b93-dd83-46e2b932fcc8\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n'
#endif
namespace tut
{
struct mem_data
{
};
typedef test_group<mem_data> mem_test;
typedef mem_test::object mem_object;
tut::mem_test mem_stream("LLMemoryStream");
template<> template<>
void mem_object::test<1>()
{
const char HELLO_WORLD[] = "hello world";
LLMemoryStream mem((U8*)&HELLO_WORLD[0], static_cast<S32>(strlen(HELLO_WORLD))); /* Flawfinder: ignore */
std::string hello;
std::string world;
mem >> hello >> world;
ensure_equals("first word", hello, std::string("hello"));
ensure_equals("second word", world, std::string("world"));
}
}
namespace tut
{
struct U64_data
{
};
typedef test_group<U64_data> U64_test;
typedef U64_test::object U64_object;
tut::U64_test U64_testcase("U64_conversion");
// U64_to_str
template<> template<>
void U64_object::test<1>()
{
U64 val;
std::string val_str;
char result[256];
std::string result_str;
val = U64L(18446744073709551610); // slightly less than MAX_U64
val_str = "18446744073709551610";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.1", val_str, result_str);
val = 0;
val_str = "0";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.2", val_str, result_str);
val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF
val_str = "18446744073709551615";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.3", val_str, result_str);
// overflow - will result in warning at compile time
val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0
val_str = "0";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.4", val_str, result_str);
val = U64L(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615
val_str = "18446744073709551615";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.5", val_str, result_str);
val = U64L(10000000000000000000); // testing preserving of 0s
val_str = "10000000000000000000";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.6", val_str, result_str);
val = 1; // testing no leading 0s
val_str = "1";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.7", val_str, result_str);
val = U64L(18446744073709551615); // testing exact sized buffer for result
val_str = "18446744073709551615";
memset(result, 'A', sizeof(result)); // initialize buffer with all 'A'
U64_to_str(val, result, sizeof("18446744073709551615")); //pass in the exact size
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.8", val_str, result_str);
val = U64L(18446744073709551615); // testing smaller sized buffer for result
val_str = "1844";
memset(result, 'A', sizeof(result)); // initialize buffer with all 'A'
U64_to_str(val, result, 5); //pass in a size of 5. should only copy first 4 integers and add a null terminator
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.9", val_str, result_str);
}
// str_to_U64
template<> template<>
void U64_object::test<2>()
{
U64 val;
U64 result;
val = U64L(18446744073709551610); // slightly less than MAX_U64
result = str_to_U64("18446744073709551610");
ensure_equals("str_to_U64 converted 2.1", val, result);
val = U64L(0); // empty string
result = str_to_U64(LLStringUtil::null);
ensure_equals("str_to_U64 converted 2.2", val, result);
val = U64L(0); // 0
result = str_to_U64("0");
ensure_equals("str_to_U64 converted 2.3", val, result);
val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF
result = str_to_U64("18446744073709551615");
ensure_equals("str_to_U64 converted 2.4", val, result);
// overflow - will result in warning at compile time
val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0
result = str_to_U64("18446744073709551616");
ensure_equals("str_to_U64 converted 2.5", val, result);
val = U64L(1234); // process till first non-integral character
result = str_to_U64("1234A5678");
ensure_equals("str_to_U64 converted 2.6", val, result);
val = U64L(5678); // skip all non-integral characters
result = str_to_U64("ABCD5678");
ensure_equals("str_to_U64 converted 2.7", val, result);
// should it skip negative sign and process
// rest of string or return 0
val = U64L(1234); // skip initial negative sign
result = str_to_U64("-1234");
ensure_equals("str_to_U64 converted 2.8", val, result);
val = U64L(5678); // stop at negative sign in the middle
result = str_to_U64("5678-1234");
ensure_equals("str_to_U64 converted 2.9", val, result);
val = U64L(0); // no integers
result = str_to_U64("AaCD");
ensure_equals("str_to_U64 converted 2.10", val, result);
}
// U64_to_F64
template<> template<>
void U64_object::test<3>()
{
F64 val;
F64 result;
result = 18446744073709551610.0;
val = U64_to_F64(U64L(18446744073709551610));
ensure_equals("U64_to_F64 converted 3.1", val, result);
result = 18446744073709551615.0; // 0xFFFFFFFFFFFFFFFF
val = U64_to_F64(U64L(18446744073709551615));
ensure_equals("U64_to_F64 converted 3.2", val, result);
result = 0.0; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0
// overflow - will result in warning at compile time
val = U64_to_F64(U64L(18446744073709551615)+1);
ensure_equals("U64_to_F64 converted 3.3", val, result);
result = 0.0; // 0
val = U64_to_F64(U64L(0));
ensure_equals("U64_to_F64 converted 3.4", val, result);
result = 1.0; // odd
val = U64_to_F64(U64L(1));
ensure_equals("U64_to_F64 converted 3.5", val, result);
result = 2.0; // even
val = U64_to_F64(U64L(2));
ensure_equals("U64_to_F64 converted 3.6", val, result);
result = U64L(0x7FFFFFFFFFFFFFFF) * 1.0L; // 0x7FFFFFFFFFFFFFFF
val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF));
ensure_equals("U64_to_F64 converted 3.7", val, result);
}
// llstrtou64
// seems to be deprecated - could not find it being used
// anywhere in the tarball - skipping unit tests for now
}
namespace tut
{
struct hash_data
{
};
typedef test_group<hash_data> hash_test;
typedef hash_test::object hash_object;
tut::hash_test hash_tester("LLHash");
template<> template<>
void hash_object::test<1>()
{
const char * str1 = "test string one";
const char * same_as_str1 = "test string one";
size_t hash1 = llhash(str1);
size_t same_as_hash1 = llhash(same_as_str1);
ensure("Hashes from identical strings should be equal", hash1 == same_as_hash1);
char str[100];
strcpy( str, "Another test" );
size_t hash2 = llhash(str);
strcpy( str, "Different string, same pointer" );
size_t hash3 = llhash(str);
ensure("Hashes from same pointer but different string should not be equal", hash2 != hash3);
}
}
+136
View File
@@ -0,0 +1,136 @@
/**
* @file lazyeventapi_test.cpp
* @author Nat Goodspeed
* @date 2022-06-18
* @brief Test for lazyeventapi.
*
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
* Copyright (c) 2022, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "lazyeventapi.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "llevents.h"
#include "llsdutil.h"
// observable side effect, solely for testing
static LLSD data;
// LLEventAPI listener subclass
class MyListener: public LLEventAPI
{
public:
// need this trivial forwarding constructor
// (of course do any other initialization your subclass requires)
MyListener(const LL::LazyEventAPIParams& params):
LLEventAPI(params)
{}
// example operation, registered by LazyEventAPI subclass below
void set_data(const LLSD& event)
{
data = event["data"];
}
};
// LazyEventAPI registrar subclass
class MyRegistrar: public LL::LazyEventAPI<MyListener>
{
using super = LL::LazyEventAPI<MyListener>;
using super::listener;
public:
// LazyEventAPI subclass initializes like a classic LLEventAPI subclass
// constructor, with API name and desc plus add() calls for the defined
// operations
MyRegistrar():
super("Test", "This is a test LLEventAPI")
{
add("set", "This is a set operation", &listener::set_data);
}
};
// Normally we'd declare a static instance of MyRegistrar -- but because we
// want to test both with and without, defer declaration to individual test
// methods.
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct lazyeventapi_data
{
lazyeventapi_data()
{
// before every test, reset 'data'
data.clear();
}
~lazyeventapi_data()
{
// after every test, reset LLEventPumps
LLEventPumps::deleteSingleton();
}
};
typedef test_group<lazyeventapi_data> lazyeventapi_group;
typedef lazyeventapi_group::object object;
lazyeventapi_group lazyeventapigrp("lazyeventapi");
template<> template<>
void object::test<1>()
{
set_test_name("LazyEventAPI");
// this is where the magic (should) happen
// 'register' still a keyword until C++17
MyRegistrar regster;
LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "hey"));
ensure_equals("failed to set data", data.asString(), "hey");
}
template<> template<>
void object::test<2>()
{
set_test_name("No LazyEventAPI");
// Because the MyRegistrar declaration in test<1>() is local, because
// it has been destroyed, we fully expect NOT to reach a MyListener
// instance with this post.
LLEventPumps::instance().obtain("Test").post(llsd::map("op", "set", "data", "moot"));
ensure("accidentally set data", ! data.isDefined());
}
template<> template<>
void object::test<3>()
{
set_test_name("LazyEventAPI metadata");
MyRegistrar regster;
// Of course we have 'regster' in hand; we don't need to search for
// it. But this next test verifies that we can find (all) LazyEventAPI
// instances using LazyEventAPIBase::instance_snapshot. Normally we
// wouldn't search; normally we'd just look at each instance in the
// loop body.
const MyRegistrar* found = nullptr;
for (const auto& registrar : LL::LazyEventAPIBase::instance_snapshot())
if ((found = dynamic_cast<const MyRegistrar*>(&registrar)))
break;
ensure("Failed to find MyRegistrar via LLInstanceTracker", found);
ensure_equals("wrong API name", found->getName(), "Test");
ensure_contains("wrong API desc", found->getDesc(), "test LLEventAPI");
ensure_equals("wrong API field", found->getDispatchKey(), "op");
// Normally we'd just iterate over *found. But for test purposes,
// actually capture the range of NameDesc pairs in a vector.
std::vector<LL::LazyEventAPIBase::NameDesc> ops{ found->begin(), found->end() };
ensure_equals("failed to find operations", ops.size(), 1);
ensure_equals("wrong operation name", ops[0].first, "set");
ensure_contains("wrong operation desc", ops[0].second, "set operation");
LLSD metadata{ found->getMetadata(ops[0].first) };
ensure_equals("bad metadata name", metadata["name"].asString(), ops[0].first);
ensure_equals("bad metadata desc", metadata["desc"].asString(), ops[0].second);
}
} // namespace tut
+152
View File
@@ -0,0 +1,152 @@
/**
* @file listener.h
* @author Nat Goodspeed
* @date 2009-03-06
* @brief Useful for tests of the LLEventPump family of classes
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LISTENER_H)
#define LL_LISTENER_H
#include "llsd.h"
#include "llevents.h"
#include "tests/StringVec.h"
#include <iostream>
/*****************************************************************************
* test listener class
*****************************************************************************/
class Listener;
std::ostream& operator<<(std::ostream&, const Listener&);
/// Bear in mind that this is strictly for testing
class Listener
{
public:
/// Every Listener is instantiated with a name
Listener(const std::string& name):
mName(name)
{
// std::cout << *this << ": ctor\n";
}
/*==========================================================================*|
// These methods are only useful when trying to track Listener instance
// lifespan
Listener(const Listener& that):
mName(that.mName),
mLastEvent(that.mLastEvent)
{
std::cout << *this << ": copy\n";
}
virtual ~Listener()
{
std::cout << *this << ": dtor\n";
}
|*==========================================================================*/
/// You can request the name
std::string getName() const { return mName; }
/// This is a typical listener method that returns 'false' when done,
/// allowing subsequent listeners on the LLEventPump to process the
/// incoming event.
bool call(const LLSD& event)
{
// std::cout << *this << "::call(" << event << ")\n";
mLastEvent = event;
return false;
}
/// This is an alternate listener that returns 'true' when done, which
/// stops processing of the incoming event.
bool callstop(const LLSD& event)
{
// std::cout << *this << "::callstop(" << event << ")\n";
mLastEvent = event;
return true;
}
/// ListenMethod can represent either call() or callstop().
typedef bool (Listener::*ListenMethod)(const LLSD&);
/**
* This helper method is only because our test code makes so many
* repetitive listen() calls to ListenerMethods. In real code, you should
* call LLEventPump::listen() directly so it can examine the specific
* object you pass to boost::bind().
*/
LLBoundListener listenTo(LLEventPump& pump,
ListenMethod method=&Listener::call,
const LLEventPump::NameList& after=LLEventPump::empty,
const LLEventPump::NameList& before=LLEventPump::empty)
{
return pump.listen(getName(), boost::bind(method, this, _1), after, before);
}
/// Both call() and callstop() set mLastEvent. Retrieve it.
LLSD getLastEvent() const
{
// std::cout << *this << "::getLastEvent() -> " << mLastEvent << "\n";
return mLastEvent;
}
/// Reset mLastEvent to a known state.
void reset(const LLSD& to = LLSD())
{
// std::cout << *this << "::reset(" << to << ")\n";
mLastEvent = to;
}
private:
std::string mName;
LLSD mLastEvent;
};
std::ostream& operator<<(std::ostream& out, const Listener& listener)
{
out << "Listener(" << listener.getName() /* << "@" << &listener */ << ')';
return out;
}
/**
* This class tests the relative order in which various listeners on a given
* LLEventPump are called. Each listen() call binds a particular string, which
* we collect for later examination. The actual event is ignored.
*/
struct Collect
{
bool add(const std::string& bound, const LLSD& event)
{
result.push_back(bound);
return false;
}
void clear() { result.clear(); }
StringVec result;
};
struct Concat
{
bool operator()(const LLSD& event)
{
result += event.asString();
return false;
}
void clear() { result.clear(); }
std::string result;
};
#endif /* ! defined(LL_LISTENER_H) */
@@ -0,0 +1,144 @@
/**
* @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";
}
+80
View File
@@ -0,0 +1,80 @@
/**
* @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
};
+77
View File
@@ -0,0 +1,77 @@
/**
* @file llbase64_test.cpp
* @author James Cook
* @date 2007-02-04
*
* $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 <string>
#include "linden_common.h"
#include "../llbase64.h"
#include "../lluuid.h"
#include "../test/lltut.h"
namespace tut
{
struct base64_data
{
};
typedef test_group<base64_data> base64_test;
typedef base64_test::object base64_object;
tut::base64_test base64("LLBase64");
template<> template<>
void base64_object::test<1>()
{
std::string result;
result = LLBase64::encode(NULL, 0);
ensure("encode nothing", (result == "") );
LLUUID nothing;
result = LLBase64::encode(&nothing.mData[0], UUID_BYTES);
ensure("encode blank uuid",
(result == "AAAAAAAAAAAAAAAAAAAAAA==") );
LLUUID id("526a1e07-a19d-baed-84c4-ff08a488d15e");
result = LLBase64::encode(&id.mData[0], UUID_BYTES);
ensure("encode random uuid",
(result == "UmoeB6Gduu2ExP8IpIjRXg==") );
}
template<> template<>
void base64_object::test<2>()
{
std::string result;
U8 blob[40] = { 115, 223, 172, 255, 140, 70, 49, 125, 236, 155, 45, 199, 101, 17, 164, 131, 230, 19, 80, 64, 112, 53, 135, 98, 237, 12, 26, 72, 126, 14, 145, 143, 118, 196, 11, 177, 132, 169, 195, 134 };
result = LLBase64::encode(&blob[0], 40);
ensure("encode 40 bytes",
(result == "c9+s/4xGMX3smy3HZRGkg+YTUEBwNYdi7QwaSH4OkY92xAuxhKnDhg==") );
}
}
+67
View File
@@ -0,0 +1,67 @@
/**
* @file llcond_test.cpp
* @author Nat Goodspeed
* @date 2019-07-18
* @brief Test for llcond.
*
* $LicenseInfo:firstyear=2019&license=viewerlgpl$
* Copyright (c) 2019, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llcond.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "llcoros.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llcond_data
{
LLScalarCond<int> cond{0};
};
typedef test_group<llcond_data> llcond_group;
typedef llcond_group::object object;
llcond_group llcondgrp("llcond");
template<> template<>
void object::test<1>()
{
set_test_name("Immediate gratification");
cond.set_one(1);
ensure("wait_for_equal() failed",
cond.wait_for_equal(F32Milliseconds(1), 1));
ensure("wait_for_unequal() should have failed",
! cond.wait_for_unequal(F32Milliseconds(1), 1));
}
template<> template<>
void object::test<2>()
{
set_test_name("Simple two-coroutine test");
LLCoros::instance().launch(
"test<2>",
[this]()
{
// Lambda immediately entered -- control comes here first.
ensure_equals(cond.get(), 0);
cond.set_all(1);
cond.wait_equal(2);
ensure_equals(cond.get(), 2);
cond.set_all(3);
});
// Main coroutine is resumed only when the lambda waits.
ensure_equals(cond.get(), 1);
cond.set_all(2);
cond.wait_equal(3);
}
} // namespace tut
+213
View File
@@ -0,0 +1,213 @@
/**
* @file lldate_test.cpp
* @author Adroit
* @date 2007-02
* @brief LLDate 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 "../llstring.h"
#include "../lldate.h"
#include "../test/lltut.h"
#define VALID_DATE "2003-04-30T04:00:00Z"
#define VALID_DATE_LEAP "2004-02-29T04:00:00Z"
#define VALID_DATE_HOUR_BOUNDARY "2003-04-30T23:59:59Z"
#define VALID_DATE_FRACTIONAL_SECS "2007-09-26T20:31:33.70Z"
// invalid format
#define INVALID_DATE_MISSING_YEAR "-04-30T22:59:59Z"
#define INVALID_DATE_MISSING_MONTH "1900-0430T22:59:59Z"
#define INVALID_DATE_MISSING_DATE "1900-0430-T22:59:59Z"
#define INVALID_DATE_MISSING_T "1900-04-30-22:59:59Z"
#define INVALID_DATE_MISSING_HOUR "1900-04-30T:59:59Z"
#define INVALID_DATE_MISSING_MIN "1900-04-30T01::59Z"
#define INVALID_DATE_MISSING_SEC "1900-04-30T01:59Z"
#define INVALID_DATE_MISSING_Z "1900-04-30T01:59:23"
#define INVALID_DATE_EMPTY ""
// invalid values
// apr 1.1.1 seems to not care about constraining the date to valid
// dates. Put these back when the parser checks.
#define LL_DATE_PARSER_CHECKS_BOUNDARY 0
//#define INVALID_DATE_24HOUR_BOUNDARY "2003-04-30T24:00:00Z"
//#define INVALID_DATE_LEAP "2003-04-29T04:00:00Z"
//#define INVALID_DATE_HOUR "2003-04-30T24:59:59Z"
//#define INVALID_DATE_MIN "2003-04-30T22:69:59Z"
//#define INVALID_DATE_SEC "2003-04-30T22:59:69Z"
//#define INVALID_DATE_YEAR "0-04-30T22:59:59Z"
//#define INVALID_DATE_MONTH "2003-13-30T22:59:59Z"
//#define INVALID_DATE_DAY "2003-04-35T22:59:59Z"
namespace tut
{
struct date_test
{
};
typedef test_group<date_test> date_test_t;
typedef date_test_t::object date_test_object_t;
tut::date_test_t tut_date_test("LLDate");
/* format validation */
template<> template<>
void date_test_object_t::test<1>()
{
LLDate date(VALID_DATE);
std::string expected_string;
bool result;
expected_string = VALID_DATE;
ensure_equals("Valid Date failed" , expected_string, date.asString());
result = date.fromString(VALID_DATE_LEAP);
expected_string = VALID_DATE_LEAP;
ensure_equals("VALID_DATE_LEAP failed" , expected_string, date.asString());
result = date.fromString(VALID_DATE_HOUR_BOUNDARY);
expected_string = VALID_DATE_HOUR_BOUNDARY;
ensure_equals("VALID_DATE_HOUR_BOUNDARY failed" , expected_string, date.asString());
result = date.fromString(VALID_DATE_FRACTIONAL_SECS);
expected_string = VALID_DATE_FRACTIONAL_SECS;
ensure_equals("VALID_DATE_FRACTIONAL_SECS failed" , expected_string, date.asString());
result = date.fromString(INVALID_DATE_MISSING_YEAR);
ensure_equals("INVALID_DATE_MISSING_YEAR should have failed" , result, false);
result = date.fromString(INVALID_DATE_MISSING_MONTH);
ensure_equals("INVALID_DATE_MISSING_MONTH should have failed" , result, false);
result = date.fromString(INVALID_DATE_MISSING_DATE);
ensure_equals("INVALID_DATE_MISSING_DATE should have failed" , result, false);
result = date.fromString(INVALID_DATE_MISSING_T);
ensure_equals("INVALID_DATE_MISSING_T should have failed" , result, false);
result = date.fromString(INVALID_DATE_MISSING_HOUR);
ensure_equals("INVALID_DATE_MISSING_HOUR should have failed" , result, false);
result = date.fromString(INVALID_DATE_MISSING_MIN);
ensure_equals("INVALID_DATE_MISSING_MIN should have failed" , result, false);
result = date.fromString(INVALID_DATE_MISSING_SEC);
ensure_equals("INVALID_DATE_MISSING_SEC should have failed" , result, false);
result = date.fromString(INVALID_DATE_MISSING_Z);
ensure_equals("INVALID_DATE_MISSING_Z should have failed" , result, false);
result = date.fromString(INVALID_DATE_EMPTY);
ensure_equals("INVALID_DATE_EMPTY should have failed" , result, false);
}
/* Invalid Value Handling */
template<> template<>
void date_test_object_t::test<2>()
{
#if LL_DATE_PARSER_CHECKS_BOUNDARY
LLDate date;
std::string expected_string;
bool result;
result = date.fromString(INVALID_DATE_24HOUR_BOUNDARY);
ensure_equals("INVALID_DATE_24HOUR_BOUNDARY should have failed" , result, false);
ensure_equals("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!" , date.secondsSinceEpoch(), 0);
result = date.fromString(INVALID_DATE_LEAP);
ensure_equals("INVALID_DATE_LEAP should have failed" , result, false);
result = date.fromString(INVALID_DATE_HOUR);
ensure_equals("INVALID_DATE_HOUR should have failed" , result, false);
result = date.fromString(INVALID_DATE_MIN);
ensure_equals("INVALID_DATE_MIN should have failed" , result, false);
result = date.fromString(INVALID_DATE_SEC);
ensure_equals("INVALID_DATE_SEC should have failed" , result, false);
result = date.fromString(INVALID_DATE_YEAR);
ensure_equals("INVALID_DATE_YEAR should have failed" , result, false);
result = date.fromString(INVALID_DATE_MONTH);
ensure_equals("INVALID_DATE_MONTH should have failed" , result, false);
result = date.fromString(INVALID_DATE_DAY);
ensure_equals("INVALID_DATE_DAY should have failed" , result, false);
#endif
}
/* API checks */
template<> template<>
void date_test_object_t::test<3>()
{
LLDate date;
std::istringstream stream(VALID_DATE);
std::string expected_string = VALID_DATE;
date.fromStream(stream);
ensure_equals("fromStream failed", date.asString(), expected_string);
}
template<> template<>
void date_test_object_t::test<4>()
{
LLDate date1(VALID_DATE);
LLDate date2(date1);
ensure_equals("LLDate(const LLDate& date) constructor failed", date1.asString(), date2.asString());
}
template<> template<>
void date_test_object_t::test<5>()
{
LLDate date1(VALID_DATE);
LLDate date2(date1.secondsSinceEpoch());
ensure_equals("secondsSinceEpoch not equal",date1.secondsSinceEpoch(), date2.secondsSinceEpoch());
ensure_equals("LLDate created using secondsSinceEpoch not equal", date1.asString(), date2.asString());
}
template<> template<>
void date_test_object_t::test<6>()
{
LLDate date(VALID_DATE);
std::ostringstream stream;
stream << date;
std::string expected_str = VALID_DATE;
ensure_equals("ostringstream failed", expected_str, stream.str());
}
template<> template<>
void date_test_object_t::test<7>()
{
LLDate date;
std::istringstream stream(VALID_DATE);
stream >> date;
std::string expected_str = VALID_DATE;
std::ostringstream out_stream;
out_stream << date;
ensure_equals("<< failed", date.asString(),expected_str);
ensure_equals("<< to >> failed", stream.str(),out_stream.str());
}
}
@@ -0,0 +1,628 @@
/**
* @file lldeadmantimer_test.cpp
* @brief Tests for the LLDeadmanTimer class.
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "../lldeadmantimer.h"
#include "../llsd.h"
#include "../lltimer.h"
#include "../test/lltut.h"
// Convert between floating point time deltas and U64 time deltas.
// Reflects an implementation detail inside lldeadmantimer.cpp
static LLDeadmanTimer::time_type float_time_to_u64(F64 delta)
{
return LLDeadmanTimer::time_type(delta * get_timer_info().mClockFrequency);
}
static F64 u64_time_to_float(LLDeadmanTimer::time_type delta)
{
return delta * get_timer_info().mClockFrequencyInv;
}
namespace tut
{
struct deadmantimer_test
{
deadmantimer_test()
{
// LLTimer internals updating
get_timer_info().update();
}
};
typedef test_group<deadmantimer_test> deadmantimer_group_t;
typedef deadmantimer_group_t::object deadmantimer_object_t;
tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
// Basic construction test and isExpired() call
template<> template<>
void deadmantimer_object_t::test<1>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// Construct with zero horizon - not useful generally but will be useful in testing
template<> template<>
void deadmantimer_object_t::test<2>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(0.0, false); // Zero is pre-expired
ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()",
timer.isExpired(0, started, stopped, count), false);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(0.0, true); // Zero is pre-expired
ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
}
}
// "pre-expired" timer - starting a timer with a 0.0 horizon will result in
// expiration on first test.
template<> template<>
void deadmantimer_object_t::test<3>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(0.0, false);
timer.start(0);
ensure_equals("WOCM isExpired() returns true with 0.0 horizon time",
timer.isExpired(0, started, stopped, count), true);
ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(0.0, true);
timer.start(0);
ensure_equals("WCM isExpired() returns true with 0.0 horizon time",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
}
}
// "pre-expired" timer - bell rings are ignored as we're already expired.
template<> template<>
void deadmantimer_object_t::test<4>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(0.0, false);
timer.start(0);
timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring",
timer.isExpired(0, started, stopped, count), true);
ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(0.0, true);
timer.start(0);
timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
}
}
// start(0) test - unexpired timer reports unexpired
template<> template<>
void deadmantimer_object_t::test<5>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
timer.start(0);
ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time",
timer.isExpired(0, started, stopped, count), false);
ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
timer.start(0);
ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time",
timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// start() test - start in the past but not beyond 1 horizon
template<> template<>
void deadmantimer_object_t::test<6>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
timer.start(the_past);
ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
timer.isExpired(now, started, stopped, count), false);
ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
timer.start(the_past);
ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// start() test - start in the past but well beyond 1 horizon
template<> template<>
void deadmantimer_object_t::test<7>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now,started, stopped, count), true);
ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
}
}
// isExpired() test - results are read-once. Probes after first true are false.
template<> template<>
void deadmantimer_object_t::test<8>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(10.0, false);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now, started, stopped, count), true);
started = 42.0;
stopped = 97.0;
count = U64L(8);
ensure_equals("WOCM t8 - second isExpired() returns false after true",
timer.isExpired(now, started, stopped, count), false);
ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(10.0, true);
// Would like to do subtraction on current time but can't because
// the implementation on Windows is zero-based. We wrap around
// the backside resulting in a large U64 number.
LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
timer.start(the_past);
ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
started = 42.0;
stopped = 97.0;
count = U64L(8);
user_cpu = 29000;
sys_cpu = 57000;
ensure_equals("WCM t8 - second isExpired() returns false after true",
timer.isExpired(now, started, stopped, count), false);
ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000));
ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
}
}
// ringBell() test - see that we can keep a timer from expiring
template<> template<>
void deadmantimer_object_t::test<9>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(5.0, false);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count), true);
ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(5.0, true);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
}
}
// restart after expiration test - verify that restarts behave well
template<> template<>
void deadmantimer_object_t::test<10>()
{
{
// Without cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8));
LLDeadmanTimer timer(5.0, false);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count), true);
ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false);
// Jump forward and restart
now += float_time_to_u64(1.0);
real_start = u64_time_to_float(now);
timer.start(now);
// Run a modified bell ring sequence
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
timer.isExpired(now, started, stopped, count), false);
last_good_ring = u64_time_to_float(now);
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump",
timer.isExpired(now, started, stopped, count), true);
ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4);
ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8));
ensure_equals("WOCM t10 - single read only - 2nd start",
timer.isExpired(now, started, stopped, count), false);
}
{
// With cpu metrics
F64 started(42.0), stopped(97.0);
U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
LLDeadmanTimer timer(5.0, true);
LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
F64 real_start(u64_time_to_float(now));
timer.start(0);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
F64 last_good_ring(u64_time_to_float(now));
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4);
ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10));
ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
// Jump forward and restart
now += float_time_to_u64(1.0);
real_start = u64_time_to_float(now);
timer.start(now);
// Run a modified bell ring sequence
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
now += float_time_to_u64(1.0);
timer.ringBell(now, 1);
ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
last_good_ring = u64_time_to_float(now);
// Jump forward and expire
now += float_time_to_u64(10.0);
ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4);
ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8));
ensure_equals("WCM t10 - single read only - 2nd start",
timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
}
}
} // end namespace tut
@@ -0,0 +1,302 @@
/**
* @file lldependencies_tut.cpp
* @author Nat Goodspeed
* @date 2008-09-17
* @brief Test of lldependencies.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$
*/
// STL headers
#include <iostream>
#include <string>
// std headers
// external library headers
#include <boost/assign/list_of.hpp>
// Precompiled header
#include "linden_common.h"
// associated header
#include "../lldependencies.h"
// other Linden headers
#if LL_WINDOWS
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
#endif
/*****************************************************************************
* Display helpers: must be defined BEFORE lltut.h!
*****************************************************************************/
// Display an arbitary value as itself...
template<typename T>
std::ostream& display(std::ostream& out, const T& value)
{
out << value;
return out;
}
// ...but display std::string enclosed in double quotes.
template<>
std::ostream& display(std::ostream& out, const std::string& value)
{
out << '"' << value << '"';
return out;
}
// display any sequence compatible with Boost.Range
template<typename SEQUENCE>
std::ostream& display_seq(std::ostream& out,
const std::string& open, const SEQUENCE& seq, const std::string& close)
{
out << open;
typename boost::range_const_iterator<SEQUENCE>::type
sli = boost::begin(seq),
slend = boost::end(seq);
if (sli != slend)
{
display(out, *sli);
while (++sli != slend)
{
out << ", ";
display(out, *sli);
}
}
out << close;
return out;
}
// helper to dump a StringList to std::cout if needed
template<typename ENTRY>
std::ostream& operator<<(std::ostream& out, const std::vector<ENTRY>& list)
{
display_seq(out, "(", list, ")");
return out;
}
template<typename ENTRY>
std::ostream& operator<<(std::ostream& out, const std::set<ENTRY>& set)
{
display_seq(out, "{", set, "}");
return out;
}
/*****************************************************************************
* Now we can #include lltut.h
*****************************************************************************/
#include "../test/lltut.h"
/*****************************************************************************
* Other helpers
*****************************************************************************/
using boost::assign::list_of;
typedef LLDependencies<> StringDeps;
typedef StringDeps::KeyList StringList;
// We use the very cool boost::assign::list_of() construct to specify vectors
// of strings inline. For reasons on which I'm not entirely clear, though, it
// needs a helper function. You can use list_of() to construct an implicit
// StringList (std::vector<std::string>) by conversion, e.g. for a function
// parameter -- but if you simply write StringList(list_of("etc.")), you get
// ambiguity errors. Shrug!
template<typename CONTAINER>
CONTAINER make(const CONTAINER& data)
{
return data;
}
const std::string& extract_key(const LLDependencies<>::value_type& entry)
{
return entry.first;
}
// helper to return a StringList of keys from LLDependencies::sort()
StringList sorted_keys(LLDependencies<>& deps)
{
// 1. Call deps.sort(), returning a value_type range of (key, node) pairs.
// 2. Use make_transform_range() to obtain a range of just keys.
// 3. Use instance_from_range to instantiate a StringList from that range.
// 4. Return by value "slices" instance_from_range<StringList> (a subclass
// of StringList) to its base class StringList.
return instance_from_range<StringList>(make_transform_range(deps.sort(), extract_key));
}
template<typename RANGE>
bool is_empty(const RANGE& range)
{
return boost::begin(range) == boost::end(range);
}
/*****************************************************************************
* tut test group
*****************************************************************************/
namespace tut
{
struct deps_data
{
};
typedef test_group<deps_data> deps_group;
typedef deps_group::object deps_object;
tut::deps_group depsgr("LLDependencies");
template<> template<>
void deps_object::test<1>()
{
StringDeps deps;
StringList empty;
// The quick brown fox jumps over the lazy yellow dog.
// (note, "The" and "the" are distinct, else this test wouldn't work)
deps.add("lazy");
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")));
deps.add("jumps");
ensure("found lazy", deps.get("lazy"));
ensure("not found dog.", ! deps.get("dog."));
// NOTE: Maybe it's overkill to test each of these intermediate
// results before all the interdependencies have been specified. My
// thought is simply that if the order changes, I'd like to know why.
// A change to the implementation of boost::topological_sort() would
// be an acceptable reason, and you can simply update the expected
// test output.
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")));
deps.add("The", 0, empty, list_of("fox")("dog."));
// Test key accessors
ensure("empty before deps for missing key", is_empty(deps.get_before_range("bogus")));
ensure("empty before deps for jumps", is_empty(deps.get_before_range("jumps")));
ensure_equals(instance_from_range< std::set<std::string> >(deps.get_before_range("The")),
make< std::set<std::string> >(list_of("dog.")("fox")));
// resume building dependencies
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")("The")));
deps.add("the", 0, list_of("The"));
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("jumps")("The")("the")));
deps.add("fox", 0, list_of("The"), list_of("jumps"));
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")));
deps.add("the", 0, list_of("The")); // same, see if cache works
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")));
deps.add("jumps", 0, empty, list_of("over")); // update jumps deps
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")));
/*==========================================================================*|
// It drives me nuts that this test doesn't work in the test
// framework, because -- for reasons unknown -- running the test
// framework on Mac OS X 10.5 Leopard and Windows XP Pro, the catch
// clause below doesn't catch the exception. Something about the TUT
// test framework?!? The identical code works fine in a standalone
// test program. Commenting out the test for now, in hopes that our
// real builds will be able to catch Cycle exceptions...
try
{
// We've already specified fox -> jumps and jumps -> over. Try an
// impossible constraint.
deps.add("over", 0, empty, list_of("fox"));
}
catch (const StringDeps::Cycle& e)
{
std::cout << "Cycle detected: " << e.what() << '\n';
// It's legal to add() an impossible constraint because we don't
// detect the cycle until sort(). So sort() can't know the minimum set
// of nodes to remove to make the StringDeps object valid again.
// Therefore we must break the cycle by hand.
deps.remove("over");
}
|*==========================================================================*/
deps.add("dog.", 0, list_of("yellow")("lazy"));
ensure_equals(instance_from_range< std::set<std::string> >(deps.get_after_range("dog.")),
make< std::set<std::string> >(list_of("lazy")("yellow")));
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("fox")("jumps")("dog.")));
deps.add("quick", 0, list_of("The"), list_of("fox")("brown"));
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("the")("quick")("fox")("jumps")("dog.")));
deps.add("over", 0, list_of("jumps"), list_of("yellow")("the"));
ensure_equals(sorted_keys(deps), make<StringList>(list_of("lazy")("The")("quick")("fox")("jumps")("over")("the")("dog.")));
deps.add("yellow", 0, list_of("the"), list_of("lazy"));
ensure_equals(sorted_keys(deps), make<StringList>(list_of("The")("quick")("fox")("jumps")("over")("the")("yellow")("lazy")("dog.")));
deps.add("brown");
// By now the dependencies are pretty well in place. A change to THIS
// order should be viewed with suspicion.
ensure_equals(sorted_keys(deps), make<StringList>(list_of("The")("quick")("brown")("fox")("jumps")("over")("the")("yellow")("lazy")("dog.")));
StringList keys(make<StringList>(list_of("The")("brown")("dog.")("fox")("jumps")("lazy")("over")("quick")("the")("yellow")));
ensure_equals(instance_from_range<StringList>(deps.get_key_range()), keys);
#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
// This is the succinct way, works on modern compilers
ensure_equals(instance_from_range<StringList>(make_transform_range(deps.get_range(), extract_key)), keys);
#else // gcc 3.3
StringDeps::range got_range(deps.get_range());
StringDeps::iterator kni = got_range.begin(), knend = got_range.end();
StringList::iterator ki = keys.begin(), kend = keys.end();
for ( ; kni != knend && ki != kend; ++kni, ++ki)
{
ensure_equals(kni->first, *ki);
}
ensure("get_range() returns proper length", kni == knend && ki == kend);
#endif // gcc 3.3
// blow off get_node_range() because they're all LLDependenciesEmpty instances
}
template<> template<>
void deps_object::test<2>()
{
typedef LLDependencies<std::string, int> NameIndexDeps;
NameIndexDeps nideps;
const NameIndexDeps& const_nideps(nideps);
nideps.add("def", 2, list_of("ghi"));
nideps.add("ghi", 3);
nideps.add("abc", 1, list_of("def"));
NameIndexDeps::range range(nideps.get_range());
ensure_equals(range.begin()->first, "abc");
ensure_equals(range.begin()->second, 1);
range.begin()->second = 0;
range.begin()->second = 1;
NameIndexDeps::const_range const_range(const_nideps.get_range());
NameIndexDeps::const_iterator const_iterator(const_range.begin());
++const_iterator;
ensure_equals(const_iterator->first, "def");
ensure_equals(const_iterator->second, 2);
// NameIndexDeps::node_range node_range(nideps.get_node_range());
// ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1)(2)(3)));
// *node_range.begin() = 0;
// *node_range.begin() = 1;
NameIndexDeps::const_node_range const_node_range(const_nideps.get_node_range());
ensure_equals(instance_from_range<std::vector<int> >(const_node_range), make< std::vector<int> >(list_of(1)(2)(3)));
NameIndexDeps::const_key_range const_key_range(const_nideps.get_key_range());
ensure_equals(instance_from_range<StringList>(const_key_range), make<StringList>(list_of("abc")("def")("ghi")));
NameIndexDeps::sorted_range sorted(const_nideps.sort());
NameIndexDeps::sorted_iterator sortiter(sorted.begin());
ensure_equals(sortiter->first, "ghi");
ensure_equals(sortiter->second, 3);
// test all iterator-flavored versions of get_after_range()
StringList def(make<StringList>(list_of("def")));
ensure("empty abc before list", is_empty(nideps.get_before_range(nideps.get_range().begin())));
ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_range().begin())),
def);
ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_range().begin())),
def);
// ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_node_range().begin())),
// def);
ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_node_range().begin())),
def);
ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_key_range().begin())),
def);
// advance from "ghi" to "def", which must come after "ghi"
++sortiter;
ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(sortiter)),
make<StringList>(list_of("ghi")));
}
} // namespace tut
+937
View File
@@ -0,0 +1,937 @@
/**
* @file llerror_test.cpp
* @date December 2006
* @brief error unit tests
*
* $LicenseInfo:firstyear=2006&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 <vector>
#include <stdexcept>
#include "linden_common.h"
#include "../llerror.h"
#include "../llerrorcontrol.h"
#include "../llsd.h"
#include "../test/lltut.h"
enum LogFieldIndex
{
TIME_FIELD,
LEVEL_FIELD,
TAGS_FIELD,
LOCATION_FIELD,
FUNCTION_FIELD,
MSG_FIELD
};
static const char* FieldName[] =
{
"TIME",
"LEVEL",
"TAGS",
"LOCATION",
"FUNCTION",
"MSG"
};
namespace
{
#ifdef __clang__
# pragma clang diagnostic ignored "-Wunused-function"
#endif
#if __GNUC__
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
void test_that_error_h_includes_enough_things_to_compile_a_message()
{
LL_INFOS() << "!" << LL_ENDL;
}
}
namespace
{
static bool fatalWasCalled = false;
struct FatalWasCalled: public std::runtime_error
{
FatalWasCalled(const std::string& what): std::runtime_error(what) {}
};
void fatalCall(const std::string& msg) { throw FatalWasCalled(msg); }
}
// Because we use LLError::setFatalFunction(fatalCall), any LL_ERRS call we
// issue will throw FatalWasCalled. But we want the test program to continue.
// So instead of writing:
// LL_ERRS("tag") << "some message" << LL_ENDL;
// write:
// CATCH(LL_ERRS("tag"), "some message");
#define CATCH(logcall, expr) \
try \
{ \
logcall << expr << LL_ENDL; \
} \
catch (const FatalWasCalled&) \
{ \
fatalWasCalled = true; \
}
namespace tut
{
class TestRecorder : public LLError::Recorder
{
public:
TestRecorder()
{
showTime(false);
}
virtual ~TestRecorder()
{}
virtual void recordMessage(LLError::ELevel level,
const std::string& message)
{
mMessages.push_back(message);
}
int countMessages() { return (int) mMessages.size(); }
void clearMessages() { mMessages.clear(); }
std::string message(int n)
{
std::ostringstream test_name;
test_name << "testing message " << n << ", not enough messages";
tut::ensure(test_name.str(), n < countMessages());
return mMessages[n];
}
private:
typedef std::vector<std::string> MessageVector;
MessageVector mMessages;
};
struct ErrorTestData
{
LLError::RecorderPtr mRecorder;
LLError::SettingsStoragePtr mPriorErrorSettings;
ErrorTestData():
mRecorder(new TestRecorder())
{
fatalWasCalled = false;
mPriorErrorSettings = LLError::saveAndResetSettings();
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
LLError::setFatalFunction(fatalCall);
LLError::addRecorder(mRecorder);
}
~ErrorTestData()
{
LLError::removeRecorder(mRecorder);
LLError::restoreSettings(mPriorErrorSettings);
}
int countMessages()
{
return std::dynamic_pointer_cast<TestRecorder>(mRecorder)->countMessages();
}
void clearMessages()
{
std::dynamic_pointer_cast<TestRecorder>(mRecorder)->clearMessages();
}
void setWantsTime(bool t)
{
std::dynamic_pointer_cast<TestRecorder>(mRecorder)->showTime(t);
}
void setWantsMultiline(bool t)
{
std::dynamic_pointer_cast<TestRecorder>(mRecorder)->showMultiline(t);
}
std::string message(int n)
{
return std::dynamic_pointer_cast<TestRecorder>(mRecorder)->message(n);
}
void ensure_message_count(int expectedCount)
{
ensure_equals("message count", countMessages(), expectedCount);
}
std::string message_field(int msgnum, LogFieldIndex fieldnum)
{
std::ostringstream test_name;
test_name << "testing message " << msgnum << ", not enough messages";
tut::ensure(test_name.str(), msgnum < countMessages());
std::string msg(message(msgnum));
std::string field_value;
// find the start of the field; fields are separated by a single space
size_t scan = 0;
int on_field = 0;
while ( scan < msg.length() && on_field < fieldnum )
{
// fields are delimited by one space
if ( ' ' == msg[scan] )
{
if ( on_field < FUNCTION_FIELD )
{
on_field++;
}
// except function, which may have embedded spaces so ends with " : "
else if ( ( on_field == FUNCTION_FIELD )
&& ( ':' == msg[scan+1] && ' ' == msg[scan+2] )
)
{
on_field++;
scan +=2;
}
}
scan++;
}
size_t start_field = scan;
size_t fieldlen = 0;
if ( fieldnum < FUNCTION_FIELD )
{
fieldlen = msg.find(' ', start_field) - start_field;
}
else if ( fieldnum == FUNCTION_FIELD )
{
fieldlen = msg.find(" : ", start_field) - start_field;
}
else if ( MSG_FIELD == fieldnum ) // no delimiter, just everything to the end
{
fieldlen = msg.length() - start_field;
}
return msg.substr(start_field, fieldlen);
}
void ensure_message_field_equals(int msgnum, LogFieldIndex fieldnum, const std::string& expectedText)
{
std::ostringstream test_name;
test_name << "testing message " << msgnum << " field " << FieldName[fieldnum] << "\n message: \"" << message(msgnum) << "\"\n ";
ensure_equals(test_name.str(), message_field(msgnum, fieldnum), expectedText);
}
void ensure_message_does_not_contain(int n, const std::string& expectedText)
{
std::ostringstream test_name;
test_name << "testing message " << n;
ensure_does_not_contain(test_name.str(), message(n), expectedText);
}
};
typedef test_group<ErrorTestData> ErrorTestGroup;
typedef ErrorTestGroup::object ErrorTestObject;
ErrorTestGroup errorTestGroup("error");
template<> template<>
void ErrorTestObject::test<1>()
// basic test of output
{
LL_INFOS() << "test" << LL_ENDL;
LL_INFOS() << "bob" << LL_ENDL;
ensure_message_field_equals(0, MSG_FIELD, "test");
ensure_message_field_equals(1, MSG_FIELD, "bob");
}
}
namespace
{
void writeSome()
{
LL_DEBUGS("WriteTag","AnotherTag") << "one" << LL_ENDL;
LL_INFOS("WriteTag") << "two" << LL_ENDL;
LL_WARNS("WriteTag") << "three" << LL_ENDL;
CATCH(LL_ERRS("WriteTag"), "four");
}
};
namespace tut
{
template<> template<>
void ErrorTestObject::test<2>()
// messages are filtered based on default level
{
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
writeSome();
ensure_message_field_equals(0, MSG_FIELD, "one");
ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG");
ensure_message_field_equals(0, TAGS_FIELD, "#WriteTag#AnotherTag#");
ensure_message_field_equals(1, MSG_FIELD, "two");
ensure_message_field_equals(1, LEVEL_FIELD, "INFO");
ensure_message_field_equals(1, TAGS_FIELD, "#WriteTag#");
ensure_message_field_equals(2, MSG_FIELD, "three");
ensure_message_field_equals(2, LEVEL_FIELD, "WARNING");
ensure_message_field_equals(2, TAGS_FIELD, "#WriteTag#");
ensure_message_field_equals(3, MSG_FIELD, "four");
ensure_message_field_equals(3, LEVEL_FIELD, "ERROR");
ensure_message_field_equals(3, TAGS_FIELD, "#WriteTag#");
ensure_message_count(4);
LLError::setDefaultLevel(LLError::LEVEL_INFO);
writeSome();
ensure_message_field_equals(4, MSG_FIELD, "two");
ensure_message_field_equals(5, MSG_FIELD, "three");
ensure_message_field_equals(6, MSG_FIELD, "four");
ensure_message_count(7);
LLError::setDefaultLevel(LLError::LEVEL_WARN);
writeSome();
ensure_message_field_equals(7, MSG_FIELD, "three");
ensure_message_field_equals(8, MSG_FIELD, "four");
ensure_message_count(9);
LLError::setDefaultLevel(LLError::LEVEL_ERROR);
writeSome();
ensure_message_field_equals(9, MSG_FIELD, "four");
ensure_message_count(10);
LLError::setDefaultLevel(LLError::LEVEL_NONE);
writeSome();
ensure_message_count(10);
}
template<> template<>
void ErrorTestObject::test<3>()
// error type string in output
{
writeSome();
ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG");
ensure_message_field_equals(1, LEVEL_FIELD, "INFO");
ensure_message_field_equals(2, LEVEL_FIELD, "WARNING");
ensure_message_field_equals(3, LEVEL_FIELD, "ERROR");
ensure_message_count(4);
}
template<> template<>
void ErrorTestObject::test<4>()
// file abbreviation
{
std::string prev, abbreviateFile = __FILE__;
do
{
prev = abbreviateFile;
abbreviateFile = LLError::abbreviateFile(abbreviateFile);
// __FILE__ is assumed to end with
// indra/llcommon/tests/llerror_test.cpp. This test used to call
// abbreviateFile() exactly once, then check below whether it
// still contained the string 'indra'. That fails if the FIRST
// part of the pathname also contains indra! Certain developer
// machine images put local directory trees under
// /ngi-persist/indra, which is where we observe the problem. So
// now, keep calling abbreviateFile() until it returns its
// argument unchanged, THEN check.
} while (abbreviateFile != prev);
ensure_ends_with("file name abbreviation",
abbreviateFile,
"llcommon/tests/llerror_test.cpp"
);
ensure_does_not_contain("file name abbreviation",
abbreviateFile, "indra");
std::string someFile =
#if LL_WINDOWS
"C:/amy/bob/cam.cpp"
#else
"/amy/bob/cam.cpp"
#endif
;
std::string someAbbreviation = LLError::abbreviateFile(someFile);
ensure_equals("non-indra file abbreviation",
someAbbreviation, someFile);
}
}
namespace
{
std::string locationString(int line)
{
std::ostringstream location;
location << LLError::abbreviateFile(__FILE__)
<< "(" << line << ")";
return location.str();
}
std::string writeReturningLocation()
{
LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__;
return locationString(this_line);
}
void writeReturningLocationAndFunction(std::string& location, std::string& function)
{
LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__;
location = locationString(this_line);
function = __FUNCTION__;
}
std::string errorReturningLocation()
{
int this_line = __LINE__; CATCH(LL_ERRS(), "die");
return locationString(this_line);
}
}
/* The following helper functions and class members all log a simple message
from some particular function scope. Each function takes a bool argument
that indicates if it should log its own name or not (in the manner that
existing log messages often do.) The functions all return their C++
name so that test can be substantial mechanized.
*/
std::string logFromGlobal(bool id)
{
LL_INFOS() << (id ? "logFromGlobal: " : "") << "hi" << LL_ENDL;
return "logFromGlobal";
}
static std::string logFromStatic(bool id)
{
LL_INFOS() << (id ? "logFromStatic: " : "") << "hi" << LL_ENDL;
return "logFromStatic";
}
namespace
{
std::string logFromAnon(bool id)
{
LL_INFOS() << (id ? "logFromAnon: " : "") << "hi" << LL_ENDL;
return "logFromAnon";
}
}
namespace Foo {
std::string logFromNamespace(bool id)
{
LL_INFOS() << (id ? "Foo::logFromNamespace: " : "") << "hi" << LL_ENDL;
//return "Foo::logFromNamespace";
// there is no standard way to get the namespace name, hence
// we won't be testing for it
return "logFromNamespace";
}
}
namespace
{
class ClassWithNoLogType {
public:
std::string logFromMember(bool id)
{
LL_INFOS() << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << LL_ENDL;
return "ClassWithNoLogType::logFromMember";
}
static std::string logFromStatic(bool id)
{
LL_INFOS() << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << LL_ENDL;
return "ClassWithNoLogType::logFromStatic";
}
};
class ClassWithLogType {
LOG_CLASS(ClassWithLogType);
public:
std::string logFromMember(bool id)
{
LL_INFOS() << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << LL_ENDL;
return "ClassWithLogType::logFromMember";
}
static std::string logFromStatic(bool id)
{
LL_INFOS() << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << LL_ENDL;
return "ClassWithLogType::logFromStatic";
}
};
std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); }
std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); }
std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); }
void ensure_has(const std::string& message,
const std::string& actual, const std::string& expected)
{
std::string::size_type n1 = actual.find(expected);
if (n1 == std::string::npos)
{
std::stringstream ss;
ss << message << ": " << "expected to find a copy of '" << expected
<< "' in actual '" << actual << "'";
throw tut::failure(ss.str().c_str());
}
}
typedef std::string (*LogFromFunction)(bool);
void testLogName(LLError::RecorderPtr recorder, LogFromFunction f,
const std::string& class_name = "")
{
std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->clearMessages();
std::string name = f(false);
f(true);
std::string messageWithoutName = std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(0);
std::string messageWithName = std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(1);
ensure_has(name + " logged without name",
messageWithoutName, name);
ensure_has(name + " logged with name",
messageWithName, name);
if (!class_name.empty())
{
ensure_has(name + "logged without name",
messageWithoutName, class_name);
ensure_has(name + "logged with name",
messageWithName, class_name);
}
}
}
namespace
{
void writeMsgNeedsEscaping()
{
LL_DEBUGS("WriteTag") << "backslash\\" << LL_ENDL;
LL_INFOS("WriteTag") << "newline\nafternewline" << LL_ENDL;
LL_WARNS("WriteTag") << "return\rafterreturn" << LL_ENDL;
LL_DEBUGS("WriteTag") << "backslash\\backslash\\" << LL_ENDL;
LL_INFOS("WriteTag") << "backslash\\newline\nanothernewline\nafternewline" << LL_ENDL;
LL_WARNS("WriteTag") << "backslash\\returnnewline\r\n\\afterbackslash" << LL_ENDL;
}
};
namespace tut
{
template<> template<>
void ErrorTestObject::test<5>()
// backslash, return, and newline are not escaped with backslashes
{
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
setWantsMultiline(true);
writeMsgNeedsEscaping(); // but should not be now
ensure_message_field_equals(0, MSG_FIELD, "backslash\\");
ensure_message_field_equals(1, MSG_FIELD, "newline\nafternewline");
ensure_message_field_equals(2, MSG_FIELD, "return\rafterreturn");
ensure_message_field_equals(3, MSG_FIELD, "backslash\\backslash\\");
ensure_message_field_equals(4, MSG_FIELD, "backslash\\newline\nanothernewline\nafternewline");
ensure_message_field_equals(5, MSG_FIELD, "backslash\\returnnewline\r\n\\afterbackslash");
ensure_message_count(6);
}
}
namespace tut
{
template<> template<>
// class/function information in output
void ErrorTestObject::test<6>()
{
testLogName(mRecorder, logFromGlobal);
testLogName(mRecorder, logFromStatic);
testLogName(mRecorder, logFromAnon);
testLogName(mRecorder, logFromNamespace);
testLogName(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType");
testLogName(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType");
}
}
namespace
{
std::string innerLogger()
{
LL_INFOS() << "inside" << LL_ENDL;
return "moo";
}
std::string outerLogger()
{
LL_INFOS() << "outside(" << innerLogger() << ")" << LL_ENDL;
return "bar";
}
class LogWhileLogging
{
public:
void print(std::ostream& out) const
{
LL_INFOS() << "logging" << LL_ENDL;
out << "baz";
}
};
std::ostream& operator<<(std::ostream& out, const LogWhileLogging& l)
{ l.print(out); return out; }
void metaLogger()
{
LogWhileLogging l;
LL_INFOS() << "meta(" << l << ")" << LL_ENDL;
}
}
namespace tut
{
template<> template<>
// handle nested logging
void ErrorTestObject::test<7>()
{
outerLogger();
ensure_message_field_equals(0, MSG_FIELD, "inside");
ensure_message_field_equals(1, MSG_FIELD, "outside(moo)");
ensure_message_count(2);
metaLogger();
ensure_message_field_equals(2, MSG_FIELD, "logging");
ensure_message_field_equals(3, MSG_FIELD, "meta(baz)");
ensure_message_count(4);
}
template<> template<>
// special handling of LL_ERRS() calls
void ErrorTestObject::test<8>()
{
std::string location = errorReturningLocation();
ensure_message_field_equals(0, LOCATION_FIELD, location);
ensure_message_field_equals(0, MSG_FIELD, "die");
ensure_message_count(1);
ensure("fatal callback called", fatalWasCalled);
}
}
namespace
{
std::string roswell()
{
return "1947-07-08T03:04:05Z";
}
void ufoSighting()
{
LL_INFOS() << "ufo" << LL_ENDL;
}
}
namespace tut
{
template<> template<>
// time in output (for recorders that need it)
void ErrorTestObject::test<9>()
{
LLError::setTimeFunction(roswell);
setWantsTime(false);
ufoSighting();
ensure_message_field_equals(0, MSG_FIELD, "ufo");
ensure_message_does_not_contain(0, roswell());
setWantsTime(true);
ufoSighting();
ensure_message_field_equals(1, MSG_FIELD, "ufo");
ensure_message_field_equals(1, TIME_FIELD, roswell());
}
template<> template<>
// output order
void ErrorTestObject::test<10>()
{
LLError::setTimeFunction(roswell);
setWantsTime(true);
std::string location,
function;
writeReturningLocationAndFunction(location, function);
ensure_equals("order is time level tags location function message",
message(0),
roswell() + " INFO " + "# " /* no tag */ + location + " " + function + " : " + "apple");
}
template<> template<>
// multiple recorders
void ErrorTestObject::test<11>()
{
LLError::RecorderPtr altRecorder(new TestRecorder());
LLError::addRecorder(altRecorder);
LL_INFOS() << "boo" << LL_ENDL;
ensure_message_field_equals(0, MSG_FIELD, "boo");
ensure_equals("alt recorder count", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 1);
ensure_contains("alt recorder message 0", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(0), "boo");
LLError::setTimeFunction(roswell);
LLError::RecorderPtr anotherRecorder(new TestRecorder());
std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->showTime(true);
LLError::addRecorder(anotherRecorder);
LL_INFOS() << "baz" << LL_ENDL;
std::string when = roswell();
ensure_message_does_not_contain(1, when);
ensure_equals("alt recorder count", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 2);
ensure_does_not_contain("alt recorder message 1", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(1), when);
ensure_equals("another recorder count", std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->countMessages(), 1);
ensure_contains("another recorder message 0", std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->message(0), when);
LLError::removeRecorder(altRecorder);
LLError::removeRecorder(anotherRecorder);
}
}
class TestAlpha
{
LOG_CLASS(TestAlpha);
public:
static void doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; }
static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; }
static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; }
static void doError() { CATCH(LL_ERRS(), "ate eels"); }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
class TestBeta
{
LOG_CLASS(TestBeta);
public:
static void doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; }
static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; }
static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; }
static void doError() { CATCH(LL_ERRS(), "big easy"); }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
namespace tut
{
template<> template<>
// filtering by class
void ErrorTestObject::test<12>()
{
LLError::setDefaultLevel(LLError::LEVEL_WARN);
LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_field_equals(0, MSG_FIELD, "aim west");
ensure_message_field_equals(1, MSG_FIELD, "ate eels");
ensure_message_field_equals(2, MSG_FIELD, "buy iron");
ensure_message_field_equals(3, MSG_FIELD, "bad word");
ensure_message_field_equals(4, MSG_FIELD, "big easy");
ensure_message_count(5);
}
template<> template<>
// filtering by function, and that it will override class filtering
void ErrorTestObject::test<13>()
{
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN);
LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG);
LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE);
TestBeta::doAll();
ensure_message_field_equals(0, MSG_FIELD, "buy iron");
ensure_message_field_equals(1, MSG_FIELD, "bad word");
ensure_message_count(2);
}
template<> template<>
// filtering by file
// and that it is overridden by both class and function filtering
void ErrorTestObject::test<14>()
{
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
LLError::setFileLevel(LLError::abbreviateFile(__FILE__),
LLError::LEVEL_WARN);
LLError::setClassLevel("TestAlpha", LLError::LEVEL_INFO);
LLError::setFunctionLevel("TestAlpha::doError",
LLError::LEVEL_NONE);
LLError::setFunctionLevel("TestBeta::doError",
LLError::LEVEL_NONE);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_field_equals(0, MSG_FIELD, "any idea");
ensure_message_field_equals(1, MSG_FIELD, "aim west");
ensure_message_field_equals(2, MSG_FIELD, "bad word");
ensure_message_count(3);
}
template<> template<>
// proper cached, efficient lookup of filtering
void ErrorTestObject::test<15>()
{
LLError::setDefaultLevel(LLError::LEVEL_NONE);
TestAlpha::doInfo();
ensure_message_count(0);
ensure_equals("first check", LLError::shouldLogCallCount(), 1);
TestAlpha::doInfo();
ensure_message_count(0);
ensure_equals("second check", LLError::shouldLogCallCount(), 1);
LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG);
TestAlpha::doInfo();
ensure_message_count(1);
ensure_equals("third check", LLError::shouldLogCallCount(), 2);
TestAlpha::doInfo();
ensure_message_count(2);
ensure_equals("fourth check", LLError::shouldLogCallCount(), 2);
LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN);
TestAlpha::doInfo();
ensure_message_count(2);
ensure_equals("fifth check", LLError::shouldLogCallCount(), 3);
TestAlpha::doInfo();
ensure_message_count(2);
ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
}
template<> template<>
// configuration from LLSD
void ErrorTestObject::test<16>()
{
LLSD config;
config["print-location"] = true;
config["default-level"] = "DEBUG";
LLSD set1;
set1["level"] = "WARN";
set1["files"][0] = LLError::abbreviateFile(__FILE__);
LLSD set2;
set2["level"] = "INFO";
set2["classes"][0] = "TestAlpha";
LLSD set3;
set3["level"] = "NONE";
set3["functions"][0] = "TestAlpha::doError";
set3["functions"][1] = "TestBeta::doError";
config["settings"][0] = set1;
config["settings"][1] = set2;
config["settings"][2] = set3;
LLError::configure(config);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_field_equals(0, MSG_FIELD, "any idea");
ensure_message_field_equals(1, MSG_FIELD, "aim west");
ensure_message_field_equals(2, MSG_FIELD, "bad word");
ensure_message_count(3);
// make sure reconfiguring works
LLSD config2;
config2["default-level"] = "WARN";
LLError::configure(config2);
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_field_equals(3, MSG_FIELD, "aim west");
ensure_message_field_equals(4, MSG_FIELD, "ate eels");
ensure_message_field_equals(5, MSG_FIELD, "bad word");
ensure_message_field_equals(6, MSG_FIELD, "big easy");
ensure_message_count(7);
}
}
namespace tut
{
template<> template<>
void ErrorTestObject::test<17>()
// backslash, return, and newline are escaped with backslashes
{
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
writeMsgNeedsEscaping();
ensure_message_field_equals(0, MSG_FIELD, "backslash\\\\");
ensure_message_field_equals(1, MSG_FIELD, "newline\\nafternewline");
ensure_message_field_equals(2, MSG_FIELD, "return\\rafterreturn");
ensure_message_field_equals(3, MSG_FIELD, "backslash\\\\backslash\\\\");
ensure_message_field_equals(4, MSG_FIELD, "backslash\\\\newline\\nanothernewline\\nafternewline");
ensure_message_field_equals(5, MSG_FIELD, "backslash\\\\returnnewline\\r\\n\\\\afterbackslash");
ensure_message_count(6);
}
}
namespace
{
std::string writeTagWithSpaceReturningLocation()
{
int this_line = __LINE__; CATCH(LL_DEBUGS("Write Tag"), "not allowed");
return locationString(this_line);
}
};
namespace tut
{
template<> template<>
void ErrorTestObject::test<18>()
// space character is not allowed in a tag
{
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
fatalWasCalled = false;
std::string location = writeTagWithSpaceReturningLocation();
std::string expected = "Space is not allowed in a log tag at " + location;
ensure_message_field_equals(0, LEVEL_FIELD, "ERROR");
ensure_message_field_equals(0, MSG_FIELD, expected);
ensure("fatal callback called", fatalWasCalled);
}
}
/* Tests left:
handling of classes without LOG_CLASS
live update of filtering from file
syslog recorder
file recorder
cerr/stderr recorder
fixed buffer recorder
windows recorder
mutex use when logging (?)
strange careful about to crash handling (?)
*/
+336
View File
@@ -0,0 +1,336 @@
/**
* @file coroutine_test.cpp
* @author Nat Goodspeed
* @date 2009-04-22
* @brief Test for coroutine.
*
* $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$
*/
#define BOOST_RESULT_OF_USE_TR1 1
#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/utility.hpp>
#include "linden_common.h"
#include <iostream>
#include <string>
#include <typeinfo>
#include "../test/lltut.h"
#include "../test/lltestapp.h"
#include "llsd.h"
#include "llsdutil.h"
#include "llevents.h"
#include "llcoros.h"
#include "lleventfilter.h"
#include "lleventcoro.h"
#include "../test/debug.h"
#include "../test/sync.h"
using namespace llcoro;
/*****************************************************************************
* Test helpers
*****************************************************************************/
/// Simulate an event API whose response is immediate: sent on receipt of the
/// initial request, rather than after some delay. This is the case that
/// distinguishes postAndSuspend() from calling post(), then calling
/// suspendUntilEventOn().
class ImmediateAPI
{
public:
ImmediateAPI(Sync& sync):
mPump("immediate", true),
mSync(sync)
{
mPump.listen("API", boost::bind(&ImmediateAPI::operator(), this, _1));
}
LLEventPump& getPump() { return mPump; }
// Invoke this with an LLSD map containing:
// ["value"]: Integer value. We will reply with ["value"] + 1.
// ["reply"]: Name of LLEventPump on which to send response.
bool operator()(const LLSD& event) const
{
mSync.bump();
LLSD::Integer value(event["value"]);
LLEventPumps::instance().obtain(event["reply"]).post(value + 1);
return false;
}
private:
LLEventStream mPump;
Sync& mSync;
};
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct test_data
{
Sync mSync;
ImmediateAPI immediateAPI{mSync};
std::string replyName, errorName, threw, stringdata;
LLSD result, errordata;
int which;
LLTestApp testApp;
void explicit_wait(std::shared_ptr<LLCoros::Promise<std::string>>& cbp);
void waitForEventOn1();
void coroPump();
void postAndWait1();
void coroPumpPost();
};
typedef test_group<test_data> coroutine_group;
typedef coroutine_group::object object;
coroutine_group coroutinegrp("coroutine");
void test_data::explicit_wait(std::shared_ptr<LLCoros::Promise<std::string>>& cbp)
{
BEGIN
{
mSync.bump();
// The point of this test is to verify / illustrate suspending a
// coroutine for something other than an LLEventPump. In other
// words, this shows how to adapt to any async operation that
// provides a callback-style notification (and prove that it
// works).
// Perhaps we would send a request to a remote server and arrange
// for cbp->set_value() to be called on response.
// For test purposes, instead of handing 'callback' (or an
// adapter) off to some I/O subsystem, we'll just pass it back to
// our caller.
cbp = std::make_shared<LLCoros::Promise<std::string>>();
LLCoros::Future<std::string> future = LLCoros::getFuture(*cbp);
// calling get() on the future causes us to suspend
debug("about to suspend");
stringdata = future.get();
mSync.bump();
ensure_equals("Got it", stringdata, "received");
}
END
}
template<> template<>
void object::test<1>()
{
set_test_name("explicit_wait");
DEBUG;
// Construct the coroutine instance that will run explicit_wait.
std::shared_ptr<LLCoros::Promise<std::string>> respond;
LLCoros::instance().launch("test<1>",
[this, &respond](){ explicit_wait(respond); });
mSync.bump();
// When the coroutine waits for the future, it returns here.
debug("about to respond");
// Now we're the I/O subsystem delivering a result. This should make
// the coroutine ready.
respond->set_value("received");
// but give it a chance to wake up
mSync.yield();
// ensure the coroutine ran and woke up again with the intended result
ensure_equals(stringdata, "received");
}
void test_data::waitForEventOn1()
{
BEGIN
{
mSync.bump();
result = suspendUntilEventOn("source");
mSync.bump();
}
END
}
template<> template<>
void object::test<2>()
{
set_test_name("waitForEventOn1");
DEBUG;
LLCoros::instance().launch("test<2>", [this](){ waitForEventOn1(); });
mSync.bump();
debug("about to send");
LLEventPumps::instance().obtain("source").post("received");
// give waitForEventOn1() a chance to run
mSync.yield();
debug("back from send");
ensure_equals(result.asString(), "received");
}
void test_data::coroPump()
{
BEGIN
{
mSync.bump();
LLCoroEventPump waiter;
replyName = waiter.getName();
result = waiter.suspend();
mSync.bump();
}
END
}
template<> template<>
void object::test<3>()
{
set_test_name("coroPump");
DEBUG;
LLCoros::instance().launch("test<3>", [this](){ coroPump(); });
mSync.bump();
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
// give coroPump() a chance to run
mSync.yield();
debug("back from send");
ensure_equals(result.asString(), "received");
}
void test_data::postAndWait1()
{
BEGIN
{
mSync.bump();
result = postAndSuspend(LLSDMap("value", 17), // request event
immediateAPI.getPump(), // requestPump
"reply1", // replyPump
"reply"); // request["reply"] = name
mSync.bump();
}
END
}
template<> template<>
void object::test<4>()
{
set_test_name("postAndWait1");
DEBUG;
LLCoros::instance().launch("test<4>", [this](){ postAndWait1(); });
ensure_equals(result.asInteger(), 18);
}
void test_data::coroPumpPost()
{
BEGIN
{
mSync.bump();
LLCoroEventPump waiter;
result = waiter.postAndSuspend(LLSDMap("value", 17),
immediateAPI.getPump(), "reply");
mSync.bump();
}
END
}
template<> template<>
void object::test<5>()
{
set_test_name("coroPumpPost");
DEBUG;
LLCoros::instance().launch("test<5>", [this](){ coroPumpPost(); });
ensure_equals(result.asInteger(), 18);
}
template <class PUMP>
void test()
{
PUMP pump(typeid(PUMP).name());
bool running{false};
LLSD data{LLSD::emptyArray()};
// start things off by posting once before even starting the listener
// coro
LL_DEBUGS() << "test() posting first" << LL_ENDL;
LLSD first{LLSDMap("desc", "first")("value", 0)};
bool consumed = pump.post(first);
ensure("should not have consumed first", ! consumed);
// now launch the coro
LL_DEBUGS() << "test() launching listener coro" << LL_ENDL;
running = true;
LLCoros::instance().launch(
"listener",
[&pump, &running, &data](){
// important for this test that we consume posted values
LLCoros::instance().set_consuming(true);
// should immediately retrieve 'first' without waiting
LL_DEBUGS() << "listener coro waiting for first" << LL_ENDL;
data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
// Don't use ensure() from within the coro -- ensure() failure
// throws tut::fail, which won't propagate out to the main
// test driver, which will result in an odd failure.
// Wait for 'second' because it's not already pending.
LL_DEBUGS() << "listener coro waiting for second" << LL_ENDL;
data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
// and wait for 'third', which should involve no further waiting
LL_DEBUGS() << "listener coro waiting for third" << LL_ENDL;
data.append(llcoro::suspendUntilEventOnWithTimeout(pump, 0.1, LLSD()));
LL_DEBUGS() << "listener coro done" << LL_ENDL;
running = false;
});
// back from coro at the point where it's waiting for 'second'
LL_DEBUGS() << "test() posting second" << LL_ENDL;
LLSD second{llsd::map("desc", "second", "value", 1)};
consumed = pump.post(second);
ensure("should have consumed second", consumed);
// This is a key point: even though we've post()ed the value for which
// the coroutine is waiting, it's actually still suspended until we
// pause for some other reason. The coroutine will only pick up one
// value at a time from our 'pump'. It's important to exercise the
// case when we post() two values before it picks up either.
LL_DEBUGS() << "test() posting third" << LL_ENDL;
LLSD third{llsd::map("desc", "third", "value", 2)};
consumed = pump.post(third);
ensure("should NOT yet have consumed third", ! consumed);
// now just wait for coro to finish -- which it eventually will, given
// that all its suspend calls have short timeouts.
while (running)
{
LL_DEBUGS() << "test() waiting for coro done" << LL_ENDL;
llcoro::suspendUntilTimeout(0.1);
}
// okay, verify expected results
ensure_equals("should have received three values", data,
llsd::array(first, second, third));
LL_DEBUGS() << "test() done" << LL_ENDL;
}
template<> template<>
void object::test<6>()
{
set_test_name("LLEventMailDrop");
tut::test<LLEventMailDrop>();
}
template<> template<>
void object::test<7>()
{
set_test_name("LLEventLogProxyFor<LLEventMailDrop>");
tut::test< LLEventLogProxyFor<LLEventMailDrop> >();
}
}
File diff suppressed because it is too large Load Diff
+484
View File
@@ -0,0 +1,484 @@
/**
* @file lleventfilter_test.cpp
* @author Nat Goodspeed
* @date 2009-03-06
* @brief Test for lleventfilter.
*
* $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 "lleventfilter.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "stringize.h"
#include "llsdutil.h"
#include "listener.h"
#include "tests/wrapllerrs.h"
#include <typeinfo>
/*****************************************************************************
* Test classes
*****************************************************************************/
// Strictly speaking, we're testing LLEventTimeoutBase rather than the
// production LLEventTimeout (using LLTimer) because we don't want every test
// run to pause for some number of seconds until we reach a real timeout. But
// as we've carefully put all functionality except actual LLTimer calls into
// LLEventTimeoutBase, that should suffice. We're not not not trying to test
// LLTimer here.
class TestEventTimeout: public LLEventTimeoutBase
{
public:
TestEventTimeout():
mElapsed(true)
{}
TestEventTimeout(LLEventPump& source):
LLEventTimeoutBase(source),
mElapsed(true)
{}
// test hook
void forceTimeout(bool timeout=true) { mElapsed = timeout; }
protected:
virtual void setCountdown(F32 seconds) { mElapsed = false; }
virtual bool countdownElapsed() const { return mElapsed; }
private:
bool mElapsed;
};
// Similar remarks about LLEventThrottle: we're actually testing the logic in
// LLEventThrottleBase, dummying out the LLTimer and LLEventTimeout used by
// the production LLEventThrottle class.
class TestEventThrottle: public LLEventThrottleBase
{
public:
TestEventThrottle(F32 interval):
LLEventThrottleBase(interval),
mAlarmRemaining(-1.f),
mTimerRemaining(-1.f)
{}
TestEventThrottle(LLEventPump& source, F32 interval):
LLEventThrottleBase(source, interval),
mAlarmRemaining(-1.f),
mTimerRemaining(-1.f)
{}
/*----- implementation of LLEventThrottleBase timing functionality -----*/
virtual void alarmActionAfter(F32 interval, const LLEventTimeoutBase::Action& action) /*override*/
{
mAlarmRemaining = interval;
mAlarmAction = action;
}
virtual bool alarmRunning() const /*override*/
{
// decrementing to exactly 0 should mean the alarm fires
return mAlarmRemaining > 0.f;
}
virtual void alarmCancel() /*override*/
{
mAlarmRemaining = -1.f;
}
virtual void timerSet(F32 interval) /*override*/
{
mTimerRemaining = interval;
}
virtual F32 timerGetRemaining() const /*override*/
{
// LLTimer.getRemainingTimeF32() never returns negative; 0.0 means expired
return (mTimerRemaining > 0.0f)? mTimerRemaining : 0.0f;
}
/*------------------- methods for manipulating time --------------------*/
void alarmAdvance(F32 delta)
{
bool wasRunning = alarmRunning();
mAlarmRemaining -= delta;
if (wasRunning && ! alarmRunning())
{
mAlarmAction();
}
}
void timerAdvance(F32 delta)
{
// This simple implementation, like alarmAdvance(), completely ignores
// HOW negative mTimerRemaining might go. All that matters is whether
// it's negative. We trust that no test method in this source will
// drive it beyond the capacity of an F32. Seems like a safe assumption.
mTimerRemaining -= delta;
}
void advance(F32 delta)
{
// Advance the timer first because it has no side effects.
// alarmAdvance() might call flush(), which will need to see the
// change in the timer.
timerAdvance(delta);
alarmAdvance(delta);
}
F32 mAlarmRemaining, mTimerRemaining;
LLEventTimeoutBase::Action mAlarmAction;
};
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct filter_data
{
// The resemblance between this test data and that in llevents_tut.cpp
// is not coincidental.
filter_data():
pumps(LLEventPumps::instance()),
mainloop(pumps.obtain("mainloop")),
listener0("first"),
listener1("second")
{}
LLEventPumps& pumps;
LLEventPump& mainloop;
Listener listener0;
Listener listener1;
void check_listener(const std::string& desc, const Listener& listener, const LLSD& got)
{
ensure_equals(STRINGIZE(listener << ' ' << desc),
listener.getLastEvent(), got);
}
};
typedef test_group<filter_data> filter_group;
typedef filter_group::object filter_object;
filter_group filtergrp("lleventfilter");
template<> template<>
void filter_object::test<1>()
{
set_test_name("LLEventMatching");
LLEventPump& driver(pumps.obtain("driver"));
listener0.reset(0);
// Listener isn't derived from LLEventTrackable specifically to test
// various connection-management mechanisms. But that means we have a
// couple of transient Listener objects, one of which is listening to
// a persistent LLEventPump. Capture those connections in local
// LLTempBoundListener instances so they'll disconnect
// on destruction.
LLTempBoundListener temp1(
listener0.listenTo(driver));
// Construct a pattern LLSD: desired Event must have a key "foo"
// containing string "bar"
LLSD pattern;
pattern.insert("foo", "bar");
LLEventMatching filter(driver, pattern);
listener1.reset(0);
LLTempBoundListener temp2(
listener1.listenTo(filter));
driver.post(1);
check_listener("direct", listener0, LLSD(1));
check_listener("filtered", listener1, LLSD(0));
// Okay, construct an LLSD map matching the pattern
LLSD data;
data["foo"] = "bar";
data["random"] = 17;
driver.post(data);
check_listener("direct", listener0, data);
check_listener("filtered", listener1, data);
}
template<> template<>
void filter_object::test<2>()
{
set_test_name("LLEventTimeout::actionAfter()");
LLEventPump& driver(pumps.obtain("driver"));
TestEventTimeout filter(driver);
listener0.reset(0);
LLTempBoundListener temp1(
listener0.listenTo(filter));
// Use listener1.call() as the Action for actionAfter(), since it
// already provides a way to sense the call
listener1.reset(0);
// driver --> filter --> listener0
filter.actionAfter(20,
boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
// Okay, (fake) timer is ticking. 'filter' can only sense the timer
// when we pump mainloop. Do that right now to take the logic path
// before either the anticipated event arrives or the timer expires.
mainloop.post(17);
check_listener("no timeout 1", listener1, LLSD(0));
// Expected event arrives...
driver.post(1);
check_listener("event passed thru", listener0, LLSD(1));
// Should have canceled the timer. Verify that by asserting that the
// time has expired, then pumping mainloop again.
filter.forceTimeout();
mainloop.post(17);
check_listener("no timeout 2", listener1, LLSD(0));
// Verify chained actionAfter() calls, that is, that a second
// actionAfter() resets the timer established by the first
// actionAfter().
filter.actionAfter(20,
boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
// Since our TestEventTimeout class isn't actually manipulating time
// (quantities of seconds), only a bool "elapsed" flag, sense that by
// forcing the flag between actionAfter() calls.
filter.forceTimeout();
// Pumping mainloop here would result in a timeout (as we'll verify
// below). This state simulates a ticking timer that has not yet timed
// out. But now, before a mainloop event lets 'filter' recognize
// timeout on the previous actionAfter() call, pretend we're pushing
// that timeout farther into the future.
filter.actionAfter(20,
boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
// Look ma, no timeout!
mainloop.post(17);
check_listener("no timeout 3", listener1, LLSD(0));
// Now let the updated actionAfter() timer expire.
filter.forceTimeout();
// Notice the timeout.
mainloop.post(17);
check_listener("timeout", listener1, LLSD("timeout"));
// Timing out cancels the timer. Verify that.
listener1.reset(0);
filter.forceTimeout();
mainloop.post(17);
check_listener("no timeout 4", listener1, LLSD(0));
// Reset the timer and then cancel() it.
filter.actionAfter(20,
boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
// neither expired nor satisified
mainloop.post(17);
check_listener("no timeout 5", listener1, LLSD(0));
// cancel
filter.cancel();
// timeout!
filter.forceTimeout();
mainloop.post(17);
check_listener("no timeout 6", listener1, LLSD(0));
}
template<> template<>
void filter_object::test<3>()
{
set_test_name("LLEventTimeout::eventAfter()");
LLEventPump& driver(pumps.obtain("driver"));
TestEventTimeout filter(driver);
listener0.reset(0);
LLTempBoundListener temp1(
listener0.listenTo(filter));
filter.eventAfter(20, LLSD("timeout"));
// Okay, (fake) timer is ticking. 'filter' can only sense the timer
// when we pump mainloop. Do that right now to take the logic path
// before either the anticipated event arrives or the timer expires.
mainloop.post(17);
check_listener("no timeout 1", listener0, LLSD(0));
// Expected event arrives...
driver.post(1);
check_listener("event passed thru", listener0, LLSD(1));
// Should have canceled the timer. Verify that by asserting that the
// time has expired, then pumping mainloop again.
filter.forceTimeout();
mainloop.post(17);
check_listener("no timeout 2", listener0, LLSD(1));
// Set timer again.
filter.eventAfter(20, LLSD("timeout"));
// Now let the timer expire.
filter.forceTimeout();
// Notice the timeout.
mainloop.post(17);
check_listener("timeout", listener0, LLSD("timeout"));
// Timing out cancels the timer. Verify that.
listener0.reset(0);
filter.forceTimeout();
mainloop.post(17);
check_listener("no timeout 3", listener0, LLSD(0));
}
template<> template<>
void filter_object::test<4>()
{
set_test_name("LLEventTimeout::errorAfter()");
WrapLLErrs capture;
LLEventPump& driver(pumps.obtain("driver"));
TestEventTimeout filter(driver);
listener0.reset(0);
LLTempBoundListener temp1(
listener0.listenTo(filter));
filter.errorAfter(20, "timeout");
// Okay, (fake) timer is ticking. 'filter' can only sense the timer
// when we pump mainloop. Do that right now to take the logic path
// before either the anticipated event arrives or the timer expires.
mainloop.post(17);
check_listener("no timeout 1", listener0, LLSD(0));
// Expected event arrives...
driver.post(1);
check_listener("event passed thru", listener0, LLSD(1));
// Should have canceled the timer. Verify that by asserting that the
// time has expired, then pumping mainloop again.
filter.forceTimeout();
mainloop.post(17);
check_listener("no timeout 2", listener0, LLSD(1));
// Set timer again.
filter.errorAfter(20, "timeout");
// Now let the timer expire.
filter.forceTimeout();
// Notice the timeout.
std::string threw = capture.catch_llerrs([this](){
mainloop.post(17);
});
ensure_contains("errorAfter() timeout exception", threw, "timeout");
// Timing out cancels the timer. Verify that.
listener0.reset(0);
filter.forceTimeout();
mainloop.post(17);
check_listener("no timeout 3", listener0, LLSD(0));
}
template<> template<>
void filter_object::test<5>()
{
set_test_name("LLEventThrottle");
TestEventThrottle throttle(3);
Concat cat;
throttle.listen("concat", boost::ref(cat));
// (sequence taken from LLEventThrottleBase Doxygen comments)
// 1: post(): event immediately passed to listeners, next no sooner than 4
throttle.advance(1);
throttle.post("1");
ensure_equals("1", cat.result, "1"); // delivered immediately
// 2: post(): deferred: waiting for 3 seconds to elapse
throttle.advance(1);
throttle.post("2");
ensure_equals("2", cat.result, "1"); // "2" not yet delivered
// 3: post(): deferred
throttle.advance(1);
throttle.post("3");
ensure_equals("3", cat.result, "1"); // "3" not yet delivered
// 4: no post() call, but event delivered to listeners; next no sooner than 7
throttle.advance(1);
ensure_equals("4", cat.result, "13"); // "3" delivered
// 6: post(): deferred
throttle.advance(2);
throttle.post("6");
ensure_equals("6", cat.result, "13"); // "6" not yet delivered
// 7: no post() call, but event delivered; next no sooner than 10
throttle.advance(1);
ensure_equals("7", cat.result, "136"); // "6" delivered
// 12: post(): immediately passed to listeners, next no sooner than 15
throttle.advance(5);
throttle.post(";12");
ensure_equals("12", cat.result, "136;12"); // "12" delivered
// 17: post(): immediately passed to listeners, next no sooner than 20
throttle.advance(5);
throttle.post(";17");
ensure_equals("17", cat.result, "136;12;17"); // "17" delivered
}
template<class PUMP>
void test()
{
PUMP pump(typeid(PUMP).name());
LLSD data{LLSD::emptyArray()};
bool consumed{true};
// listener that appends to 'data'
// but that also returns the current value of 'consumed'
// Instantiate this separately because we're going to listen()
// multiple times with the same lambda: LLEventMailDrop only replays
// queued events on a new listen() call.
auto lambda =
[&data, &consumed](const LLSD& event)->bool
{
data.append(event);
return consumed;
};
{
LLTempBoundListener conn = pump.listen("lambda", lambda);
pump.post("first");
}
// first post() should certainly be received by listener
ensure_equals("first", data, llsd::array("first"));
// the question is, since consumed was true, did it queue the value?
data = LLSD::emptyArray();
{
// if it queued the value, it would be delivered on subsequent
// listen() call
LLTempBoundListener conn = pump.listen("lambda", lambda);
}
ensure_equals("empty1", data, LLSD::emptyArray());
data = LLSD::emptyArray();
// now let's NOT consume the posted data
consumed = false;
{
LLTempBoundListener conn = pump.listen("lambda", lambda);
pump.post("second");
pump.post("third");
}
// the two events still arrive
ensure_equals("second,third1", data, llsd::array("second", "third"));
data = LLSD::emptyArray();
{
// when we reconnect, these should be delivered again
// but this time they should be consumed
consumed = true;
LLTempBoundListener conn = pump.listen("lambda", lambda);
}
// unconsumed events were delivered again
ensure_equals("second,third2", data, llsd::array("second", "third"));
data = LLSD::emptyArray();
{
// when we reconnect this time, no more unconsumed events
LLTempBoundListener conn = pump.listen("lambda", lambda);
}
ensure_equals("empty2", data, LLSD::emptyArray());
}
template<> template<>
void filter_object::test<6>()
{
set_test_name("LLEventMailDrop");
tut::test<LLEventMailDrop>();
}
template<> template<>
void filter_object::test<7>()
{
set_test_name("LLEventLogProxyFor<LLEventMailDrop>");
tut::test< LLEventLogProxyFor<LLEventMailDrop> >();
}
} // namespace tut
/*****************************************************************************
* Link dependencies
*****************************************************************************/
#include "llsdutil.cpp"
+323
View File
@@ -0,0 +1,323 @@
/**
* @file llexception_test.cpp
* @author Nat Goodspeed
* @date 2016-08-12
* @brief Tests for throwing exceptions.
*
* This isn't a regression test: it doesn't need to be run every build, which
* is why the corresponding line in llcommon/CMakeLists.txt is commented out.
* Rather it's a head-to-head test of what kind of exception information we
* can collect from various combinations of exception base classes, type of
* throw verb and sequences of catch clauses.
*
* This "test" makes no ensure() calls: its output goes to stdout for human
* examination.
*
* As of 2016-08-12 with Boost 1.57, we come to the following conclusions.
* These should probably be re-examined from time to time as we update Boost.
*
* - It is indisputably beneficial to use BOOST_THROW_EXCEPTION() rather than
* plain throw. The macro annotates the exception object with the filename,
* line number and function name from which the exception was thrown.
*
* - That being the case, deriving only from boost::exception isn't an option.
* Every exception object passed to BOOST_THROW_EXCEPTION() must be derived
* directly or indirectly from std::exception. The only question is whether
* to also derive from boost::exception. We decided to derive LLException
* from both, as it makes message output slightly cleaner, but this is a
* trivial reason: if a strong reason emerges to prefer single inheritance,
* dropping the boost::exception base class shouldn't be a problem.
*
* - (As you will have guessed, ridiculous things like a char* or int or a
* class derived from neither boost::exception nor std::exception can only
* be caught by that specific type or (...), and
* boost::current_exception_diagnostic_information() simply throws up its
* hands and confesses utter ignorance. Stay away from such nonsense.)
*
* - But if you derive from std::exception, to nat's surprise,
* boost::current_exception_diagnostic_information() gives as much
* information about exceptions in a catch (...) clause as you can get from
* a specific catch (const std::exception&) clause, notably the concrete
* exception class and the what() string. So instead of a sequence like
*
* try { ... }
* catch (const boost::exception& e) { ... boost-flavored logging ... }
* catch (const std::exception& e) { ... std::exception logging ... }
* catch (...) { ... generic logging ... }
*
* we should be able to get away with only a catch (...) clause that logs
* boost::current_exception_diagnostic_information().
*
* - Going further: boost::current_exception_diagnostic_information() provides
* just as much information even within a std::set_terminate() handler. So
* it might not even be strictly necessary to include a catch (...) clause
* since the viewer does use std::set_terminate().
*
* - (We might consider adding a catch (int) clause because Kakadu internally
* throws ints, and who knows if one of those might leak out. If it does,
* boost::current_exception_diagnostic_information() can do nothing with it.
* A catch (int) clause could at least log the value and rethrow.)
*
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
* Copyright (c) 2016, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llexception.h"
// STL headers
// std headers
#include <typeinfo>
// external library headers
#include <boost/throw_exception.hpp>
// other Linden headers
#include "../test/lltut.h"
// helper for display output
// usage: std::cout << center(some string value, fill char, width) << std::endl;
// (assumes it's the only thing on that particular line)
struct center
{
center(const std::string& label, char fill, std::size_t width):
mLabel(label),
mFill(fill),
mWidth(width)
{}
// Use friend declaration not because we need to grant access, but because
// it lets us declare a free operator like a member function.
friend std::ostream& operator<<(std::ostream& out, const center& ctr)
{
std::size_t padded = ctr.mLabel.length() + 2;
std::size_t left = (ctr.mWidth - padded) / 2;
std::size_t right = ctr.mWidth - left - padded;
return out << std::string(left, ctr.mFill) << ' ' << ctr.mLabel << ' '
<< std::string(right, ctr.mFill);
}
std::string mLabel;
char mFill;
std::size_t mWidth;
};
/*****************************************************************************
* Four kinds of exceptions: derived from boost::exception, from
* std::exception, from both, from neither
*****************************************************************************/
// Interestingly, we can't use this variant with BOOST_THROW_EXCEPTION()
// (which we want) -- we reach a failure topped by this comment:
// //All boost exceptions are required to derive from std::exception,
// //to ensure compatibility with BOOST_NO_EXCEPTIONS.
struct FromBoost: public boost::exception
{
FromBoost(const std::string& what): mWhat(what) {}
~FromBoost() throw() {}
std::string what() const { return mWhat; }
std::string mWhat;
};
struct FromStd: public std::runtime_error
{
FromStd(const std::string& what): std::runtime_error(what) {}
};
struct FromBoth: public boost::exception, public std::runtime_error
{
FromBoth(const std::string& what): std::runtime_error(what) {}
};
// Same deal with FromNeither: can't use with BOOST_THROW_EXCEPTION().
struct FromNeither
{
FromNeither(const std::string& what): mWhat(what) {}
std::string what() const { return mWhat; }
std::string mWhat;
};
/*****************************************************************************
* Two kinds of throws: plain throw and BOOST_THROW_EXCEPTION()
*****************************************************************************/
template <typename EXC>
void plain_throw(const std::string& what)
{
throw EXC(what);
}
template <typename EXC>
void boost_throw(const std::string& what)
{
BOOST_THROW_EXCEPTION(EXC(what));
}
// Okay, for completeness, functions that throw non-class values. We wouldn't
// even deign to consider these if we hadn't found examples in our own source
// code! (Note that Kakadu's internal exception support is still based on
// throwing ints.)
void throw_char_ptr(const std::string& what)
{
throw what.c_str(); // umm...
}
void throw_int(const std::string& what)
{
throw int(what.length());
}
/*****************************************************************************
* Three sequences of catch clauses:
* boost::exception then ...,
* std::exception then ...,
* or just ...
*****************************************************************************/
void catch_boost_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
{
try
{
thrower(what);
}
catch (const boost::exception& e)
{
std::cout << "catch (const boost::exception& e)" << std::endl;
std::cout << "e is " << typeid(e).name() << std::endl;
std::cout << "boost::diagnostic_information(e):\n'"
<< boost::diagnostic_information(e) << "'" << std::endl;
// no way to report e.what()
}
catch (...)
{
std::cout << "catch (...)" << std::endl;
std::cout << "boost::current_exception_diagnostic_information():\n'"
<< boost::current_exception_diagnostic_information() << "'"
<< std::endl;
}
}
void catch_std_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
{
try
{
thrower(what);
}
catch (const std::exception& e)
{
std::cout << "catch (const std::exception& e)" << std::endl;
std::cout << "e is " << typeid(e).name() << std::endl;
std::cout << "boost::diagnostic_information(e):\n'"
<< boost::diagnostic_information(e) << "'" << std::endl;
std::cout << "e.what: '"
<< e.what() << "'" << std::endl;
}
catch (...)
{
std::cout << "catch (...)" << std::endl;
std::cout << "boost::current_exception_diagnostic_information():\n'"
<< boost::current_exception_diagnostic_information() << "'"
<< std::endl;
}
}
void catch_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
{
try
{
thrower(what);
}
catch (...)
{
std::cout << "catch (...)" << std::endl;
std::cout << "boost::current_exception_diagnostic_information():\n'"
<< boost::current_exception_diagnostic_information() << "'"
<< std::endl;
}
}
/*****************************************************************************
* Try a particular kind of throw against each of three catch sequences
*****************************************************************************/
void catch_several(void (*thrower)(const std::string&), const std::string& what)
{
std::cout << std::string(20, '-') << "catch_boost_dotdotdot(" << what << ")" << std::endl;
catch_boost_dotdotdot(thrower, "catch_boost_dotdotdot(" + what + ")");
std::cout << std::string(20, '-') << "catch_std_dotdotdot(" << what << ")" << std::endl;
catch_std_dotdotdot(thrower, "catch_std_dotdotdot(" + what + ")");
std::cout << std::string(20, '-') << "catch_dotdotdot(" << what << ")" << std::endl;
catch_dotdotdot(thrower, "catch_dotdotdot(" + what + ")");
}
/*****************************************************************************
* For a particular kind of exception, try both kinds of throw against all
* three catch sequences
*****************************************************************************/
template <typename EXC>
void catch_both_several(const std::string& what)
{
std::cout << std::string(20, '*') << "plain_throw<" << what << ">" << std::endl;
catch_several(plain_throw<EXC>, "plain_throw<" + what + ">");
std::cout << std::string(20, '*') << "boost_throw<" << what << ">" << std::endl;
catch_several(boost_throw<EXC>, "boost_throw<" + what + ">");
}
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llexception_data
{
};
typedef test_group<llexception_data> llexception_group;
typedef llexception_group::object object;
llexception_group llexceptiongrp("llexception");
template<> template<>
void object::test<1>()
{
set_test_name("throwing exceptions");
// For each kind of exception, try both kinds of throw against all
// three catch sequences
std::size_t margin = 72;
std::cout << center("FromStd", '=', margin) << std::endl;
catch_both_several<FromStd>("FromStd");
std::cout << center("FromBoth", '=', margin) << std::endl;
catch_both_several<FromBoth>("FromBoth");
std::cout << center("FromBoost", '=', margin) << std::endl;
// can't throw with BOOST_THROW_EXCEPTION(), just use catch_several()
catch_several(plain_throw<FromBoost>, "plain_throw<FromBoost>");
std::cout << center("FromNeither", '=', margin) << std::endl;
// can't throw this with BOOST_THROW_EXCEPTION() either
catch_several(plain_throw<FromNeither>, "plain_throw<FromNeither>");
std::cout << center("const char*", '=', margin) << std::endl;
// We don't expect BOOST_THROW_EXCEPTION() to throw anything so daft
// as a const char* or an int, so don't bother with
// catch_both_several() -- just catch_several().
catch_several(throw_char_ptr, "throw_char_ptr");
std::cout << center("int", '=', margin) << std::endl;
catch_several(throw_int, "throw_int");
}
template<> template<>
void object::test<2>()
{
set_test_name("reporting exceptions");
try
{
LLTHROW(LLException("badness"));
}
catch (...)
{
LOG_UNHANDLED_EXCEPTION("llexception test<2>()");
}
}
} // namespace tut
+121
View File
@@ -0,0 +1,121 @@
/**
* @file lltiming_test.cpp
* @date 2006-07-23
* @brief Tests the timers.
*
* $LicenseInfo:firstyear=2006&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 "../llframetimer.h"
#include "../llsd.h"
#include "../test/lltut.h"
namespace tut
{
struct frametimer_test
{
frametimer_test()
{
LLFrameTimer::updateFrameTime();
}
};
typedef test_group<frametimer_test> frametimer_group_t;
typedef frametimer_group_t::object frametimer_object_t;
tut::frametimer_group_t frametimer_instance("LLFrameTimer");
template<> template<>
void frametimer_object_t::test<1>()
{
F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds();
LLFrameTimer timer;
timer.setExpiryAt(seconds_since_epoch);
F64 expires_at = timer.expiresAt();
ensure_distance(
"set expiry matches get expiry",
expires_at,
seconds_since_epoch,
0.001);
}
template<> template<>
void frametimer_object_t::test<2>()
{
F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds();
seconds_since_epoch += 10.0;
LLFrameTimer timer;
timer.setExpiryAt(seconds_since_epoch);
F64 expires_at = timer.expiresAt();
ensure_distance(
"set expiry matches get expiry 1",
expires_at,
seconds_since_epoch,
0.001);
seconds_since_epoch += 10.0;
timer.setExpiryAt(seconds_since_epoch);
expires_at = timer.expiresAt();
ensure_distance(
"set expiry matches get expiry 2",
expires_at,
seconds_since_epoch,
0.001);
}
template<> template<>
void frametimer_object_t::test<3>()
{
clock_t t1 = clock();
ms_sleep(200);
clock_t t2 = clock();
clock_t elapsed = t2 - t1 + 1;
std::cout << "Note: using clock(), ms_sleep() actually took " << (long)elapsed << "ms" << std::endl;
F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds();
seconds_since_epoch += 2.0;
LLFrameTimer timer;
timer.setExpiryAt(seconds_since_epoch);
/*
* Note that the ms_sleep(200) below is only guaranteed to return
* in 200ms _or_more_, so it should be true that by the 10th
* iteration we've gotten to the 2 seconds requested above
* and the timer should expire, but it can expire in fewer iterations
* if one or more of the ms_sleep calls takes longer.
* (as it did when we moved to Mac OS X 10.10)
*/
int iterations_until_expiration = 0;
while ( !timer.hasExpired() )
{
ms_sleep(200);
LLFrameTimer::updateFrameTime();
iterations_until_expiration++;
}
ensure("timer took too long to expire", iterations_until_expiration <= 10);
}
/*
template<> template<>
void frametimer_object_t::test<4>()
{
}
*/
}
+163
View File
@@ -0,0 +1,163 @@
/**
* @file llheteromap_test.cpp
* @author Nat Goodspeed
* @date 2016-10-12
* @brief Test for llheteromap.
*
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
* Copyright (c) 2016, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llheteromap.h"
// STL headers
#include <set>
// std headers
// external library headers
// (pacify clang)
std::ostream& operator<<(std::ostream& out, const std::set<std::string>& strset);
// other Linden headers
#include "../test/lltut.h"
static std::string clog;
static std::set<std::string> dlog;
// want to be able to use ensure_equals() on a set<string>
std::ostream& operator<<(std::ostream& out, const std::set<std::string>& strset)
{
out << '{';
const char* delim = "";
for (std::set<std::string>::const_iterator si(strset.begin()), se(strset.end());
si != se; ++si)
{
out << delim << '"' << *si << '"';
delim = ", ";
}
out << '}';
return out;
}
// unrelated test classes
struct Chalk
{
int dummy;
std::string name;
Chalk():
dummy(0)
{
clog.append("a");
}
~Chalk()
{
dlog.insert("a");
}
private:
Chalk(const Chalk&); // no implementation
};
struct Cheese
{
std::string name;
Cheese()
{
clog.append("e");
}
~Cheese()
{
dlog.insert("e");
}
private:
Cheese(const Cheese&); // no implementation
};
struct Chowdah
{
char displace[17];
std::string name;
Chowdah()
{
displace[0] = '\0';
clog.append("o");
}
~Chowdah()
{
dlog.insert("o");
}
private:
Chowdah(const Chowdah&); // no implementation
};
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llheteromap_data
{
llheteromap_data()
{
clog.erase();
dlog.clear();
}
};
typedef test_group<llheteromap_data> llheteromap_group;
typedef llheteromap_group::object object;
llheteromap_group llheteromapgrp("llheteromap");
template<> template<>
void object::test<1>()
{
set_test_name("create, get, delete");
{
LLHeteroMap map;
{
// create each instance
Chalk& chalk = map.obtain<Chalk>();
chalk.name = "Chalk";
Cheese& cheese = map.obtain<Cheese>();
cheese.name = "Cheese";
Chowdah& chowdah = map.obtain<Chowdah>();
chowdah.name = "Chowdah";
} // refs go out of scope
{
// verify each instance
Chalk& chalk = map.obtain<Chalk>();
ensure_equals(chalk.name, "Chalk");
Cheese& cheese = map.obtain<Cheese>();
ensure_equals(cheese.name, "Cheese");
Chowdah& chowdah = map.obtain<Chowdah>();
ensure_equals(chowdah.name, "Chowdah");
}
} // destroy map
// Chalk, Cheese and Chowdah should have been created in specific order
ensure_equals(clog, "aeo");
// We don't care what order they're destroyed in, as long as each is
// appropriately destroyed.
std::set<std::string> dtorset;
for (const char* cp = "aeo"; *cp; ++cp)
dtorset.insert(std::string(1, *cp));
ensure_equals(dlog, dtorset);
}
} // namespace tut
@@ -0,0 +1,272 @@
/**
* @file llinstancetracker_test.cpp
* @author Nat Goodspeed
* @date 2009-11-10
* @brief Test for llinstancetracker.
*
* $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 "llinstancetracker.h"
// STL headers
#include <string>
#include <vector>
#include <set>
#include <algorithm> // std::sort()
#include <stdexcept>
// std headers
// other Linden headers
#include "../test/lltut.h"
struct Badness: public std::runtime_error
{
Badness(const std::string& what): std::runtime_error(what) {}
};
struct Keyed: public LLInstanceTracker<Keyed, std::string>
{
Keyed(const std::string& name):
LLInstanceTracker<Keyed, std::string>(name),
mName(name)
{}
std::string mName;
};
struct Unkeyed: public LLInstanceTracker<Unkeyed>
{
Unkeyed(const std::string& thrw="")
{
// LLInstanceTracker should respond appropriately if a subclass
// constructor throws an exception. Specifically, it should run
// LLInstanceTracker's destructor and remove itself from the
// underlying container.
if (! thrw.empty())
{
throw Badness(thrw);
}
}
};
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llinstancetracker_data
{
};
typedef test_group<llinstancetracker_data> llinstancetracker_group;
typedef llinstancetracker_group::object object;
llinstancetracker_group llinstancetrackergrp("llinstancetracker");
template<> template<>
void object::test<1>()
{
ensure_equals(Keyed::instanceCount(), 0);
{
Keyed one("one");
ensure_equals(Keyed::instanceCount(), 1);
auto found = Keyed::getInstance("one");
ensure("couldn't find stack Keyed", bool(found));
ensure_equals("found wrong Keyed instance", found.get(), &one);
{
std::unique_ptr<Keyed> two(new Keyed("two"));
ensure_equals(Keyed::instanceCount(), 2);
auto found = Keyed::getInstance("two");
ensure("couldn't find heap Keyed", bool(found));
ensure_equals("found wrong Keyed instance", found.get(), two.get());
}
ensure_equals(Keyed::instanceCount(), 1);
}
auto found = Keyed::getInstance("one");
ensure("Keyed key lives too long", ! found);
ensure_equals(Keyed::instanceCount(), 0);
}
template<> template<>
void object::test<2>()
{
ensure_equals(Unkeyed::instanceCount(), 0);
std::weak_ptr<Unkeyed> dangling;
{
Unkeyed one;
ensure_equals(Unkeyed::instanceCount(), 1);
std::weak_ptr<Unkeyed> found = one.getWeak();
ensure(! found.expired());
{
std::unique_ptr<Unkeyed> two(new Unkeyed);
ensure_equals(Unkeyed::instanceCount(), 2);
}
ensure_equals(Unkeyed::instanceCount(), 1);
// store a weak pointer to a temp Unkeyed instance
dangling = found;
} // make that instance vanish
// check the now-invalid pointer to the destroyed instance
ensure("weak_ptr<Unkeyed> failed to track destruction", dangling.expired());
ensure_equals(Unkeyed::instanceCount(), 0);
}
template<> template<>
void object::test<3>()
{
Keyed one("one"), two("two"), three("three");
// We don't want to rely on the underlying container delivering keys
// in any particular order. That allows us the flexibility to
// reimplement LLInstanceTracker using, say, a hash map instead of a
// std::map. We DO insist that every key appear exactly once.
typedef std::vector<std::string> StringVector;
auto snap = Keyed::key_snapshot();
StringVector keys(snap.begin(), snap.end());
std::sort(keys.begin(), keys.end());
StringVector::const_iterator ki(keys.begin());
ensure_equals(*ki++, "one");
ensure_equals(*ki++, "three");
ensure_equals(*ki++, "two");
// Use ensure() here because ensure_equals would want to display
// mismatched values, and frankly that wouldn't help much.
ensure("didn't reach end", ki == keys.end());
// Use a somewhat different approach to order independence with
// instance_snapshot(): explicitly capture the instances we know in a
// set, and delete them as we iterate through.
typedef std::set<Keyed*> InstanceSet;
InstanceSet instances;
instances.insert(&one);
instances.insert(&two);
instances.insert(&three);
for (auto& ref : Keyed::instance_snapshot())
{
ensure_equals("spurious instance", instances.erase(&ref), 1);
}
ensure_equals("unreported instance", instances.size(), 0);
}
template<> template<>
void object::test<4>()
{
Unkeyed one, two, three;
typedef std::set<Unkeyed*> KeySet;
KeySet instances;
instances.insert(&one);
instances.insert(&two);
instances.insert(&three);
for (auto& ref : Unkeyed::instance_snapshot())
{
ensure_equals("spurious instance", instances.erase(&ref), 1);
}
ensure_equals("unreported instance", instances.size(), 0);
}
template<> template<>
void object::test<5>()
{
std::string desc("delete Keyed with outstanding instance_snapshot");
set_test_name(desc);
Keyed* keyed = new Keyed(desc);
// capture a snapshot but do not yet traverse it
auto snapshot = Keyed::instance_snapshot();
// delete the one instance
delete keyed;
// traversing the snapshot should reflect the deletion
// avoid ensure_equals() because it requires the ability to stream the
// two values to std::ostream
ensure(snapshot.begin() == snapshot.end());
}
template<> template<>
void object::test<6>()
{
std::string desc("delete Keyed with outstanding key_snapshot");
set_test_name(desc);
Keyed* keyed = new Keyed(desc);
// capture a snapshot but do not yet traverse it
auto snapshot = Keyed::key_snapshot();
// delete the one instance
delete keyed;
// traversing the snapshot should reflect the deletion
// avoid ensure_equals() because it requires the ability to stream the
// two values to std::ostream
ensure(snapshot.begin() == snapshot.end());
}
template<> template<>
void object::test<7>()
{
set_test_name("delete Unkeyed with outstanding instance_snapshot");
std::string what;
Unkeyed* unkeyed = new Unkeyed;
// capture a snapshot but do not yet traverse it
auto snapshot = Unkeyed::instance_snapshot();
// delete the one instance
delete unkeyed;
// traversing the snapshot should reflect the deletion
// avoid ensure_equals() because it requires the ability to stream the
// two values to std::ostream
ensure(snapshot.begin() == snapshot.end());
}
template<> template<>
void object::test<8>()
{
set_test_name("exception in subclass ctor");
typedef std::set<Unkeyed*> InstanceSet;
InstanceSet existing;
// We can't use the iterator-range InstanceSet constructor because
// beginInstances() returns an iterator that dereferences to an
// Unkeyed&, not an Unkeyed*.
for (auto& ref : Unkeyed::instance_snapshot())
{
existing.insert(&ref);
}
try
{
// We don't expect the assignment to take place because we expect
// Unkeyed to respond to the non-empty string param by throwing.
// We know the LLInstanceTracker base-class constructor will have
// run before Unkeyed's constructor, therefore the new instance
// will have added itself to the underlying set. The whole
// question is, when Unkeyed's constructor throws, will
// LLInstanceTracker's destructor remove it from the set? I
// realize we're testing the C++ implementation more than
// Unkeyed's implementation, but this seems an important point to
// nail down.
new Unkeyed("throw");
}
catch (const Badness&)
{
}
// Ensure that every member of the new, updated set of Unkeyed
// instances was also present in the original set. If that's not true,
// it's because our new Unkeyed ended up in the updated set despite
// its constructor exception.
for (auto& ref : Unkeyed::instance_snapshot())
{
ensure("failed to remove instance", existing.find(&ref) != existing.end());
}
}
} // namespace tut
+239
View File
@@ -0,0 +1,239 @@
/**
* @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
+660
View File
@@ -0,0 +1,660 @@
/**
* @file llleap_test.cpp
* @author Nat Goodspeed
* @date 2012-02-21
* @brief Test for llleap.
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Copyright (c) 2012, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llleap.h"
// STL headers
// std headers
#include <functional>
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
#include "../test/catch_and_store_what_in.h"
#include "wrapllerrs.h" // CaptureLog
#include "llevents.h"
#include "llprocess.h"
#include "llstring.h"
#include "stringize.h"
#include "StringVec.h"
#if defined(LL_WINDOWS)
#define sleep(secs) _sleep((secs) * 1000)
// WOLF-300: It appears that driving a megabyte of data through an LLLeap pipe
// causes Windows abdominal pain such that it later fails code-signing in some
// mysterious way. Entirely suppressing these LLLeap tests pushes the failure
// rate MUCH lower. Can we re-enable them with a smaller data size on Windows?
const size_t BUFFERED_LENGTH = 100*1024;
#else // not Windows
const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data
#endif
// capture std::weak_ptrs to LLLeap instances so we can tell when they expire
typedef std::vector<std::weak_ptr<LLLeap>> LLLeapVector;
void waitfor(const LLLeapVector& instances, int timeout=60)
{
int i;
for (i = 0; i < timeout; ++i)
{
// Every iteration, test whether any of the passed LLLeap instances
// still exist (are still running).
bool found = false;
for (auto& ptr : instances)
{
if (! ptr.expired())
{
found = true;
break;
}
}
// If we made it through all of 'instances' without finding one that's
// still running, we're done.
if (! found)
{
/*==========================================================================*|
std::cout << instances.size() << " LLLeap instances terminated in "
<< i << " seconds, proceeding" << std::endl;
|*==========================================================================*/
return;
}
// Found an instance that's still running. Wait and pump LLProcess.
sleep(1);
LLEventPumps::instance().obtain("mainloop").post(LLSD());
}
tut::ensure(STRINGIZE("at least 1 of " << instances.size()
<< " LLLeap instances timed out ("
<< timeout << " seconds) without terminating"),
i < timeout);
}
void waitfor(LLLeap* instance, int timeout=60)
{
LLLeapVector instances;
instances.push_back(instance->getWeak());
waitfor(instances, timeout);
}
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llleap_data
{
llleap_data():
reader(".py",
// This logic is adapted from vita.viewerclient.receiveEvent()
[](std::ostream& out){ out <<
"import re\n"
"import os\n"
"import sys\n"
"\n"
"import llsd\n"
"\n"
"class ProtocolError(Exception):\n"
" def __init__(self, msg, data):\n"
" Exception.__init__(self, msg)\n"
" self.data = data\n"
"\n"
"class ParseError(ProtocolError):\n"
" pass\n"
"\n"
"def get():\n"
" hdr = []\n"
" while b':' not in hdr and len(hdr) < 20:\n"
" hdr.append(sys.stdin.buffer.read(1))\n"
" if not hdr:\n"
" sys.exit(0)\n"
" if not hdr[-1] == b':':\n"
" raise ProtocolError('Expected len:data, got %r' % hdr, hdr)\n"
" try:\n"
" length = int(b''.join(hdr[:-1]))\n"
" except ValueError:\n"
" raise ProtocolError('Non-numeric len %r' % hdr[:-1], hdr[:-1])\n"
" parts = []\n"
" received = 0\n"
" while received < length:\n"
" parts.append(sys.stdin.buffer.read(length - received))\n"
" received += len(parts[-1])\n"
" data = b''.join(parts)\n"
" assert len(data) == length\n"
" try:\n"
" return llsd.parse(data)\n"
// Seems the old indra.base.llsd module didn't properly
// convert IndexError (from running off end of string) to
// LLSDParseError.
" except (IndexError, llsd.LLSDParseError) as e:\n"
" msg = 'Bad received packet (%s)' % e\n"
" print('%s, %s bytes:' % (msg, len(data)), file=sys.stderr)\n"
" showmax = 40\n"
// We've observed failures with very large packets;
// dumping the entire packet wastes time and space.
// But if the error states a particular byte offset,
// truncate to (near) that offset when dumping data.
" location = re.search(r' at (byte|index) ([0-9]+)', str(e))\n"
" if not location:\n"
" # didn't find offset, dump whole thing, no ellipsis\n"
" ellipsis = ''\n"
" else:\n"
" # found offset within error message\n"
" trunc = int(location.group(2)) + showmax\n"
" data = data[:trunc]\n"
" ellipsis = '... (%s more)' % (length - trunc)\n"
" offset = -showmax\n"
" for offset in range(0, len(data)-showmax, showmax):\n"
" print('%04d: %r +' % \\\n"
" (offset, data[offset:offset+showmax]), file=sys.stderr)\n"
" offset += showmax\n"
" print('%04d: %r%s' % \\\n"
" (offset, data[offset:], ellipsis), file=sys.stderr)\n"
" raise ParseError(msg, data)\n"
"\n"
"# deal with initial stdin message\n"
// this will throw if the initial write to stdin doesn't
// follow len:data protocol, or if we couldn't find 'pump'
// in the dict
"_reply = get()['pump']\n"
"\n"
"def replypump():\n"
" return _reply\n"
"\n"
"def put(req):\n"
" sys.stdout.buffer.write(b'%d:%b' % (len(req), req))\n"
" sys.stdout.flush()\n"
"\n"
"def send(pump, data):\n"
" put(llsd.format_notation(dict(pump=pump, data=data)))\n"
"\n"
"def request(pump, data):\n"
" # we expect 'data' is a dict\n"
" data['reply'] = _reply\n"
" send(pump, data)\n";}),
// Get the actual pathname of the NamedExtTempFile and trim off
// the ".py" extension. (We could cache reader.getName() in a
// separate member variable, but I happen to know getName() just
// returns a NamedExtTempFile member rather than performing any
// computation, so I don't mind calling it twice.) Then take the
// basename.
reader_module(LLProcess::basename(
reader.getName().substr(0, reader.getName().length()-3))),
PYTHON(LLStringUtil::getenv("PYTHON"))
{
ensure("Set PYTHON to interpreter pathname", !PYTHON.empty());
}
NamedExtTempFile reader;
const std::string reader_module;
const std::string PYTHON;
};
typedef test_group<llleap_data> llleap_group;
typedef llleap_group::object object;
llleap_group llleapgrp("llleap");
template<> template<>
void object::test<1>()
{
set_test_name("multiple LLLeap instances");
NamedExtTempFile script("py",
"import time\n"
"time.sleep(1)\n");
LLLeapVector instances;
instances.push_back(LLLeap::create(get_test_name(),
StringVec{PYTHON, script.getName()})->getWeak());
instances.push_back(LLLeap::create(get_test_name(),
StringVec{PYTHON, script.getName()})->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.
waitfor(instances);
}
template<> template<>
void object::test<2>()
{
set_test_name("stderr to log");
NamedExtTempFile script("py",
"import sys\n"
"sys.stderr.write('''Hello from Python!\n"
"note partial line''')\n");
StringVec vcommand{ PYTHON, script.getName() };
CaptureLog log(LLError::LEVEL_INFO);
waitfor(LLLeap::create(get_test_name(), vcommand));
log.messageWith("Hello from Python!");
log.messageWith("note partial line");
}
template<> template<>
void object::test<3>()
{
set_test_name("bad stdout protocol");
NamedExtTempFile script("py",
"print('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "Hello from Python!");
}
template<> template<>
void object::test<4>()
{
set_test_name("leftover stdout");
NamedExtTempFile script("py",
"import sys\n"
// note lack of newline
"sys.stdout.write('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("Discarding"), "Hello from Python!");
}
template<> template<>
void object::test<5>()
{
set_test_name("bad stdout len prefix");
NamedExtTempFile script("py",
"import sys\n"
"sys.stdout.write('5a2:something')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "5a2:");
}
template<> template<>
void object::test<6>()
{
set_test_name("empty plugin vector");
std::string threw = catch_what<LLLeap::Error>([](){
LLLeap::create("empty", StringVec());
});
ensure_contains("LLLeap::Error", threw, "no plugin");
// try the suppress-exception variant
ensure("bad launch returned non-NULL", ! LLLeap::create("empty", StringVec(), false));
}
template<> template<>
void object::test<7>()
{
set_test_name("bad launch");
// Synthesize bogus executable name
std::string BADPYTHON(PYTHON.substr(0, PYTHON.length()-1) + "x");
CaptureLog log;
std::string threw = catch_what<LLLeap::Error>([&BADPYTHON](){
LLLeap::create("bad exe", BADPYTHON);
});
ensure_contains("LLLeap::create() didn't throw", threw, "failed");
log.messageWith("failed");
log.messageWith(BADPYTHON);
// try the suppress-exception variant
ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false));
}
// Generic self-contained listener: derive from this and override its
// call() method, then tell somebody to post on the pump named getName().
// Control will reach your call() override.
struct ListenerBase
{
// Pass the pump name you want; will tweak for uniqueness.
ListenerBase(const std::string& name):
mPump(name, true)
{
mPump.listen(name, boost::bind(&ListenerBase::call, this, _1));
}
virtual ~ListenerBase() {} // pacify MSVC
virtual bool call(const LLSD& request)
{
return false;
}
LLEventPump& getPump() { return mPump; }
const LLEventPump& getPump() const { return mPump; }
std::string getName() const { return mPump.getName(); }
void post(const LLSD& data) { mPump.post(data); }
LLEventStream mPump;
};
// Mimic a dummy little LLEventAPI that merely sends a reply back to its
// requester on the "reply" pump.
struct AckAPI: public ListenerBase
{
AckAPI(): ListenerBase("AckAPI") {}
virtual bool call(const LLSD& request)
{
LLEventPumps::instance().obtain(request["reply"]).post("ack");
return false;
}
};
// Give LLLeap script a way to post success/failure.
struct Result: public ListenerBase
{
Result(): ListenerBase("Result") {}
virtual bool call(const LLSD& request)
{
mData = request;
return false;
}
void ensure() const
{
tut::ensure(std::string("never posted to ") + getName(), mData.isDefined());
// Post an empty string for success, non-empty string is failure message.
tut::ensure(mData, mData.asString().empty());
}
LLSD mData;
};
template<> template<>
void object::test<8>()
{
set_test_name("round trip");
AckAPI api;
Result result;
NamedExtTempFile script("py",
[&](std::ostream& out){ out <<
"from " << reader_module << " import *\n"
// make a request on our little API
"request(pump='" << api.getName() << "', data={})\n"
// wait for its response
"resp = get()\n"
"result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
" else 'bad: ' + str(resp)\n"
"send(pump='" << result.getName() << "', data=result)\n";});
waitfor(LLLeap::create(get_test_name(),
StringVec{PYTHON, script.getName()}));
result.ensure();
}
struct ReqIDAPI: public ListenerBase
{
ReqIDAPI(): ListenerBase("ReqIDAPI") {}
virtual bool call(const LLSD& request)
{
// free function from llevents.h
sendReply(LLSD(), request);
return false;
}
};
template<> template<>
void object::test<9>()
{
set_test_name("many small messages");
// It's not clear to me whether there's value in iterating many times
// over a send/receive loop -- I don't think that will exercise any
// interesting corner cases. This test first sends a large number of
// messages, then receives all the responses. The intent is to ensure
// that some of that data stream crosses buffer boundaries, loop
// iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
ReqIDAPI api;
Result result;
NamedExtTempFile script("py",
[&](std::ostream& out){ out <<
"import sys\n"
"from " << reader_module << " import *\n"
// Note that since reader imports llsd, this
// 'import *' gets us llsd too.
"sample = llsd.format_notation(dict(pump='" <<
api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
// The whole packet has length prefix too: "len:data"
"samplen = len(str(len(sample))) + 1 + len(sample)\n"
// guess how many messages it will take to
// accumulate BUFFERED_LENGTH
"count = int(" << BUFFERED_LENGTH << "/samplen)\n"
"print('Sending %s requests' % count, file=sys.stderr)\n"
"for i in range(count):\n"
" request('" << api.getName() << "', dict(reqid=i))\n"
// The assumption in this specific test that
// replies will arrive in the same order as
// requests is ONLY valid because the API we're
// invoking sends replies instantly. If the API
// had to wait for some external event before
// sending its reply, replies could arrive in
// arbitrary order, and we'd have to tick them
// off from a set.
"result = ''\n"
"for i in range(count):\n"
" resp = get()\n"
" if resp['data']['reqid'] != i:\n"
" result = 'expected reqid=%s in %s' % (i, resp)\n"
" break\n"
"send(pump='" << result.getName() << "', data=result)\n";});
waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}),
300); // needs more realtime than most tests
result.ensure();
}
// This is the body of test<10>, extracted so we can run it over a number
// of large-message sizes.
void test_large_message(const std::string& PYTHON, const std::string& reader_module,
const std::string& test_name, size_t size)
{
ReqIDAPI api;
Result result;
NamedExtTempFile script("py",
[&](std::ostream& out){ out <<
"import sys\n"
"from " << reader_module << " import *\n"
// Generate a very large string value.
"desired = int(sys.argv[1])\n"
// 7 chars per item: 6 digits, 1 comma
"count = int((desired - 50)/7)\n"
"large = ''.join('%06d,' % i for i in range(count))\n"
// Pass 'large' as reqid because we know the API
// will echo reqid, and we want to receive it back.
"request('" << api.getName() << "', dict(reqid=large))\n"
"try:\n"
" resp = get()\n"
"except ParseError as e:\n"
" # try to find where e.data diverges from expectation\n"
// Normally we'd expect a 'pump' key in there,
// too, with value replypump(). But Python
// serializes keys in a different order than C++,
// so incoming data start with 'data'.
// Truthfully, though, if we get as far as 'pump'
// before we find a difference, something's very
// strange.
" expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
" chunk = 40\n"
" for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
" if e.data[offset:offset+chunk] != \\\n"
" expect[offset:offset+chunk]:\n"
" print('Offset %06d: expect %r,\\n'\\\n"
" ' get %r' %\\\n"
" (offset,\n"
" expect[offset:offset+chunk],\n"
" e.data[offset:offset+chunk]),\n"
" file=sys.stderr)\n"
" break\n"
" else:\n"
" print('incoming data matches expect?!', file=sys.stderr)\n"
" send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
" sys.exit(1)\n"
"\n"
"echoed = resp['data']['reqid']\n"
"if echoed == large:\n"
" send('" << result.getName() << "', '')\n"
" sys.exit(0)\n"
// Here we know echoed did NOT match; try to find where
"for i in range(count):\n"
" start = 7*i\n"
" end = 7*(i+1)\n"
" if end > len(echoed)\\\n"
" or echoed[start:end] != large[start:end]:\n"
" send('" << result.getName() << "',\n"
" 'at offset %s, expected %r but got %r' %\n"
" (start, large[start:end], echoed[start:end]))\n"
"sys.exit(1)\n";});
waitfor(LLLeap::create(test_name,
StringVec{PYTHON, script.getName(), stringize(size)}),
180); // try a longer timeout
result.ensure();
}
struct TestLargeMessage
{
TestLargeMessage(const std::string& PYTHON_, const std::string& reader_module_,
const std::string& test_name_):
PYTHON(PYTHON_),
reader_module(reader_module_),
test_name(test_name_)
{}
bool operator()(size_t left, size_t right) const
{
// We don't know whether upper_bound is going to pass the "sought
// value" as the left or the right operand. We pass 0 as the
// "sought value" so we can distinguish it. Of course that means
// the sequence we're searching must not itself contain 0!
size_t size;
bool success;
if (left)
{
size = left;
// Consider our return value carefully. Normal binary_search
// (or, in our case, upper_bound) expects a container sorted
// in ascending order, and defaults to the std::less
// comparator. Our container is in fact in ascending order, so
// return consistently with std::less. Here we were called as
// compare(item, sought). If std::less were called that way,
// 'true' would mean to move right (to higher numbers) within
// the sequence: the item being considered is less than the
// sought value. For us, that means that test_large_message()
// success should return 'true'.
success = true;
}
else
{
size = right;
// Here we were called as compare(sought, item). If std::less
// were called that way, 'true' would mean to move left (to
// lower numbers) within the sequence: the sought value is
// less than the item being considered. For us, that means
// test_large_message() FAILURE should return 'true', hence
// test_large_message() success should return 'false'.
success = false;
}
try
{
test_large_message(PYTHON, reader_module, test_name, size);
std::cout << "test_large_message(" << size << ") succeeded" << std::endl;
return success;
}
catch (const failure& e)
{
std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
return ! success;
}
}
const std::string PYTHON, reader_module, test_name;
};
// The point of this function is to try to find a size at which
// test_large_message() can succeed. We still want the overall test to
// fail; otherwise we won't get the coder's attention -- but if
// test_large_message() fails, try to find a plausible size at which it
// DOES work.
void test_or_split(const std::string& PYTHON, const std::string& reader_module,
const std::string& test_name, size_t size)
{
try
{
test_large_message(PYTHON, reader_module, test_name, size);
}
catch (const failure& e)
{
std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
// If it still fails below 4K, give up: subdividing any further is
// pointless.
if (size >= 4096)
{
try
{
// Recur with half the size
size_t smaller(size/2);
test_or_split(PYTHON, reader_module, test_name, smaller);
// Recursive call will throw if test_large_message()
// failed, therefore we only reach the line below if it
// succeeded.
std::cout << "but test_large_message(" << smaller << ") succeeded" << std::endl;
// Binary search for largest size that works. But since
// std::binary_search() only returns bool, actually use
// std::upper_bound(), consistent with our desire to find
// the LARGEST size that works. First generate a sorted
// container of all the sizes we intend to try, from
// 'smaller' (known to work) to 'size' (known to fail). We
// could whomp up magic iterators to do this dynamically,
// without actually instantiating a vector, but for a test
// program this will do. At least preallocate the vector.
// Per TestLargeMessage comments, it's important that this
// vector not contain 0.
std::vector<size_t> sizes;
sizes.reserve((size - smaller)/4096 + 1);
for (size_t sz(smaller), szend(size); sz < szend; sz += 4096)
sizes.push_back(sz);
// our comparator
TestLargeMessage tester(PYTHON, reader_module, test_name);
// Per TestLargeMessage comments, pass 0 as the sought value.
std::vector<size_t>::const_iterator found =
std::upper_bound(sizes.begin(), sizes.end(), 0, tester);
if (found != sizes.end() && found != sizes.begin())
{
std::cout << "test_large_message(" << *(found - 1)
<< ") is largest that succeeds" << std::endl;
}
else
{
std::cout << "cannot determine largest test_large_message(size) "
<< "that succeeds" << std::endl;
}
}
catch (const failure&)
{
// The recursive test_or_split() call above has already
// handled the exception. We don't want our caller to see
// innermost exception; propagate outermost (below).
}
}
// In any case, because we reached here through failure of
// our original test_large_message(size) call, ensure failure
// propagates.
throw e;
}
}
template<> template<>
void object::test<10>()
{
set_test_name("very large message");
test_or_split(PYTHON, reader_module, get_test_name(), BUFFERED_LENGTH);
}
} // namespace tut
@@ -0,0 +1,137 @@
/**
* @file llmainthreadtask_test.cpp
* @author Nat Goodspeed
* @date 2019-12-05
* @brief Test for llmainthreadtask.
*
* $LicenseInfo:firstyear=2019&license=viewerlgpl$
* Copyright (c) 2019, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llmainthreadtask.h"
// STL headers
// std headers
#include <atomic>
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "../test/sync.h"
#include "llthread.h" // on_main_thread()
#include "lleventtimer.h"
#include "lockstatic.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llmainthreadtask_data
{
// 5-second timeout
Sync mSync{F32Milliseconds(5000.0f)};
llmainthreadtask_data()
{
// we're not testing the result; this is just to cache the
// initial thread as the main thread.
on_main_thread();
}
};
typedef test_group<llmainthreadtask_data> llmainthreadtask_group;
typedef llmainthreadtask_group::object object;
llmainthreadtask_group llmainthreadtaskgrp("llmainthreadtask");
template<> template<>
void object::test<1>()
{
set_test_name("inline");
bool ran = false;
bool result = LLMainThreadTask::dispatch(
[&ran]()->bool{
ran = true;
return true;
});
ensure("didn't run lambda", ran);
ensure("didn't return result", result);
}
struct StaticData
{
std::mutex mMutex; // LockStatic looks for mMutex
bool ran{false};
};
typedef llthread::LockStatic<StaticData> LockStatic;
template<> template<>
void object::test<2>()
{
set_test_name("cross-thread");
skip("This test is prone to build-time hangs");
std::atomic_bool result(false);
// wrapping our thread lambda in a packaged_task will catch any
// exceptions it might throw and deliver them via future
std::packaged_task<void()> thread_work(
[this, &result](){
// unblock test<2>()'s yield_until(1)
mSync.set(1);
// dispatch work to main thread -- should block here
bool on_main(
LLMainThreadTask::dispatch(
[]()->bool{
// have to lock static mutex to set static data
LockStatic()->ran = true;
// indicate whether task was run on the main thread
return on_main_thread();
}));
// wait for test<2>() to unblock us again
mSync.yield_until(3);
result = on_main;
});
auto thread_result = thread_work.get_future();
std::thread thread;
try
{
// run thread_work
thread = std::thread(std::move(thread_work));
// wait for thread to set(1)
mSync.yield_until(1);
// try to acquire the lock, should block because thread has it
LockStatic lk;
// wake up when dispatch() unlocks the static mutex
ensure("shouldn't have run yet", !lk->ran);
ensure("shouldn't have returned yet", !result);
// unlock so the task can acquire the lock
lk.unlock();
// run the task -- should unblock thread, which will immediately block
// on mSync
LLEventTimer::updateClass();
// 'lk', having unlocked, can no longer be used to access; relock with
// a new LockStatic instance
ensure("should now have run", LockStatic()->ran);
ensure("returned too early", !result);
// okay, let thread perform the assignment
mSync.set(3);
}
catch (...)
{
// A test failure exception anywhere in the try block can cause
// the test program to terminate without explanation when
// ~thread() finds that 'thread' is still joinable. We could
// either join() or detach() it -- but since it might be blocked
// waiting for something from the main thread that now can never
// happen, it's safer to detach it.
thread.detach();
throw;
}
// 'thread' should be all done now
thread.join();
// deliver any exception thrown by thread_work
thread_result.get();
ensure("ran changed", LockStatic()->ran);
ensure("didn't run on main thread", result);
}
} // namespace tut
+117
View File
@@ -0,0 +1,117 @@
/**
* @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");
}
};
+230
View File
@@ -0,0 +1,230 @@
/**
* @file llpounceable_test.cpp
* @author Nat Goodspeed
* @date 2015-05-22
* @brief Test for llpounceable.
*
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
* Copyright (c) 2015, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "llpounceable.h"
// STL headers
// std headers
// external library headers
#include <boost/bind.hpp>
// other Linden headers
#include "../test/lltut.h"
/*----------------------------- string testing -----------------------------*/
void append(std::string* dest, const std::string& src)
{
dest->append(src);
}
/*-------------------------- Data-struct testing ---------------------------*/
struct Data
{
Data(const std::string& data):
mData(data)
{}
const std::string mData;
};
void setter(Data** dest, Data* ptr)
{
*dest = ptr;
}
static Data* static_check = 0;
// Set up an extern pointer to an LLPounceableStatic so the linker will fill
// in the forward reference from below, before runtime.
extern LLPounceable<Data*, LLPounceableStatic> gForward;
struct EnqueueCall
{
EnqueueCall()
{
// Intentionally use a forward reference to an LLPounceableStatic that
// we believe is NOT YET CONSTRUCTED. This models the scenario in
// which a constructor in another translation unit runs before
// constructors in this one. We very specifically want callWhenReady()
// to work even in that case: we need the LLPounceableQueueImpl to be
// initialized even if the LLPounceable itself is not.
gForward.callWhenReady(boost::bind(setter, &static_check, _1));
}
} nqcall;
// When this declaration is processed, we should enqueue the
// setter(&static_check, _1) call for when gForward is set non-NULL. Needless
// to remark, we want this call not to crash.
// Now declare gForward. Its constructor should not run until after nqcall's.
LLPounceable<Data*, LLPounceableStatic> gForward;
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct llpounceable_data
{
};
typedef test_group<llpounceable_data> llpounceable_group;
typedef llpounceable_group::object object;
llpounceable_group llpounceablegrp("llpounceable");
template<> template<>
void object::test<1>()
{
set_test_name("LLPounceableStatic out-of-order test");
// LLPounceable<T, LLPounceableStatic>::callWhenReady() must work even
// before LLPounceable's constructor runs. That's the whole point of
// implementing it with an LLSingleton queue. This models (say)
// LLPounceableStatic<LLMessageSystem*, LLPounceableStatic>.
ensure("static_check should still be null", ! static_check);
Data myData("test<1>");
gForward = &myData; // should run setter
ensure_equals("static_check should be &myData", static_check, &myData);
}
template<> template<>
void object::test<2>()
{
set_test_name("LLPounceableQueue different queues");
// We expect that LLPounceable<T, LLPounceableQueue> should have
// different queues because that specialization stores the queue
// directly in the LLPounceable instance.
Data *aptr = 0, *bptr = 0;
LLPounceable<Data*> a, b;
a.callWhenReady(boost::bind(setter, &aptr, _1));
b.callWhenReady(boost::bind(setter, &bptr, _1));
ensure("aptr should be null", ! aptr);
ensure("bptr should be null", ! bptr);
Data adata("a"), bdata("b");
a = &adata;
ensure_equals("aptr should be &adata", aptr, &adata);
// but we haven't yet set b
ensure("bptr should still be null", !bptr);
b = &bdata;
ensure_equals("bptr should be &bdata", bptr, &bdata);
}
template<> template<>
void object::test<3>()
{
set_test_name("LLPounceableStatic different queues");
// LLPounceable<T, LLPounceableStatic> should also have a distinct
// queue for each instance, but that engages an additional map lookup
// because there's only one LLSingleton for each T.
Data *aptr = 0, *bptr = 0;
LLPounceable<Data*, LLPounceableStatic> a, b;
a.callWhenReady(boost::bind(setter, &aptr, _1));
b.callWhenReady(boost::bind(setter, &bptr, _1));
ensure("aptr should be null", ! aptr);
ensure("bptr should be null", ! bptr);
Data adata("a"), bdata("b");
a = &adata;
ensure_equals("aptr should be &adata", aptr, &adata);
// but we haven't yet set b
ensure("bptr should still be null", !bptr);
b = &bdata;
ensure_equals("bptr should be &bdata", bptr, &bdata);
}
template<> template<>
void object::test<4>()
{
set_test_name("LLPounceable<T> looks like T");
// We want LLPounceable<T, TAG> to be drop-in replaceable for a plain
// T for read constructs. In particular, it should behave like a dumb
// pointer -- and with zero abstraction cost for such usage.
Data* aptr = 0;
Data a("a");
// should be able to initialize a pounceable (when its constructor
// runs)
LLPounceable<Data*> pounceable(&a);
// should be able to pass LLPounceable<T> to function accepting T
setter(&aptr, pounceable);
ensure_equals("aptr should be &a", aptr, &a);
// should be able to dereference with *
ensure_equals("deref with *", (*pounceable).mData, "a");
// should be able to dereference with ->
ensure_equals("deref with ->", pounceable->mData, "a");
// bool operations
ensure("test with operator bool()", pounceable);
ensure("test with operator !()", ! (! pounceable));
}
template<> template<>
void object::test<5>()
{
set_test_name("Multiple callWhenReady() queue items");
Data *p1 = 0, *p2 = 0, *p3 = 0;
Data a("a");
LLPounceable<Data*> pounceable;
// queue up a couple setter() calls for later
pounceable.callWhenReady(boost::bind(setter, &p1, _1));
pounceable.callWhenReady(boost::bind(setter, &p2, _1));
// should still be pending
ensure("p1 should be null", !p1);
ensure("p2 should be null", !p2);
ensure("p3 should be null", !p3);
pounceable = 0;
// assigning a new empty value shouldn't flush the queue
ensure("p1 should still be null", !p1);
ensure("p2 should still be null", !p2);
ensure("p3 should still be null", !p3);
// using whichever syntax
pounceable.reset(0);
// try to make ensure messages distinct... tough to pin down which
// ensure() failed if multiple ensure() calls in the same test<n> have
// the same message!
ensure("p1 should again be null", !p1);
ensure("p2 should again be null", !p2);
ensure("p3 should again be null", !p3);
pounceable.reset(&a); // should flush queue
ensure_equals("p1 should be &a", p1, &a);
ensure_equals("p2 should be &a", p2, &a);
ensure("p3 still not set", !p3);
// immediate call
pounceable.callWhenReady(boost::bind(setter, &p3, _1));
ensure_equals("p3 should be &a", p3, &a);
}
template<> template<>
void object::test<6>()
{
set_test_name("queue order");
std::string data;
LLPounceable<std::string*> pounceable;
pounceable.callWhenReady(boost::bind(append, _1, "a"));
pounceable.callWhenReady(boost::bind(append, _1, "b"));
pounceable.callWhenReady(boost::bind(append, _1, "c"));
pounceable = &data;
ensure_equals("callWhenReady() must preserve chronological order",
data, "abc");
std::string data2;
pounceable = NULL;
pounceable.callWhenReady(boost::bind(append, _1, "d"));
pounceable.callWhenReady(boost::bind(append, _1, "e"));
pounceable.callWhenReady(boost::bind(append, _1, "f"));
pounceable = &data2;
ensure_equals("LLPounceable must reset queue when fired",
data2, "def");
}
template<> template<>
void object::test<7>()
{
set_test_name("compile-fail test, uncomment to check");
// The following declaration should fail: only LLPounceableQueue and
// LLPounceableStatic should work as tags.
// LLPounceable<Data*, int> pounceable;
}
} // namespace tut
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
/**
* @file llprocessor_test.cpp
* @date 2010-06-01
*
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "../test/lltut.h"
#include "../llprocessor.h"
namespace tut
{
struct processor
{
};
typedef test_group<processor> processor_t;
typedef processor_t::object processor_object_t;
tut::processor_t tut_processor("LLProcessor");
template<> template<>
void processor_object_t::test<1>()
{
set_test_name("LLProcessorInfo regression test");
LLProcessorInfo pi;
F64 freq = pi.getCPUFrequency();
//bool sse = pi.hasSSE();
//bool sse2 = pi.hasSSE2();
//bool alitvec = pi.hasAltivec();
std::string family = pi.getCPUFamilyName();
std::string brand = pi.getCPUBrandName();
//std::string steam = pi.getCPUFeatureDescription();
ensure_not_equals("Unknown Brand name", brand, "Unknown");
ensure_not_equals("Unknown Family name", family, "Unknown");
ensure("Reasonable CPU Frequency > 100 && < 10000", freq > 100 && freq < 10000);
}
}
+91
View File
@@ -0,0 +1,91 @@
/**
* @file llprocinfo_test.cpp
* @brief Tests for the LLProcInfo class.
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "../llprocinfo.h"
#include "../test/lltut.h"
#include "../lltimer.h"
static const LLProcInfo::time_type bad_user(289375U), bad_system(275U);
namespace tut
{
struct procinfo_test
{
procinfo_test()
{
}
};
typedef test_group<procinfo_test> procinfo_group_t;
typedef procinfo_group_t::object procinfo_object_t;
tut::procinfo_group_t procinfo_instance("LLProcInfo");
// Basic invocation works
template<> template<>
void procinfo_object_t::test<1>()
{
LLProcInfo::time_type user(bad_user), system(bad_system);
set_test_name("getCPUUsage() basic function");
LLProcInfo::getCPUUsage(user, system);
ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user);
ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system);
}
// Time increases
template<> template<>
void procinfo_object_t::test<2>()
{
LLProcInfo::time_type user(bad_user), system(bad_system);
LLProcInfo::time_type user2(bad_user), system2(bad_system);
set_test_name("getCPUUsage() increases over time");
LLProcInfo::getCPUUsage(user, system);
for (int i(0); i < 100000; ++i)
{
ms_sleep(0);
}
LLProcInfo::getCPUUsage(user2, system2);
ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true);
ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true);
}
} // end namespace tut
+124
View File
@@ -0,0 +1,124 @@
/**
* @file llrandom_test.cpp
* @author Phoenix
* @date 2007-01-25
*
* $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 "../test/lltut.h"
#include "../llrand.h"
#include "stringize.h"
// In llrand.h, every function is documented to return less than the high end
// -- specifically, because you can pass a negative extent, they're documented
// never to return a value equal to the extent.
// So that we don't need two different versions of ensure_in_range(), when
// testing extent < 0, negate the return value and the extent before passing
// into ensure_in_range().
template <typename NUMBER>
void ensure_in_range(const std::string_view& name,
NUMBER value, NUMBER low, NUMBER high)
{
auto failmsg{ stringize(name, " >= ", low, " (", value, ')') };
tut::ensure(failmsg, (value >= low));
failmsg = stringize(name, " < ", high, " (", value, ')');
tut::ensure(failmsg, (value < high));
}
namespace tut
{
struct random
{
};
typedef test_group<random> random_t;
typedef random_t::object random_object_t;
tut::random_t tut_random("LLSeedRand");
template<> template<>
void random_object_t::test<1>()
{
for(S32 ii = 0; ii < 100000; ++ii)
{
ensure_in_range("frand", ll_frand(), 0.0f, 1.0f);
}
}
template<> template<>
void random_object_t::test<2>()
{
for(S32 ii = 0; ii < 100000; ++ii)
{
ensure_in_range("drand", ll_drand(), 0.0, 1.0);
}
}
template<> template<>
void random_object_t::test<3>()
{
for(S32 ii = 0; ii < 100000; ++ii)
{
ensure_in_range("frand(2.0f)", ll_frand(2.0f) - 1.0f, -1.0f, 1.0f);
}
}
template<> template<>
void random_object_t::test<4>()
{
for(S32 ii = 0; ii < 100000; ++ii)
{
// Negate the result so we don't have to allow a templated low-end
// comparison as well.
ensure_in_range("-frand(-7.0)", -ll_frand(-7.0), 0.0f, 7.0f);
}
}
template<> template<>
void random_object_t::test<5>()
{
for(S32 ii = 0; ii < 100000; ++ii)
{
ensure_in_range("-drand(-2.0)", -ll_drand(-2.0), 0.0, 2.0);
}
}
template<> template<>
void random_object_t::test<6>()
{
for(S32 ii = 0; ii < 100000; ++ii)
{
ensure_in_range("rand(100)", ll_rand(100), 0, 100);
}
}
template<> template<>
void random_object_t::test<7>()
{
for(S32 ii = 0; ii < 100000; ++ii)
{
ensure_in_range("-rand(-127)", -ll_rand(-127), 0, 127);
}
}
}
File diff suppressed because it is too large Load Diff
+324
View File
@@ -0,0 +1,324 @@
/**
* @file llsingleton_test.cpp
* @date 2011-08-11
* @brief Unit test for the LLSingleton class
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llsingleton.h"
#include "../test/lltut.h"
#include "wrapllerrs.h"
#include "llsd.h"
// Capture execution sequence by appending to log string.
std::string sLog;
#define DECLARE_CLASS(CLS) \
struct CLS: public LLSingleton<CLS> \
{ \
LLSINGLETON(CLS); \
~CLS(); \
public: \
static enum dep_flag { \
DEP_NONE, /* no dependency */ \
DEP_CTOR, /* dependency in ctor */ \
DEP_INIT /* dependency in initSingleton */ \
} sDepFlag; \
\
void initSingleton() override; \
void cleanupSingleton() override; \
}; \
\
CLS::dep_flag CLS::sDepFlag = DEP_NONE
DECLARE_CLASS(A);
DECLARE_CLASS(B);
#define DEFINE_MEMBERS(CLS, OTHER) \
CLS::CLS() \
{ \
sLog.append(#CLS); \
if (sDepFlag == DEP_CTOR) \
{ \
(void)OTHER::instance(); \
} \
} \
\
void CLS::initSingleton() \
{ \
sLog.append("i" #CLS); \
if (sDepFlag == DEP_INIT) \
{ \
(void)OTHER::instance(); \
} \
} \
\
void CLS::cleanupSingleton() \
{ \
sLog.append("x" #CLS); \
} \
\
CLS::~CLS() \
{ \
sLog.append("~" #CLS); \
}
DEFINE_MEMBERS(A, B)
DEFINE_MEMBERS(B, A)
namespace tut
{
struct singleton
{
// We need a class created with the LLSingleton template to test with.
class LLSingletonTest: public LLSingleton<LLSingletonTest>
{
LLSINGLETON_EMPTY_CTOR(LLSingletonTest);
};
};
typedef test_group<singleton> singleton_t;
typedef singleton_t::object singleton_object_t;
tut::singleton_t tut_singleton("LLSingleton");
template<> template<>
void singleton_object_t::test<1>()
{
}
template<> template<>
void singleton_object_t::test<2>()
{
LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
ensure(singleton_test);
}
template<> template<>
void singleton_object_t::test<3>()
{
//Construct the instance
LLSingletonTest::getInstance();
ensure(LLSingletonTest::instanceExists());
//Delete the instance
LLSingletonTest::deleteSingleton();
ensure(!LLSingletonTest::instanceExists());
//Construct it again.
LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
ensure(singleton_test);
ensure(LLSingletonTest::instanceExists());
}
#define TESTS(CLS, OTHER, N0, N1, N2, N3) \
template<> template<> \
void singleton_object_t::test<N0>() \
{ \
set_test_name("just " #CLS); \
CLS::sDepFlag = CLS::DEP_NONE; \
OTHER::sDepFlag = OTHER::DEP_NONE; \
sLog.clear(); \
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS "i" #CLS); \
LLSingletonBase::deleteAll(); \
ensure_equals(sLog, #CLS "i" #CLS "x" #CLS "~" #CLS); \
} \
\
template<> template<> \
void singleton_object_t::test<N1>() \
{ \
set_test_name(#CLS " ctor depends " #OTHER); \
CLS::sDepFlag = CLS::DEP_CTOR; \
OTHER::sDepFlag = OTHER::DEP_NONE; \
sLog.clear(); \
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS); \
LLSingletonBase::deleteAll(); \
ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \
} \
\
template<> template<> \
void singleton_object_t::test<N2>() \
{ \
set_test_name(#CLS " init depends " #OTHER); \
CLS::sDepFlag = CLS::DEP_INIT; \
OTHER::sDepFlag = OTHER::DEP_NONE; \
sLog.clear(); \
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \
LLSingletonBase::deleteAll(); \
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \
} \
\
template<> template<> \
void singleton_object_t::test<N3>() \
{ \
set_test_name(#CLS " circular init"); \
CLS::sDepFlag = CLS::DEP_INIT; \
OTHER::sDepFlag = OTHER::DEP_CTOR; \
sLog.clear(); \
\
(void)CLS::instance(); \
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \
LLSingletonBase::deleteAll(); \
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "~" #CLS "x" #OTHER "~" #OTHER); \
}
TESTS(A, B, 4, 5, 6, 7)
TESTS(B, A, 8, 9, 10, 11)
#define PARAMSINGLETON(cls) \
class cls: public LLParamSingleton<cls> \
{ \
LLSINGLETON(cls, const LLSD::String& str): mDesc(str) {} \
cls(LLSD::Integer i): mDesc(i) {} \
\
public: \
std::string desc() const { return mDesc.asString(); } \
\
private: \
LLSD mDesc; \
}
// Declare two otherwise-identical LLParamSingleton classes so we can
// validly initialize each using two different constructors. If we tried
// to test that with a single LLParamSingleton class within the same test
// program, we'd get 'trying to use deleted LLParamSingleton' errors.
PARAMSINGLETON(PSing1);
PARAMSINGLETON(PSing2);
template<> template<>
void singleton_object_t::test<12>()
{
set_test_name("LLParamSingleton");
WrapLLErrs catcherr;
// query methods
ensure("false positive on instanceExists()", ! PSing1::instanceExists());
ensure("false positive on wasDeleted()", ! PSing1::wasDeleted());
// try to reference before initializing
std::string threw = catcherr.catch_llerrs([](){
(void)PSing1::instance();
});
ensure_contains("too-early instance() didn't throw", threw, "Uninitialized");
// getInstance() behaves the same as instance()
threw = catcherr.catch_llerrs([](){
(void)PSing1::getInstance();
});
ensure_contains("too-early getInstance() didn't throw", threw, "Uninitialized");
// initialize using LLSD::String constructor
PSing1::initParamSingleton("string");
ensure_equals(PSing1::instance().desc(), "string");
ensure("false negative on instanceExists()", PSing1::instanceExists());
// try to initialize again
threw = catcherr.catch_llerrs([](){
PSing1::initParamSingleton("again");
});
ensure_contains("second ctor(string) didn't throw", threw, "twice");
// try to initialize using the other constructor -- should be
// well-formed, but illegal at runtime
threw = catcherr.catch_llerrs([](){
PSing1::initParamSingleton(17);
});
ensure_contains("other ctor(int) didn't throw", threw, "twice");
PSing1::deleteSingleton();
ensure("false negative on wasDeleted()", PSing1::wasDeleted());
threw = catcherr.catch_llerrs([](){
(void)PSing1::instance();
});
ensure_contains("accessed deleted LLParamSingleton", threw, "deleted");
}
template<> template<>
void singleton_object_t::test<13>()
{
set_test_name("LLParamSingleton alternate ctor");
WrapLLErrs catcherr;
// We don't have to restate all the tests for PSing1. Only test validly
// using the other constructor.
PSing2::initParamSingleton(17);
ensure_equals(PSing2::instance().desc(), "17");
// can't do it twice
std::string threw = catcherr.catch_llerrs([](){
PSing2::initParamSingleton(34);
});
ensure_contains("second ctor(int) didn't throw", threw, "twice");
// can't use the other constructor either
threw = catcherr.catch_llerrs([](){
PSing2::initParamSingleton("string");
});
ensure_contains("other ctor(string) didn't throw", threw, "twice");
}
class CircularPCtor: public LLParamSingleton<CircularPCtor>
{
LLSINGLETON(CircularPCtor)
{
// never mind indirection, just go straight for the circularity
(void)instance();
}
};
template<> template<>
void singleton_object_t::test<14>()
{
set_test_name("Circular LLParamSingleton constructor");
WrapLLErrs catcherr;
std::string threw = catcherr.catch_llerrs([](){
CircularPCtor::initParamSingleton();
});
ensure_contains("constructor circularity didn't throw", threw, "constructor");
}
class CircularPInit: public LLParamSingleton<CircularPInit>
{
LLSINGLETON_EMPTY_CTOR(CircularPInit);
public:
virtual void initSingleton() override
{
// never mind indirection, just go straight for the circularity
CircularPInit *pt = getInstance();
if (!pt)
{
throw;
}
}
};
template<> template<>
void singleton_object_t::test<15>()
{
set_test_name("Circular LLParamSingleton initSingleton()");
WrapLLErrs catcherr;
std::string threw = catcherr.catch_llerrs([](){
CircularPInit::initParamSingleton();
});
ensure("initSingleton() circularity threw", threw.empty());
}
}
+194
View File
@@ -0,0 +1,194 @@
/**
* @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
+871
View File
@@ -0,0 +1,871 @@
/**
* @file llstring_test.cpp
* @author Adroit, Steve Linden, Tofu Linden
* @date 2006-12-24
* @brief Test cases of llstring.cpp
*
* $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 <boost/assign/list_of.hpp>
#include "../llstring.h"
#include "StringVec.h" // must come BEFORE lltut.h
#include "../test/lltut.h"
using boost::assign::list_of;
namespace tut
{
struct string_index
{
};
typedef test_group<string_index> string_index_t;
typedef string_index_t::object string_index_object_t;
tut::string_index_t tut_string_index("LLString");
template<> template<>
void string_index_object_t::test<1>()
{
std::string llstr1;
ensure("Empty std::string", (llstr1.size() == 0) && llstr1.empty());
std::string llstr2("Hello");
ensure("std::string = Hello", (!strcmp(llstr2.c_str(), "Hello")) && (llstr2.size() == 5) && !llstr2.empty());
std::string llstr3(llstr2);
ensure("std::string = std::string(std::string)", (!strcmp(llstr3.c_str(), "Hello")) && (llstr3.size() == 5) && !llstr3.empty());
std::string str("Hello World");
std::string llstr4(str, 6);
ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (!strcmp(llstr4.c_str(), "World")) && (llstr4.size() == 5) && !llstr4.empty());
std::string llstr5(str, str.size());
ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (llstr5.size() == 0) && llstr5.empty());
std::string llstr6(5, 'A');
ensure("std::string = std::string(count, c)", (!strcmp(llstr6.c_str(), "AAAAA")) && (llstr6.size() == 5) && !llstr6.empty());
std::string llstr7("Hello World", 5);
ensure("std::string(s, n)", (!strcmp(llstr7.c_str(), "Hello")) && (llstr7.size() == 5) && !llstr7.empty());
std::string llstr8("Hello World", 6, 5);
ensure("std::string(s, n, count)", (!strcmp(llstr8.c_str(), "World")) && (llstr8.size() == 5) && !llstr8.empty());
std::string llstr9("Hello World", sizeof("Hello World")-1, 5); // go past end
ensure("std::string(s, n, count) goes past end", (llstr9.size() == 0) && llstr9.empty());
}
template<> template<>
void string_index_object_t::test<3>()
{
std::string str("Len=5");
ensure("isValidIndex failed", LLStringUtil::isValidIndex(str, 0) == true &&
LLStringUtil::isValidIndex(str, 5) == true &&
LLStringUtil::isValidIndex(str, 6) == false);
std::string str1;
ensure("isValidIndex failed fo rempty string", LLStringUtil::isValidIndex(str1, 0) == false);
}
template<> template<>
void string_index_object_t::test<4>()
{
std::string str_val(" Testing the extra whitespaces ");
LLStringUtil::trimHead(str_val);
ensure_equals("1: trimHead failed", str_val, "Testing the extra whitespaces ");
std::string str_val1("\n\t\r\n Testing the extra whitespaces ");
LLStringUtil::trimHead(str_val1);
ensure_equals("2: trimHead failed", str_val1, "Testing the extra whitespaces ");
}
template<> template<>
void string_index_object_t::test<5>()
{
std::string str_val(" Testing the extra whitespaces ");
LLStringUtil::trimTail(str_val);
ensure_equals("1: trimTail failed", str_val, " Testing the extra whitespaces");
std::string str_val1("\n Testing the extra whitespaces \n\t\r\n ");
LLStringUtil::trimTail(str_val1);
ensure_equals("2: trimTail failed", str_val1, "\n Testing the extra whitespaces");
}
template<> template<>
void string_index_object_t::test<6>()
{
std::string str_val(" \t \r Testing the extra \r\n whitespaces \n \t ");
LLStringUtil::trim(str_val);
ensure_equals("1: trim failed", str_val, "Testing the extra \r\n whitespaces");
}
template<> template<>
void string_index_object_t::test<7>()
{
std::string str("Second LindenLabs");
LLStringUtil::truncate(str, 6);
ensure_equals("1: truncate", str, "Second");
// further truncate more than the length
LLStringUtil::truncate(str, 0);
ensure_equals("2: truncate", str, "");
}
template<> template<>
void string_index_object_t::test<8>()
{
std::string str_val("SecondLife Source");
LLStringUtil::toUpper(str_val);
ensure_equals("toUpper failed", str_val, "SECONDLIFE SOURCE");
}
template<> template<>
void string_index_object_t::test<9>()
{
std::string str_val("SecondLife Source");
LLStringUtil::toLower(str_val);
ensure_equals("toLower failed", str_val, "secondlife source");
}
template<> template<>
void string_index_object_t::test<10>()
{
std::string str_val("Second");
ensure("1. isHead failed", LLStringUtil::isHead(str_val, "SecondLife Source") == true);
ensure("2. isHead failed", LLStringUtil::isHead(str_val, " SecondLife Source") == false);
std::string str_val2("");
ensure("3. isHead failed", LLStringUtil::isHead(str_val2, "") == false);
}
template<> template<>
void string_index_object_t::test<11>()
{
std::string str_val("Hello.\n\n Lindenlabs. \n This is \na simple test.\n");
std::string orig_str_val(str_val);
LLStringUtil::addCRLF(str_val);
ensure_equals("addCRLF failed", str_val, "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n");
LLStringUtil::removeCRLF(str_val);
ensure_equals("removeCRLF failed", str_val, orig_str_val);
}
template<> template<>
void string_index_object_t::test<12>()
{
std::string str_val("Hello.\n\n\t \t Lindenlabs. \t\t");
std::string orig_str_val(str_val);
LLStringUtil::replaceTabsWithSpaces(str_val, 1);
ensure_equals("replaceTabsWithSpaces failed", str_val, "Hello.\n\n Lindenlabs. ");
LLStringUtil::replaceTabsWithSpaces(orig_str_val, 0);
ensure_equals("replaceTabsWithSpaces failed for 0", orig_str_val, "Hello.\n\n Lindenlabs. ");
str_val = "\t\t\t\t";
LLStringUtil::replaceTabsWithSpaces(str_val, 0);
ensure_equals("replaceTabsWithSpaces failed for all tabs", str_val, "");
}
template<> template<>
void string_index_object_t::test<13>()
{
std::string str_val("Hello.\n\n\t\t\r\nLindenlabsX.");
LLStringUtil::replaceNonstandardASCII(str_val, 'X');
ensure_equals("replaceNonstandardASCII failed", str_val, "Hello.\n\nXXX\nLindenlabsX.");
}
template<> template<>
void string_index_object_t::test<14>()
{
std::string str_val("Hello.\n\t\r\nABCDEFGHIABABAB");
LLStringUtil::replaceChar(str_val, 'A', 'X');
ensure_equals("1: replaceChar failed", str_val, "Hello.\n\t\r\nXBCDEFGHIXBXBXB");
std::string str_val1("Hello.\n\t\r\nABCDEFGHIABABAB");
}
template<> template<>
void string_index_object_t::test<15>()
{
std::string str_val("Hello.\n\r\t");
ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == true);
str_val = "ABC ";
ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == false);
}
template<> template<>
void string_index_object_t::test<16>()
{
std::string str_val("Hello.\n\r\t Again!");
LLStringUtil::stripNonprintable(str_val);
ensure_equals("stripNonprintable failed", str_val, "Hello. Again!");
str_val = "\r\n\t\t";
LLStringUtil::stripNonprintable(str_val);
ensure_equals("stripNonprintable resulting in empty string failed", str_val, "");
str_val = "";
LLStringUtil::stripNonprintable(str_val);
ensure_equals("stripNonprintable of empty string resulting in empty string failed", str_val, "");
}
template<> template<>
void string_index_object_t::test<17>()
{
bool value;
std::string str_val("1");
ensure("convertToBOOL 1 failed", LLStringUtil::convertToBOOL(str_val, value) && value);
str_val = "T";
ensure("convertToBOOL T failed", LLStringUtil::convertToBOOL(str_val, value) && value);
str_val = "t";
ensure("convertToBOOL t failed", LLStringUtil::convertToBOOL(str_val, value) && value);
str_val = "TRUE";
ensure("convertToBOOL TRUE failed", LLStringUtil::convertToBOOL(str_val, value) && value);
str_val = "True";
ensure("convertToBOOL True failed", LLStringUtil::convertToBOOL(str_val, value) && value);
str_val = "true";
ensure("convertToBOOL true failed", LLStringUtil::convertToBOOL(str_val, value) && value);
str_val = "0";
ensure("convertToBOOL 0 failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
str_val = "F";
ensure("convertToBOOL F failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
str_val = "f";
ensure("convertToBOOL f failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
str_val = "FALSE";
ensure("convertToBOOL FASLE failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
str_val = "False";
ensure("convertToBOOL False failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
str_val = "false";
ensure("convertToBOOL false failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
str_val = "Tblah";
ensure("convertToBOOL false failed", !LLStringUtil::convertToBOOL(str_val, value));
}
template<> template<>
void string_index_object_t::test<18>()
{
U8 value;
std::string str_val("255");
ensure("1: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 255);
str_val = "0";
ensure("2: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 0);
str_val = "-1";
ensure("3: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));
str_val = "256"; // bigger than MAX_U8
ensure("4: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));
}
template<> template<>
void string_index_object_t::test<19>()
{
S8 value;
std::string str_val("127");
ensure("1: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 127);
str_val = "0";
ensure("2: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 0);
str_val = "-128";
ensure("3: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == -128);
str_val = "128"; // bigger than MAX_S8
ensure("4: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));
str_val = "-129";
ensure("5: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));
}
template<> template<>
void string_index_object_t::test<20>()
{
S16 value;
std::string str_val("32767");
ensure("1: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 32767);
str_val = "0";
ensure("2: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 0);
str_val = "-32768";
ensure("3: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == -32768);
str_val = "32768";
ensure("4: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));
str_val = "-32769";
ensure("5: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));
}
template<> template<>
void string_index_object_t::test<21>()
{
U16 value;
std::string str_val("65535"); //0xFFFF
ensure("1: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 65535);
str_val = "0";
ensure("2: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 0);
str_val = "-1";
ensure("3: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));
str_val = "65536";
ensure("4: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));
}
template<> template<>
void string_index_object_t::test<22>()
{
U32 value;
std::string str_val("4294967295"); //0xFFFFFFFF
ensure("1: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 4294967295UL);
str_val = "0";
ensure("2: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 0);
str_val = "4294967296";
ensure("3: convertToU32 failed", !LLStringUtil::convertToU32(str_val, value));
}
template<> template<>
void string_index_object_t::test<23>()
{
S32 value;
std::string str_val("2147483647"); //0x7FFFFFFF
ensure("1: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 2147483647);
str_val = "0";
ensure("2: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 0);
// Avoid "unary minus operator applied to unsigned type" warning on VC++. JC
S32 min_val = -2147483647 - 1;
str_val = "-2147483648";
ensure("3: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == min_val);
str_val = "2147483648";
ensure("4: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));
str_val = "-2147483649";
ensure("5: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));
}
template<> template<>
void string_index_object_t::test<24>()
{
F32 value;
std::string str_val("2147483647"); //0x7FFFFFFF
ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647);
str_val = "0";
ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0);
/* Need to find max/min F32 values
str_val = "-2147483648";
ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648);
str_val = "2147483648";
ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
str_val = "-2147483649";
ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
*/
}
template<> template<>
void string_index_object_t::test<25>()
{
F64 value;
std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF
ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL);
str_val = "0";
ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);
/* Need to find max/min F64 values
str_val = "-2147483648";
ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648);
str_val = "2147483648";
ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
str_val = "-2147483649";
ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
*/
}
template<> template<>
void string_index_object_t::test<26>()
{
const char* str1 = NULL;
const char* str2 = NULL;
ensure("1: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0);
str2 = "A";
ensure("2: compareStrings failed", LLStringUtil::compareStrings(str1, str2) > 0);
ensure("3: compareStrings failed", LLStringUtil::compareStrings(str2, str1) < 0);
str1 = "A is smaller than B";
str2 = "B is greater than A";
ensure("4: compareStrings failed", LLStringUtil::compareStrings(str1, str2) < 0);
str2 = "A is smaller than B";
ensure("5: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0);
}
template<> template<>
void string_index_object_t::test<27>()
{
const char* str1 = NULL;
const char* str2 = NULL;
ensure("1: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0);
str2 = "A";
ensure("2: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) > 0);
ensure("3: compareInsensitive failed", LLStringUtil::compareInsensitive(str2, str1) < 0);
str1 = "A is equal to a";
str2 = "a is EQUAL to A";
ensure("4: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0);
}
template<> template<>
void string_index_object_t::test<28>()
{
std::string lhs_str("PROgraM12files");
std::string rhs_str("PROgram12Files");
ensure("compareDict 1 failed", LLStringUtil::compareDict(lhs_str, rhs_str) < 0);
ensure("precedesDict 1 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == true);
lhs_str = "PROgram12Files";
rhs_str = "PROgram12Files";
ensure("compareDict 2 failed", LLStringUtil::compareDict(lhs_str, rhs_str) == 0);
ensure("precedesDict 2 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false);
lhs_str = "PROgram12Files";
rhs_str = "PROgRAM12FILES";
ensure("compareDict 3 failed", LLStringUtil::compareDict(lhs_str, rhs_str) > 0);
ensure("precedesDict 3 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false);
}
template<> template<>
void string_index_object_t::test<29>()
{
char str1[] = "First String...";
char str2[100];
LLStringUtil::copy(str2, str1, 100);
ensure("LLStringUtil::copy with enough dest length failed", strcmp(str2, str1) == 0);
LLStringUtil::copy(str2, str1, sizeof("First"));
ensure("LLStringUtil::copy with less dest length failed", strcmp(str2, "First") == 0);
}
template<> template<>
void string_index_object_t::test<30>()
{
std::string str1 = "This is the sentence...";
std::string str2 = "This is the ";
std::string str3 = "first ";
std::string str4 = "This is the first sentence...";
std::string str5 = "This is the sentence...first ";
std::string dest;
dest = str1;
LLStringUtil::copyInto(dest, str3, str2.length());
ensure("LLStringUtil::copyInto insert failed", dest == str4);
dest = str1;
LLStringUtil::copyInto(dest, str3, dest.length());
ensure("LLStringUtil::copyInto append failed", dest == str5);
}
template<> template<>
void string_index_object_t::test<31>()
{
std::string stripped;
// Plain US ASCII text, including spaces and punctuation,
// should not be altered.
std::string simple_text = "Hello, world!";
stripped = LLStringFn::strip_invalid_xml(simple_text);
ensure("Simple text passed unchanged", stripped == simple_text);
// Control characters should be removed
// except for 0x09, 0x0a, 0x0d
std::string control_chars;
for (char c = 0x01; c < 0x20; c++)
{
control_chars.push_back(c);
}
std::string allowed_control_chars;
allowed_control_chars.push_back( (char)0x09 );
allowed_control_chars.push_back( (char)0x0a );
allowed_control_chars.push_back( (char)0x0d );
stripped = LLStringFn::strip_invalid_xml(control_chars);
ensure("Only tab, LF, CR control characters allowed",
stripped == allowed_control_chars);
// UTF-8 should be passed intact, including high byte
// characters. Try Francais (with C squiggle cedilla)
std::string french = "Fran";
french.push_back( (char)0xC3 );
french.push_back( (char)0xA7 );
french += "ais";
stripped = LLStringFn::strip_invalid_xml( french );
ensure("UTF-8 high byte text is allowed", french == stripped );
}
template<> template<>
void string_index_object_t::test<32>()
{
// Test LLStringUtil::format() string interpolation
LLStringUtil::format_map_t fmt_map;
std::string s;
int subcount;
fmt_map["[TRICK1]"] = "[A]";
fmt_map["[A]"] = "a";
fmt_map["[B]"] = "b";
fmt_map["[AAA]"] = "aaa";
fmt_map["[BBB]"] = "bbb";
fmt_map["[TRICK2]"] = "[A]";
fmt_map["[EXPLOIT]"] = "!!!!!!!!!!!![EXPLOIT]!!!!!!!!!!!!";
fmt_map["[KEYLONGER]"] = "short";
fmt_map["[KEYSHORTER]"] = "Am I not a long string?";
fmt_map["?"] = "?";
fmt_map["[DELETE]"] = "";
fmt_map["[]"] = "[]"; // doesn't do a substitution, but shouldn't crash either
for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
{
// Test when source string is entirely one key
std::string s1 = (std::string)iter->first;
std::string s2 = (std::string)iter->second;
subcount = LLStringUtil::format(s1, fmt_map);
ensure_equals("LLStringUtil::format: Raw interpolation result", s1, s2);
if (s1 == "?" || s1 == "[]") // no interp expected
{
ensure_equals("LLStringUtil::format: Raw interpolation result count", 0, subcount);
}
else
{
ensure_equals("LLStringUtil::format: Raw interpolation result count", 1, subcount);
}
}
for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
{
// Test when source string is one key, duplicated
std::string s1 = (std::string)iter->first;
std::string s2 = (std::string)iter->second;
s = s1 + s1 + s1 + s1;
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: Rawx4 interpolation result", s, s2 + s2 + s2 + s2);
if (s1 == "?" || s1 == "[]") // no interp expected
{
ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 0, subcount);
}
else
{
ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 4, subcount);
}
}
// Test when source string has no keys
std::string srcs = "!!!!!!!!!!!!!!!!";
s = srcs;
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: No key test result", s, srcs);
ensure_equals("LLStringUtil::format: No key test result count", 0, subcount);
// Test when source string has no keys and is empty
std::string srcs3;
s = srcs3;
subcount = LLStringUtil::format(s, fmt_map);
ensure("LLStringUtil::format: No key test3 result", s.empty());
ensure_equals("LLStringUtil::format: No key test3 result count", 0, subcount);
// Test a substitution where a key is substituted with blankness
std::string srcs2 = "[DELETE]";
s = srcs2;
subcount = LLStringUtil::format(s, fmt_map);
ensure("LLStringUtil::format: Delete key test2 result", s.empty());
ensure_equals("LLStringUtil::format: Delete key test2 result count", 1, subcount);
// Test an assorted substitution
std::string srcs4 = "[TRICK1][A][B][AAA][BBB][TRICK2][KEYLONGER][KEYSHORTER]?[DELETE]";
s = srcs4;
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: Assorted Test1 result", s, "[A]abaaabbb[A]shortAm I not a long string??");
ensure_equals("LLStringUtil::format: Assorted Test1 result count", 9, subcount);
// Test an assorted substitution
std::string srcs5 = "[DELETE]?[KEYSHORTER][KEYLONGER][TRICK2][BBB][AAA][B][A][TRICK1]";
s = srcs5;
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "?Am I not a long string?short[A]bbbaaaba[A]");
ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
// Test on nested brackets
std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]";
s = srcs6;
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]");
ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
// Test an assorted substitution
std::string srcs8 = "foo[DELETE]bar?";
s = srcs8;
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: Assorted Test3 result", s, "foobar?");
ensure_equals("LLStringUtil::format: Assorted Test3 result count", 1, subcount);
}
template<> template<>
void string_index_object_t::test<33>()
{
// Test LLStringUtil::format() string interpolation
LLStringUtil::format_map_t blank_fmt_map;
std::string s;
int subcount;
// Test substituting out of a blank format_map
std::string srcs6 = "12345";
s = srcs6;
subcount = LLStringUtil::format(s, blank_fmt_map);
ensure_equals("LLStringUtil::format: Blankfmt Test1 result", s, "12345");
ensure_equals("LLStringUtil::format: Blankfmt Test1 result count", 0, subcount);
// Test substituting a blank string out of a blank format_map
std::string srcs7;
s = srcs7;
subcount = LLStringUtil::format(s, blank_fmt_map);
ensure("LLStringUtil::format: Blankfmt Test2 result", s.empty());
ensure_equals("LLStringUtil::format: Blankfmt Test2 result count", 0, subcount);
}
template<> template<>
void string_index_object_t::test<34>()
{
// Test that incorrect LLStringUtil::format() use does not explode.
LLStringUtil::format_map_t nasty_fmt_map;
std::string s;
int subcount;
nasty_fmt_map[""] = "never used"; // see, this is nasty.
// Test substituting out of a nasty format_map
std::string srcs6 = "12345";
s = srcs6;
subcount = LLStringUtil::format(s, nasty_fmt_map);
ensure_equals("LLStringUtil::format: Nastyfmt Test1 result", s, "12345");
ensure_equals("LLStringUtil::format: Nastyfmt Test1 result count", 0, subcount);
// Test substituting a blank string out of a nasty format_map
std::string srcs7;
s = srcs7;
subcount = LLStringUtil::format(s, nasty_fmt_map);
ensure("LLStringUtil::format: Nastyfmt Test2 result", s.empty());
ensure_equals("LLStringUtil::format: Nastyfmt Test2 result count", 0, subcount);
}
template<> template<>
void string_index_object_t::test<35>()
{
// Make sure startsWith works
std::string string("anybody in there?");
std::string substr("anybody");
ensure("startsWith works.", LLStringUtil::startsWith(string, substr));
}
template<> template<>
void string_index_object_t::test<36>()
{
// Make sure startsWith correctly fails
std::string string("anybody in there?");
std::string substr("there");
ensure("startsWith fails.", !LLStringUtil::startsWith(string, substr));
}
template<> template<>
void string_index_object_t::test<37>()
{
// startsWith fails on empty strings
std::string value("anybody in there?");
std::string empty;
ensure("empty string.", !LLStringUtil::startsWith(value, empty));
ensure("empty substr.", !LLStringUtil::startsWith(empty, value));
ensure("empty everything.", !LLStringUtil::startsWith(empty, empty));
}
template<> template<>
void string_index_object_t::test<38>()
{
// Make sure endsWith works correctly
std::string string("anybody in there?");
std::string substr("there?");
ensure("endsWith works.", LLStringUtil::endsWith(string, substr));
}
template<> template<>
void string_index_object_t::test<39>()
{
// Make sure endsWith correctly fails
std::string string("anybody in there?");
std::string substr("anybody");
ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
substr = "there";
ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
substr = "ther?";
ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
}
template<> template<>
void string_index_object_t::test<40>()
{
// endsWith fails on empty strings
std::string value("anybody in there?");
std::string empty;
ensure("empty string.", !LLStringUtil::endsWith(value, empty));
ensure("empty substr.", !LLStringUtil::endsWith(empty, value));
ensure("empty everything.", !LLStringUtil::endsWith(empty, empty));
}
template<> template<>
void string_index_object_t::test<41>()
{
set_test_name("getTokens(\"delims\")");
ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec());
ensure_equals("only delims",
LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec());
ensure_equals("sequence of delims",
LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one"));
// nat considers this a dubious implementation side effect, but I'd
// hate to change it now...
ensure_equals("noncontiguous tokens",
LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one"));
ensure_equals("space-padded tokens",
LLStringUtil::getTokens(", one , two ,", ","), list_of("one")("two"));
ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one"));
}
// Shorthand for verifying that getTokens() behaves the same when you
// don't pass a string of escape characters, when you pass an empty string
// (different overloads), and when you pass a string of characters that
// aren't actually present.
void ensure_getTokens(const std::string& desc,
const std::string& string,
const std::string& drop_delims,
const std::string& keep_delims,
const std::string& quotes,
const std::vector<std::string>& expect)
{
ensure_equals(desc + " - no esc",
LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes),
expect);
ensure_equals(desc + " - empty esc",
LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""),
expect);
ensure_equals(desc + " - unused esc",
LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"),
expect);
}
void ensure_getTokens(const std::string& desc,
const std::string& string,
const std::string& drop_delims,
const std::string& keep_delims,
const std::vector<std::string>& expect)
{
ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect);
}
template<> template<>
void string_index_object_t::test<42>()
{
set_test_name("getTokens(\"delims\", etc.)");
// Signatures to test in this method:
// getTokens(string, drop_delims, keep_delims [, quotes [, escapes]])
// If you omit keep_delims, you get the older function (test above).
// cases like the getTokens(string, delims) tests above
ensure_getTokens("empty string", "", " ", "", StringVec());
ensure_getTokens("only delims",
" \r\n ", " \r\n", "", StringVec());
ensure_getTokens("sequence of delims",
",,, one ,,,", ", ", "", list_of("one"));
// Note contrast with the case in the previous method
ensure_getTokens("noncontiguous tokens",
", ,, , one ,,,", ", ", "", list_of("one"));
ensure_getTokens("space-padded tokens",
", one , two ,", ", ", "",
list_of("one")("two"));
ensure_getTokens("no delims", "one", ",", "", list_of("one"));
// drop_delims vs. keep_delims
ensure_getTokens("arithmetic",
" ab+def / xx* yy ", " ", "+-*/",
list_of("ab")("+")("def")("/")("xx")("*")("yy"));
// quotes
ensure_getTokens("no quotes",
"She said, \"Don't go.\"", " ", ",", "",
list_of("She")("said")(",")("\"Don't")("go.\""));
ensure_getTokens("quotes",
"She said, \"Don't go.\"", " ", ",", "\"",
list_of("She")("said")(",")("Don't go."));
ensure_getTokens("quotes and delims",
"run c:/'Documents and Settings'/someone", " ", "", "'",
list_of("run")("c:/Documents and Settings/someone"));
ensure_getTokens("unmatched quote",
"baby don't leave", " ", "", "'",
list_of("baby")("don't")("leave"));
ensure_getTokens("adjacent quoted",
"abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'",
list_of("abcdef \"ghijkl' mnopqr"));
ensure_getTokens("quoted empty string",
"--set SomeVar ''", " ", "", "'",
list_of("--set")("SomeVar")(""));
// escapes
// Don't use backslash as an escape for these tests -- you'll go nuts
// between the C++ string scanner and getTokens() escapes. Test with
// something else!
ensure_equals("escaped delims",
LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"),
list_of(" a")("-")("dog-gone phrase"));
ensure_equals("escaped quotes",
LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"),
list_of("say:")("this isn't working."));
ensure_equals("escaped escape",
LLStringUtil::getTokens("want x^^2", " ", "", "", "^"),
list_of("want")("x^2"));
ensure_equals("escape at end",
LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"),
list_of("it's up")("there^"));
}
}
+146
View File
@@ -0,0 +1,146 @@
/**
* @file llsingleton_test.cpp
* @date 2011-08-11
* @brief Unit test for the LLSingleton class
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "lltrace.h"
#include "lltracethreadrecorder.h"
#include "lltracerecording.h"
#include "../test/lltut.h"
#ifdef LL_WINDOWS
#pragma warning(disable : 4244) // possible loss of data on conversions
#endif
namespace LLUnits
{
// using powers of 2 to allow strict floating point equality
LL_DECLARE_BASE_UNIT(Ounces, "oz");
LL_DECLARE_DERIVED_UNIT(TallCup, "", Ounces, / 12);
LL_DECLARE_DERIVED_UNIT(GrandeCup, "", Ounces, / 16);
LL_DECLARE_DERIVED_UNIT(VentiCup, "", Ounces, / 20);
LL_DECLARE_BASE_UNIT(Grams, "g");
LL_DECLARE_DERIVED_UNIT(Milligrams, "mg", Grams, * 1000);
}
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Ounces);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, TallCup);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, GrandeCup);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, VentiCup);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Grams);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milligrams);
namespace tut
{
using namespace LLTrace;
struct trace
{
ThreadRecorder mRecorder;
};
typedef test_group<trace> trace_t;
typedef trace_t::object trace_object_t;
tut::trace_t tut_singleton("LLTrace");
static CountStatHandle<S32> sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast.");
static SampleStatHandle<F32Milligrams> sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient");
static EventStatHandle<S32Ounces> sOuncesPerCup("cupsize", "Large, huge, or ginormous");
static F32 sCaffeineLevel(0.f);
const F32Milligrams sCaffeinePerOz(18.f);
void drink_coffee(S32 num_cups, S32Ounces cup_size)
{
add(sCupsOfCoffeeConsumed, num_cups);
for (S32 i = 0; i < num_cups; i++)
{
record(sOuncesPerCup, cup_size);
}
sCaffeineLevel += F32Ounces(num_cups * cup_size).value() * sCaffeinePerOz.value();
sample(sCaffeineLevelStat, sCaffeineLevel);
}
// basic data collection
template<> template<>
void trace_object_t::test<1>()
{
sample(sCaffeineLevelStat, sCaffeineLevel);
Recording all_day;
Recording at_work;
Recording after_3pm;
all_day.start();
{
// warm up with one grande cup
drink_coffee(1, S32TallCup(1));
// go to work
at_work.start();
{
// drink 3 tall cups, 1 after 3 pm
drink_coffee(2, S32GrandeCup(1));
after_3pm.start();
drink_coffee(1, S32GrandeCup(1));
}
at_work.stop();
drink_coffee(1, S32VentiCup(1));
}
// don't need to stop recordings to get accurate values out of them
//after_3pm.stop();
//all_day.stop();
ensure("count stats are counted when recording is active",
at_work.getSum(sCupsOfCoffeeConsumed) == 3
&& all_day.getSum(sCupsOfCoffeeConsumed) == 5
&& after_3pm.getSum(sCupsOfCoffeeConsumed) == 2);
ensure("measurement sums are counted when recording is active",
at_work.getSum(sOuncesPerCup) == S32Ounces(48)
&& all_day.getSum(sOuncesPerCup) == S32Ounces(80)
&& after_3pm.getSum(sOuncesPerCup) == S32Ounces(36));
ensure("measurement min is specific to when recording is active",
at_work.getMin(sOuncesPerCup) == S32GrandeCup(1)
&& all_day.getMin(sOuncesPerCup) == S32TallCup(1)
&& after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1));
ensure("measurement max is specific to when recording is active",
at_work.getMax(sOuncesPerCup) == S32GrandeCup(1)
&& all_day.getMax(sOuncesPerCup) == S32VentiCup(1)
&& after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1));
ensure("sample min is specific to when recording is active",
at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value()
&& all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f)
&& after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value());
ensure("sample max is specific to when recording is active",
at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value()
&& all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()
&& after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value());
}
}
File diff suppressed because it is too large Load Diff
+388
View File
@@ -0,0 +1,388 @@
/**
* @file llsingleton_test.cpp
* @date 2011-08-11
* @brief Unit test for the LLSingleton class
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2011, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llunits.h"
#include "../test/lltut.h"
namespace LLUnits
{
// using powers of 2 to allow strict floating point equality
LL_DECLARE_BASE_UNIT(Quatloos, "Quat");
LL_DECLARE_DERIVED_UNIT(Latinum, "Lat", Quatloos, / 4);
LL_DECLARE_DERIVED_UNIT(Solari, "Sol", Latinum, * 16);
}
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Quatloos);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Latinum);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Solari);
namespace LLUnits
{
LL_DECLARE_BASE_UNIT(Celcius, "c");
LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32);
LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f);
}
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Celcius);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Fahrenheit);
LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kelvin);
namespace tut
{
using namespace LLUnits;
struct units
{
};
typedef test_group<units> units_t;
typedef units_t::object units_object_t;
tut::units_t tut_singleton("LLUnit");
// storage type conversions
template<> template<>
void units_object_t::test<1>()
{
LLUnit<F32, Quatloos> float_quatloos;
ensure("default float unit is zero", float_quatloos == F32Quatloos(0.f));
LLUnit<F32, Quatloos> float_initialize_quatloos(1);
ensure("non-zero initialized unit", float_initialize_quatloos == F32Quatloos(1.f));
LLUnit<S32, Quatloos> int_quatloos;
ensure("default int unit is zero", int_quatloos == S32Quatloos(0));
int_quatloos = S32Quatloos(42);
ensure("int assignment is preserved", int_quatloos == S32Quatloos(42));
float_quatloos = int_quatloos;
ensure("float assignment from int preserves value", float_quatloos == F32Quatloos(42.f));
int_quatloos = float_quatloos;
ensure("int assignment from float preserves value", int_quatloos == S32Quatloos(42));
float_quatloos = F32Quatloos(42.1f);
int_quatloos = float_quatloos;
ensure("int units truncate float units on assignment", int_quatloos == S32Quatloos(42));
LLUnit<U32, Quatloos> unsigned_int_quatloos(float_quatloos);
ensure("unsigned int can be initialized from signed int", unsigned_int_quatloos == S32Quatloos(42));
S32Solari int_solari(1);
float_quatloos = int_solari;
ensure("fractional units are preserved in conversion from integer to float type", float_quatloos == F32Quatloos(0.25f));
int_quatloos = S32Quatloos(1);
F32Solari float_solari = int_quatloos;
ensure("can convert with fractional intermediates from integer to float type", float_solari == F32Solari(4.f));
}
// conversions to/from base unit
template<> template<>
void units_object_t::test<2>()
{
LLUnit<F32, Quatloos> quatloos(1.f);
LLUnit<F32, Latinum> latinum_bars(quatloos);
ensure("conversion between units is automatic via initialization", latinum_bars == F32Latinum(1.f / 4.f));
latinum_bars = S32Latinum(256);
quatloos = latinum_bars;
ensure("conversion between units is automatic via assignment, and bidirectional", quatloos == S32Quatloos(1024));
LLUnit<S32, Quatloos> single_quatloo(1);
LLUnit<F32, Latinum> quarter_latinum = single_quatloo;
ensure("division of integer unit preserves fractional values when converted to float unit", quarter_latinum == F32Latinum(0.25f));
}
// conversions across non-base units
template<> template<>
void units_object_t::test<3>()
{
LLUnit<F32, Quatloos> quatloos(1024);
LLUnit<F32, Solari> solari(quatloos);
ensure("conversions can work between indirectly related units: Quatloos -> Latinum -> Solari", solari == S32Solari(4096));
LLUnit<F32, Latinum> latinum_bars = solari;
ensure("Non base units can be converted between each other", latinum_bars == S32Latinum(256));
}
// math operations
template<> template<>
void units_object_t::test<4>()
{
// exercise math operations
LLUnit<F32, Quatloos> quatloos(1.f);
quatloos *= 4.f;
ensure(quatloos == S32Quatloos(4));
quatloos = quatloos * 2;
ensure(quatloos == S32Quatloos(8));
quatloos = 2.f * quatloos;
ensure(quatloos == S32Quatloos(16));
quatloos += F32Quatloos(4.f);
ensure(quatloos == S32Quatloos(20));
quatloos += S32Quatloos(4);
ensure(quatloos == S32Quatloos(24));
quatloos = quatloos + S32Quatloos(4);
ensure(quatloos == S32Quatloos(28));
quatloos = S32Quatloos(4) + quatloos;
ensure(quatloos == S32Quatloos(32));
quatloos += quatloos * 3;
ensure(quatloos == S32Quatloos(128));
quatloos -= quatloos / 4 * 3;
ensure(quatloos == S32Quatloos(32));
quatloos = quatloos - S32Quatloos(8);
ensure(quatloos == S32Quatloos(24));
quatloos -= S32Quatloos(4);
ensure(quatloos == S32Quatloos(20));
quatloos -= F32Quatloos(4.f);
ensure(quatloos == S32Quatloos(16));
quatloos /= 2.f;
ensure(quatloos == S32Quatloos(8));
quatloos = quatloos / 4;
ensure(quatloos == S32Quatloos(2));
F32 ratio = quatloos / LLUnit<F32, Quatloos>(2.f);
ensure(ratio == 1);
ratio = quatloos / LLUnit<F32, Solari>(8.f);
ensure(ratio == 1);
quatloos += LLUnit<F32, Solari>(8.f);
ensure(quatloos == S32Quatloos(4));
quatloos -= LLUnit<F32, Latinum>(1.f);
ensure(quatloos == S32Quatloos(0));
}
// comparison operators
template<> template<>
void units_object_t::test<5>()
{
LLUnit<S32, Quatloos> quatloos(1);
ensure("can perform less than comparison against same type", quatloos < S32Quatloos(2));
ensure("can perform less than comparison against different storage type", quatloos < F32Quatloos(2.f));
ensure("can perform less than comparison against different units", quatloos < S32Latinum(5));
ensure("can perform less than comparison against different storage type and units", quatloos < F32Latinum(5.f));
ensure("can perform greater than comparison against same type", quatloos > S32Quatloos(0));
ensure("can perform greater than comparison against different storage type", quatloos > F32Quatloos(0.f));
ensure("can perform greater than comparison against different units", quatloos > S32Latinum(0));
ensure("can perform greater than comparison against different storage type and units", quatloos > F32Latinum(0.f));
}
bool accept_explicit_quatloos(S32Quatloos q)
{
return true;
}
bool accept_implicit_quatloos(S32Quatloos q)
{
return true;
}
// signature compatibility
template<> template<>
void units_object_t::test<6>()
{
S32Quatloos quatloos(1);
ensure("can pass unit values as argument", accept_explicit_quatloos(S32Quatloos(1)));
ensure("can pass unit values as argument", accept_explicit_quatloos(quatloos));
}
// implicit units
template<> template<>
void units_object_t::test<7>()
{
LLUnit<F32, Quatloos> quatloos;
LLUnitImplicit<F32, Quatloos> quatloos_implicit = quatloos + S32Quatloos(1);
ensure("can initialize implicit unit from explicit", quatloos_implicit == 1);
quatloos = quatloos_implicit;
ensure("can assign implicit unit to explicit unit", quatloos == S32Quatloos(1));
quatloos += quatloos_implicit;
ensure("can perform math operation using mixture of implicit and explicit units", quatloos == S32Quatloos(2));
// math operations on implicits
quatloos_implicit = 1;
ensure(quatloos_implicit == 1);
quatloos_implicit += 2;
ensure(quatloos_implicit == 3);
quatloos_implicit *= 2;
ensure(quatloos_implicit == 6);
quatloos_implicit -= 1;
ensure(quatloos_implicit == 5);
quatloos_implicit /= 5;
ensure(quatloos_implicit == 1);
quatloos_implicit = quatloos_implicit + 3 + quatloos_implicit;
ensure(quatloos_implicit == 5);
quatloos_implicit = 10 - quatloos_implicit - 1;
ensure(quatloos_implicit == 4);
quatloos_implicit = 2 * quatloos_implicit * 2;
ensure(quatloos_implicit == 16);
F32 one_half = quatloos_implicit / (quatloos_implicit * 2);
ensure(one_half == 0.5f);
// implicit conversion to POD
F32 float_val = quatloos_implicit;
ensure("implicit units convert implicitly to regular values", float_val == 16);
S32 int_val = (S32)quatloos_implicit;
ensure("implicit units convert implicitly to regular values", int_val == 16);
// conversion of implicits
LLUnitImplicit<F32, Latinum> latinum_implicit(2);
ensure("implicit units of different types are comparable", latinum_implicit * 2 == quatloos_implicit);
quatloos_implicit += F32Quatloos(10);
ensure("can add-assign explicit units", quatloos_implicit == 26);
quatloos_implicit -= F32Quatloos(10);
ensure("can subtract-assign explicit units", quatloos_implicit == 16);
// comparisons
ensure("can compare greater than implicit unit", quatloos_implicit > F32QuatloosImplicit(0.f));
ensure("can compare greater than non-implicit unit", quatloos_implicit > F32Quatloos(0.f));
ensure("can compare greater than or equal to implicit unit", quatloos_implicit >= F32QuatloosImplicit(0.f));
ensure("can compare greater than or equal to non-implicit unit", quatloos_implicit >= F32Quatloos(0.f));
ensure("can compare less than implicit unit", quatloos_implicit < F32QuatloosImplicit(20.f));
ensure("can compare less than non-implicit unit", quatloos_implicit < F32Quatloos(20.f));
ensure("can compare less than or equal to implicit unit", quatloos_implicit <= F32QuatloosImplicit(20.f));
ensure("can compare less than or equal to non-implicit unit", quatloos_implicit <= F32Quatloos(20.f));
}
// precision tests
template<> template<>
void units_object_t::test<8>()
{
U32Bytes max_bytes(U32_MAX);
S32Megabytes mega_bytes = max_bytes;
ensure("max available precision is used when converting units", mega_bytes == (S32Megabytes)4095);
mega_bytes = (S32Megabytes)-5 + (U32Megabytes)1;
ensure("can mix signed and unsigned in units addition", mega_bytes == (S32Megabytes)-4);
mega_bytes = (U32Megabytes)5 + (S32Megabytes)-1;
ensure("can mix unsigned and signed in units addition", mega_bytes == (S32Megabytes)4);
}
// default units
template<> template<>
void units_object_t::test<9>()
{
U32Gigabytes GB(1);
U32Megabytes MB(GB);
U32Kilobytes KB(GB);
U32Bytes B(GB);
ensure("GB -> MB conversion", MB.value() == 1024);
ensure("GB -> KB conversion", KB.value() == 1024 * 1024);
ensure("GB -> B conversion", B.value() == 1024 * 1024 * 1024);
KB = U32Kilobytes(1);
U32Kilobits Kb(KB);
U32Bits b(KB);
ensure("KB -> Kb conversion", Kb.value() == 8);
ensure("KB -> b conversion", b.value() == 8 * 1024);
U32Days days(1);
U32Hours hours(days);
U32Minutes minutes(days);
U32Seconds seconds(days);
U32Milliseconds ms(days);
ensure("days -> hours conversion", hours.value() == 24);
ensure("days -> minutes conversion", minutes.value() == 24 * 60);
ensure("days -> seconds conversion", seconds.value() == 24 * 60 * 60);
ensure("days -> ms conversion", ms.value() == 24 * 60 * 60 * 1000);
U32Kilometers km(1);
U32Meters m(km);
U32Centimeters cm(km);
U32Millimeters mm(km);
ensure("km -> m conversion", m.value() == 1000);
ensure("km -> cm conversion", cm.value() == 1000 * 100);
ensure("km -> mm conversion", mm.value() == 1000 * 1000);
U32Gigahertz GHz(1);
U32Megahertz MHz(GHz);
U32Kilohertz KHz(GHz);
U32Hertz Hz(GHz);
ensure("GHz -> MHz conversion", MHz.value() == 1000);
ensure("GHz -> KHz conversion", KHz.value() == 1000 * 1000);
ensure("GHz -> Hz conversion", Hz.value() == 1000 * 1000 * 1000);
F32Radians rad(6.2831853071795f);
S32Degrees deg(rad);
ensure("radians -> degrees conversion", deg.value() == 360);
F32Percent percent(50);
F32Ratio ratio(percent);
ensure("percent -> ratio conversion", ratio.value() == 0.5f);
U32Kilotriangles ktris(1);
U32Triangles tris(ktris);
ensure("kilotriangles -> triangles conversion", tris.value() == 1000);
}
bool value_near(F32 value, F32 target, F32 threshold)
{
return fabsf(value - target) < threshold;
}
// linear transforms
template<> template<>
void units_object_t::test<10>()
{
F32Celcius float_celcius(100);
F32Fahrenheit float_fahrenheit(float_celcius);
ensure("floating point celcius -> fahrenheit conversion using linear transform", value_near(float_fahrenheit.value(), 212, 0.1f) );
float_celcius = float_fahrenheit;
ensure("floating point fahrenheit -> celcius conversion using linear transform (round trip)", value_near(float_celcius.value(), 100.f, 0.1f) );
S32Celcius int_celcius(100);
S32Fahrenheit int_fahrenheit(int_celcius);
ensure("integer celcius -> fahrenheit conversion using linear transform", int_fahrenheit.value() == 212);
int_celcius = int_fahrenheit;
ensure("integer fahrenheit -> celcius conversion using linear transform (round trip)", int_celcius.value() == 100);
}
}
+423
View File
@@ -0,0 +1,423 @@
/**
* @file lluri_test.cpp
* @brief LLURI unit tests
* @date September 2006
*
* $LicenseInfo:firstyear=2006&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 "../llsd.h"
#include "../lluri.h"
#include "../test/lltut.h"
namespace tut
{
struct URITestData {
void checkParts(const LLURI& u,
const char* expectedScheme,
const char* expectedOpaque,
const char* expectedAuthority,
const char* expectedPath,
const char* expectedQuery = "")
{
ensure_equals("scheme", u.scheme(), expectedScheme);
ensure_equals("opaque", u.opaque(), expectedOpaque);
ensure_equals("authority", u.authority(), expectedAuthority);
ensure_equals("path", u.path(), expectedPath);
ensure_equals("query", u.query(), expectedQuery);
}
void escapeRoundTrip(const std::string& uri_raw_1)
{
std::string uri_esc_1(LLURI::escape(uri_raw_1));
std::string uri_raw_2(LLURI::unescape(uri_esc_1));
ensure_equals("escape/unescape raw", uri_raw_2, uri_raw_1);
std::string uri_esc_2(LLURI::escape(uri_raw_2));
ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1);
}
};
typedef test_group<URITestData> URITestGroup;
typedef URITestGroup::object URITestObject;
URITestGroup uriTestGroup("LLURI");
template<> template<>
void URITestObject::test<1>()
{
LLURI u("http://abc.com/def/ghi?x=37&y=hello");
ensure_equals("scheme", u.scheme(), "http");
ensure_equals("authority", u.authority(), "abc.com");
ensure_equals("path", u.path(), "/def/ghi");
ensure_equals("query", u.query(), "x=37&y=hello");
ensure_equals("host name", u.hostName(), "abc.com");
ensure_equals("host port", u.hostPort(), 80);
LLSD query = u.queryMap();
ensure_equals("query x", query["x"].asInteger(), 37);
ensure_equals("query y", query["y"].asString(), "hello");
query = LLURI::queryMap("x=22.23&y=https://lindenlab.com/");
ensure_equals("query x", query["x"].asReal(), 22.23);
ensure_equals("query y", query["y"].asURI().asString(), "https://lindenlab.com/");
}
template<> template<>
void URITestObject::test<2>()
{
set_test_name("empty string");
checkParts(LLURI(""), "", "", "", "");
}
template<> template<>
void URITestObject::test<3>()
{
set_test_name("no scheme");
checkParts(LLURI("foo"), "", "foo", "", "");
checkParts(LLURI("foo%3A"), "", "foo:", "", "");
}
template<> template<>
void URITestObject::test<4>()
{
set_test_name("scheme w/o paths");
checkParts(LLURI("mailto:zero@ll.com"),
"mailto", "zero@ll.com", "", "");
checkParts(LLURI("silly://abc/def?foo"),
"silly", "//abc/def?foo", "", "");
}
template<> template<>
void URITestObject::test<5>()
{
set_test_name("authority section");
checkParts(LLURI("http:///"),
"http", "///", "", "/");
checkParts(LLURI("http://abc"),
"http", "//abc", "abc", "");
checkParts(LLURI("http://a%2Fb/cd"),
"http", "//a/b/cd", "a/b", "/cd");
checkParts(LLURI("http://host?"),
"http", "//host?", "host", "");
}
template<> template<>
void URITestObject::test<6>()
{
set_test_name("path section");
checkParts(LLURI("http://host/a/b/"),
"http", "//host/a/b/", "host", "/a/b/");
checkParts(LLURI("http://host/a%3Fb/"),
"http", "//host/a?b/", "host", "/a?b/");
checkParts(LLURI("http://host/a:b/"),
"http", "//host/a:b/", "host", "/a:b/");
}
template<> template<>
void URITestObject::test<7>()
{
set_test_name("query string");
checkParts(LLURI("http://host/?"),
"http", "//host/?", "host", "/", "");
checkParts(LLURI("http://host/?x"),
"http", "//host/?x", "host", "/", "x");
checkParts(LLURI("http://host/??"),
"http", "//host/??", "host", "/", "?");
checkParts(LLURI("http://host/?%3F"),
"http", "//host/??", "host", "/", "?");
}
template<> template<>
void URITestObject::test<8>()
{
LLSD path;
path.append("x");
path.append("123");
checkParts(LLURI::buildHTTP("host", path),
"http", "//host/x/123", "host", "/x/123");
LLSD query;
query["123"] = "12";
query["abcd"] = "abc";
checkParts(LLURI::buildHTTP("host", path, query),
"http", "//host/x/123?123=12&abcd=abc",
"host", "/x/123", "123=12&abcd=abc");
ensure_equals(LLURI::buildHTTP("host", "").asString(),
"http://host");
ensure_equals(LLURI::buildHTTP("host", "/").asString(),
"http://host/");
ensure_equals(LLURI::buildHTTP("host", "//").asString(),
"http://host/");
ensure_equals(LLURI::buildHTTP("host", "dir name").asString(),
"http://host/dir%20name");
ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(),
"http://host/dir%20name/");
ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(),
"http://host/dir%20name");
ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(),
"http://host/dir%20name/");
ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(),
"http://host/dir%20name/subdir%20name");
ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(),
"http://host/dir%20name/subdir%20name/");
ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(),
"http://host/dir%20name/subdir%20name");
ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(),
"http://host/dir%20name/subdir%20name/");
ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(),
"http://host/dir%20name/subdir%20name/");
}
template<> template<>
void URITestObject::test<9>()
{
set_test_name("test unescaped path components");
LLSD path;
path.append("x@*//*$&^");
path.append("123");
checkParts(LLURI::buildHTTP("host", path),
"http", "//host/x@*//*$&^/123", "host", "/x@*//*$&^/123");
}
template<> template<>
void URITestObject::test<10>()
{
set_test_name("test unescaped query components");
LLSD path;
path.append("x");
path.append("123");
LLSD query;
query["123"] = "?&*#//";
query["**@&?//"] = "abc";
checkParts(LLURI::buildHTTP("host", path, query),
"http", "//host/x/123?**@&?//=abc&123=?&*#//",
"host", "/x/123", "**@&?//=abc&123=?&*#//");
}
template<> template<>
void URITestObject::test<11>()
{
set_test_name("test unescaped host components");
LLSD path;
path.append("x");
path.append("123");
LLSD query;
query["123"] = "12";
query["abcd"] = "abc";
checkParts(LLURI::buildHTTP("hi123*33--}{:portstuffs", path, query),
"http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc",
"hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc");
}
template<> template<>
void URITestObject::test<12>()
{
set_test_name("test funky host_port values that are actually prefixes");
checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()),
"http", "//example.com:8080",
"example.com:8080", "");
checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()),
"http", "//example.com:8080/",
"example.com:8080", "/");
checkParts(LLURI::buildHTTP("http://example.com:8080/a/b", LLSD()),
"http", "//example.com:8080/a/b",
"example.com:8080", "/a/b");
}
template<> template<>
void URITestObject::test<13>()
{
const std::string unreserved =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789"
"-._~";
set_test_name("test escape");
ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67");
ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40");
ensure_equals("escaping as query variable",
LLURI::escape("http://10.0.1.4:12032/agent/god/agent-id/map/layer/?resume=http://station3.ll.com:12032/agent/203ad6df-b522-491d-ba48-4e24eb57aeff/send-postcard", unreserved + ":@!$'()*+,="),
"http:%2F%2F10.0.1.4:12032%2Fagent%2Fgod%2Fagent-id%2Fmap%2Flayer%2F%3Fresume=http:%2F%2Fstation3.ll.com:12032%2Fagent%2F203ad6df-b522-491d-ba48-4e24eb57aeff%2Fsend-postcard");
// French cedilla (C with squiggle, like in the word Francais) is UTF-8 C3 A7
#if LL_WINDOWS
#pragma warning(disable: 4309)
#endif
std::string cedilla;
cedilla.push_back( (char)0xC3 );
cedilla.push_back( (char)0xA7 );
ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7");
}
template<> template<>
void URITestObject::test<14>()
{
set_test_name("make sure escape and unescape of empty strings return empty strings.");
std::string uri_esc(LLURI::escape(""));
ensure("escape string empty", uri_esc.empty());
std::string uri_raw(LLURI::unescape(""));
ensure("unescape string empty", uri_raw.empty());
}
template<> template<>
void URITestObject::test<15>()
{
set_test_name("do some round-trip tests");
escapeRoundTrip("http://secondlife.com");
escapeRoundTrip("http://secondlife.com/url with spaces");
escapeRoundTrip("http://bad[domain]name.com/");
escapeRoundTrip("ftp://bill.gates@ms/micro$oft.com/c:\\autoexec.bat");
escapeRoundTrip("");
}
template<> template<>
void URITestObject::test<16>()
{
set_test_name("Test the default escaping");
// yes -- this mangles the url. This is expected behavior
std::string simple("http://secondlife.com");
ensure_equals(
"simple http",
LLURI::escape(simple),
"http%3A%2F%2Fsecondlife.com");
ensure_equals(
"needs escape",
LLURI::escape("http://get.secondlife.com/windows viewer"),
"http%3A%2F%2Fget.secondlife.com%2Fwindows%20viewer");
}
template<> template<>
void URITestObject::test<17>()
{
set_test_name("do some round-trip tests with very long strings.");
escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six");
escapeRoundTrip(
"'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale"
"\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tf"
"aces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204"
"\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffff"
"ff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t"
"0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t"
"\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t"
"\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreat"
"or_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundra"
"dius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0"
"\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f"
"25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000"
"000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1"
".57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemI"
"D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n");
}
template<> template<>
void URITestObject::test<18>()
{
LLURI u("secondlife:///app/login?first_name=Testert4&last_name=Tester&web_login_key=test");
// if secondlife is the scheme, LLURI should parse /app/login as path, with no authority
ensure_equals("scheme", u.scheme(), "secondlife");
ensure_equals("authority", u.authority(), "");
ensure_equals("path", u.path(), "/app/login");
ensure_equals("pathmap", u.pathArray()[0].asString(), "app");
ensure_equals("pathmap", u.pathArray()[1].asString(), "login");
ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test");
ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester");
u = LLURI("secondlife://Da Boom/128/128/128");
// if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority
ensure_equals("scheme", u.scheme(), "secondlife");
ensure_equals("authority", u.authority(), "Da Boom");
ensure_equals("path", u.path(), "/128/128/128");
ensure_equals("pathmap", u.pathArray()[0].asString(), "128");
ensure_equals("pathmap", u.pathArray()[1].asString(), "128");
ensure_equals("pathmap", u.pathArray()[2].asString(), "128");
ensure_equals("query", u.query(), "");
}
template<> template<>
void URITestObject::test<19>()
{
set_test_name("Parse about: schemes");
LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78");
ensure_equals("scheme", u.scheme(), "about");
ensure_equals("authority", u.authority(), "");
ensure_equals("path", u.path(), "blank");
ensure_equals("pathmap", u.pathArray()[0].asString(), "blank");
ensure_equals("query", u.query(), "redirect-http-hack=secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak&region=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78");
ensure_equals("query map element", u.queryMap()["redirect-http-hack"].asString(), "secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak&region=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78");
}
template<> template<>
void URITestObject::test<20>()
{
set_test_name("escapePathAndData uri test");
// Basics scheme:[//authority]path[?query][#fragment]
ensure_equals(LLURI::escapePathAndData("dirname?query"),
"dirname?query");
ensure_equals(LLURI::escapePathAndData("dirname?query=data"),
"dirname?query=data");
ensure_equals(LLURI::escapePathAndData("host://dirname/subdir name?query#fragment"),
"host://dirname/subdir%20name?query#fragment");
ensure_equals(LLURI::escapePathAndData("host://dirname/subdir name?query=some@>data#fragment"),
"host://dirname/subdir%20name?query=some@%3Edata#fragment");
ensure_equals(LLURI::escapePathAndData("host://dir[name/subdir name?query=some[data#fra[gment"),
"host://dir[name/subdir%20name?query=some%5Bdata#fra[gment");
ensure_equals(LLURI::escapePathAndData("mailto:zero@ll.com"),
"mailto:zero@ll.com");
// pre-escaped
ensure_equals(LLURI::escapePathAndData("host://dirname/subdir%20name"),
"host://dirname/subdir%20name");
// data:[<mediatype>][;base64],<data>
ensure_equals(LLURI::escapePathAndData("data:,Hello, World!"),
"data:,Hello%2C%20World%21");
ensure_equals(LLURI::escapePathAndData("data:text/html,<h1>Hello, World!</h1>"),
"data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E");
// pre-escaped
ensure_equals(LLURI::escapePathAndData("data:text/html,%3Ch1%3EHello%2C%20World!</h1>"),
"data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E");
// assume that base64 does not need escaping
ensure_equals(LLURI::escapePathAndData("data:image;base64,SGVs/bG8sIFd/vcmxkIQ%3D%3D!-&*?="),
"data:image;base64,SGVs/bG8sIFd/vcmxkIQ%3D%3D!-&*?=");
}
}
+124
View File
@@ -0,0 +1,124 @@
/**
* @file stringize_test.cpp
* @author Nat Goodspeed
* @date 2008-09-12
* @brief Test of stringize.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
|*==========================================================================*/
// STL headers
#include <iomanip>
// Precompiled header
#include "linden_common.h"
// associated header
#include "../stringize.h"
// std headers
// external library headers
// other Linden headers
#include "../llsd.h"
#include "../test/lltut.h"
namespace tut
{
struct stringize_data
{
stringize_data():
c('c'),
s(17),
i(34),
l(68),
f(3.14159265358979f),
d(3.14159265358979),
// Including a space differentiates this from
// boost::lexical_cast<std::string>, which doesn't handle embedded
// spaces so well.
abc("abc def")
{
llsd["i"] = i;
llsd["d"] = d;
llsd["abc"] = abc;
def = L"def ghi";
}
char c;
short s;
int i;
long l;
float f;
double d;
std::string abc;
std::wstring def;
LLSD llsd;
};
typedef test_group<stringize_data> stringize_group;
typedef stringize_group::object stringize_object;
tut::stringize_group strzgrp("stringize_h");
template<> template<>
void stringize_object::test<1>()
{
ensure_equals(stringize(c), "c");
ensure_equals(stringize(s), "17");
ensure_equals(stringize(i), "34");
ensure_equals(stringize(l), "68");
ensure_equals(stringize(f), "3.14159");
ensure_equals(stringize(d), "3.14159");
ensure_equals(stringize(abc), "abc def");
ensure_equals(stringize(def), "def ghi"); //Will generate LL_WARNS() due to narrowing.
ensure_equals(stringize(llsd), "{'abc':'abc def','d':r3.14159,'i':i34}");
}
template<> template<>
void stringize_object::test<2>()
{
ensure_equals(STRINGIZE("c is " << c), "c is c");
ensure_equals(STRINGIZE(std::setprecision(4) << d), "3.142");
}
template<> template<>
void stringize_object::test<3>()
{
//Tests rely on validity of wstring_to_utf8str()
ensure_equals(wstring_to_utf8str(wstringize(c)), wstring_to_utf8str(L"c"));
ensure_equals(wstring_to_utf8str(wstringize(s)), wstring_to_utf8str(L"17"));
ensure_equals(wstring_to_utf8str(wstringize(i)), wstring_to_utf8str(L"34"));
ensure_equals(wstring_to_utf8str(wstringize(l)), wstring_to_utf8str(L"68"));
ensure_equals(wstring_to_utf8str(wstringize(f)), wstring_to_utf8str(L"3.14159"));
ensure_equals(wstring_to_utf8str(wstringize(d)), wstring_to_utf8str(L"3.14159"));
ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(L"abc def"));
ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(wstringize(abc.c_str())));
ensure_equals(wstring_to_utf8str(wstringize(def)), wstring_to_utf8str(L"def ghi"));
// ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}"));
}
} // namespace tut
@@ -0,0 +1,70 @@
/**
* @file threadsafeschedule_test.cpp
* @author Nat Goodspeed
* @date 2021-10-04
* @brief Test for threadsafeschedule.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "threadsafeschedule.h"
// STL headers
// std headers
#include <chrono>
// external library headers
// other Linden headers
#include "../test/lltut.h"
using namespace std::literals::chrono_literals; // ms suffix
using namespace std::literals::string_literals; // s suffix
using Queue = LL::ThreadSafeSchedule<std::string>;
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct threadsafeschedule_data
{
Queue queue;
};
typedef test_group<threadsafeschedule_data> threadsafeschedule_group;
typedef threadsafeschedule_group::object object;
threadsafeschedule_group threadsafeschedulegrp("threadsafeschedule");
template<> template<>
void object::test<1>()
{
set_test_name("push");
// Simply calling push() a few times might result in indeterminate
// delivery order if the resolution of steady_clock is coarser than
// the real time required for each push() call. Explicitly increment
// the timestamp for each one -- but since we're passing explicit
// timestamps, make the queue reorder them.
auto now{ Queue::Clock::now() };
queue.push(Queue::TimeTuple(now + 200ms, "ghi"s));
// Given the various push() overloads, you have to match the type
// exactly: conversions are ambiguous.
queue.push(now, "abc"s);
queue.push(now + 100ms, "def"s);
queue.close();
auto entry = queue.pop();
ensure_equals("failed to pop first", std::get<0>(entry), "abc"s);
entry = queue.pop();
ensure_equals("failed to pop second", std::get<0>(entry), "def"s);
ensure("queue not closed", queue.isClosed());
ensure("queue prematurely done", ! queue.done());
std::string s;
bool popped = queue.tryPopFor(1s, s);
ensure("failed to pop third", popped);
ensure_equals("third is wrong", s, "ghi"s);
popped = queue.tryPop(s);
ensure("queue not empty", ! popped);
ensure("queue not done", queue.done());
}
} // namespace tut
+47
View File
@@ -0,0 +1,47 @@
/**
* @file tuple_test.cpp
* @author Nat Goodspeed
* @date 2021-10-04
* @brief Test for tuple.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "tuple.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "../test/lltut.h"
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct tuple_data
{
};
typedef test_group<tuple_data> tuple_group;
typedef tuple_group::object object;
tuple_group tuplegrp("tuple");
template<> template<>
void object::test<1>()
{
set_test_name("tuple");
std::tuple<std::string, int> tup{ "abc", 17 };
std::tuple<int, std::string, int> ptup{ tuple_cons(34, tup) };
std::tuple<std::string, int> tup2;
int i;
std::tie(i, tup2) = tuple_split(ptup);
ensure_equals("tuple_car() fail", i, 34);
ensure_equals("tuple_cdr() (0) fail", std::get<0>(tup2), "abc");
ensure_equals("tuple_cdr() (1) fail", std::get<1>(tup2), 17);
}
} // namespace tut
+239
View File
@@ -0,0 +1,239 @@
/**
* @file workqueue_test.cpp
* @author Nat Goodspeed
* @date 2021-10-07
* @brief Test for workqueue.
*
* $LicenseInfo:firstyear=2021&license=viewerlgpl$
* Copyright (c) 2021, Linden Research, Inc.
* $/LicenseInfo$
*/
// Precompiled header
#include "linden_common.h"
// associated header
#include "workqueue.h"
// STL headers
// std headers
#include <chrono>
#include <deque>
// external library headers
// other Linden headers
#include "../test/lltut.h"
#include "../test/catch_and_store_what_in.h"
#include "llcond.h"
#include "llcoros.h"
#include "lleventcoro.h"
#include "llstring.h"
#include "stringize.h"
using namespace LL;
using namespace std::literals::chrono_literals; // ms suffix
using namespace std::literals::string_literals; // s suffix
/*****************************************************************************
* TUT
*****************************************************************************/
namespace tut
{
struct workqueue_data
{
WorkSchedule queue{"queue"};
};
typedef test_group<workqueue_data> workqueue_group;
typedef workqueue_group::object object;
workqueue_group workqueuegrp("workqueue");
template<> template<>
void object::test<1>()
{
set_test_name("name");
ensure_equals("didn't capture name", queue.getKey(), "queue");
ensure("not findable", WorkSchedule::getInstance("queue") == queue.getWeak().lock());
WorkSchedule q2;
ensure("has no name", LLStringUtil::startsWith(q2.getKey(), "WorkQueue"));
}
template<> template<>
void object::test<2>()
{
set_test_name("post");
bool wasRun{ false };
// We only get away with binding a simple bool because we're running
// the work on the same thread.
queue.post([&wasRun](){ wasRun = true; });
queue.close();
ensure("ran too soon", ! wasRun);
queue.runUntilClose();
ensure("didn't run", wasRun);
}
template<> template<>
void object::test<3>()
{
set_test_name("postEvery");
// record of runs
using Shared = std::deque<WorkSchedule::TimePoint>;
// This is an example of how to share data between the originator of
// postEvery(work) and the work item itself, since usually a WorkSchedule
// is used to dispatch work to a different thread. Neither of them
// should call any of LLCond's wait methods: you don't want to stall
// either the worker thread or the originating thread (conventionally
// main). Use LLCond or a subclass even if all you want to do is
// signal the work item that it can quit; consider LLOneShotCond.
LLCond<Shared> data;
auto start = WorkSchedule::TimePoint::clock::now();
// 2s seems like a long time to wait, since it directly impacts the
// duration of this test program. Unfortunately GitHub's Mac runners
// are pretty wimpy, and we're getting spurious "too late" errors just
// because the thread doesn't wake up as soon as we want.
auto interval = 2s;
queue.postEvery(
interval,
[&data, count = 0]
() mutable
{
// record the timestamp at which this instance is running
data.update_one(
[](Shared& data)
{
data.push_back(WorkSchedule::TimePoint::clock::now());
});
// by the 3rd call, return false to stop
return (++count < 3);
});
// no convenient way to close() our queue while we've got a
// postEvery() running, so run until we have exhausted the iterations
// or we time out waiting
for (auto finish = start + 10*interval;
WorkSchedule::TimePoint::clock::now() < finish &&
data.get([](const Shared& data){ return data.size(); }) < 3; )
{
queue.runPending();
std::this_thread::sleep_for(interval/10);
}
// Take a copy of the captured deque.
Shared result = data.get();
ensure_equals("called wrong number of times", result.size(), 3);
// postEvery() assumes you want the first call to happen right away.
// Pretend our start time was (interval) earlier than that, to make
// our too early/too late tests uniform for all entries.
start -= interval;
for (size_t i = 0; i < result.size(); ++i)
{
auto diff = result[i] - start;
start += interval;
try
{
ensure(STRINGIZE("call " << i << " too soon"), diff >= interval);
ensure(STRINGIZE("call " << i << " too late"), diff < interval*1.5);
}
catch (const tut::failure&)
{
auto interval_ms = interval / 1ms;
auto diff_ms = diff / 1ms;
std::cerr << "interval " << interval_ms
<< "ms; diff " << diff_ms << "ms" << std::endl;
throw;
}
}
}
template<> template<>
void object::test<4>()
{
set_test_name("postTo");
WorkSchedule main("main");
auto qptr = WorkSchedule::getInstance("queue");
int result = 0;
main.postTo(
qptr,
[](){ return 17; },
// Note that a postTo() *callback* can safely bind a reference to
// a variable on the invoking thread, because the callback is run
// on the invoking thread. (Of course the bound variable must
// survive until the callback is called.)
[&result](int i){ result = i; });
// this should post the callback to main
qptr->runOne();
// this should run the callback
main.runOne();
ensure_equals("failed to run int callback", result, 17);
std::string alpha;
// postTo() handles arbitrary return types
main.postTo(
qptr,
[](){ return "abc"s; },
[&alpha](const std::string& s){ alpha = s; });
qptr->runPending();
main.runPending();
ensure_equals("failed to run string callback", alpha, "abc");
}
template<> template<>
void object::test<5>()
{
set_test_name("postTo with void return");
WorkSchedule main("main");
auto qptr = WorkSchedule::getInstance("queue");
std::string observe;
main.postTo(
qptr,
// The ONLY reason we can get away with binding a reference to
// 'observe' in our work callable is because we're directly
// calling qptr->runOne() on this same thread. It would be a
// mistake to do that if some other thread were servicing 'queue'.
[&observe](){ observe = "queue"; },
[&observe](){ observe.append(";main"); });
qptr->runOne();
main.runOne();
ensure_equals("failed to run both lambdas", observe, "queue;main");
}
template<> template<>
void object::test<6>()
{
set_test_name("waitForResult");
std::string stored;
// Try to call waitForResult() on this thread's main coroutine. It
// should throw because the main coroutine must service the queue.
auto what{ catch_what<WorkSchedule::Error>(
[this, &stored](){ stored = queue.waitForResult(
[](){ return "should throw"; }); }) };
ensure("lambda should not have run", stored.empty());
ensure_not("waitForResult() should have thrown", what.empty());
ensure(STRINGIZE("should mention waitForResult: " << what),
what.find("waitForResult") != std::string::npos);
// Call waitForResult() on a coroutine, with a string result.
LLCoros::instance().launch(
"waitForResult string",
[this, &stored]()
{ stored = queue.waitForResult(
[](){ return "string result"; }); });
llcoro::suspend();
// Nothing will have happened yet because, even if the coroutine did
// run immediately, all it did was to queue the inner lambda on
// 'queue'. Service it.
queue.runOne();
llcoro::suspend();
ensure_equals("bad waitForResult return", stored, "string result");
// Call waitForResult() on a coroutine, with a void callable.
stored.clear();
bool done = false;
LLCoros::instance().launch(
"waitForResult void",
[this, &stored, &done]()
{
queue.waitForResult([&stored](){ stored = "ran"; });
done = true;
});
llcoro::suspend();
queue.runOne();
llcoro::suspend();
ensure_equals("didn't run coroutine", stored, "ran");
ensure("void waitForResult() didn't return", done);
}
} // namespace tut
+240
View File
@@ -0,0 +1,240 @@
/**
* @file wrapllerrs.h
* @author Nat Goodspeed
* @date 2009-03-11
* @brief Define a class useful for unit tests that engage llerrs (LL_ERRS) functionality
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_WRAPLLERRS_H)
#define LL_WRAPLLERRS_H
#if LL_WINDOWS
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
#include <tut/tut.hpp>
#include "llerrorcontrol.h"
#include "llexception.h"
#include "stringize.h"
#include "../test/catch_and_store_what_in.h"
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <list>
#include <string>
struct WrapLLErrs
{
WrapLLErrs():
// Resetting Settings discards the default Recorder that writes to
// stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the
// console output of successful tests, potentially confusing things.
mPriorErrorSettings(LLError::saveAndResetSettings()),
// Save shutdown function called by LL_ERRS
mPriorFatal(LLError::getFatalFunction())
{
// Make LL_ERRS call our own operator() method
LLError::setFatalFunction(
[this](const std::string& message){ (*this)(message); });
}
~WrapLLErrs()
{
LLError::setFatalFunction(mPriorFatal);
LLError::restoreSettings(mPriorErrorSettings);
}
struct FatalException: public LLException
{
FatalException(const std::string& what): LLException(what) {}
};
void operator()(const std::string& message)
{
// Save message for later in case consumer wants to sense the result directly
error = message;
// Also throw an appropriate exception since calling code is likely to
// assume that control won't continue beyond LL_ERRS.
LLTHROW(FatalException(message));
}
/// Convenience wrapper for catch_what<FatalException>()
//
// The implementation makes it clear that this function need not be a
// member; it could easily be a free function. It is a member because it
// makes no sense to attempt to catch FatalException unless there is a
// WrapLLErrs instance in scope. Without a live WrapLLErrs instance, any
// LL_ERRS() reached by code within 'func' would terminate the test
// program instead of throwing FatalException.
//
// We were tempted to introduce a free function, likewise accepting
// arbitrary 'func', that would instantiate WrapLLErrs and then call
// catch_llerrs() on that instance. We decided against it, for this
// reason: on extending a test function containing a single call to that
// free function, a maintainer would most likely make additional calls to
// that free function, instead of switching to an explicit WrapLLErrs
// declaration with several calls to its catch_llerrs() member function.
// Even a construct such as WrapLLErrs().catch_llerrs(...) would make the
// object declaration more visible; it's not unreasonable to expect a
// maintainer to extend that by naming and reusing the WrapLLErrs instance.
template <typename FUNC>
std::string catch_llerrs(FUNC func)
{
return catch_what<FatalException>(func);
}
std::string error;
LLError::SettingsStoragePtr mPriorErrorSettings;
LLError::FatalFunction mPriorFatal;
};
/**
* Capture log messages. This is adapted (simplified) from the one in
* llerror_test.cpp.
*/
class CaptureLogRecorder : public LLError::Recorder, public boost::noncopyable
{
public:
CaptureLogRecorder()
: LLError::Recorder(),
boost::noncopyable(),
mMessages()
{
}
virtual ~CaptureLogRecorder()
{
}
virtual void recordMessage(LLError::ELevel level, const std::string& message)
{
mMessages.push_back(message);
}
friend inline
std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log)
{
return log.streamto(out);
}
/// Don't assume the message we want is necessarily the LAST log message
/// emitted by the underlying code; search backwards through all messages
/// for the sought string.
std::string messageWith(const std::string& search, bool required)
{
for (MessageList::const_reverse_iterator rmi(mMessages.rbegin()), rmend(mMessages.rend());
rmi != rmend; ++rmi)
{
if (rmi->find(search) != std::string::npos)
return *rmi;
}
// failed to find any such message
if (! required)
return std::string();
throw tut::failure(STRINGIZE("failed to find '" << search
<< "' in captured log messages:\n"
<< *this));
}
std::ostream& streamto(std::ostream& out) const
{
MessageList::const_iterator mi(mMessages.begin()), mend(mMessages.end());
if (mi != mend)
{
// handle first message separately: it doesn't get a newline
out << *mi++;
for ( ; mi != mend; ++mi)
{
// every subsequent message gets a newline
out << '\n' << *mi;
}
}
return out;
}
private:
typedef std::list<std::string> MessageList;
MessageList mMessages;
};
/**
* Capture log messages. This is adapted (simplified) from the one in
* llerror_test.cpp.
*/
class CaptureLog : public boost::noncopyable
{
public:
CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG)
// Mostly what we're trying to accomplish by saving and resetting
// LLError::Settings is to bypass the default RecordToStderr and
// RecordToWinDebug Recorders. As these are visible only inside
// llerror.cpp, we can't just call LLError::removeRecorder() with
// each. For certain tests we need to produce, capture and examine
// DEBUG log messages -- but we don't want to spam the user's console
// with that output. If it turns out that saveAndResetSettings() has
// some bad effect, give up and just let the DEBUG level log messages
// display.
: boost::noncopyable(),
mFatalFunction(LLError::getFatalFunction()),
mOldSettings(LLError::saveAndResetSettings()),
mRecorder(new CaptureLogRecorder())
{
// reinstate the FatalFunction we just reset
LLError::setFatalFunction(mFatalFunction);
LLError::setDefaultLevel(level);
LLError::addRecorder(mRecorder);
}
~CaptureLog()
{
LLError::removeRecorder(mRecorder);
LLError::restoreSettings(mOldSettings);
}
/// Don't assume the message we want is necessarily the LAST log message
/// emitted by the underlying code; search backwards through all messages
/// for the sought string.
std::string messageWith(const std::string& search, bool required=true)
{
return std::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required);
}
std::ostream& streamto(std::ostream& out) const
{
return std::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
}
friend inline std::ostream& operator<<(std::ostream& out, const CaptureLog& self)
{
return self.streamto(out);
}
private:
LLError::FatalFunction mFatalFunction;
LLError::SettingsStoragePtr mOldSettings;
LLError::RecorderPtr mRecorder;
};
#endif /* ! defined(LL_WRAPLLERRS_H) */