/*
 * testdetach.cpp --
 *
 * Tests of the detach and callback facility.
 *
 * Copyright (c) 2000-2003, JYL Software Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF
 * JYL SOFTWARE INC. IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#ifndef	_WIN32
#include <unistd.h>
#endif
#include "test.h"

static int
test_detach1()
{
    e4_Node n;
    e4_Vertex v;
    int rank = 0;

    /*
     * Test detaching a vertex and a node.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach1 failed (step 0)\n");
	    return 1;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach1 failed (step 1)\n");
	    return 1;
	}
	if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot() ||
	    !n.IsDetached()) {
	    fprintf(stderr, "test detach1 failed (step 2)\n");
	    return 1;
	}

	/*
	 * Try to detach the root node.
	 */

	if (!n.Detach() || !n.IsValid() || !n.IsDetached()) {
	    fprintf(stderr, "test detach1 failed (step 3)\n");
	    return 1;
	}

	/*
	 * Create a detached node and see if it's detached.
	 */

	if (!s.CreateDetachedNode(n) || !n.IsValid() || n.IsRoot() ||
	    !n.IsDetached()) {
	    fprintf(stderr, "test detach1 failed (step 4)\n");
	    return 1;
	}

	/*
	 * Try to detach it.
	 */

	if (!n.Detach() || !n.IsValid() || !n.IsDetached()) {	
	    fprintf(stderr, "test detach1 failed (step 5)\n");
	    return 1;
	}

	/*
	 * Add a vertex.
	 */

	if (!n.AddVertexRef("v", E4_IOLAST, rank, 0, v) || !v.IsValid()) {
	    fprintf(stderr, "test detach1 failed (step 6)\n");
	    return 1;
	}
	if (!v.Detach() || !v.IsValid() || !v.IsDetached() ||
	    !n.IsValid() || !n.IsDetached() ||
	    (n.VertexCount() != 0) || (n.OccurrenceCount() != 0) ||
	    (n.ParentCount() != 0)) {
	    fprintf(stderr, "test detach1 failed (step 7)\n");
	    return 1;
	}
	if (!n.Detach() || !n.IsValid() || !n.IsDetached() ||
	    !v.IsValid() || !v.IsDetached()) {
	    fprintf(stderr, "test detach1 failed (step 8)\n");
	    return 1;
	}

	n = invalidNode;
	v = invalidVertex;

	if (!s.IsValid()) {
	    fprintf(stderr, "test detach1 failed (step 9)\n");
	    return 1;
	}

	s.Delete();

	if (s.IsValid() || n.IsValid() || v.IsValid()) {
	    fprintf(stderr, "test detach1 failed (step 10)\n");
	    return 1;
	}
    }

    return 0;
}

static int
test_detach2()
{
    int rank = 0, i = 0;
    e4_Node n1, n2, n3;
    e4_Vertex v1, v2;

    /*
     * n2 is a child of n1 and has a vertex v2. n1 has a vertex v1, whose
     * value is the node n2.
     *
     * detach v2, then n2, then check that v1 is invalid and the vertex count
     * of n1 goes down, then detach n1.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach2 failed (step 0)\n");
	    return 2;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach2 failed (step 1)\n");
	    return 2;
	}
	if (!s.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot() ||
	    !n1.IsDetached()) {
	    fprintf(stderr, "test detach2 failed (step 2)\n");
	    return 2;
	}
	if (!n1.AddNodeRef("v1", E4_IOFIRST, rank, n2, v1) ||
	    !n2.IsValid() || !v1.IsValid()) {
	    fprintf(stderr, "test detach2 failed (step 3)\n");
	    return 2;
	}
	for (i = 0; i < 10; i++) {
	    if (!n1.AddVertex("foo", E4_IOLAST, rank, i)) {
		fprintf(stderr, "test detach2 failed (step 4.1.%d)\n", i);
		return 2;
	    }
	    if (n1.VertexCount() != i + 2) {
		fprintf(stderr, "test detach2 failed (step 4.2.%d)\n", i);
		return 2;
	    }
	}
	if (!n2.AddVertexRef("v1", E4_IOFIRST, rank, 24, v2) || 
	    !v2.IsValid() || (n2.VertexCount() != 1)) {
	    fprintf(stderr, "test detach2 failed (step 5)\n");
	    return 2;
	}
	if (!v2.Detach() || !v2.IsValid() || !v2.IsDetached() ||
	    !n2.IsValid() || n2.IsDetached() ||
	    !v1.IsValid() ||
	    !n1.IsValid() || (n2.VertexCount() != 0) || 
	    (n1.VertexCount() != 11)) {
	    fprintf(stderr, "test detach2 failed (step 6)\n");
	    return 2;
	}
	if (!n2.Detach() || !n2.IsValid() || !n2.IsDetached() ||
	    !v1.IsValid() || !v1.Get(n3) || (n2 != n3) ||
	    !n1.IsValid() || (n1.VertexCount() != 10)) {
	    fprintf(stderr, "test detach2 failed (step 7)\n");
	    return 2;
	}
	if (!n1.Detach() || !n1.IsValid() || !n1.IsDetached()) {
	    fprintf(stderr, "test detach2 failed (step 8)\n");
	    return 2;
	}
	n1 = n2 = n3 = invalidNode;

	if (!s.IsValid()) {
	    fprintf(stderr, "test detach2 failed (step 9)\n");
	    return 2;
	}

	s.Delete();

	if (n1.IsValid() || n2.IsValid() || v1.IsValid() || v2.IsValid()) {
	    fprintf(stderr, "test detach2 failed (step 10)\n");
	    return 2;
	}
    }

    return 0;
}

static int
test_detach3()
{
    int rank = 0, i = 0;
    e4_Node n1;
    e4_Node children[10];

    /*
     * n1 is a node and has ten child nodes.
     *
     * Detach the five first children, check that the remaining children
     * are valid and detached, then detach n1 and drop the reference, and
     * check that all children are valid and detached.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach3 failed (step 0)\n");
	    return 3;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach3 failed (step 1)\n");
	    return 3;
	}
	if (!s.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot() ||
	    !n1.IsDetached()) {
	    fprintf(stderr, "test detach3 failed (step 2)\n");
	    return 3;
	}
	for (i = 0; i < 10; i++) {
	    if (!n1.AddNode("foo", E4_IOLAST, rank, children[i]) ||
		!children[i].IsValid()) {
		fprintf(stderr, "test detach3 failed (step 3.%d)\n", i);
		return 3;
	    }
	}
	if (!n1.IsValid() || (n1.VertexCount() != 10)) {
	    fprintf(stderr, "test detach3 failed (step 4)\n");
	    return 3;
	}
	for (i = 0; i < 5; i++) {
	    if (!children[i].Detach() || !children[i].IsValid() ||
		!children[i].IsDetached() ||
		!n1.IsValid() || (n1.VertexCount() != 9 - i)) {
		fprintf(stderr, "test detach3 failed (step 5.%d)\n", i);
		return 3;
	    }
	}
	for (i = 5; i < 10; i++) {
	    if (!children[i].IsValid()) {
		fprintf(stderr, "test detach3 failed (step 6.%d)\n", i);
		return 3;
	    }
	}
	if (!n1.Detach() || !n1.IsValid() || !n1.IsDetached()) {
	    fprintf(stderr, "test detach3 failed (step 7)\n");
	    return 3;
	}
	for (i = 0; i < 10; i++) {
	    if (!children[i].IsValid()) {
		fprintf(stderr, "test detach3 failed (step 8.1.%d)\n", i);
		return 3;
	    }
	    if ((i < 5) && (!children[i].IsDetached())) {
		fprintf(stderr, "test detach3 failed (step 8.2.%d)\n", i);
		return 3;
	    }
	}

	/*
	 * Discard the last reference to n1.
	 */

	n1 = invalidNode;

	/*
	 * However, because n1 is the root node, the node is not collected and
	 * the five last children are not detached.
	 */

	for (i = 0; i < 10; i++) {
	    if (!children[i].IsValid()) {
		fprintf(stderr, "test detach3 failed (step 9.1.%d)\n", i);
		return 3;
	    }
	    if ((i > 4) && (children[i].IsDetached())) {
		fprintf(stderr, "test detach3 failed (step 9.2.%d)\n", i);
		return 3;
	    }
	}

	/*
	 * Set the root node to a new node. This will cause n1 to be collected
	 * and the last five children will also become detached.
	 */

	if (!s.CreateDetachedNode(n1) || !n1.IsValid() || n1.IsRoot() ||
	    !n1.IsDetached()) {
	    fprintf(stderr, "test detach3 failed (step 10)\n");
	    return 3;
	}
	if (!s.SetRootNode(n1) || !n1.IsValid() || !n1.IsRoot() ||
	    !n1.IsDetached()) {
	    fprintf(stderr, "test detach3 failed (step 11)\n");
	    return 3;
	}
	for (i = 0; i < 10; i++) {
	    if (!children[i].IsValid()) {
		fprintf(stderr, "test detach3 failed (step 12.1.%d)\n", i);
		return 3;
	    }
	    if (!(children[i].IsDetached())) {
		fprintf(stderr, "test detach3 failed (step 12.2.%d)\n", i);
		return 3;
	    }
	}

	for (i = 0; i < 10; i++) {
	    children[i] = invalidNode;
	}

	if (!s.IsValid()) {
	    fprintf(stderr, "test detach3 failed (step 13)\n");
	    return 3;
	}

	s.Delete();
    }

    return 0;
}

static int
test_detach4()
{
    int i= 0, rank = 0;
    e4_Node n1, n2;

    /*
     * n2 occurs in n1 ten times. Detach n2 and see if the vertex count
     * of n1 drops by ten.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach4 failed (step 0)\n");
	    return 4;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach4 failed (step 1)\n");
	    return 4;
	}
	if (!s.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot() ||
	    !n1.IsDetached()) {
	    fprintf(stderr, "test detach4 failed (step 2)\n");
	    return 4;
	}
	if (!n1.AddNode("foo", E4_IOLAST, rank, n2) || !n2.IsValid()) {
	    fprintf(stderr, "test detach4 failed (step 3)\n");
	    return 4;
	}
	for (i = 1; i < 10; i++) {
	    if (!n1.AddVertex("foo", E4_IOLAST, rank, n2) ||
		(n1.VertexCount() != i + 1)) {
		fprintf(stderr, "test detach4 failed (step 4.1.%d)\n", i);
		return 4;
	    }
	    if ((n2.OccurrenceCount() != i + 1) ||
		(n2.OccurrenceCount(n1) != i + 1)) {
		fprintf(stderr, "test detach4 failed (step 4.2.%d)\n", i);
		return 4;
	    }
	}
	for (i = 0; i < 10; i++) {
	    if (!n1.AddVertex("bar", E4_IOLAST, rank, i) ||
		(n1.VertexCount() != i + 11)) {
		fprintf(stderr, "test detach4 failed (step 5.%d)\n", i);
		return 4;
	    }
	}
	if (!n2.Detach() || !n2.IsValid() || !n2.IsDetached() ||
	    !n1.IsValid() || (n1.VertexCount() != 10)) {
	    fprintf(stderr, "test detach4 failed (step 6)\n");
	    return 4;
	}
	n2 = invalidNode;

	if (!n1.AddNode("foo", E4_IOLAST, rank, n2) || !n2.IsValid()) {
	    fprintf(stderr, "test detach4 failed (step 7)\n");
	    return 4;
	}
	for (i = 1; i < 10; i++) {
	    if (!n1.AddVertex("foo", E4_IOLAST, rank, n2) ||
		(n1.VertexCount() != i + 11)) {
		fprintf(stderr, "test detach4 failed (step 8.1.%d)\n", i);
		return 4;
	    }
	    if ((n2.OccurrenceCount() != i + 1) ||
		(n2.OccurrenceCount(n1) != i + 1)) {
		fprintf(stderr, "test detach4 failed (step 8.2.%d)\n", i);
		return 4;
	    }
	}

	/*
	 * Because n1 is the root node, detaching it has no effect on
	 * its children, because the root node is not collected.
	 */

	if (!n1.Detach() || !n1.IsValid() || !n1.IsDetached() ||
	    !n2.IsValid() || n2.IsDetached()) {
	    fprintf(stderr, "test detach4 failed (step 9)\n");
	    return 4;
	}

	if (!s.CreateDetachedNode(n1) || !n1.IsValid() || n1.IsRoot() ||
	    !n1.IsDetached()) {
	    fprintf(stderr, "test detach4 failed (step 10)\n");
	    return 4;
	}
	if (!s.SetRootNode(n1) || !n1.IsValid() || !n1.IsRoot() ||
	    !n1.IsDetached()) {
	    fprintf(stderr, "test detach4 failed (step 11)\n");
	    return 4;
	}
	if (!n2.IsValid() || !n2.IsDetached()) {
	    fprintf(stderr, "test detach4 failed (step 12)\n");
	    return 4;
	}

	n1 = n2 = invalidNode;

	if (!s.IsValid()) {
	    fprintf(stderr, "test detach4 failed (step 13)\n");
	    return 4;
	}

	s.Delete();
    }

    return 0;
}

static int
test_detach5()
{
    e4_Node t, r;
    e4_Vertex v1, v2, v3, v4, v5;
    int rank = 0;

    /*
     * The node t is the value of five vertices. Detach t and see that it
     * and the five vertices which hold it as their value become detached.
     * Restore the situation and detach each vertex in turn. The node t
     * should now become detached only when the last vertex is detached.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach5 failed (step 0)\n");
	    return 5;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach5 failed (step 1)\n");
	    return 5;
	}
	if (!s.GetRootNode(r) || !r.IsValid() || !r.IsRoot() ||
	    !r.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 2)\n");
	    return 5;
	}
	if (!r.AddNodeRef("v1", E4_IOLAST, rank, t, v1) || !v1.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v1.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 3)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v2", E4_IOLAST, rank, t, v2) || !v2.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v2.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 4)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v3", E4_IOLAST, rank, t, v3) || !v3.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v3.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 5)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v4", E4_IOLAST, rank, t, v4) || !v4.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v4.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 6)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v5", E4_IOLAST, rank, t, v5) || !v5.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v5.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 7)\n");
	    return 5;
	}
	if ((t.OccurrenceCount() != 5) || (t.ParentCount() != 1) ||
	    (t.OccurrenceCount(r) != 5)) {
	    fprintf(stderr, "test detach5 failed (step 8)\n");
	    return 5;
	}
	if (!t.Detach() || !t.IsValid() ||
	    !v1.IsValid() || !v1.IsDetached() ||
	    !v2.IsValid() || !v2.IsDetached() ||
	    !v3.IsValid() || !v3.IsDetached() ||
	    !v4.IsValid() || !v4.IsDetached() ||
	    !v5.IsValid() || !v5.IsDetached() ||
	    (r.VertexCount() != 0) || 
	    (t.ParentCount() != 0) || (t.OccurrenceCount() != 0)) {
	    fprintf(stderr, "test detach5 failed (step 9)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v1", E4_IOLAST, rank, t, v1) || !v1.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v1.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 10)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v2", E4_IOLAST, rank, t, v2) || !v2.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v2.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 11)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v3", E4_IOLAST, rank, t, v3) || !v3.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v3.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 12)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v4", E4_IOLAST, rank, t, v4) || !v4.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v4.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 13)\n");
	    return 5;
	}
	if (!r.AddVertexRef("v5", E4_IOLAST, rank, t, v5) || !v5.IsValid() ||
	    !t.IsValid() || t.IsDetached() || v5.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 14)\n");
	    return 5;
	}
	if ((t.OccurrenceCount() != 5) || (t.ParentCount() != 1) ||
	    (t.OccurrenceCount(r) != 5)) {
	    fprintf(stderr, "test detach5 failed (step 15) %d %d %d\n",
		    t.OccurrenceCount(), t.ParentCount(),
		    t.OccurrenceCount(r));
	    return 5;
	}
	if (!v1.Detach() || !v1.IsValid() || !v1.IsDetached() ||
	    !t.IsValid() || t.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 16)\n");
	    return 5;
	}
	if (!v2.Detach() || !v2.IsValid() || !v2.IsDetached() ||
	    !t.IsValid() || t.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 17)\n");
	    return 5;
	}
	if (!v3.Detach() || !v3.IsValid() || !v3.IsDetached() ||
	    !t.IsValid() || t.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 18)\n");
	    return 5;
	}
	if (!v4.Detach() || !v4.IsValid() || !v4.IsDetached() ||
	    !t.IsValid() || t.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 19)\n");
	    return 5;
	}
	if (!v5.Detach() || !v5.IsValid() || !v5.IsDetached() ||
	    !t.IsValid() || !t.IsDetached()) {
	    fprintf(stderr, "test detach5 failed (step 20)\n");
	    if (!v5.IsValid()) {
	      fprintf(stderr, "v5 invalid\n");
	    }
	    if (!v5.IsDetached()) {
	      fprintf(stderr, "v5 not detached\n");
	    }
	    if (!t.IsValid()) {
	      fprintf(stderr, "t invalid\n");
	    }
	    if (!t.IsDetached()) {
	      fprintf(stderr, "t not detached\n");
	    }
	    return 5;
	}

	if (!s.IsValid()) {
	    fprintf(stderr, "test detach5 failed (step 21)\n");
	    return 5;
	}

	s.Delete();
    }

    return 0;
}

static int
test_detach6()
{
    e4_Node r, n;
    e4_Vertex v, pv, nv;
    int i = 0, rank = 0;

    /*
     * Test the following:
     * - A detached node has zero occurrences and zero parents.
     * - A detached vertex has no prev or next.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach6 failed (step 0)\n");
	    return 6;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach6 failed (step 1)\n");
	    return 6;
	}
	if (!s.GetRootNode(r) || !r.IsValid() || !r.IsRoot() ||
	    !r.IsDetached()) {
	    fprintf(stderr, "test detach6 failed (step 3)\n");
	    return 6;
	}
	if (!r.AddNode("n", E4_IOLAST, rank, n) || !n.IsValid() ||
	    n.IsRoot() || n.IsDetached()) {
	    fprintf(stderr, "test detach6 failed (step 4)\n");
	    return 6;
	}
	for (i = 0; i < 10; i++) {
	    if (!n.AddVertex("f", E4_IOLAST, rank, r) || !r.IsValid() ||
		r.IsDetached() || (r.OccurrenceCount() != i + 1) ||
		(r.OccurrenceCount(n) != i + 1)) {
		fprintf(stderr, "test detach6 failed (step 5.%d)\n", i);
		return 5;
	    }
	}
	if (!r.Detach() || !r.IsValid() || !r.IsDetached() ||
	    (r.OccurrenceCount() != 0) || (r.OccurrenceCount(n) != 0) ||
	    (r.ParentCount() != 0)) {
	    fprintf(stderr, "test detach6 failed (step 6) %d %d %d %d %d\n",
		    r.IsValid(), r.IsDetached(), r.OccurrenceCount(),
		    r.OccurrenceCount(n), r.ParentCount());
	    return 6;
	}
	for (i = 0; i < 10; i++) {
	    if (!n.AddVertex("f", E4_IOLAST, rank, r) || !r.IsValid() ||
		r.IsDetached() || (r.OccurrenceCount() != i + 1) ||
		(r.OccurrenceCount(n) != i + 1)) {
		fprintf(stderr, "test detach6 failed (step 7.%d)\n", i);
		return 6;
	    }
	}
	for (i = 10; i > 0; i--) {

	    pv = invalidVertex;
	    nv = invalidVertex;

	    if (!n.GetVertexRefByRank(i, v) || !v.IsValid() ||
		v.IsDetached() || (r.OccurrenceCount() != i) ||
		(r.OccurrenceCount(n) != i) || (r.ParentCount() != 1)) {
		fprintf(stderr, "test detach6 failed (step 8.1.%d)\n", i);
		return 6;
	    }
	    if ((i != 1) && !v.Prev(1, pv) && !v.Next(1, nv) &&
		(pv == invalidVertex) && (nv == invalidVertex)) {
		fprintf(stderr, "test detach6 failed (step 8.2.%d)\n", i);
		return 6;
	    }

	    pv = invalidVertex;
	    nv = invalidVertex;

	    if (!v.Detach() || !v.IsValid() || !v.IsDetached() ||
		(n.VertexCount() != i - 1) || (r.OccurrenceCount() != i - 1) ||
		(r.OccurrenceCount(n) != i - 1) ||
		v.Prev(1, pv) || v.Next(1, nv) ||
		(pv != invalidVertex) || (nv != invalidVertex)) {
		fprintf(stderr, "test detach6 failed (step 8.3.%d)\n", i);
		fprintf(stderr, "%d 1 %d 1 %d %d %d %d %d %d %d 1 %d 1\n",
			v.IsValid(),
			v.IsDetached(),
			n.VertexCount(), i - 1,
			r.OccurrenceCount(), i - 1,
			r.OccurrenceCount(n), i - 1,
			(pv == invalidVertex),
			(nv == invalidVertex));
		return 6;
	    }
	    if (((i == 1) && (r.ParentCount() != 0)) ||
		((i > 1) && (r.ParentCount() != 1))) {
		fprintf(stderr, "test detach6 failed (step 8.4.%d), %d\n", 
			i, n.ParentCount());
		return 6;
	    }
	}

	s.Delete();
    }

    return 0;
}

static int
test_detach7()
{
    e4_Node r, n;
    int rank = 0;

    /*
     * Test suspected bug: a root node that is attached by self reference
     * stays detached?
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach7 failed (step 0)\n");
	    return 7;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach7 failed (step 1)\n");
	    return 7;
	}
	if (!s.GetRootNode(r) || !r.IsValid() || !r.IsRoot() ||
	    !r.IsDetached()) {
	    fprintf(stderr, "test detach7 failed (step 3)\n");
	    return 7;
	}
	if (!r.IsDetached()) {
	    fprintf(stderr, "test detach7 failed (step 4)\n");
	    return 7;
	}
	if (!r.AddVertex("frob", E4_IOLAST, rank, r) ||
	    r.IsDetached()) {
	    fprintf(stderr, "test detach7 failed (step 5)\n");
	    return 7;
	}
	if (!r.GetParent(n) || !n.IsValid() || (n != r)) {
	    fprintf(stderr, "test detach7 failed (step 6)\n");
	    return 7;
	}

	s.Delete();
    }

    return 0;
}

static int
test_detach8()
{
    e4_Node r, n, n1;
    e4_Vertex v;
    int rank = 0;
    int val = 0;

    /*
     * Test detaching and attaching a node via various methods.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test detach8 failed (step 0)\n");
	    return 8;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test detach8 failed (step 1)\n");
	    return 8;
	}
	if (!s.GetRootNode(r) || !r.IsValid() || !r.IsRoot() ||
	    !r.IsDetached()) {
	    fprintf(stderr, "test detach8 failed (step 3)\n");
	    return 8;
	}
	if (!r.IsDetached()) {
	    fprintf(stderr, "test detach8 failed (step 4)\n");
	    return 8;
	}
	if (!r.AddVertex("foo", E4_IOLAST, rank, (int) 12)) {
	    fprintf(stderr, "test detach8 failed (step 5)\n");
	    return 8;
	}
	if (!r.GetVertexRef("foo", v) || !v.IsValid()) {
	    fprintf(stderr, "test detach8 failed (step 6)\n");
	    return 8;
	}
	if (!v.Get(val) || (val != 12)) {
	    fprintf(stderr, "test detach8 failed (step 7)\n");
	    return 8;
	}
	if (!r.AddNode("froboz", E4_IOLAST, rank, n) || !n.IsValid() ||
	    n.IsDetached()) {
	    fprintf(stderr, "test detach8 failed (step 8)\n");
	    return 8;
	}
	if (!n.Detach() || !n.IsDetached() || !n.IsValid() ||
	    r.VertexCount() != 1) {
	    fprintf(stderr, "test detach8 failed (step 9)\n");
	    return 8;
	}
	if (!r.SetVertex("foo", n) || n.IsDetached() || !n.IsValid() ||
	    (r.VertexCount() != 1) ||
	    !r.GetVertex("foo", n1) || !n1.IsValid() || (n != n1) ||
	    !v.Get(n1) || !n1.IsValid() || (n != n1)) {
	    fprintf(stderr, "test detach8 failed (step 10)\n");
	    return 8;
	}
	if (!r.SetVertex("foo", (int) 33) || !n.IsDetached() || !n.IsValid() ||
	    (r.VertexCount() != 1) || !r.GetVertex("foo", val) ||
	    (val != 33) || !v.Get(val) || (val != 33)) {
	    fprintf(stderr, "test detach8 failed (step 11)\n");
	    return 8;
	}
	if (!v.Set(n) || !v.IsValid() || v.IsDetached() || n.IsDetached() ||
	    !n.IsValid() ||
	    !r.GetVertex("foo", n1) || !n1.IsValid() || (n != n1) ||
	    !v.Get(n1) || !n1.IsValid() || (n != n1)) {
	    fprintf(stderr, "test detach8 failed (step 12)\n");
	    return 8;
	}
	if (!v.Detach() || !v.IsValid() || !v.IsDetached() || !n.IsValid() ||
	    !n.IsDetached() || (r.VertexCount() != 0) ||
	    !v.Get(n1) || !n1.IsValid() || (n != n1)) {
	    fprintf(stderr, "test detach8 failed (step 13)\n");
	    return 8;
	}
	if (!r.MoveVertex(v, E4_IOLAST, rank) || !v.IsValid() ||
	    v.IsDetached() || !n.IsValid() || n.IsDetached() ||
	    !r.GetVertex("foo", n1) || !n1.IsValid() || (n != n1) ||
	    (r.VertexCount() != 1)) {
	    fprintf(stderr, "test detach8 failed (step 14)\n");
	    return 8;
	}
	if (!r.SetVertex("foo", (int) 12) || !n.IsValid() || !n.IsDetached() ||
	    !v.IsValid() || v.IsDetached() || (r.VertexCount() != 1)) {
	    fprintf(stderr, "test detach8 failed (step 15)\n");
	    return 8;
	}
	if (!v.Set(n) || !n.IsValid() || n.IsDetached() || !v.IsValid() ||
	    v.IsDetached() ||
	    !r.GetVertex("foo", n1) || !n1.IsValid() || (n != n1)) {
	    fprintf(stderr, "test detach8 failed (step 16)\n");
	    return 8;
	}
	if (!r.DetachVertex("foo") || !v.IsValid() || !v.IsDetached() ||
	    !n.IsValid() || !n.IsDetached() ||
	    !v.Get(n1) || !n1.IsValid() || (n != n1)) {
	    fprintf(stderr, "test detach8 failed (step 17)\n");
	    return 8;
	}
	if (!r.MoveVertex(v, E4_IOLAST, rank) || !v.IsValid() ||
	    v.IsDetached() || !n.IsValid() || n.IsDetached() ||
	    !r.GetVertex("foo", n1) || !n1.IsValid() || (n != n1) ||
	    (r.VertexCount() != 1)) {
	    fprintf(stderr, "test detach8 failed (step 18)\n");
	    return 8;
	}
	if (!r.AddVertex("bar", E4_IOLAST, rank, n) || !n.IsValid() ||
	    n.IsDetached() || (n.ParentCount() != 1) ||
	    (n.OccurrenceCount() != 2) || (n.OccurrenceCount(r) != 2) ||
	    (r.VertexCount() != 2) || !r.GetVertex("bar", n1) ||
	    !n1.IsValid() || (n != n1)) {
	    fprintf(stderr, "test detach8 failed (step 19)\n");
	    return 8;
	}
	if (!r.AddNode("zop", E4_IOLAST, rank, n) || !n.IsValid() ||
	    n.IsDetached() || (n.ParentCount() != 1) ||
	    (n.OccurrenceCount() != 1) || (!n.GetParent(n1)) ||
	    !n1.IsValid()) {
	    fprintf(stderr, "test detach8 failed (step 20)\n");
	    return 8;
	}
	n1 = n;
	if (!r.SetNode("zop", n) || !n.IsValid() || n.IsDetached() ||
	    (n.ParentCount() != 1) || (n.OccurrenceCount() != 1) ||
	    (n.OccurrenceCount(r) != 1) ||
	    !n1.IsValid() || !n1.IsDetached()) {
	    fprintf(stderr, "test detach8 failed (step 21)\n");
	    return 8;
	}
	if (!r.GetVertexRef("zop", v) || !v.IsValid() || v.IsDetached() ||
	    !v.Get(n1) || !n1.IsValid() || n1.IsDetached() || (n != n1)) {
	    fprintf(stderr, "test detach8 failed (step 22)\n");
	    return 8;
	}
	rank = v.Rank();
	if (!r.SetNodeByRank(rank, n) || !n.IsValid() || n.IsDetached() ||
	    (n.ParentCount() != 1) || (n.OccurrenceCount() != 1) ||
	    (n.OccurrenceCount(r) != 1) ||
	    !n1.IsValid() || !n1.IsDetached()) {
	    fprintf(stderr, "test detach8 failed (step 23)\n");
	    return 8;
	}

	s.Delete();
    }

    return 0;
}

int
test_detach()
{
    int result = 0;

    fprintf(stderr, "Running detach tests: ");
    result = test_detach1();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".");
    result = test_detach2();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".");
    result = test_detach3();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".");
    result = test_detach4();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".");
    result = test_detach5();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".");
    result = test_detach6();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".");
    result = test_detach7();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".");
    result = test_detach8();
    if (result != 0) {
	return result;
    }
    fprintf(stderr, ".\n");

    return 0;
}
