phoenix-firestorm/indra/llcommon/tests/llinstancetracker_test.cpp

284 lines
9.9 KiB
C++

/**
* @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
// external library headers
#include <boost/scoped_ptr.hpp>
// other Linden headers
#include "../test/lltut.h"
#include "wrapllerrs.h"
struct Badness: public std::runtime_error
{
Badness(const std::string& what): std::runtime_error(what) {}
};
struct Keyed: public LLInstanceTracker<Keyed, std::string>
{
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);
Keyed* found = Keyed::getInstance("one");
ensure("couldn't find stack Keyed", found);
ensure_equals("found wrong Keyed instance", found, &one);
{
boost::scoped_ptr<Keyed> two(new Keyed("two"));
ensure_equals(Keyed::instanceCount(), 2);
Keyed* found = Keyed::getInstance("two");
ensure("couldn't find heap Keyed", found);
ensure_equals("found wrong Keyed instance", found, two.get());
}
ensure_equals(Keyed::instanceCount(), 1);
}
Keyed* 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);
Unkeyed* dangling = NULL;
{
Unkeyed one;
ensure_equals(Unkeyed::instanceCount(), 1);
Unkeyed* found = Unkeyed::getInstance(&one);
ensure_equals(found, &one);
{
boost::scoped_ptr<Unkeyed> two(new Unkeyed);
ensure_equals(Unkeyed::instanceCount(), 2);
Unkeyed* found = Unkeyed::getInstance(two.get());
ensure_equals(found, two.get());
}
ensure_equals(Unkeyed::instanceCount(), 1);
// store an unwise pointer to a temp Unkeyed instance
dangling = &one;
} // make that instance vanish
// check the now-invalid pointer to the destroyed instance
ensure("getInstance(T*) failed to track destruction", ! Unkeyed::getInstance(dangling));
ensure_equals(Unkeyed::instanceCount(), 0);
}
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;
StringVector keys(Keyed::beginKeys(), Keyed::endKeys());
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
// beginInstances(): 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 (Keyed::instance_iter ii(Keyed::beginInstances()), iend(Keyed::endInstances());
ii != iend; ++ii)
{
Keyed& ref = *ii;
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 (Unkeyed::instance_iter ii(Unkeyed::beginInstances()), iend(Unkeyed::endInstances()); ii != iend; ++ii)
{
Unkeyed& ref = *ii;
ensure_equals("spurious instance", instances.erase(&ref), 1);
}
ensure_equals("unreported instance", instances.size(), 0);
}
template<> template<>
void object::test<5>()
{
set_test_name("delete Keyed with outstanding instance_iter");
std::string what;
Keyed* keyed = new Keyed("delete Keyed with outstanding instance_iter");
{
WrapLLErrs wrapper;
Keyed::instance_iter i(Keyed::beginInstances());
what = catch_llerrs([&keyed](){
delete keyed;
});
}
ensure(! what.empty());
}
template<> template<>
void object::test<6>()
{
set_test_name("delete Keyed with outstanding key_iter");
std::string what;
Keyed* keyed = new Keyed("delete Keyed with outstanding key_it");
{
WrapLLErrs wrapper;
Keyed::key_iter i(Keyed::beginKeys());
what = catch_llerrs([&keyed](){
delete keyed;
});
}
ensure(! what.empty());
}
template<> template<>
void object::test<7>()
{
set_test_name("delete Unkeyed with outstanding instance_iter");
std::string what;
Unkeyed* unkeyed = new Unkeyed;
{
WrapLLErrs wrapper;
Unkeyed::instance_iter i(Unkeyed::beginInstances());
what = catch_llerrs([&unkeyed](){
delete unkeyed;
});
}
ensure(! what.empty());
}
template<> template<>
void object::test<8>()
{
set_test_name("exception in subclass ctor");
typedef std::set<Unkeyed*> InstanceSet;
InstanceSet existing;
// We can't use the iterator-range InstanceSet constructor because
// beginInstances() returns an iterator that dereferences to an
// Unkeyed&, not an Unkeyed*.
for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
ukend(Unkeyed::endInstances());
uki != ukend; ++uki)
{
existing.insert(&*uki);
}
try
{
// We don't expect the assignment to take place because we expect
// Unkeyed to respond to the non-empty string param by throwing.
// We know the LLInstanceTracker base-class constructor will have
// run before Unkeyed's constructor, therefore the new instance
// will have added itself to the underlying set. The whole
// question is, when Unkeyed's constructor throws, will
// LLInstanceTracker's destructor remove it from the set? I
// realize we're testing the C++ implementation more than
// Unkeyed's implementation, but this seems an important point to
// nail down.
new Unkeyed("throw");
}
catch (const Badness&)
{
}
// Ensure that every member of the new, updated set of Unkeyed
// instances was also present in the original set. If that's not true,
// it's because our new Unkeyed ended up in the updated set despite
// its constructor exception.
for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
ukend(Unkeyed::endInstances());
uki != ukend; ++uki)
{
ensure("failed to remove instance", existing.find(&*uki) != existing.end());
}
}
} // namespace tut