/*
	Test-ZKVTree.cpp
	Author: James Russell

	Purpose :	Unit tests for ZKVTree

	Changelog :
	12/18/2011 - Removed dependency on ZString (crertel)
	2/20/2011 - Created (jcrussell)
*/


#include "ZUnitTest.hpp"

#include <ZUtil/ZSmartPointer.hpp>
#include <ZUtil/ZKVTree.hpp>

#include <iostream>

static const char* test_Put_Add_Begin_End();
static const char* test_Find_Iterator();
static const char* test_Get();
static const char* test_Erase_Clear();
static const char* test_Find();
static const char* test_Merge();
static const char* test_EmptyKey();
static const char* test_NestedSubscript();
static const char* test_Operators();

//List of unit tests
ZUnitTest ZKVTreeUnitTests[] =
{
	{ "ZKVTree: Put, Add, Begin, End",	test_Put_Add_Begin_End },
	{ "ZKVTree: Find, Iterator",		test_Find_Iterator },
	{ "ZKVTree: Get",					test_Get },
	{ "ZKVTree: Erase, Clear",			test_Erase_Clear },
	{ "ZKVTree: Merge",					test_Merge },
	{ "ZKVTree: Empty Key",				test_EmptyKey },
	{ "ZKVTree: Nested Subscript",		test_NestedSubscript },
	{ "ZKVTree: Operators",				test_Operators },
};

DECLARE_ZTESTBLOCK(ZKVTree);

/*************************************************************************/

static const char* test_Put_Add_Begin_End()
{
	ZKVTree tree1;
	ZKVTree tree2;

	// adding out of order to test the insert order
	tree1.Put("a", "1");
	tree1.Put("b", "4");
	tree1.Put("a[1]", "2");
	tree1.Put("a[2]", "3");
	tree1.Put("A", "5");
	tree1.Put("A.b", "6");
	tree1.Put("A.c", "8");
	tree1.Put("A.d", "9");
	tree1.Put("A.b[1]", "7");
	tree1.Put("A.B", "10");
	tree1.Put("A.B.c[0]", "11");
	tree1.Put("A.B.c[1]", "12");
	tree1.Put("A.B[1]", "13");
	tree1.Put("A.B[1].c", "14");
	tree1.Put("A.B[1].d", "15");
	tree1.Put("B", "16");
	tree1.Put("B.c", "17");
	tree1.Put("B.d", "18");

	size_t count = 1;
	
	for (ZKVTree::Iterator itr = tree1.Begin(); itr != tree1.End(); ++itr)
	{
		//This is just to inspect the order of values iterated
		ZString name = itr.GetName();
		ZString	val = itr.GetValue();

		TASSERT(ZStringAlgo::NumericInt(val) == (int)count, "ZRegistry::Iterator visited nodes in incorrect order!");

		count++;
	}
	
	//This will build the registry as described at the top of ZRegistry.hpp
	tree2.Put("A", "0");
	tree2.Add("A", "B", "8");
	tree2.Add("A.B", "c", "16");
	tree2.Add("A.B", "c", "32");
	tree2.Add("A.B", "", "100");
	tree2.Add("A.B", "", "200");
	tree2.Add("A", "B", "8");
	tree2.Add("A.B[1]", "c", "64");
	tree2.Add("A.B[1]", "d", "128");
	tree2.Add("A.B[1]", "", "10");
	tree2.Add("A.B[1]", "", "20");
	tree2.Add("A", "b", "5");
	tree2.Add("A", "b", "10");
	tree2.Add("A", "c", "15");
	tree2.Add("A", "d", "20");
	tree2.Put("B", "8");
	tree2.Add("B", "c", "50");
	tree2.Add("B", "d", "100");
	tree2.Add("a", "1");
	tree2.Add("a", "2");
	tree2.Add("a", "3");
	tree2.Put("b", "4");
	
	//Test using get on each of those
	TASSERT(tree2.Get("a") == "1", "GetValue() failed to return correct value for path 'a'!");
	TASSERT(tree2.Get("a[1]") == "2", "GetValue() failed to return correct value for path 'a[1]'!");
	TASSERT(tree2.Get("a[2]") == "3", "GetValue() failed to return correct value for path 'a[2]'!");
	TASSERT(tree2.Get("b") == "4", "GetValue() failed to return correct value for path 'b'!");
	TASSERT(tree2.Get("A.b") == "5", "GetValue() failed to return correct value for path 'A.b'!");
	TASSERT(tree2.Get("A.b[1]") == "10", "GetValue() failed to return correct value for path 'A.b[1]'!");
	TASSERT(tree2.Get("A.c") == "15", "GetValue() failed to return correct value for path 'A.c'!");
	TASSERT(tree2.Get("A.d") == "20", "GetValue() failed to return correct value for path 'A.d'!");
	TASSERT(tree2.Get("B.c") == "50", "GetValue() faield to return correct value for path 'B.c'!");
	TASSERT(tree2.Get("B.d") == "100", "GetValue() failed to return correct value for path 'B.d'!");
	TASSERT(tree2.Get("A.B.c") == "16", "GetValue() failed to return correct value for path 'A.B.c'!");
	TASSERT(tree2.Get("A.B.c[1]") == "32", "GetValue() failed to return correct value for path 'A.B.c[1]'!");
	TASSERT(tree2.Get("A.B[1].c") == "64", "GetValue() failed to return corrrect value for path 'A.B[1].c'!");
	TASSERT(tree2.Get("A.B[1].d") == "128", "GetValue() failed to return correct value for path 'A.B[1].d'!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Find_Iterator()
{	
	ZKVTree tree;

	tree.Put("a", "1");
	tree.Put("b", "4");
	tree.Put("a[1]", "2");
	tree.Put("a[2]", "3");
	tree.Put("A", "5");
	tree.Put("A.b", "6");
	tree.Put("A.c", "8");
	tree.Put("A.d", "9");
	tree.Put("A.b[1]", "7");
	tree.Put("A.B", "10");
	tree.Put("A.B.c[0]", "11");
	tree.Put("A.B.c[1]", "12");
	tree.Put("A.B[1]", "13");
	tree.Put("A.B[1].c", "14");
	tree.Put("A.B[1].d", "15");
	tree.Put("B", "16");
	tree.Put("B.c", "17");
	tree.Put("B.d", "18");

	ZKVTree::Iterator itr = tree.Find("A.B");

	TASSERT(itr.GetName() == "B", "GetName() failed after ZKVTree::Find('A.B')!");
	TASSERT(itr.GetValue() == "10", "GetValue() failed after ZKVTree::Find('A.B')!");

	itr.NextSibling();

	TASSERT(itr.GetName() == "B", "GetName() failed after Iterator::NextSibling()!");
	TASSERT(itr.GetValue() == "13", "GetName() failed after Iterator::NextSibling()!");

	itr.Child(1);

	TASSERT(itr.GetName() == "d", "GetName() failed after Iterator::Child()!");
	TASSERT(itr.GetValue() == "15", "GetValue() failed after Iterator::Child()!");

	itr.PrevSibling();

	TASSERT(itr.GetName() == "c", "GetName() failed after Iterator::PrevSibling()!");
	TASSERT(itr.GetValue() == "14", "GetValue() failed after Iterator::PrevSibling()!");

	itr.Parent();

	TASSERT(itr.GetName() == "B", "GetName() failed after Iterator::NextSibling()!");
	TASSERT(itr.GetValue() == "13", "GetName() failed after Iterator::NextSibling()!");
	TASSERT(itr.GetSiblingCount() == 6, "GetSiblingCount() failed on node 'A.B[1]'!");
	TASSERT(itr.GetChildCount() == 2, "GetChildCount() failed on node 'A.B[1]'");

	ZKVTree::Iterator child_itr = itr.ChildItr(0);
	ZKVTree::Iterator sibling_itr = itr.SiblingItr(1);

	TASSERT(child_itr.GetName() == "c", "GetName() failed after Iterator::GetChild(0)!");
	TASSERT(child_itr.GetValue() == "14", "GetValue() failed after Iterator::GetChild(1)!");
	TASSERT(sibling_itr.GetName() == "b", "GetName() failed after GetSibling(1)!");
	TASSERT(sibling_itr.GetValue() == "7", "GetValue() failed after GetSibling(1)!");

	child_itr = tree.Find(itr, "d");

	TASSERT(child_itr.GetName() == "d", "GetName() failed after Find(itr, 'd')!");
	TASSERT(child_itr.GetValue() == "15", "GetValue() failed after Find(itr, 'd')!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Get()
{
	ZKVTree tree;

	TASSERT(tree.Put("a", "1") != tree.End(), "PutValue() failed on a valid path 'a'!");
	TASSERT(tree.Put("a[1]", "2") != tree.End(), "PutValue() failed on a valid path 'a[1]'!");

	TASSERT(tree.Put("A.b", "5") != tree.End(), "PutValue failed on a valid path 'A.b'!");
	TASSERT(tree.Put("A.b[1]", "10") != tree.End(), "PutValue failed on a valid path 'A.b[1]'!");

	ZString val;

	val = tree.Get("a");

	TASSERT(val == "1", "GetValue() on the path 'a' returned an incorrect value!");

	val = tree.Get("a[1]");

	TASSERT(val == "2", "GetValue() on the path 'a[1]' returned an incorrect value!");

	val = tree.Get("A.b");

	TASSERT(val == "5", "GetValue() on the path 'A.b' returned an incorrect value!");

	val = tree.Get("A.b[1]");

	TASSERT(val == "10", "GetValue() on the path 'A.b[1]' returned an incorrect value!");

	tree.Put("a", "3");

	val = tree.Get("a");

	TASSERT(val == "3", "GetValue() on the path 'a' returned an incorrect value after overwrite!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Erase_Clear()
{
	ZKVTree tree;

	tree.Put("a", "1");
	tree.Put("b", "4");
	tree.Put("a[1]", "2");
	tree.Put("a[2]", "3");
	tree.Put("A", "5");
	tree.Put("A.b", "6");
	tree.Put("A.c", "8");
	tree.Put("A.d", "9");
	tree.Put("A.b[1]", "7");
	tree.Put("A.B", "10");
	tree.Put("A.B.c[0]", "11");
	tree.Put("A.B.c[1]", "12");
	tree.Put("A.B[1]", "13");
	tree.Put("A.B[1].c", "14");
	tree.Put("A.B[1].d", "15");
	tree.Put("B", "16");
	tree.Put("B.c", "17");
	tree.Put("B.d", "18");

	tree.Erase("A.B[1]");

	TASSERT(tree.Find("A.B") != tree.End(), "Find('A.B') returns invalid iterator after Erase('A.B[1]')!");

	TASSERT(tree.Find("A.B[1]") == tree.End(), "Find('A.B[1]') returns valid iterator after Erase('A.B[1]')!");
	TASSERT(tree.Find("A.B[1].c") == tree.End(), "Find('A.B[1].c') returns valid iterator after Erase('A.B[1]')!");

	tree.Put("A.B[1]", "derp");

	TASSERT(tree.Find("A.B[1]").GetValue() == "derp", "Find('A.B[1]') returns invalid iterator after Erase / Put!");

	tree.Clear();

	ZKVTree::Iterator itr = tree.Begin();

	TASSERT(itr == tree.End(), "Clear failed to erase values from the tree!");
	TASSERT(tree.Find("B") == tree.End(), "Find('B') returns valid iterator after Clear()!");
	TASSERT(tree.Find("B.c") == tree.End(), "Find('B.c') returns valid iterator after Clear()!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Merge()
{
	ZKVTree tree1;
	ZKVTree tree2;
	ZKVTree tree3;

	tree1.Add("a", "10");
	tree1.Add("b", "40");
	tree1.Add("a", "20");
	tree1.Add("a", "30");

	tree2.Add("a", "10");
	tree2.Add("b", "40");
	tree2.Add("a", "20");
	tree2.Add("a", "30");

	tree3.Put("a", "1");
	tree3.Put("b", "4");
	tree3.Put("a[1]", "2");
	tree3.Put("a[2]", "3");
	tree3.Put("A", "5");
	tree3.Put("A.b", "6");
	tree3.Put("A.c", "8");
	tree3.Put("A.d", "9");
	tree3.Put("A.b[1]", "7");
	tree3.Put("A.B", "10");
	tree3.Put("A.B.c[0]", "11");
	tree3.Put("A.B.c[1]", "12");
	tree3.Put("A.B[1]", "13");
	tree3.Put("A.B[1].c", "14");
	tree3.Put("A.B[1].d", "15");
	tree3.Put("B", "16");
	tree3.Put("B.c", "17");
	tree3.Put("B.d", "18");

	tree1.Merge(tree3, false);
	tree2.Merge(tree3, true);

	TASSERT(tree1.Get("a") == "10", "Get('a') returned invalid value after Merge(false)!");
	TASSERT(tree1.Get("b") == "40", "Get('b') returned invalid value after Merge(false)!");
	TASSERT(tree1.Get("a[1]") == "20", "Get('a[1]') returned invalid value after Merge(false)!");
	TASSERT(tree1.Get("a[2]") == "30", "Get('a[2]') returned invalid value after Merge(false)!");

	TASSERT(tree2.Get("a") == "1", "Get('a') returned invalid value after Merge(true)!");
	TASSERT(tree2.Get("b") == "4", "Get('b') returned invalid value after Merge(true)!");
	TASSERT(tree2.Get("a[1]") == "2", "Get('a[1]') returned invalid value after Merge(true)!");
	TASSERT(tree2.Get("a[2]") == "3", "Get('a[2]') returned invalid value after Merge(true)!");

	TASSERT(tree1.Get("A.b") == "6", "Get('A.b') returned invalid value after Merge(false)!");
	TASSERT(tree2.Get("A.b") == "6", "Get('A.b') returned invalid value after Merge(true)!");

	TASSERT(tree1.Get("A.B[1].c") == "14", "Get('A.B[1].c') returned invalid value after Merge(false)!");
	TASSERT(tree2.Get("A.B[1].c") == "14", "Get('A.B[1].c') returned invalid value after Merge(true)!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_EmptyKey()
{
	ZKVTree tree;

	tree.Add("", "1");
	tree.Add("", "2");
	tree.Add("", "3");
	tree.Add("", "4");

	TASSERT(tree.Get("") == "1", "First unnamed key has invalid value!");
	TASSERT(tree.Get("[1]") == "2", "Second unnamed key has invalid value!");
	TASSERT(tree.Get("[2]") == "3", "Third unnamed key has invalid value!");
	TASSERT(tree.Get("[3]") == "4", "Fourth unnamed key has invalid value!");

	tree.Add("", "", "11");

	TASSERT(tree.Get("[0].[0]") == "11", "First child of first unnamed key has invalid value!");
	TASSERT(tree.Get(".") == "11", "Invalid path conversion on first child of first unnamed key!");
	
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_NestedSubscript()
{
	ZKVTree tree;

	tree.Put("A", "A1");
	tree.Put("A[0][0]", "A11");
	tree.Add("A", "", "A12");
	
	TASSERT(tree.Get("A[0]") == "A1", "Key A[0] has invalid value!");
	TASSERT(tree.Get("A[0][0]") == "A11", "Key A[0][0] has invalid value!");
	TASSERT(tree.Get("A[0][1]") == "A12", "Key A[0][1] has invalid value!");

	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* test_Operators()
{
	ZKVTree tree1;
	ZKVTree tree2;

	tree1.Put("A", "A1");
	tree1.Put("A[0][0]", "A11");
	tree1.Add("A", "", "A12");

	tree2 = tree1;

	TASSERT(tree2.Get("A[0]") == "A1", "Key A[0] has invalid value!");
	TASSERT(tree2.Get("A[0][0]") == "A11", "Key A[0][0] has invalid value!");
	TASSERT(tree2.Get("A[0][1]") == "A12", "Key A[0][1] has invalid value!");

	return ZTEST_SUCCESS;

}

