231 lines
8.5 KiB
C++
231 lines
8.5 KiB
C++
/**
|
|
* @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
|