/*
 * teststorage.cpp --
 *
 * Tests of the e4_StorageImpl class.
 *
 * 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 "e4graph.h"
#include "test.h"

static int
test_storage1()
{
    /*
     * Test that we are able to open an e4Storage file (and create it
     * if needed).
     */

    {
        e4_Storage s;

	if (!clean_storage("foo.db", s)) {
            fprintf(stderr, "test storage1 failed, step 0\n");
            return 1;
        }
        if (!s.IsValid()) {
            fprintf(stderr, "test storage1 failed, step 1\n");
            return 1;
        }
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage1 failed, step 2\n");
	    return 1;
	}
    }

    return 0;
}

static int
test_storage2()
{
    e4_Node n;

    /*
     * Test that we are able to get the root node from a storage (and
     * create it if needed).
     */
    
    {
        e4_Storage s;

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

    return 0;
}

static int
test_storage4()
{
    e4_Node n;
    int rank = 0, i = 0;

    /*
     * Test that Commit works.
     */
    
    {
        e4_Storage s;

	if (!clean_storage("foo.db", s)) {
            fprintf(stderr, "test storage4 failed (step 0)\n");
            return 4;
        }
        if (!s.IsValid()) {
            fprintf(stderr, "test storage4 failed (step 1)\n");
            return 4;
        }
        if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
            fprintf(stderr, "test storage4 failed (step 2.0)\n");
            return 4;
        }
	if (!n.AddVertex("foo", E4_IOLAST, rank, 42)) {
	    fprintf(stderr, "test storage4 failed (step 2.1)\n");
	    return 4;
	}
        s.Commit();
    }
    if (n.IsValid()) {
        fprintf(stderr, "test storage4 failed (step 4)\n");
        return 4;
    }
    {
        e4_Storage s("foo.db", E4_METAKIT);

        if (!s.IsValid()) {
            fprintf(stderr, "test storage4 failed (step 5)\n");
            return 4;
        }
	if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
            fprintf(stderr, "test storage4 failed (step 6)\n");
            return 4;
        }
	if ((n.VertexCount() != 1) || (!n.GetVertex("foo", i)) ||
	    (i != 42)) {
	    fprintf(stderr, "test storage4 failed (step 7)\n");
	    return 4;
	}
	s.Delete();
    }
    if (n.IsValid()) {
        fprintf(stderr, "test storage4 failed (step 8)\n");
        return 4;
    }
    return 0;
}

static int
test_storage5()
{
    /*
     * Test that auto-commit works.
     */
    
    {
        e4_Storage s1;
	e4_Node n;
	int rank = 0;

	if (!clean_storage("foo.db", s1)) {
            fprintf(stderr, "test storage5 failed (step 0)\n");
            return 5;
        }
        if (!s1.IsValid()) {
            fprintf(stderr, "test storage5 failed (step 1)\n");
            return 5;
        }
	if (!s1.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
            fprintf(stderr, "test storage5 failed (step 2)\n");
            return 5;
        }
	if (n.VertexCount() != 0) {
	    fprintf(stderr, "test storage5 failed (step 2.a), 0 vs. %d\n",
		    n.VertexCount());
	    return 5;
	}
	if (!n.AddVertex("foo", E4_IOLAST, rank, 42)) {
            fprintf(stderr, "test storage5 failed (step 3)\n");
            return 5;
        }
	if (n.VertexCount() != 1) {
	    fprintf(stderr, "test storage5 failed (step 3.a), 1 vs. %d\n",
		    n.VertexCount());
	    return 5;
	}

	/*
	 * Auto-commit should be true by default.
	 */

	if (!HASSTATE(s1, E4_COMMITATCLOSE)) {
            fprintf(stderr, "test storage5 failed (step 4)\n");
            return 5;
        }

	/*
	 * Make sure that auto-commit is on.
	 */

	SETSTATE(s1, E4_COMMITATCLOSE);

	/*
	 * Check that now it's on.
	 */

	if (!HASSTATE(s1, E4_COMMITATCLOSE)) {
            fprintf(stderr, "test storage5 failed (step 5)\n");
            return 5;
        }

	/*
	 * Now rely on the C++ scoping rules to cause a commit.
	 */
    }
    {
        e4_Storage s2("foo.db", E4_METAKIT);
	e4_Node n1;
	int i = 0;

        if (!s2.IsValid()) {
            fprintf(stderr, "test storage5 failed (step 6)\n");
            return 5;
        }
	if (!s2.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot()) {
            fprintf(stderr, "test storage5 failed (step 7)\n");
            return 5;
        }
	if (n1.VertexCount() != 1) {
	  fprintf(stderr, "vertexcount != 1, %d\n", n1.VertexCount());
	}
	if (!n1.GetVertex("foo", i)) {
	  fprintf(stderr, "failed to get value of vertex foo\n");
	}
	if (i != 42) {
	  fprintf(stderr, "expected value 42, got %d\n", i);
	}
	if ((n1.VertexCount() != 1) || (!n1.GetVertex("foo", i)) ||
	    (i != 42)) {
            fprintf(stderr, "test storage5 failed (step 8)\n");
            return 5;
        }
    }
    {
	e4_Storage s3("foo.db", E4_METAKIT);

	s3.Delete();
    }

    return 0;
}

static int
test_storage6()
{
    e4_Node n;
    
    /*
     * Test assignment operator, caching, operator==, operator!=
     */

    {
        e4_Storage s1;

	if (!clean_storage("foo.db", s1)) {
            fprintf(stderr, "test storage6 failed (step 0)\n");
            return 6;
        }
        if (!s1.IsValid()) {
            fprintf(stderr, "test storage6 failed (step 1)\n");
            return 6;
        }

        e4_Storage s2 = s1;
        if (!s2.IsValid()) {
            fprintf(stderr, "test storage6 failed (step 2)\n");
            return 6;
        }
        if (s2 == s1) {
        } else {
            fprintf(stderr, "test storage6 failed (step 3)\n");
            return 6;
        }
        if (s1 != s2) {
            fprintf(stderr, "test storage6 failed (step 4)\n");
            return 6;
        }

        /*
         * The next line should return the same storage as s1.
         */
        
        e4_Storage s3("foo.db", E4_METAKIT);

        if (!s3.IsValid()) {
            fprintf(stderr, "test storage6 failed (step 5)\n");
            return 6;
        }
        if (s3 != s1) {
            fprintf(stderr, "test storage6 failed (step 6)\n");
            return 6;
        }
        e4_Storage s4 = s3;
        if (!s3.IsValid()) {
            fprintf(stderr, "test storage6 failed (step 7)\n");
            return 6;
        }
        if (s4 == s1) {
        } else {
            fprintf(stderr, "test storage6 failed (step 8)\n");
            return 6;
        }
        if (s4 != s1) {
            fprintf(stderr, "test storage6 failed (step 9)\n");
            return 6;
        }

	if (!s1.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
            fprintf(stderr, "test storage6 failed (step 10)\n");
            return 6;
        }
        if ((s1 == n) || (s2 == n) || (s3 == n) || (s4 == n)) {
            fprintf(stderr, "test storage6 failed (step 13)\n");
            return 6;
        }
	s1.Delete();

	if (s1.IsValid() || s2.IsValid() || s3.IsValid() || s4.IsValid()) {
	    fprintf(stderr, "test storage6 failed (step 14)\n");
	    return 6;
	}

	s2.Delete();
	s3.Delete();
	s4.Delete();

	if (s1.IsValid() || s2.IsValid() || s3.IsValid() || s4.IsValid() ||
	    n.IsValid()) {
	    fprintf(stderr, "test storage6 failed (step 15)\n");
	    return 6;
	}
    }

    return 0;
}

static int
test_storage8()
{
    e4_Node n;
    int rank = 0, i = 0;
    
    /*
     * Test that auto-commit off works.
     */

    {
        e4_Storage s;

	if (!clean_storage("foo.db", s)) {
            fprintf(stderr, "test storage8 failed (step 0)\n");
            return 8;
        }
        if (!s.IsValid()) {
            fprintf(stderr, "test storage8 failed (step 1)\n");
            return 8;
        }
	if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot() ||
	    (n.VertexCount() != 0)) {
            fprintf(stderr, "test storage8 failed (step 2)\n");
            return 8;
        }
	if (!n.AddVertex("foo", E4_IOLAST, rank, 42)) {
            fprintf(stderr, "test storage8 failed (step 3)\n");
            return 8;
        }
	CLEARSTATE(s, E4_COMMITATCLOSE);

	if (HASSTATE(s, E4_COMMITATCLOSE)) {
            fprintf(stderr, "test storage8 failed (step 4)\n");
            return 8;
        }
    }
    if (n.IsValid()) {
        fprintf(stderr, "test storage8 failed (step 5)\n");
        return 8;
    }
    {
        e4_Storage s("foo.db", E4_METAKIT);
        if (!s.IsValid()) {
            fprintf(stderr, "test storage8 failed (step 6)\n");
            return 8;
        }
	if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot() ||
	    (n.VertexCount() != 0) || (n.GetVertex("foo", i)) ||
	    (i == 42)) {
            fprintf(stderr, "test storage8 failed (step 7)\n");
            return 8;
        }
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage8 failed (step 8)\n");
	    return 8;
	}
    }

    return 0;
}

static int
test_storage9()
{
    /*
     * Test that we can have several e4_Storages open at the same time.
     */

    {
        e4_Storage s1;

	if (!clean_storage("foo1.db", s1)) {
            fprintf(stderr, "test storage9 failed (step 0)\n");
            return 9;
        }
        if (!s1.IsValid()) {
            fprintf(stderr, "test storage9 failed (step 1)\n");
            return 9;
        }

        e4_Storage s2;

	if (!clean_storage("foo2.db", s2)) {
            fprintf(stderr, "test storage9 failed (step 2.0)\n");
            return 9;
        }
        if (!s2.IsValid()) {
            fprintf(stderr, "test storage9 failed (step 2.1)\n");
            return 9;
        }

        e4_Storage s3;

	if (!clean_storage("foo3.db", s3)) {
            fprintf(stderr, "test storage9 failed (step 3.0)\n");
            return 9;
        }
        if (!s3.IsValid()) {
            fprintf(stderr, "test storage9 failed (step 3.1)\n");
            return 9;
        }

        e4_Storage s4;

	if (!clean_storage("foo4.db", s4)) {
            fprintf(stderr, "test storage9 failed (step 4.0)\n");
            return 9;
        }
        if (!s4.IsValid()) {
            fprintf(stderr, "test storage9 failed (step 4.1)\n");
            return 9;
        }
        if ((s1 == s2) || (s1 == s3) || (s1 == s4)) {
            fprintf(stderr, "test storage9 failed (step 5)\n");
            return 9;
        }
	s1.Delete();
	s2.Delete();
	s3.Delete();
	s4.Delete();
    }

    return 0;
}

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

    /*
     * Test that creating stuff in one storage does not show up in the other.
     */

    {
        e4_Storage s1;

	if (!clean_storage("foo1.db", s1)) {
            fprintf(stderr, "test storage10 failed (step 1.0)\n");
            return 10;
        }
        if (!s1.IsValid()) {
            fprintf(stderr, "test storage10 failed (step 1.1)\n");
            return 10;
        }

        e4_Storage s2;

	if (!clean_storage("foo2.db", s2)) {
            fprintf(stderr, "test storage10 failed (step 2.0)\n");
            return 10;
        }
        if (!s2.IsValid()) {
            fprintf(stderr, "test storage10 failed (step 2.1)\n");
            return 10;
        }

	if (!s1.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot() ||
	    !s2.GetRootNode(n2) || !n2.IsValid() || !n2.IsRoot()) {
            fprintf(stderr, "test storage10 failed (step 3)\n");
            return 10;
        }
	if ((n1.VertexCount() != 0) || (n2.VertexCount() != 0)) {
            fprintf(stderr, "test storage10 failed (step 4)\n");
            return 10;
        }
	if (!n1.AddVertex("foo", E4_IOLAST, rank, 42) ||
	    (n1.VertexCount() != 1) ||
	    (n2.VertexCount() != 0) ||
	    (n2.GetVertex("foo", i)) ||
	    (i == 42)) {
            fprintf(stderr, "test storage10 failed (step 5)\n");
            return 10;
        }
    }
    if (n1.IsValid() || n2.IsValid()) {
        fprintf(stderr, "test storage10 failed (step 6)\n");
        return 10;
    }
    {
        e4_Storage s1("foo1.db", E4_METAKIT);
        e4_Storage s2("foo2.db", E4_METAKIT);

        if (!s1.IsValid() || !s2.IsValid()) {
            fprintf(stderr, "test storage10 failed (step 7)\n");
            return 10;
        }
        if (s1 == s2) {
            fprintf(stderr, "test storage10 failed (step 8)\n");
            return 10;
        }
	if (!s1.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot() ||
	    (n1.VertexCount() != 1) || (!n1.GetVertex("foo", i)) ||
	    (i != 42) ||
	    !s2.GetRootNode(n2) || !n2.IsValid() || !n2.IsRoot() ||
	    (n2.VertexCount() != 0) || (n2.GetVertex("foo", j)) ||
	    (j == 42)) {
            fprintf(stderr, "test storage10 failed (step 9)\n");
            return 10;
        }
    }
    {
	e4_Storage s1("foo1.db", E4_METAKIT);
	e4_Storage s2("foo2.db", E4_METAKIT);

	s1.Delete();
	s2.Delete();
    }

    return 0;
}

static int
test_storage13()
{
    e4_Node n1, n2;

    /*
     * Test that if we get the same root node, we get the same storage.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage13 failed (step 0)\n");
	    return 13;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage13 failed (step 1)\n");
	    return 13;
	}
	if (!s.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot()) {
	    fprintf(stderr, "test storage13 failed (step 2)\n");
	    return 13;
	}
	if (!s.GetRootNode(n2) || !n2.IsValid() || !n2.IsRoot() ||
	    (n1 != n2) || (n2 != n1)) {
	    fprintf(stderr, "test storage13 failed (step 4)\n");
	    return 13;
	}

	e4_Storage s1, s2;

	if (!n1.GetStorage(s1) || !s1.IsValid() || (s1 != s) || (s != s1) ||
	    !n2.GetStorage(s2) || !s2.IsValid() || (s2 != s) || (s != s2) ||
	    (s1 != s2) || (s2 != s1)) {
	    fprintf(stderr, "test storage13 failed (step 5)\n");
	    return 13;
	}

	s.Delete();

	if (s.IsValid()) {
	    fprintf(stderr, "test storage13 failed (step 6)\n");
	    return 13;
	}
    }
    if (n1.IsValid() || n2.IsValid()) {
	fprintf(stderr, "test storage13 failed (step 7)\n");
	return 13;
    }

    return 0;
}

static int
test_storage14()
{
    e4_Node n1, n2, n3, n4;
    int rank = 0;

    /*
     * Test e4_Storage::SetRootNode(). Test that IsRoot() returns true
     * only on the root node.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage14 failed (step 0)\n");
	    return 14;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage14 failed (step 1)\n");
	    return 14;
	}
	if (!s.GetRootNode(n1) || !n1.IsValid() || !n1.IsRoot()) {
	    fprintf(stderr, "test storage14 failed (step 2)\n");
	    return 14;
	}
	if (!n1.AddNode("n2", E4_IOLAST, rank, n2) || !n2.IsValid() ||
	    n2.IsRoot()) {
	    fprintf(stderr, "test storage14 failed (step 3)\n");
	    return 14;
	}
	if (!n2.AddNode("n3", E4_IOLAST, rank, n3) || !n3.IsValid() ||
	    n3.IsRoot()) {
	    fprintf(stderr, "test storage14 failed (step 4)\n");
	    return 14;
	}
	if (!n3.AddNode("n4", E4_IOLAST, rank, n4) || !n4.IsValid() ||
	    n4.IsRoot()) {
	    fprintf(stderr, "test storage14 failed (step 5)\n");
	    return 14;
	}
	if (!s.SetRootNode(n2) || !n1.IsValid() || n1.IsRoot() ||
	    !n2.IsValid() || !n2.IsRoot() ||
	    !n3.IsValid() || n3.IsRoot() ||
	    !n4.IsValid() || n4.IsRoot()) {
	    fprintf(stderr, "test storage14 failed (step 6)\n");
	    return 14;
	}
	if (!s.SetRootNode(n3) || !n1.IsValid() || n1.IsRoot() ||
	    !n2.IsValid() || n2.IsRoot() ||
	    !n3.IsValid() || !n3.IsRoot() ||
	    !n4.IsValid() || n4.IsRoot()) {
	    fprintf(stderr, "test storage14 failed (step 7)\n");
	    return 14;
	}
	if (!s.SetRootNode(n4) || !n1.IsValid() || n1.IsRoot() ||
	    !n2.IsValid() || n2.IsRoot() ||
	    !n3.IsValid() || n3.IsRoot() ||
	    !n4.IsValid() || !n4.IsRoot()) {
	    fprintf(stderr, "test storage14 failed (step 8)\n");
	    return 14;
	}
	if ((n1 == n2) || (n1 == n3) || (n1 == n3) || (n1 == n4)) {
	    fprintf(stderr, "test storage14 failed (step 11)\n");
	    return 14;
	}
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage14 failed (step 12)\n");
	    return 14;
	}
    }
    if (n1.IsValid() || n2.IsValid() || n3.IsValid() || n4.IsValid()) {
	fprintf(stderr, "test storage14 failed (step 13)\n");
	return 14;
    }

    return 0;
}

static int
test_storage15()
{
    const char *nm;

    /*
     * Test GetName() and GetDriver().
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage15 failed (step 0)\n");
	    return 15;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage15 failed (step 1)\n");
	    return 15;
	}
	if (strcmp(s.GetDriver(), E4_METAKIT) != 0) {
	    fprintf(stderr, "test storage15 failed (step 2)\n");
	    return 15;
	}
	nm = s.GetName();
	if (nm == NULL) {
	    fprintf(stderr, "test storage15 failed (step 3)\n");
	    return 15;
	}
	if (strcmp(nm, "foo.db") != 0) {
	    fprintf(stderr, "test storage15 failed (step 3)\n");
	    return 15;
	}
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage15 failed (step 4)\n");
	    return 15;
	}
    }

    return 0;
}

static int
test_storage17()
{
    e4_Node n;

    /*
     * Test that deleting the instance of the e4_StorageImpl invalidates any
     * instances of e4_NodeImpl for that storage.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage17 failed (step 0)\n");
	    return 17;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage17 failed (step 1)\n");
	    return 17;
	}
	if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
	    fprintf(stderr, "test storage17 failed (step 2)\n");
	    return 17;
	}
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage17 failed (step 3)\n");
	    return 17;
	}
	if (n.IsValid()) {
	    fprintf(stderr, "test storage17 failed (step 4)\n");
	    return 17;
	}
    }
    if (n.IsValid()) {
	fprintf(stderr, "test storage17 failed (step 5)\n");
	return 17;
    }

    return 0;
}

static int
test_storage18()
{
    /*
     * Test e4_StorageImpl::IsValid under various conditions.
     */

    {
	e4_Storage s;
	
	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage18 failed (step 0)\n");
	    return 18;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage18 failed (step 1)\n");
	    return 18;
	}
	{
	    e4_Node n;

	    /*
	     * Implicitly assume s.IsValid().
	     */

	    if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
		fprintf(stderr, "test storage18 failed (step 2)\n");
		return 18;
	    }
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage18 failed (step 3)\n");
	    return 18;
	}
	{
	    e4_Node n;

	    if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
		fprintf(stderr, "test storage18 failed (step 4)\n");
		return 18;
	    }
	    if (!s.IsValid()) {
		fprintf(stderr, "test storage18 failed (step 5)\n");
		return 18;
	    }
	    n.Detach();
	    if (!s.IsValid()) {
		fprintf(stderr, "test storage18 failed (step 6)\n");
		return 18;
	    }
	    if (!n.IsValid() || !n.IsRoot() || !n.IsDetached()) {
		fprintf(stderr, "test storage18 failed (step 7)\n");
		return 18;
	    }
	    s.Delete();
	    if (s.IsValid()) {
		fprintf(stderr, "test storage18 failed (step 8)\n");
		return 18;
	    }
	    if (n.IsValid()) {
		fprintf(stderr, "test storage18 failed (step 9)\n");
		return 18;
	    }
	}
	if (s.IsValid()) {
	    fprintf(stderr, "test storage18 failed (step 10)\n");
	    return 18;
	}
    }

    return 0;
}

static int
test_storage19()
{
    /*
     * Test that the same underlying storage is used if it is opened
     * more than once for the same file and driver name. Test that it returns
     * different entities for different file names.
     */

    {
	e4_Storage s1;

	if (!clean_storage("foo1.db", s1)) {
	    fprintf(stderr, "test storage19 failed (step 1.0)\n");
	    return 19;
	}
	if (!s1.IsValid()) {
	    fprintf(stderr, "test storage19 failed (step 1.1)\n");
	    return 19;
	}

	e4_Storage s2;

	if (!clean_storage("foo2.db", s2)) {
	    fprintf(stderr, "test storage19 failed (step 2.0)\n");
	    return 19;
	}
	if (!s2.IsValid()) {
	    fprintf(stderr, "test storage19 failed (step 2.1)\n");
	    return 19;
	}
	if (s1 == s2) {
	    fprintf(stderr, "test storage19 failed (step 3)\n");
	    return 19;
	}

	/*
	 * The s1 and s2 variables go out of scope without Deleteing
	 * the underlying storages.
	 */
    }
    {
	e4_Storage s3("foo1.db", E4_METAKIT);
	if (!s3.IsValid()) {
	    fprintf(stderr, "test storage19 failed (step 4)\n");
	    return 19;
	}
	e4_Storage s4("foo1.db", E4_METAKIT);
	if (!s4.IsValid()) {
	    fprintf(stderr, "test storage19 failed (step 5)\n");
	    return 19;
	}
	if (s3 != s4) {
	    fprintf(stderr, "test storage19 failed (step 6)\n");
	    return 19;
	}
	s3.Delete();
	if (s3.IsValid() || s4.IsValid()) {
	    fprintf(stderr, "test storage19 failed (step 7)\n");
	    return 19;
	}
	e4_Storage s5("foo2.db", E4_METAKIT);
	if (!s5.IsValid()) {
	    fprintf(stderr, "test storage19 failed (step 8)\n");
	    return 19;
	}
	s5.Delete();
	if (s5.IsValid()) {
	    fprintf(stderr, "test storage19 failed (step 9)\n");
	    return 19;
	}
    }

    return 0;
}

static int
test_storage20()
{
    e4_Storage *sp;

    {
        {
	    e4_Storage x;

	    if (!clean_storage("foo.db", x)) {
		fprintf(stderr, "test storage20 failed (step 0)\n");
		return 20;
	    }
	}

	e4_Storage s;

	/*
	 * Try new-style construction.
	 */

	sp = new e4_Storage("foo.db", E4_METAKIT);
	s = *sp;
	delete sp;

	if (!s.IsValid()) {
	    fprintf(stderr, "test storage20 failed (step 1)\n");
	    return 20;
	}
	sp = new e4_Storage("foo.db", E4_METAKIT);
	if (!sp->IsValid()) {
	    fprintf(stderr, "test storage20 failed (step 2)\n");
	    return 20;
	}
	if (*sp != s) {
	    fprintf(stderr, "test storage20 failed (step 3)\n");
	    return 20;
	}
	delete sp;
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage20 failed (step 4)\n");
	    return 20;
	}
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage20 failed (step 5)\n");
	    return 20;
	}
    }

    return 0;
}

static int
test_storage21()
{
    {
	e4_Storage x;
	
	if (!clean_storage("foo.db", x)) {
	    fprintf(stderr, "test storage21 failed (step 0)\n");
	    return 20;
	}
    }

    e4_Storage *sp;

    /*
     * Test operator==, operator!= and IsValid with new-style construction.
     */

    sp = new e4_Storage("foo.db", E4_METAKIT);
    if (!sp->IsValid()) {
	fprintf(stderr, "test storage21 failed (step 1)\n");
	return 21;
    }
    {
	e4_Storage s1("foo.db", E4_METAKIT);
	if (!s1.IsValid()) {
	    fprintf(stderr, "test storage21 failed (step 2)\n");
	    return 21;
	}
	if (s1 != *sp) {
	    fprintf(stderr, "test storage21 failed (step 3)\n");
	    return 21;
	}
	if (s1 == *sp) {
	} else {
	    fprintf(stderr, "test storage21 failed (step 4)\n");
	    return 21;
	}
	e4_Storage s2;
	if (s2.IsValid()) {
	    fprintf(stderr, "test storage21 failed (step 5)\n");
	    return 21;
	}
	e4_Storage s3;
	if (s3.IsValid()) {
	    fprintf(stderr, "test storage21 failed (step 6)\n");
	    return 21;
	}
	if (s3 == s2) {
	} else {
	    fprintf(stderr, "test storage21 failed (step 7)\n");
	    return 21;
	}
	if ((s3 != s2) || (s2 != s3)) {
	    fprintf(stderr, "test storage21 failed (step 8)\n");
	    return 21;
	}
    }
    if (!sp->IsValid()) {
	fprintf(stderr, "test storage21 failed (step 9)\n");
	return 21;
    }
    {
	e4_Storage s5("foo.db", E4_METAKIT);

	if ((!s5.IsValid()) || (!sp->IsValid())) {
	    fprintf(stderr, "test storage21 failed (step 10)\n");
	    return 21;
	}
	delete sp;
	if (!s5.IsValid()) {
	    fprintf(stderr, "test storage21 failed (step 11)\n");
	    return 21;
	}
	s5.Delete();
	if (s5.IsValid()) {
	    fprintf(stderr, "test storage21 failed (step 12)\n");
	    return 21;
	}
    }

    return 0;
}

static int
test_storage22()
{
    /*
     * Test invalidStorage.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage22 failed (step 0)\n");
	    return 22;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage22 failed (step 1)\n");
	    return 22;
	}
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage22 failed (step 2)\n");
	    return 22;
	}
	s = invalidStorage;
	if (s.IsValid()) {
	    fprintf(stderr, "test storage22 failed (step 3)\n");
	    return 22;
	}
    }

    return 0;
}

static int
test_storage24()
{
    e4_Node n, rn;
    e4_Vertex v;

    /*
     * Test e4_Storage::CreateDetachedNode
     * and e4_Storage::CreateDetachedVertex.
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage24 failed (step 0)\n");
	    return 24;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage24 failed (step 1)\n");
	    return 24;
	}
	if (!s.GetRootNode(rn) || !rn.IsValid() || !rn.IsRoot()) {
	    fprintf(stderr, "test storage24 failed (step 3)\n");
	    return 24;
	}
	if (!s.CreateDetachedNode(n) || !n.IsValid() || !n.IsDetached() ||
	    (n == rn) || n.IsRoot()) {
	    fprintf(stderr, "test storage24 failed (step 4)\n");
	    return 24;
	}
	if ((n.VertexCount() != 0) || (n.ParentCount() != 0)) {
	    fprintf(stderr, "test storage24 failed (step 5)\n");
	    return 24;
	}
	if (!s.CreateDetachedVertex("foo", n, v) || !v.IsValid() ||
	    !v.IsDetached() || !n.IsDetached()) {
	    fprintf(stderr, "test storage24 failed (step 6)\n");
	    return 24;
	}

	/*
	 * Assign a new detached vertex to "v", this makes the previous
	 * vertex unreachable.
	 */

	if (!s.CreateDetachedVertex("foo", 32, v) || !v.IsValid() ||
	    !v.IsDetached()) {
	    fprintf(stderr, "test storage24 failed (step 7)\n");
	    return 24;
	}

	/*
	 * The above assignment to "v" made the previous detached vertex
	 * unreachable. "n" should still be valid and detached.
	 */

	if (!n.IsValid() || !n.IsDetached()) {
	    fprintf(stderr, "test storage24 failed (step 8)\n");
	    return 24;
	}

	if (!s.CreateDetachedVertex("foo", (double) 34.45, v) || 
	    !v.IsValid() || !v.IsDetached()) {
	    fprintf(stderr, "test storage24 failed (step 9)\n");
	    return 24;
	}
	if (!s.CreateDetachedVertex("foo", "hello there", v) ||
	    !v.IsValid() || !v.IsDetached()) {
	    fprintf(stderr, "test storage24 failed (step 10)\n");
	    return 24;
	}
	s.Delete();
	if (s.IsValid() || n.IsValid() || v.IsValid()) {
	    fprintf(stderr, "test storage24 failed (step 11)\n");
	    return 24;
	}
    }

    return 0;
}

static int
test_storage26()
{
    e4_Node n;
    int oldState = 0, i = 0;

    /*
     * Tests e4_Storage::NeedsGC(), E4_AUTOGC and e4_Storage::DoGC().
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage26 failed (step 0)\n");
	    return 26;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage26 failed (step 1)\n");
	    return 26;
	}
	for (i = 0; i < 10; i++) {
	    if (!s.CreateDetachedNode(n) || !n.IsValid() || !n.IsDetached()) {
		fprintf(stderr, "test storage26 failed (step 3.%d)\n", i);
		return 26;
	    }
	}

	/*
	 * The default should be that GC is not deferred, and since its
	 * not deferred, there should be no need for GC right now.
	 */

	if (s.NeedsGC() || (!HASSTATE(s, E4_AUTOGC))) {
	    fprintf(stderr, "test storage26 failed (step 4)\n");
	    return 26;
	}

	/*
	 * Now make GC deferred.
	 */
	
	oldState = s.GetState();
	CLEARSTATE(s, E4_AUTOGC);
	if (HASSTATE(s, E4_AUTOGC) || ((oldState & E4_AUTOGC) == 0)) {
	    fprintf(stderr, "test storage26 failed (step 5)\n");
	    return 26;
	}

	/*
	 * Cause some recycle-able storage to accummulate.
	 */

	for (i = 0; i < 10; i++) {
	    if (!s.CreateDetachedNode(n) || !n.IsValid() || !n.IsDetached()) {
		fprintf(stderr, "test storage26 failed (step 6.%d)\n", i);
		return 26;
	    }
	}

	/*
	 * Check that we notice there's some recycle-able storage
	 * in this storage.
	 */

	if (!s.NeedsGC()) {
	    fprintf(stderr, "test storage26 failed (step 7)\n");
	    return 26;
	}

	/*
	 * Do a GC and see if we collected anything. Because
	 * the request is explicit, we should have collected
	 * and not need to have a GC anymore.
	 */

	s.DoGC();
	if (s.NeedsGC()) {
	    fprintf(stderr, "test storage26 failed (step 8)\n");
	    return 26;
	}

	/*
	 * Cause some recycle-able storage to accummulate.
	 */

	for (i = 0; i < 10; i++) {
	    if (!s.CreateDetachedNode(n) || !n.IsValid() || !n.IsDetached()) {
		fprintf(stderr, "test storage26 failed (step 9.%d)\n", i);
		return 26;
	    }
	}

	/*
	 * OK, no longer defer GC.
	 */

	oldState = s.GetState();
	SETSTATE(s, E4_AUTOGC);
	if (!HASSTATE(s, E4_AUTOGC) ||
	    ((oldState & E4_AUTOGC) == E4_AUTOGC)) {
	    fprintf(stderr, "test storage26 failed (step 10)\n");
	    return 26;
	}

	/*
	 * Now do a GC and see if we still need a GC afterwards.
	 */

	s.DoGC();
	if (s.NeedsGC()) {
	    fprintf(stderr, "test storage26 failed (step 11)\n");
	    return 26;
	}
	s.Delete();
	if (s.IsValid()) {
	    fprintf(stderr, "test storage26 failed (step 12)\n");
	    return 26;
	}
    }

    return 0;
}

static int
test_storage27()
{
    /*
     * Tests e4_Storage::CopyTo().
     */

    {
	e4_Storage s;
	e4_Node n, on;
	e4_Vertex v;
	int i = 0, j = 0, rank = 0;
	const char *str;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage27 failed (step 0)\n");
	    return 27;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage27 failed (step 1)\n");
	    return 27;
	}
	if (!s.GetRootNode(n) || !n.IsValid() || !n.IsRoot()) {
	    fprintf(stderr, "test storage27 failed (step 2)\n");
	    return 27;
	}
	for (i = 0; i < 100; i++) {
	    if (!n.AddVertex("foo", E4_IOLAST, rank, i) ||
		(n.VertexCount() != i + 1)) {
		fprintf(stderr, "test storage27 failed (step 3.%d)\n", i);
		return 27;
	    }
	}
	for (i = 100; i < 200; i++) {
	    if (!n.AddVertex("bar", E4_IOLAST, rank, "bar") ||
		(n.VertexCount() != i + 1)) {
		fprintf(stderr, "test storage27 failed (step 4.%d)\n", 
			i - 100);
		return 27;
	    }
	}
	if (s.IsStable() || !s.Commit() || !s.IsStable()) {
	    fprintf(stderr, "test storage27 failed (step 5)\n");
	    return 27;
	}

	e4_Storage os;

	(void) clean_storage("bar.db", os);

	if (!s.CopyTo(os, false) || !s.IsStable() ||
	    os.IsStable() || !os.Commit() || !os.IsStable()) {
	    fprintf(stderr, "test storage27 failed (step 6)\n");
	    return 27;
	}
	if (!os.GetRootNode(on) || !on.IsValid() || !on.IsRoot()) {
	    fprintf(stderr, "test storage27 failed (step 7)\n");
	    return 27;
	}
	if (n.VertexCount() != 200) {
	    fprintf(stderr, "test storage27 failed (step 8)\n");
	    return 27;
	}
	for (i = 0; i < 100; i++) {
	    if (!n.GetVertexRef("foo", i+1, v) || !v.IsValid() ||
		(v.Type() != E4_VTINT) || !v.Get(j) || (j != i)) {
		fprintf(stderr, "test storage27 failed (step 9.%d)\n", i);
		return 27;
	    }
	}
	for (i = 100; i < 200; i++) {
	    if (!n.GetVertexRef("bar", i-100+1, v) || !v.IsValid() ||
		(v.Type() != E4_VTSTRING) || !v.Get(str) ||
		(strcmp(str, "bar") != 0)) {
		fprintf(stderr, "test storage27 failed (step 10.%d)\n", i);
		return 27;
	    }
	}

	os.Delete();
	s.Delete();
    }

    return 0;
}

static int
test_storage28()
{
    int maj;
    int min;
    e4_ReleaseStatus rs;
    int ri;

    /*
     * Test e4_Storage::GetVersionInfo() and e4_Storage::GetVersion().
     */

    {
	e4_Storage s;

	if (!clean_storage("foo.db", s)) {
	    fprintf(stderr, "test storage28 failed (step 0)\n");
	    return 28;
	}
	if (!s.IsValid()) {
	    fprintf(stderr, "test storage28 failed (step 1)\n");
	    return 28;
	}
	if (!s.Commit()) {
	    fprintf(stderr, "test storage28 failed (step 2)\n");
	    return 28;
	}
    }

    if (strcmp(e4_Storage::storage_version("foo.db", E4_METAKIT),
	       e4_Storage::version()) != 0) {
	fprintf(stderr, "test storage28 failed (step 3)\n");
	return 28;
    }
    if (!e4_Storage::storage_version_info("foo.db",
					  E4_METAKIT,
					  maj,
					  min,
					  rs,
					  ri)) {
	fprintf(stderr, "test storage28 failed (step 4)\n");
	return 28;
    }
    if ((maj != e4_Storage::major_version()) ||
	(min != e4_Storage::minor_version()) ||
	(rs != e4_Storage::release_status()) ||
	(ri != e4_Storage::release_iteration())) {
	fprintf(stderr, "test storage28 failed (step 5)\n");
	return 28;
    }
    {
	e4_Storage s1("foo.db", E4_METAKIT);

	s1.Delete();
    }

    return 0;
}

int
test_storage()
{
    int result = 0;
    
    fprintf(stderr, "Running e4_Storage tests: ");

    result = test_storage1();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage2();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");

    /*
     * test test_storage3 removed.
     */

    result = test_storage4();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage5();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage6();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");

    /*
     * Test test_storage7 removed.
     */

    result = test_storage8();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage9();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage10();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");

    /*
     * Tests test_storage11, test_storage12 were removed.
     */

    result = test_storage13();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage14();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage15();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");

    /*
     * Test test_storage16 was removed.
     */

    result = test_storage17();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage18();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage19();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage20();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage21();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage22();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");

    /*
     * Test test_storage23 was removed.
     */

    result = test_storage24();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");

    /*
     * Test test_storage25 was removed.
     */

    result = test_storage26();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage27();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".");
    result = test_storage28();
    if (result != 0) {
        return result;
    }
    fprintf(stderr, ".\n");
    
    return 0;
}
