/***************************************************************************
 *            test-recursive.c
 *
 *  Wed Feb  1 21:54:49 2006
 *  Copyright  2006  Neil Williams
 *  linux@codehelp.co.uk
 ****************************************************************************/
/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor
 *  Boston, MA  02110-1301,  USA 
 */

#include <glib.h>
#include <glib/gprintf.h>
#include "config.h"
#include "qof.h"
#include "test-engine-stuff.h"
#include "test-stuff.h"

#define GRAND_MODULE_NAME "recursive-grandparent"
#define PARENT_MODULE_NAME "recursive-parent"
#define CHILD_MODULE_NAME "recursive-child"
#define GRAND_MODULE_DESC "Recursive Grand Parent Test"
#define PARENT_MODULE_DESC "Recursive Parent Test"
#define CHILD_MODULE_DESC "Recursive Child Test"
#define OBJ_NAME "somename"
#define OBJ_AMOUNT "anamount"
#define OBJ_DATE "nottoday"
#define OBJ_DISCOUNT "hefty"
#define OBJ_VERSION "early"
#define OBJ_MINOR "tiny"
#define OBJ_ACTIVE "ofcourse"
#define OBJ_FLAG   "tiny_flag"
#define OBJ_RELATIVE "family"
#define OBJ_LIST "descendents"

/* set to TRUE to get QSF XML output
 * requires QSF available (i.e. make install) */
static gboolean debug = FALSE;

/* simple object structure */
typedef struct child_s
{
	QofInstance inst;
	gchar *Name;
	gchar flag;
	QofNumeric Amount;
	QofTime *date;
	gdouble discount;			/* cheap pun, I know. */
	gboolean active;
	gint32 version;
	gint64 minor;
} mychild;

/* simple object structure */
typedef struct parent_s
{
	QofInstance inst;
	mychild *child;
	gchar *Name;
	gchar flag;
	QofNumeric Amount;
	QofTime *date;
	gdouble discount;			/* cheap pun, I know. */
	gboolean active;
	gint32 version;
	gint64 minor;
} myparent;

	/* simple object structure */
typedef struct grand_s
{
	QofInstance inst;
	myparent *child;
	GList *descend;
	gchar *Name;
	gchar flag;
	QofNumeric Amount;
	QofTime *date;
	gdouble discount;			/* cheap pun, I know. */
	gboolean active;
	gint32 version;
	gint64 minor;
} mygrand;

mygrand *grand_create (QofBook *);
myparent *parent_create (QofBook *);
mychild *child_create (QofBook *);

gboolean mygrandRegister (void);
gboolean myparentRegister (void);
gboolean mychildRegister (void);

/* obvious setter functions */
void grand_setName (mygrand *, gchar *);
void grand_setAmount (mygrand *, QofNumeric);
void grand_setDate (mygrand *, QofTime *h);
void grand_setDiscount (mygrand *, gdouble);
void grand_setActive (mygrand *, gboolean);
void grand_setVersion (mygrand *, gint32);
void grand_setMinor (mygrand *, gint64);
void grand_setFlag (mygrand *, gchar);

/* obvious getter functions */
gchar *grand_getName (mygrand *);
QofNumeric grand_getAmount (mygrand *);
QofTime *grand_getDate (mygrand *);
gdouble grand_getDiscount (mygrand *);
gboolean grand_getActive (mygrand *);
gint32 grand_getVersion (mygrand *);
gint64 grand_getMinor (mygrand *);
gchar grand_getFlag (mygrand *);

/* obvious setter functions */
void parent_setName (myparent *, gchar *);
void parent_setAmount (myparent *, QofNumeric);
void parent_setDate (myparent *, QofTime *h);
void parent_setDiscount (myparent *, gdouble);
void parent_setActive (myparent *, gboolean);
void parent_setVersion (myparent *, gint32);
void parent_setMinor (myparent *, gint64);
void parent_setFlag (myparent *, gchar);

/* obvious getter functions */
gchar *parent_getName (myparent *);
QofNumeric parent_getAmount (myparent *);
QofTime *parent_getDate (myparent *);
gdouble parent_getDiscount (myparent *);
gboolean parent_getActive (myparent *);
gint32 parent_getVersion (myparent *);
gint64 parent_getMinor (myparent *);
gchar parent_getFlag (myparent *);

/* obvious setter functions */
void child_setName (mychild *, gchar *);
void child_setAmount (mychild *, QofNumeric);
void child_setDate (mychild *, QofTime *h);
void child_setDiscount (mychild *, gdouble);
void child_setActive (mychild *, gboolean);
void child_setVersion (mychild *, gint32);
void child_setMinor (mychild *, gint64);
void child_setFlag (mychild *, gchar);

/* obvious getter functions */
gchar *child_getName (mychild *);
QofNumeric child_getAmount (mychild *);
QofTime *child_getDate (mychild *);
gdouble child_getDiscount (mychild *);
gboolean child_getActive (mychild *);
gint32 child_getVersion (mychild *);
gint64 child_getMinor (mychild *);
gchar child_getFlag (mychild *);

mygrand *
grand_create (QofBook * book)
{
	mygrand *g;

	g_return_val_if_fail (book, NULL);
	g = g_new0 (mygrand, 1);
	qof_instance_init (&g->inst, GRAND_MODULE_NAME, book);
	/* implement qof_time_random? */
	g->date = qof_time_get_current ();
	g->discount = get_random_double ();;
	g->active = get_random_boolean ();
	g->version = get_random_int_in_range (1, 10000);
	g->minor = get_random_int_in_range (100001, 99999999);
	g->flag = get_random_character ();
	g->Name = get_random_string ();
	g->Amount = get_random_qof_numeric ();
	g->child = NULL;
	g->descend = NULL;
	qof_event_gen (&g->inst.entity, QOF_EVENT_CREATE, NULL);
	return g;
}

myparent *
parent_create (QofBook * book)
{
	myparent *g;

	g_return_val_if_fail (book, NULL);
	g = g_new0 (myparent, 1);
	qof_instance_init (&g->inst, PARENT_MODULE_NAME, book);
	g->date = qof_time_get_current ();
	g->discount = get_random_double ();
	g->active = get_random_boolean ();
	g->version = get_random_int_in_range (1, 10000);
	g->minor = get_random_int_in_range (100001, 99999999);
	g->flag = get_random_character ();
	g->Name = get_random_string ();
	g->Amount = get_random_qof_numeric ();
	g->child = NULL;
	qof_event_gen (&g->inst.entity, QOF_EVENT_CREATE, NULL);
	return g;
}

mychild *
child_create (QofBook * book)
{
	mychild *g;

	g_return_val_if_fail (book, NULL);
	g = g_new0 (mychild, 1);
	qof_instance_init (&g->inst, CHILD_MODULE_NAME, book);
	g->date = qof_time_get_current ();
	g->discount = get_random_double ();
	g->active = get_random_boolean ();
	g->version = get_random_int_in_range (1, 10000);
	g->minor = get_random_int_in_range (100001, 99999999);
	g->flag = get_random_character ();
	g->Name = get_random_string ();
	g->Amount = get_random_qof_numeric ();
	qof_event_gen (&g->inst.entity, QOF_EVENT_CREATE, NULL);
	return g;
}

static void
descend_cb (QofEntity * ent, gpointer user_data)
{
	mygrand *g = (mygrand *) user_data;

	g_return_if_fail (g || ent);
	g->descend = g_list_prepend (g->descend, (mychild *) ent);
}

static void
grand_setDescend (mygrand * g, QofCollection * coll)
{
	g_return_if_fail (g || coll);
	if (0 != safe_strcmp (qof_collection_get_type (coll), CHILD_MODULE_NAME))
	{
		return;
	}
	qof_collection_foreach (coll, descend_cb, g);
}

static QofCollection *
grand_getDescend (mygrand * g)
{
	QofCollection *col;
	QofEntity *ent;
	GList *list;

	g_return_val_if_fail (g, NULL);
	col = qof_collection_new (CHILD_MODULE_NAME);
	for (list = g_list_copy (g->descend); list; list = list->next)
	{
		ent = (QofEntity *) list->data;
		if (!ent)
		{
			break;
		}
		do_test (0 == safe_strcmp (ent->e_type, CHILD_MODULE_NAME),
				 "wrong entity");
		qof_collection_add_entity (col, ent);
	}
	return col;
}

static void
grand_setChild (mygrand * g, myparent * p)
{
	g_return_if_fail (g || p);
	g->child = p;
}

static myparent *
grand_getChild (mygrand * g)
{
	g_return_val_if_fail (g, NULL);
	return g->child;
}

void
grand_setFlag (mygrand * g, gchar f)
{
	g_return_if_fail (g);
	g->flag = f;
}

gchar
grand_getFlag (mygrand * g)
{
	g_return_val_if_fail (g, 'n');
	return g->flag;
}

void
grand_setMinor (mygrand * g, gint64 h)
{
	g_return_if_fail (g != NULL);
	g->minor = h;
}

gint64
grand_getMinor (mygrand * g)
{
	g_return_val_if_fail ((g != NULL), 0);
	return g->minor;
}

void
grand_setVersion (mygrand * g, gint32 h)
{
	g_return_if_fail (g != NULL);
	g->version = h;
}

gint32
grand_getVersion (mygrand * g)
{
	if (!g)
		return 0;
	return g->version;
}

void
grand_setActive (mygrand * g, gboolean h)
{
	if (!g)
		return;
	g->active = h;
}

gboolean
grand_getActive (mygrand * g)
{
	if (!g)
		return FALSE;
	return g->active;
}

void
grand_setDiscount (mygrand * g, double h)
{
	if (!g)
		return;
	g->discount = h;
}

double
grand_getDiscount (mygrand * g)
{
	if (!g)
		return 0;
	return g->discount;
}

void
grand_setDate (mygrand * g, QofTime *h)
{
	if (!g)
		return;
	g->date = h;
}

QofTime*
grand_getDate (mygrand * g)
{
	if (!g)
		return NULL;
	return g->date;
}

void
grand_setName (mygrand * g, gchar * h)
{
	if (!g || !h)
		return;
	g->Name = strdup (h);
}

gchar *
grand_getName (mygrand * g)
{
	if (!g)
		return NULL;
	return g->Name;
}

void
grand_setAmount (mygrand * g, QofNumeric h)
{
	if (!g)
		return;
	g->Amount = h;
}

QofNumeric
grand_getAmount (mygrand * g)
{
	if (!g)
		return qof_numeric_zero ();
	return g->Amount;
}

static void
parent_setChild (myparent * p, mychild * c)
{
	g_return_if_fail (p || c);
	p->child = c;
}

static mychild *
parent_getChild (myparent * p)
{
	g_return_val_if_fail (p, NULL);
	return p->child;
}

void
parent_setFlag (myparent * p, gchar f)
{
	g_return_if_fail (p);
	p->flag = f;
}

gchar
parent_getFlag (myparent * p)
{
	g_return_val_if_fail (p, 'n');
	return p->flag;
}

void
parent_setMinor (myparent * p, gint64 h)
{
	g_return_if_fail (p != NULL);
	p->minor = h;
}

gint64
parent_getMinor (myparent * p)
{
	g_return_val_if_fail ((p != NULL), 0);
	return p->minor;
}

void
parent_setVersion (myparent * p, gint32 h)
{
	g_return_if_fail (p != NULL);
	p->version = h;
}

gint32
parent_getVersion (myparent * p)
{
	if (!p)
		return 0;
	return p->version;
}

void
parent_setActive (myparent * p, gboolean h)
{
	if (!p)
		return;
	p->active = h;
}

gboolean
parent_getActive (myparent * p)
{
	if (!p)
		return FALSE;
	return p->active;
}

void
parent_setDiscount (myparent * p, double h)
{
	if (!p)
		return;
	p->discount = h;
}

double
parent_getDiscount (myparent * p)
{
	if (!p)
		return 0;
	return p->discount;
}

void
parent_setDate (myparent * p, QofTime *h)
{
	if (!p)
		return;
	p->date = h;
}

QofTime *
parent_getDate (myparent * p)
{
	if (!p)
		return NULL;
	return p->date;
}

void
parent_setName (myparent * p, gchar * h)
{
	if (!p || !h)
		return;
	p->Name = strdup (h);
}

gchar *
parent_getName (myparent * p)
{
	if (!p)
		return NULL;
	return p->Name;
}

void
parent_setAmount (myparent * p, QofNumeric h)
{
	if (!p)
		return;
	p->Amount = h;
}

QofNumeric
parent_getAmount (myparent * p)
{
	if (!p)
		return qof_numeric_zero ();
	return p->Amount;
}

void
child_setFlag (mychild * c, gchar f)
{
	g_return_if_fail (c);
	c->flag = f;
}

gchar
child_getFlag (mychild * c)
{
	g_return_val_if_fail (c, 'n');
	return c->flag;
}

void
child_setMinor (mychild * c, gint64 h)
{
	g_return_if_fail (c != NULL);
	c->minor = h;
}

gint64
child_getMinor (mychild * c)
{
	g_return_val_if_fail ((c != NULL), 0);
	return c->minor;
}

void
child_setVersion (mychild * c, gint32 h)
{
	g_return_if_fail (c != NULL);
	c->version = h;
}

gint32
child_getVersion (mychild * c)
{
	if (!c)
		return 0;
	return c->version;
}

void
child_setActive (mychild * c, gboolean h)
{
	if (!c)
		return;
	c->active = h;
}

gboolean
child_getActive (mychild * c)
{
	if (!c)
		return FALSE;
	return c->active;
}

void
child_setDiscount (mychild * c, double h)
{
	if (!c)
		return;
	c->discount = h;
}

double
child_getDiscount (mychild * c)
{
	if (!c)
		return 0;
	return c->discount;
}

void
child_setDate (mychild * c, QofTime *h)
{
	if (!c)
		return;
	c->date = h;
}

QofTime*
child_getDate (mychild * c)
{
	if (!c)
		return NULL;
	return c->date;
}

void
child_setName (mychild * c, gchar * h)
{
	if (!c || !h)
		return;
	c->Name = strdup (h);
}

gchar *
child_getName (mychild * c)
{
	if (!c)
		return NULL;
	return c->Name;
}

void
child_setAmount (mychild * c, QofNumeric h)
{
	if (!c)
		return;
	c->Amount = h;
}

QofNumeric
child_getAmount (mychild * c)
{
	if (!c)
		return qof_numeric_zero ();
	return c->Amount;
}

static QofObject grand_object_def = {
  .interface_version = QOF_OBJECT_VERSION,
  .e_type = GRAND_MODULE_NAME,
  .type_label = GRAND_MODULE_DESC,
  .create = (gpointer) grand_create,
  .book_begin = NULL,
  .book_end = NULL,
  .is_dirty = qof_collection_is_dirty,
  .mark_clean = qof_collection_mark_clean,
  .foreach = qof_collection_foreach,
  .printable = NULL,
  .version_cmp = (gint (*)(gpointer, gpointer)) 
			qof_instance_version_cmp,
};

gboolean
mygrandRegister (void)
{
	static QofParam params[] = {
		{OBJ_NAME, QOF_TYPE_STRING, (QofAccessFunc) grand_getName,
		 (QofSetterFunc) grand_setName, NULL},
		{OBJ_AMOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc) grand_getAmount,
		 (QofSetterFunc) grand_setAmount, NULL},
		{OBJ_DATE, QOF_TYPE_TIME, (QofAccessFunc) grand_getDate,
		 (QofSetterFunc) grand_setDate, NULL},
		{OBJ_DISCOUNT, QOF_TYPE_DOUBLE, (QofAccessFunc) grand_getDiscount,
		 (QofSetterFunc) grand_setDiscount, NULL},
		{OBJ_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc) grand_getActive,
		 (QofSetterFunc) grand_setActive, NULL},
		{OBJ_VERSION, QOF_TYPE_INT32, (QofAccessFunc) grand_getVersion,
		 (QofSetterFunc) grand_setVersion, NULL},
		{OBJ_MINOR, QOF_TYPE_INT64, (QofAccessFunc) grand_getMinor,
		 (QofSetterFunc) grand_setMinor, NULL},
		{OBJ_FLAG, QOF_TYPE_CHAR, (QofAccessFunc) grand_getFlag,
		 (QofSetterFunc) grand_setFlag, NULL},
		{OBJ_RELATIVE, PARENT_MODULE_NAME, (QofAccessFunc) grand_getChild,
		 (QofSetterFunc) grand_setChild, NULL},
		{OBJ_LIST, QOF_TYPE_COLLECT, (QofAccessFunc) grand_getDescend,
		 (QofSetterFunc) grand_setDescend, NULL},
		{QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc) qof_instance_get_book,
		 NULL, NULL},
		{QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc) qof_instance_get_guid,
		 NULL, NULL},
		{NULL, NULL, NULL, NULL, NULL},
	};

	qof_class_register (GRAND_MODULE_NAME, NULL, params);
/*  if(!qof_choice_create(GRAND_MODULE_NAME)) { return FALSE; }*/

	return qof_object_register (&grand_object_def);
}

static QofObject parent_object_def = {
  .interface_version = QOF_OBJECT_VERSION,
  .e_type = PARENT_MODULE_NAME,
  .type_label = PARENT_MODULE_DESC,
  .create = (gpointer) parent_create,
  .book_begin = NULL,
  .book_end = NULL,
  .is_dirty = qof_collection_is_dirty,
  .mark_clean = qof_collection_mark_clean,
  .foreach = qof_collection_foreach,
  .printable = NULL,
  .version_cmp = (gint (*)(gpointer, gpointer)) 
				qof_instance_version_cmp,
};

gboolean
myparentRegister (void)
{
	static QofParam params[] = {
		{OBJ_NAME, QOF_TYPE_STRING, (QofAccessFunc) parent_getName,
		 (QofSetterFunc) parent_setName, NULL},
		{OBJ_AMOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc) parent_getAmount,
		 (QofSetterFunc) parent_setAmount, NULL},
		{OBJ_DATE, QOF_TYPE_TIME, (QofAccessFunc) parent_getDate,
		 (QofSetterFunc) parent_setDate, NULL},
		{OBJ_DISCOUNT, QOF_TYPE_DOUBLE, (QofAccessFunc) parent_getDiscount,
		 (QofSetterFunc) parent_setDiscount, NULL},
		{OBJ_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc) parent_getActive,
		 (QofSetterFunc) parent_setActive, NULL},
		{OBJ_VERSION, QOF_TYPE_INT32, (QofAccessFunc) parent_getVersion,
		 (QofSetterFunc) parent_setVersion, NULL},
		{OBJ_MINOR, QOF_TYPE_INT64, (QofAccessFunc) parent_getMinor,
		 (QofSetterFunc) parent_setMinor, NULL},
		{OBJ_FLAG, QOF_TYPE_CHAR, (QofAccessFunc) parent_getFlag,
		 (QofSetterFunc) parent_setFlag, NULL},
		{OBJ_RELATIVE, CHILD_MODULE_NAME, (QofAccessFunc) parent_getChild,
		 (QofSetterFunc) parent_setChild, NULL},
		{QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc) qof_instance_get_book,
		 NULL, NULL},
		{QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc) qof_instance_get_guid,
		 NULL, NULL},
		{NULL, NULL, NULL, NULL, NULL},
	};

	qof_class_register (PARENT_MODULE_NAME, NULL, params);

	return qof_object_register (&parent_object_def);
}

static QofObject child_object_def = {
  .interface_version = QOF_OBJECT_VERSION,
  .e_type = CHILD_MODULE_NAME,
  .type_label = CHILD_MODULE_DESC,
  .create = (gpointer) child_create,
  .book_begin = NULL,
  .book_end = NULL,
  .is_dirty = qof_collection_is_dirty,
  .mark_clean = qof_collection_mark_clean,
  .foreach = qof_collection_foreach,
  .printable = NULL,
  .version_cmp = (gint (*)(gpointer, gpointer)) 
				qof_instance_version_cmp,
};

gboolean
mychildRegister (void)
{
	static QofParam params[] = {
		{OBJ_NAME, QOF_TYPE_STRING, (QofAccessFunc) child_getName,
		 (QofSetterFunc) child_setName, NULL},
		{OBJ_AMOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc) child_getAmount,
		 (QofSetterFunc) child_setAmount, NULL},
		{OBJ_DATE, QOF_TYPE_TIME, (QofAccessFunc) child_getDate,
		 (QofSetterFunc) child_setDate, NULL},
		{OBJ_DISCOUNT, QOF_TYPE_DOUBLE, (QofAccessFunc) child_getDiscount,
		 (QofSetterFunc) child_setDiscount, NULL},
		{OBJ_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc) child_getActive,
		 (QofSetterFunc) child_setActive, NULL},
		{OBJ_VERSION, QOF_TYPE_INT32, (QofAccessFunc) child_getVersion,
		 (QofSetterFunc) child_setVersion, NULL},
		{OBJ_MINOR, QOF_TYPE_INT64, (QofAccessFunc) child_getMinor,
		 (QofSetterFunc) child_setMinor, NULL},
		{OBJ_FLAG, QOF_TYPE_CHAR, (QofAccessFunc) child_getFlag,
		 (QofSetterFunc) child_setFlag, NULL},
		{QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc) qof_instance_get_book,
		 NULL, NULL},
		{QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc) qof_instance_get_guid,
		 NULL, NULL},
		{NULL, NULL, NULL, NULL, NULL},
	};

	qof_class_register (CHILD_MODULE_NAME, NULL, params);

	return qof_object_register (&child_object_def);
}

static void
create_data (QofSession * original, guint counter)
{
	QofCollection *coll;
	QofBook *start;
	mygrand *grand1;
	myparent *parent1;
	mychild *child1;

	start = qof_session_get_book (original);
	grand1 = (mygrand *) qof_object_new_instance (GRAND_MODULE_NAME, start);
	do_test ((NULL != &grand1->inst), "instance init");
	switch (counter)
	{
	case 0:
		{						/* NULL tree */
			do_test ((grand1 != NULL), "empty tree check");
			coll = qof_book_get_collection (start, GRAND_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Too many grandparents found - should be 1");
			coll = qof_book_get_collection (start, CHILD_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 0),
					 "child found, should be empty");
			coll = qof_book_get_collection (start, PARENT_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 0),
					 "tree not empty: parent found");
			break;
		}
	case 1:
		{						/* one parent, no child */
			parent1 =
				(myparent *) qof_object_new_instance (PARENT_MODULE_NAME,
													  start);
			grand_setChild (grand1, parent1);
			do_test ((parent1 != NULL), "single parent check");
			do_test ((grand_getChild (grand1) == parent1),
					 "set child in grandparent");
			coll = qof_book_get_collection (start, GRAND_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of grandparents, should be 1");
			coll = qof_book_get_collection (start, CHILD_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 0),
					 "Should be no child entities this iteration.");
			coll = qof_book_get_collection (start, PARENT_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of parents found, should be 1");
			break;
		}
	case 2:
		{						/* one parent, one child */
			parent1 =
				(myparent *) qof_object_new_instance (PARENT_MODULE_NAME,
													  start);
			grand_setChild (grand1, parent1);
			child1 =
				(mychild *) qof_object_new_instance (CHILD_MODULE_NAME,
													 start);
			parent1 = grand_getChild (grand1);
			parent_setChild (parent1, child1);
			do_test ((child1 != NULL), "one parent with one related child");
			do_test ((child1 == parent_getChild (parent1)),
					 "child of single parent");
			coll = qof_book_get_collection (start, GRAND_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of grandparents. Should be 1");
			coll = qof_book_get_collection (start, CHILD_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of child entities, should be 1");
			coll = qof_book_get_collection (start, PARENT_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of parents. Should be 1");
			break;
		}
	case 3:
		{						/* same grand, new parent, same child */
			child1 =
				(mychild *) qof_object_new_instance (CHILD_MODULE_NAME,
													 start);
			parent1 =
				(myparent *) qof_object_new_instance (PARENT_MODULE_NAME,
													  start);
			grand_setChild (grand1, parent1);
			parent_setChild (parent1, child1);
			do_test ((parent1 == grand_getChild (grand1)),
					 "same grandparent, new parent");
			do_test ((child1 == parent_getChild (parent1)),
					 "new parent, same child");
			coll = qof_book_get_collection (start, GRAND_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of grandparents. Should be 1, Iteration 3.");
			coll = qof_book_get_collection (start, CHILD_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of child entities, should be 1. Iteration 3.");
			coll = qof_book_get_collection (start, PARENT_MODULE_NAME);
			do_test ((qof_collection_count (coll) == 1),
					 "Wrong number of parents. Should be 1. Iteration 3.");
			break;
		}
	case 4:
		{						/* new grand, unrelated parent, child unrelated to grand */
			grand1 =
				(mygrand *) qof_object_new_instance (GRAND_MODULE_NAME,
													 start);
			parent1 =
				(myparent *) qof_object_new_instance (PARENT_MODULE_NAME,
													  start);
			child1 =
				(mychild *) qof_object_new_instance (CHILD_MODULE_NAME,
													 start);
			parent_setChild (parent1, child1);
			do_test ((NULL == grand_getChild (grand1)),
					 "new grand, unrelated parent");
			do_test ((child1 == parent_getChild (parent1)),
					 "child unrelated to grand");
			coll = grand_getDescend (grand1);
			do_test ((coll != NULL), "grandparent not valid");
			if (coll)
			{
				QofEntity *ent;

				ent = (QofEntity *) child1;
				qof_collection_add_entity (coll, ent);
				grand_setDescend (grand1, coll);
				qof_collection_destroy (coll);
				do_test ((g_list_length (grand1->descend) > 0),
						 "entity not added");
				do_test ((qof_collection_count (grand_getDescend (grand1)) >
						  0), "empty collection returned");
			}
			break;
		}
	}
}

struct tally
{
	guint nulls, total, collect;
	QofBook *book;
};

static void
check_cb (QofEntity * ent, gpointer data)
{
	QofEntity *parent, *child;
	QofCollection *coll;
	struct tally *c;
	const QofParam *param;
	mygrand *testg;
	myparent *testp;
	mychild *testc;

	c = (struct tally *) data;
	/* check the same number and type of entities
	   exist in the copied book */
	testg = (mygrand *) ent;
	/* we always have a grandparent */
	do_test ((testg != NULL), "grandparent not found");
	c->total++;
	param = qof_class_get_parameter (GRAND_MODULE_NAME, OBJ_LIST);
	coll = (QofCollection *) param->param_getfcn (ent, param);
	c->collect = qof_collection_count (coll);
	if (c->book)
	{
		qof_book_set_references (c->book);
	}
	param = qof_class_get_parameter (GRAND_MODULE_NAME, OBJ_RELATIVE);
	parent = (QofEntity *) param->param_getfcn (ent, param);
	testp = grand_getChild ((mygrand *) ent);
	/* not all grandparents have family so just keep count. */
	if (!parent)
	{
		c->nulls++;
		return;
	}
	do_test ((0 == safe_strcmp (parent_getName (testp),
								parent_getName ((myparent *) parent))),
			 "parent copy test");
	param = qof_class_get_parameter (PARENT_MODULE_NAME, OBJ_RELATIVE);
	child = param->param_getfcn (parent, param);
	testc = parent_getChild ((myparent *) parent);
	if (!child)
	{
		c->nulls++;
		return;
	}
	do_test ((0 == safe_strcmp (child_getName (testc),
								child_getName ((mychild *) child))),
			 "child copy test");
}

static void
test_recursion (QofSession * original, guint counter)
{
	QofSession *copy;
	QofCollection *grand_coll;
	struct tally c;
	QofBook *book;
	guint d, e, f;

	c.nulls = 0;
	c.total = 0;
	c.collect = 0;
	c.book = NULL;
	book = qof_session_get_book (original);
	grand_coll = qof_book_get_collection (book, GRAND_MODULE_NAME);
	copy = qof_session_new ();
	if (debug)
	{
		qof_session_begin (copy, QOF_STDOUT, TRUE, FALSE);
	}
	/* TODO: implement QOF_TYPE_CHOICE testing. */
	qof_entity_copy_coll_r (copy, grand_coll);
	/* test the original */
	qof_object_foreach (GRAND_MODULE_NAME, book, check_cb, &c);
	book = qof_session_get_book (copy);
	/* test the copy */
	d = c.nulls;
	e = c.total;
	f = c.collect;
	c.nulls = 0;
	c.total = 0;
	c.collect = 0;
	c.book = book;
	qof_object_foreach (GRAND_MODULE_NAME, book, check_cb, &c);
	do_test ((d == c.nulls), "Null parents do not match");
	do_test ((e == c.total), "Total parents do not match");
	do_test ((f == c.collect),
			 "Number of children in descendents does not match");
	if (counter == 4 && debug == TRUE)
	{
		qof_session_save (copy, NULL);
		qof_session_save (original, NULL);
	}
	qof_session_end (copy);
	copy = NULL;
}

int
main (void)
{
	QofSession *original;
	guint counter;

	qof_init ();
	mygrandRegister ();
	myparentRegister ();
	mychildRegister ();
	for (counter = 0; counter < 35; counter++)
	{
		original = qof_session_new ();
		if (debug)
		{
			qof_session_begin (original, QOF_STDOUT, TRUE, FALSE);
		}
		create_data (original, (counter % 5));
		test_recursion (original, (counter % 5));
		qof_session_end (original);
	}
	print_test_results ();
	qof_close ();
	return EXIT_SUCCESS;
}
