rcp/tests.cpp

432 lines
9.5 KiB
C++

#include <rcp.h>
#include <cassert>
#include <map>
#include <unordered_map>
#include <vector>
static int mybase_construct;
static int mybase_destruct;
static int myderived_construct;
static int myderived_destruct;
class MyBase
{
rcp_managed_root(MyBase);
protected:
MyBase(int x, int y)
{
this->x = x;
this->y = y;
mybase_construct++;
}
virtual ~MyBase()
{
mybase_destruct++;
}
public:
int x;
int y;
};
typedef rcp<MyBase> MyB;
class MyDerived : public MyBase
{
rcp_managed(MyDerived);
protected:
MyDerived(double v) : MyBase(1, 2)
{
this->v = v;
myderived_construct++;
}
virtual ~MyDerived()
{
myderived_destruct++;
}
public:
double v;
};
void test_class_hierarchy()
{
{
rcp<MyBase> mybase = MyBase::create(4, 5);
rcp<MyDerived> myderived = MyDerived::create(42.5);
}
assert(mybase_construct == 2);
assert(mybase_destruct == 2);
assert(myderived_construct == 1);
assert(myderived_destruct == 1);
}
void test_dereference()
{
rcp<MyBase> mybase = MyBase::create(2, 3);
assert(mybase->x == 2);
assert(mybase->y == 3);
assert((*mybase).x == 2);
assert((*mybase).y == 3);
rcp<MyBase> mybase2 = mybase;
assert(mybase == mybase2);
assert(!(mybase != mybase2));
}
void test_booleans()
{
rcp<MyBase> mybase = MyBase::create(2, 3);
assert(mybase);
assert(mybase != nullptr);
rcp<MyDerived> myderived;
assert(!myderived);
assert(myderived == nullptr);
}
void test_create()
{
MyB myb = MyB::create(8, 9);
assert(myb->x == 8);
}
struct MyObj
{
rcp_managed_root(MyObj);
public:
int v = {42};
template <typename R>
void add_to(R & receiver)
{
receiver.add(this);
}
};
struct Receiver
{
std::vector<rcp<MyObj>> objects;
void add(MyObj * o)
{
objects.push_back(rcp<MyObj>(o));
}
};
void test_multi_construct_from_raw_pointers()
{
Receiver r;
auto myo = MyObj::create();
for (int i = 0; i < 5; i++)
{
myo->add_to(r);
}
for (int i = 0; i < 5; i++)
{
assert(r.objects[i]->v == 42);
}
}
struct EventListener;
static std::vector<rcp<EventListener>> g_listeners;
struct EventListener
{
rcp_managed_root(EventListener);
public:
int events_received = 0;
void attach() { g_listeners.push_back(this); }
};
void test_listener_self_registration()
{
g_listeners.clear();
auto a = EventListener::create();
auto b = EventListener::create();
a->attach();
b->attach();
for (auto & l : g_listeners) l->events_received++;
assert(a->events_received == 1);
assert(b->events_received == 1);
g_listeners.clear();
}
void test_copy_assignment_decrements_previous_reference()
{
int constructed_before = mybase_construct;
int destructed_before = mybase_destruct;
{
MyB myb = MyB::create(12, 13);
MyB myb2 = MyB::create(14, 15);
myb = myb2;
assert(myb->x == 14);
assert(mybase_destruct == destructed_before + 1);
}
assert(mybase_construct == constructed_before + 2);
assert(mybase_destruct == destructed_before + 2);
}
void test_move_constructor()
{
int constructed_before = mybase_construct;
int destructed_before = mybase_destruct;
{
MyB a = MyB::create(1, 2);
MyB b = std::move(a);
assert(!a);
assert(b);
assert(b->x == 1);
assert(mybase_destruct == destructed_before);
}
assert(mybase_construct == constructed_before + 1);
assert(mybase_destruct == destructed_before + 1);
}
void test_move_assignment()
{
int constructed_before = mybase_construct;
int destructed_before = mybase_destruct;
{
MyB a = MyB::create(1, 2);
MyB b;
b = std::move(a);
assert(!a);
assert(b);
assert(b->x == 1);
assert(mybase_destruct == destructed_before);
}
assert(mybase_construct == constructed_before + 1);
assert(mybase_destruct == destructed_before + 1);
}
void test_move_assignment_same_object()
{
MyB a = MyB::create(1, 2);
MyB b = a;
assert(a.use_count() == 2);
b = std::move(a);
assert(!a);
assert(b);
assert(b->x == 1);
assert(b.use_count() == 1);
}
void test_move_assignment_releases_existing()
{
int constructed_before = mybase_construct;
int destructed_before = mybase_destruct;
{
MyB a = MyB::create(1, 2);
MyB b = MyB::create(3, 4);
b = std::move(a);
assert(!a);
assert(b->x == 1);
assert(mybase_destruct == destructed_before + 1);
}
assert(mybase_construct == constructed_before + 2);
assert(mybase_destruct == destructed_before + 2);
}
void test_cross_type_comparison()
{
rcp<MyDerived> derived = MyDerived::create(1.5);
rcp<MyBase> base = derived;
assert(base == derived);
assert(!(base != derived));
rcp<MyBase> other = MyBase::create(1, 2);
assert(base != other);
assert(!(base == other));
}
void test_reset()
{
int destructed_before = mybase_destruct;
MyB p = MyB::create(1, 2);
p.reset();
assert(!p);
assert(mybase_destruct == destructed_before + 1);
}
void test_upcast()
{
rcp<MyDerived> derived = MyDerived::create(1.5);
rcp<MyBase> base = derived;
assert(base);
assert(base->x == 1);
}
void test_move_upcast()
{
int constructed_before = mybase_construct;
int destructed_before = mybase_destruct;
{
rcp<MyDerived> derived = MyDerived::create(1.5);
rcp<MyBase> base = std::move(derived);
assert(!derived);
assert(base);
assert(base->x == 1);
assert(mybase_destruct == destructed_before);
}
assert(mybase_construct == constructed_before + 1);
assert(mybase_destruct == destructed_before + 1);
}
void test_dynamic_cast_success()
{
rcp<MyDerived> derived = MyDerived::create(1.5);
rcp<MyBase> base = derived;
rcp<MyDerived> back = rcp_dynamic_cast<MyDerived>(base);
assert(back);
assert(back->v == 1.5);
}
void test_dynamic_cast_move_success()
{
int constructed_before = mybase_construct;
int destructed_before = mybase_destruct;
{
rcp<MyBase> base = MyDerived::create(1.5);
rcp<MyDerived> derived = rcp_dynamic_cast<MyDerived>(std::move(base));
assert(!base);
assert(derived);
assert(derived->v == 1.5);
assert(mybase_destruct == destructed_before);
}
assert(mybase_construct == constructed_before + 1);
assert(mybase_destruct == destructed_before + 1);
}
void test_dynamic_cast_move_failure()
{
rcp<MyBase> base = MyBase::create(1, 2);
rcp<MyDerived> derived = rcp_dynamic_cast<MyDerived>(std::move(base));
assert(!derived);
assert(base);
}
void test_dynamic_cast_failure()
{
rcp<MyBase> base = MyBase::create(1, 2);
rcp<MyDerived> derived = rcp_dynamic_cast<MyDerived>(base);
assert(!derived);
}
/* Simulate a external class that cannot be modified */
namespace external
{
class Counter
{
public:
int start;
int count;
Counter(int start) : start(start), count(start) {}
virtual ~Counter() {}
};
}
static int external_destruct;
/* Create a managed wrapper for the external class. */
class Counter : public external::Counter
{
rcp_managed_root(Counter);
protected:
Counter(int start) : external::Counter(start) {}
~Counter() { external_destruct++; }
};
void test_ordering()
{
MyB a = MyB::create(1, 2);
MyB b = MyB::create(3, 4);
MyB a2 = a;
assert(!(a < a2));
assert(a <= a2);
assert(!(a > a2));
assert(a >= a2);
assert((a < b) != (b < a));
assert((a < b) == (b > a));
assert((a <= b) == (b >= a));
std::map<MyB, int> m;
m[a] = 1;
m[b] = 2;
assert(m[a2] == 1);
assert(m[b] == 2);
}
void test_use_count()
{
MyB a = MyB::create(1, 2);
assert(a.use_count() == 1);
{
MyB b = a;
assert(a.use_count() == 2);
assert(b.use_count() == 2);
}
assert(a.use_count() == 1);
MyB empty;
assert(empty.use_count() == 0);
}
void test_swap()
{
MyB a = MyB::create(1, 2);
MyB b = MyB::create(3, 4);
a.swap(b);
assert(a->x == 3);
assert(b->x == 1);
}
void test_hash()
{
MyB a = MyB::create(1, 2);
MyB b = a;
std::unordered_map<MyB, int> map;
map[a] = 42;
assert(map[b] == 42);
MyB c = MyB::create(3, 4);
assert(map.find(c) == map.end());
}
void test_external_class()
{
int before = external_destruct;
{
auto a = Counter::create(10);
auto b = Counter::create(20);
assert(a->count == 10);
assert(b->count == 20);
a->count++;
assert(a->count == 11);
}
assert(external_destruct == before + 2);
}
int main(int argc, char * argv[])
{
test_class_hierarchy();
test_dereference();
test_booleans();
test_create();
test_multi_construct_from_raw_pointers();
test_listener_self_registration();
test_copy_assignment_decrements_previous_reference();
test_cross_type_comparison();
test_reset();
test_move_constructor();
test_move_assignment();
test_move_assignment_same_object();
test_move_assignment_releases_existing();
test_upcast();
test_move_upcast();
test_dynamic_cast_success();
test_dynamic_cast_failure();
test_dynamic_cast_move_success();
test_dynamic_cast_move_failure();
test_ordering();
test_use_count();
test_swap();
test_hash();
test_external_class();
return 0;
}