luajit-mm/rb_test.cxx

385 lines
11 KiB
C++

#include <stdio.h>
#include <string>
#include "rbtree.h"
using namespace std;
#define KEY_VAL_DELTA 123
#define RN(v) {v + KEY_VAL_DELTA, v, RB_RED}
#define BN(v) {v + KEY_VAL_DELTA, v, RB_BLACK}
#define ARRAY_SIZE(o) (sizeof((o))/sizeof((o)[0]))
class RB_UNIT_TEST {
public:
// If come across a bug, turn on "dump_tree" to instruct the unit-tester
// to dump the RB-tree before and after the problematic operation.
//
// The "id" is just used to identify the testing case, and hence ease
// trouble-shooting.
RB_UNIT_TEST(int test_id, rb_valcolor_t* nodes, int nd_num,
bool dump_tree = false) : _test_id(test_id),
_dump_tree(dump_tree) {
fprintf(stdout, "Testing unit test %d ...", test_id);
_rbt = rbt_create_manually(nodes, nd_num);
_save_fail_cnt = _fail_cnt;
}
RB_UNIT_TEST(int test_id, bool dump_tree = false):
_test_id(test_id), _dump_tree(dump_tree) {
fprintf(stdout, "Testing unit test %d ...", test_id);
_rbt = rbt_create();
_save_fail_cnt = _fail_cnt;
}
~RB_UNIT_TEST() {
if (_rbt)
rbt_destroy(_rbt);
fprintf(stdout, " %s\n", (_save_fail_cnt == _fail_cnt) ? "succ" : "fail");
}
static void Reset_Fail_Cnt() { _fail_cnt = 0; }
static int Get_Fail_Cnt() { return _fail_cnt; }
bool Delete(int val, int expect_ret_val = 1) {
if (DeleteHelper(val, expect_ret_val))
return true;
_fail_cnt ++;
return false;
}
bool Insert(int val, int expect_ret_val = 1) {
if (InsertHelper(val, expect_ret_val))
return true;
_fail_cnt ++;
return false;
}
bool BulkInsert(int* val_vect, int vect_len) {
for (int i = 0; i < vect_len; i++) {
if (!InsertHelper(val_vect[i], 1)) {
_fail_cnt ++;
return false;
}
}
return true;
}
bool Search(int key, intptr_t val, RBS_RESULT expect_ret_val = RBS_EXACT) {
if (SearchHelper(key, val, expect_ret_val))
return true;
_fail_cnt ++;
return false;
}
bool SearchLessEqu(int key, int le_key, RBS_RESULT expect_ret_val) {
if (SearchVariantHelper(key, le_key, true /*LE*/, expect_ret_val)) {
return true;
}
_fail_cnt ++;
return false;
}
bool SearchGreaterEqu(int key, int ge_key, RBS_RESULT expect_ret_val) {
if (SearchVariantHelper(key, ge_key, false /*GE*/, expect_ret_val)) {
return true;
}
_fail_cnt ++;
return false;
}
private:
bool DeleteHelper(int val, int expect_ret_val) {
if (!_rbt)
return false;
if (_dump_tree)
Dump_Tree("before_del");
if (!rbt_verify(_rbt))
return false;
int ret = rbt_delete(_rbt, val, 0);
if (_dump_tree)
Dump_Tree("after_del");
if (ret == expect_ret_val && rbt_verify(_rbt) && VerifyKeyVal())
return true;
fprintf(stdout, " fail to delete %d;", val);
return false;
}
bool InsertHelper(int key, int expect_ret_val) {
if (!_rbt)
return false;
if (_dump_tree)
Dump_Tree("before_insert");
if (!rbt_verify(_rbt))
return false;
int ret = rbt_insert(_rbt, key, key + KEY_VAL_DELTA);
if (_dump_tree)
Dump_Tree("after_insert");
if (ret == expect_ret_val && rbt_verify(_rbt) && VerifyKeyVal())
return true;
fprintf(stdout, " fail to insert %d;", key);
return false;
}
bool SearchHelper(int key, intptr_t val, RBS_RESULT expect_ret_val) {
if (!_rbt)
return false;
if (_dump_tree)
Dump_Tree("before_search");
if (!rbt_verify(_rbt))
return false;
intptr_t tmpval;
int ret = rbt_search(_rbt, key, &tmpval);
if (_dump_tree)
Dump_Tree("after_search");
bool succ = (ret == expect_ret_val && val == tmpval);
succ = succ || expect_ret_val == RBS_FAIL;
succ = succ && rbt_verify(_rbt);
if (succ)
return true;
fprintf(stdout, " fail to search %d;", key);
return false;
}
bool SearchVariantHelper(int key, int res_key,
bool le_variant, RBS_RESULT expect_ret_val) {
if (!_rbt)
return false;
if (_dump_tree)
Dump_Tree("before_search");
if (!rbt_verify(_rbt))
return false;
intptr_t res_val;
int res_key2;
RBS_RESULT ret = rbt_search_variant(_rbt, key, &res_key2, &res_val,
le_variant ? 1 : 0);
if (_dump_tree)
Dump_Tree("after_search");
bool succ = (ret == expect_ret_val) &&
(res_key2 == res_key) && (res_val == (KEY_VAL_DELTA + res_key));
succ = succ || (expect_ret_val == RBS_FAIL);
succ = succ && rbt_verify(_rbt);
if (succ)
return true;
fprintf(stdout, " fail to search %d;", key);
return false;
}
string GetDumpFileName(const char* op_name) {
char buf[200];
int len = snprintf(buf, sizeof(buf), "test_%d_%s.dot", _test_id, op_name);
buf[len] = '\0';
return string(buf);
}
// In most testing cases, we set "value = key + KEY_VAL_DELTA" (The
// rationale is just to ease testing). This function is to verify the
// key/value relation is broken.
//
bool VerifyKeyVal() {
for (rb_iter_t iter = rbt_iter_begin(_rbt),
iter_e = rbt_iter_begin(_rbt);
iter != iter_e;
iter = rbt_iter_inc(_iter, iter)) {
rb_node_t* nd = rbt_iter_deref(iter);
if (nd->value != nd->key + KEY_VAL_DELTA)
return false;
}
return true;
}
void Dump_Tree(const char* op_name) {
#ifdef DEBUG
rbt_dump_dot(_rbt, GetDumpFileName(op_name).c_str());
#else
(void)op_name;
#endif
}
int _test_id;
bool _dump_tree;
rb_tree_t* _rbt;
static int _fail_cnt;
int _save_fail_cnt;
};
int RB_UNIT_TEST::_fail_cnt = 0;
bool
unit_test() {
RB_UNIT_TEST::Reset_Fail_Cnt();
// test 8.
{
rb_valcolor_t nodes[] = { BN(1), RN(2) };
RB_UNIT_TEST ut(8, nodes, ARRAY_SIZE(nodes));
ut.Delete(1);
ut.Delete(2);
}
/////////////////////////////////////////////////////////////////////////
//
// Insert tests
//
/////////////////////////////////////////////////////////////////////////
//
fprintf(stdout, "\n>Testing insert operation...\n");
// Test 1, Cover the case 1, 2 and 3 of insertion.
// Testing case is from book :
// Thomas H. Cormen et. al, Instruction to Algorithm, 2nd edition,
// page 282.
{
rb_valcolor_t nodes[] = { BN(11), RN(2), BN(14), BN(1), BN(7),
RN(15), RN(5), RN(8) };
RB_UNIT_TEST ut(1, nodes, ARRAY_SIZE(nodes));
ut.Insert(4);
}
// Test 2. A contrived example for covering case 1', 2' and 3'
//
{
rb_valcolor_t nodes[] = { BN(4), BN(2), RN(10), RN(1), BN(7),
BN(11), RN(6), RN(9) };
RB_UNIT_TEST ut(2, nodes, ARRAY_SIZE(nodes));
ut.Insert(8);
}
/////////////////////////////////////////////////////////////////////////
//
// Delete Tests
//
/////////////////////////////////////////////////////////////////////////
//
fprintf(stdout, "\n>Testing delete operation...\n");
// test 1
{
rb_valcolor_t nodes[] = { BN(40), BN(20), BN(60), RN(10), RN(50), RN(70) };
RB_UNIT_TEST ut(1, nodes, ARRAY_SIZE(nodes));
ut.Delete(20);
}
// test 2
{
rb_valcolor_t nodes[] = { BN(40), BN(20), BN(60), RN(10), RN(30),
RN(50), RN(70) };
RB_UNIT_TEST ut(2, nodes, ARRAY_SIZE(nodes));
ut.Delete(20);
}
// test 3
{
rb_valcolor_t nodes[] = { BN(40), BN(20), BN(60), BN(10), BN(30),
BN(50), BN(70), RN(21) };
RB_UNIT_TEST ut(3, nodes, ARRAY_SIZE(nodes));
ut.Delete(20);
}
// test 4. Test the case 1 and 2 in rbt_delete_fixup().
{
rb_valcolor_t nodes[] = { BN(8), RN(2), BN(10), BN(0), BN(4), BN(9),
BN(11), BN(-1), BN(1), BN(3), RN(6),
BN(5), BN(7) };
RB_UNIT_TEST ut(4, nodes, ARRAY_SIZE(nodes));
ut.Delete(2);
}
// test 5. Test the case 2 and 4 in rbt_delete_fixup().
{
rb_valcolor_t nodes[] = { BN(80), RN(20), BN(100), BN(00), BN(40), BN(90),
BN(110), BN(-10), BN(10), BN(30),
BN(60), RN(50), RN(70),
BN(49), BN(51), BN(69), BN(71),
BN(-11), BN(-9), BN(9), BN(11),
BN(29), BN(31),
BN(89), BN(91), BN(109), BN(111) };
RB_UNIT_TEST ut(5, nodes, ARRAY_SIZE(nodes));
ut.Delete(20);
}
// test 6. For case 1' and 2'
{
rb_valcolor_t nodes[] = { BN(4), RN(2), BN(5), BN(1), BN(3) };
RB_UNIT_TEST ut(6, nodes, ARRAY_SIZE(nodes));
ut.Delete(5);
}
// test 7. For case 2', 3' and 4'.
{
rb_valcolor_t nodes[] = { BN(6), BN(2), BN(8), BN(1), RN(4), BN(7),
BN(9), BN(3), BN(5) };
RB_UNIT_TEST ut(7, nodes, ARRAY_SIZE(nodes));
ut.Delete(8);
}
// test 8.
{
rb_valcolor_t nodes[] = { BN(1), RN(2) };
RB_UNIT_TEST ut(8, nodes, ARRAY_SIZE(nodes));
ut.Delete(1);
ut.Delete(2);
}
// test 9.
{
rb_valcolor_t nodes[] = { BN(1) };
RB_UNIT_TEST ut(9, nodes, ARRAY_SIZE(nodes));
ut.Delete(1);
}
/////////////////////////////////////////////////////////////////////////
//
// Search Tests
//
/////////////////////////////////////////////////////////////////////////
//
fprintf(stdout, "\n>Testing search operation...\n");
// test 1
{
int val[] = { 1, 2, 3, 5, 7, 8 };
RB_UNIT_TEST ut(1);
ut.BulkInsert(val, ARRAY_SIZE(val));
ut.SearchLessEqu(4, 3, RBS_LESS);
ut.SearchLessEqu(3, 3, RBS_EXACT);
ut.SearchGreaterEqu(6, 7, RBS_GREATER);
ut.SearchGreaterEqu(7, 7, RBS_EXACT);
}
return RB_UNIT_TEST::Get_Fail_Cnt() == 0;
};
int
main(int argc, char** argv) {
if (!unit_test())
return 1;
return 0;
}