/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.apache.directory.mavibot.btree;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.apache.directory.mavibot.btree.BTree;
import org.apache.directory.mavibot.btree.Cursor;
import org.apache.directory.mavibot.btree.RecordManager;
import org.apache.directory.mavibot.btree.Tuple;
import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
import org.apache.directory.mavibot.btree.serializer.LongSerializer;
import org.apache.directory.mavibot.btree.serializer.StringSerializer;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;


/**
 * test the RecordManager
 * @author <a href="mailto:labs@labs.apache.org">Mavibot labs Project</a>
 */
//@Ignore("ignoring till RM functionality is standardized")
public class RecordManagerTest
{
    private BTree<Long, String> btree = null;

    private RecordManager recordManager1 = null;

    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    private File dataDir = null;


    @Before
    public void createBTree()
    {
        dataDir = tempFolder.newFolder( UUID.randomUUID().toString() );

        openRecordManagerAndBtree();

        try
        {
            // Create a new BTree
            btree = recordManager1.addBTree( "test", new LongSerializer(), new StringSerializer(), false );
        }
        catch ( Exception e )
        {
            throw new RuntimeException( e );
        }
    }


    private void openRecordManagerAndBtree()
    {
        try
        {
            if ( recordManager1 != null )
            {
                recordManager1.close();
            }

            // Now, try to reload the file back
            recordManager1 = new RecordManager( dataDir.getAbsolutePath() );

            // load the last created btree
            if ( btree != null )
            {
                btree = recordManager1.getManagedTree( btree.getName() );
            }
        }
        catch ( Exception e )
        {
            throw new RuntimeException( e );
        }
    }


    /**
     * Test the creation of a RecordManager, and that we can read it back.  
     */
    @Test
    public void testRecordManager() throws IOException, BTreeAlreadyManagedException
    {
        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );
    }


    /**
     * Test the creation of a RecordManager with a BTree containing data.
     */
    @Test
    public void testRecordManagerWithBTree() throws IOException, BTreeAlreadyManagedException, KeyNotFoundException
    {
        // Now, add some elements in the BTree
        btree.insert( 3L, "V3" );
        btree.insert( 1L, "V1" );
        btree.insert( 5L, "V5" );

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        assertTrue( btree1.hasKey( 1L ) );
        assertTrue( btree1.hasKey( 3L ) );
        assertTrue( btree1.hasKey( 5L ) );
        assertEquals( "V1", btree1.get( 1L ) );
        assertEquals( "V3", btree1.get( 3L ) );
        assertEquals( "V5", btree1.get( 5L ) );
    }


    /**
     * Test the creation of a RecordManager with a BTree containing data, enough for some Node to be created.
     */
    @Test
    public void testRecordManagerWithBTreeLeafNode() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        // Now, add some elements in the BTree
        for ( long i = 1L; i < 32L; i++ )
        {
            btree.insert( i, "V" + i );
        }

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        for ( long i = 1L; i < 32L; i++ )
        {
            assertTrue( btree1.hasKey( i ) );
            assertEquals( "V" + i, btree1.get( i ) );
        }
    }


    /**
     * Test the creation of a RecordManager with a BTree containing 100 000 elements
     */
    @Test
    //@Ignore("This is a performance test")
    public void testRecordManagerWithBTreeLeafNode100K() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        // Don't keep any revision
        recordManager1.setKeepRevisions( false );

        String fileName = dataDir.getAbsolutePath() + "/mavibot.db";
        File file = new File( fileName );
        long fileSize = file.length();
        long nbElems = 100000L;
        System.out.println( "----- Size before = " + fileSize );

        // Now, add some elements in the BTree
        long t0 = System.currentTimeMillis();
        for ( Long i = 0L; i < nbElems; i++ )
        {
            String value = "V" + i;
            btree.insert( i, value );

            /*
            if ( !recordManager1.check() )
            {
                System.out.println( "Failure while adding element " + i );
                fail();
            }
            */

            if ( i % 10000 == 0 )
            {
                fileSize = file.length();
                System.out.println( "----- Size after insertion of " + i + " = " + fileSize );
                System.out.println( recordManager1 );
                //System.out.println( btree );
            }
        }
        long t1 = System.currentTimeMillis();

        fileSize = file.length();
        System.out.println( "Size after insertion of 100 000 elements : " + fileSize );
        System.out.println( "Time taken to write 100 000 elements : " + ( t1 - t0 ) );
        System.out.println( "  Nb elem/s : " + ( ( nbElems * 1000 ) / ( t1 - t0 ) ) );
        System.out.println( "Nb created page " + recordManager1.nbCreatedPages.get() );
        System.out.println( "Nb allocated page " + recordManager1.nbReusedPages.get() );
        System.out.println( "Nb page we have freed " + recordManager1.nbFreedPages.get() );
        System.out.println( recordManager1 );

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        long t2 = System.currentTimeMillis();
        for ( long i = 0L; i < nbElems; i++ )
        {
            //assertTrue( btree1.exist( i ) );
            assertEquals( "V" + i, btree1.get( i ) );
        }
        long t3 = System.currentTimeMillis();
        System.out.println( "Time taken to verify 100 000 elements : " + ( t3 - t2 ) );

        // Check the stored element a second time
        long t4 = System.currentTimeMillis();
        for ( long i = 0L; i < nbElems; i++ )
        {
            //assertTrue( btree1.exist( i ) );
            assertEquals( "V" + i, btree1.get( i ) );
        }
        long t5 = System.currentTimeMillis();
        System.out.println( "Time taken to verify 100 000 elements : " + ( t5 - t4 ) );
    }


    private void checkBTreeRevisionBrowse( BTree btree, long revision, long... values ) throws IOException,
        KeyNotFoundException
    {
        Cursor<Long, String> cursor = btree.browse( revision );
        List<Long> expected = new ArrayList<Long>( values.length );
        Set<Long> found = new HashSet<Long>( values.length );

        for ( long value : values )
        {
            expected.add( value );
        }

        int nb = 0;

        while ( cursor.hasNext() )
        {
            Tuple<Long, String> res = cursor.next();

            long key = res.getKey();
            assertEquals( expected.get( nb ), ( Long ) key );
            assertFalse( found.contains( key ) );
            found.add( key );
            assertEquals( "V" + key, res.getValue() );
            nb++;
        }

        assertEquals( values.length, nb );
        cursor.close();
    }


    private void checkBTreeRevisionBrowseFrom( BTree btree, long revision, long from, long... values )
        throws IOException,
        KeyNotFoundException
    {
        Cursor<Long, String> cursor = btree.browseFrom( revision, from );
        List<Long> expected = new ArrayList<Long>( values.length );
        Set<Long> found = new HashSet<Long>( values.length );

        for ( long value : values )
        {
            expected.add( value );
        }

        int nb = 0;

        while ( cursor.hasNext() )
        {
            Tuple<Long, String> res = cursor.next();

            long key = res.getKey();
            assertEquals( expected.get( nb ), ( Long ) key );
            assertFalse( found.contains( key ) );
            found.add( key );
            assertEquals( "V" + key, res.getValue() );
            nb++;
        }

        assertEquals( values.length, nb );
        cursor.close();

    }


    /**
     * Test the creation of a RecordManager with a BTree containing data, where we keep the revisions, 
     * and browse the BTree.
     */
    @Test
    public void testRecordManagerBrowseWithKeepRevisions() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        recordManager1.setKeepRevisions( true );

        // Now, add some elements in the BTree
        btree.insert( 3L, "V3" );
        long rev1 = btree.getRevision();

        btree.insert( 1L, "V1" );
        long rev2 = btree.getRevision();

        btree.insert( 5L, "V5" );
        long rev3 = btree.getRevision();

        // Check that we can browse each revision
        // revision 1
        checkBTreeRevisionBrowse( btree, rev1, 3L );

        // Revision 2
        checkBTreeRevisionBrowse( btree, rev2, 1L, 3L );

        // Revision 3
        checkBTreeRevisionBrowse( btree, rev3, 1L, 3L, 5L );

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        assertTrue( btree1.hasKey( 1L ) );
        assertTrue( btree1.hasKey( 3L ) );
        assertTrue( btree1.hasKey( 5L ) );
        assertEquals( "V1", btree1.get( 1L ) );
        assertEquals( "V3", btree1.get( 3L ) );
        assertEquals( "V5", btree1.get( 5L ) );

        // Check that we can read the revision again
        // revision 1
        checkBTreeRevisionBrowse( btree, rev1, 3L );

        // Revision 2
        checkBTreeRevisionBrowse( btree, rev2, 1L, 3L );

        // Revision 3
        checkBTreeRevisionBrowse( btree, rev3, 1L, 3L, 5L );
    }


    /**
     * Test the creation of a RecordManager with a BTree containing data, where we keep the revision, and 
     * we browse from a key
     */
    @Test
    public void testRecordManagerBrowseFromWithRevision() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        recordManager1.setKeepRevisions( true );

        // Now, add some elements in the BTree
        btree.insert( 3L, "V3" );
        long rev1 = btree.getRevision();

        btree.insert( 1L, "V1" );
        long rev2 = btree.getRevision();

        btree.insert( 5L, "V5" );
        long rev3 = btree.getRevision();

        // Check that we can browse each revision
        // revision 1
        checkBTreeRevisionBrowseFrom( btree, rev1, 3L, 3L );

        // Revision 2
        checkBTreeRevisionBrowseFrom( btree, rev2, 3L, 3L );

        // Revision 3
        checkBTreeRevisionBrowseFrom( btree, rev3, 3L, 3L, 5L );

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        assertTrue( btree1.hasKey( 1L ) );
        assertTrue( btree1.hasKey( 3L ) );
        assertTrue( btree1.hasKey( 5L ) );
        assertEquals( "V1", btree1.get( 1L ) );
        assertEquals( "V3", btree1.get( 3L ) );
        assertEquals( "V5", btree1.get( 5L ) );

        // Check that we can read the revision again
        // revision 1
        checkBTreeRevisionBrowseFrom( btree, rev1, 3L, 3L );

        // Revision 2
        checkBTreeRevisionBrowseFrom( btree, rev2, 3L, 3L );

        // Revision 3
        checkBTreeRevisionBrowseFrom( btree, rev3, 3L, 3L, 5L );
    }


    /**
     * Test a get() from a given revision
     */
    @Test
    public void testGetWithRevision() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        recordManager1.setKeepRevisions( true );

        // Now, add some elements in the BTree
        btree.insert( 3L, "V3" );
        long rev1 = btree.getRevision();

        btree.insert( 1L, "V1" );
        long rev2 = btree.getRevision();

        btree.insert( 5L, "V5" );
        long rev3 = btree.getRevision();

        // Delete one element
        btree.delete( 3L );
        long rev4 = btree.getRevision();

        // Check that we can get a value from each revision
        // revision 1
        assertEquals( "V3", btree.get( rev1, 3L ) );

        // revision 2
        assertEquals( "V1", btree.get( rev2, 1L ) );
        assertEquals( "V3", btree.get( rev2, 3L ) );

        // revision 3
        assertEquals( "V1", btree.get( rev3, 1L ) );
        assertEquals( "V3", btree.get( rev3, 3L ) );
        assertEquals( "V5", btree.get( rev3, 5L ) );

        // revision 4
        assertEquals( "V1", btree.get( rev4, 1L ) );
        assertEquals( "V5", btree.get( rev4, 5L ) );

        try
        {
            btree.get( rev4, 3L );
            fail();
        }
        catch ( KeyNotFoundException knfe )
        {
            // expected
        }

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        assertTrue( btree1.hasKey( 1L ) );
        assertFalse( btree1.hasKey( 3L ) );
        assertTrue( btree1.hasKey( 5L ) );
        assertEquals( "V1", btree1.get( 1L ) );
        assertEquals( "V5", btree1.get( 5L ) );

        // Check that we can get a value from each revision
        // revision 1
        assertEquals( "V3", btree.get( rev1, 3L ) );

        // revision 2
        assertEquals( "V1", btree.get( rev2, 1L ) );
        assertEquals( "V3", btree.get( rev2, 3L ) );

        // revision 3
        assertEquals( "V1", btree.get( rev3, 1L ) );
        assertEquals( "V3", btree.get( rev3, 3L ) );
        assertEquals( "V5", btree.get( rev3, 5L ) );

        // revision 4
        assertEquals( "V1", btree.get( rev4, 1L ) );
        assertEquals( "V5", btree.get( rev4, 5L ) );

        try
        {
            btree.get( rev4, 3L );
            fail();
        }
        catch ( KeyNotFoundException knfe )
        {
            // expected
        }
    }


    /**
     * Test a contain() from a given revision
     */
    @Test
    public void testContainWithRevision() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        recordManager1.setKeepRevisions( true );

        // Now, add some elements in the BTree
        btree.insert( 3L, "V3" );
        long rev1 = btree.getRevision();

        btree.insert( 1L, "V1" );
        long rev2 = btree.getRevision();

        btree.insert( 5L, "V5" );
        long rev3 = btree.getRevision();

        // Delete one element
        btree.delete( 3L );
        long rev4 = btree.getRevision();

        // Check that we can get a value from each revision
        // revision 1
        assertFalse( btree.contains( rev1, 1L, "V1" ) );
        assertTrue( btree.contains( rev1, 3L, "V3" ) );
        assertFalse( btree.contains( rev1, 5L, "V5" ) );

        // revision 2
        assertTrue( btree.contains( rev2, 1L, "V1" ) );
        assertTrue( btree.contains( rev2, 3L, "V3" ) );
        assertFalse( btree.contains( rev2, 5L, "V5" ) );

        // revision 3
        assertTrue( btree.contains( rev3, 1L, "V1" ) );
        assertTrue( btree.contains( rev3, 3L, "V3" ) );
        assertTrue( btree.contains( rev3, 5L, "V5" ) );

        // revision 4
        assertTrue( btree.contains( rev4, 1L, "V1" ) );
        assertFalse( btree.contains( rev4, 3L, "V3" ) );
        assertTrue( btree.contains( rev4, 5L, "V5" ) );

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        assertTrue( btree1.hasKey( 1L ) );
        assertFalse( btree1.hasKey( 3L ) );
        assertTrue( btree1.hasKey( 5L ) );
        assertEquals( "V1", btree1.get( 1L ) );
        assertEquals( "V5", btree1.get( 5L ) );

        // Check that we can get a value from each revision
        // revision 1
        assertFalse( btree.contains( rev1, 1L, "V1" ) );
        assertTrue( btree.contains( rev1, 3L, "V3" ) );
        assertFalse( btree.contains( rev1, 5L, "V5" ) );

        // revision 2
        assertTrue( btree.contains( rev2, 1L, "V1" ) );
        assertTrue( btree.contains( rev2, 3L, "V3" ) );
        assertFalse( btree.contains( rev2, 5L, "V5" ) );

        // revision 3
        assertTrue( btree.contains( rev3, 1L, "V1" ) );
        assertTrue( btree.contains( rev3, 3L, "V3" ) );
        assertTrue( btree.contains( rev3, 5L, "V5" ) );

        // revision 4
        assertTrue( btree.contains( rev4, 1L, "V1" ) );
        assertFalse( btree.contains( rev4, 3L, "V3" ) );
        assertTrue( btree.contains( rev4, 5L, "V5" ) );
    }


    /**
     * Test a hasKey() from a given revision
     */
    @Test
    public void testHasKeyWithRevision() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        recordManager1.setKeepRevisions( true );

        // Now, add some elements in the BTree
        btree.insert( 3L, "V3" );
        long rev1 = btree.getRevision();

        btree.insert( 1L, "V1" );
        long rev2 = btree.getRevision();

        btree.insert( 5L, "V5" );
        long rev3 = btree.getRevision();

        // Delete one element
        btree.delete( 3L );
        long rev4 = btree.getRevision();

        // Check that we can get a value from each revision
        // revision 1
        assertFalse( btree.hasKey( rev1, 1L ) );
        assertTrue( btree.hasKey( rev1, 3L ) );
        assertFalse( btree.hasKey( rev1, 5L ) );

        // revision 2
        assertTrue( btree.hasKey( rev2, 1L ) );
        assertTrue( btree.hasKey( rev2, 3L ) );
        assertFalse( btree.hasKey( rev2, 5L ) );

        // revision 3
        assertTrue( btree.hasKey( rev3, 1L ) );
        assertTrue( btree.hasKey( rev3, 3L ) );
        assertTrue( btree.hasKey( rev3, 5L ) );

        // revision 4
        assertTrue( btree.hasKey( rev4, 1L ) );
        assertFalse( btree.hasKey( rev4, 3L ) );
        assertTrue( btree.hasKey( rev4, 5L ) );

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        assertEquals( 1, recordManager1.getNbManagedTrees() );

        Set<String> managedBTrees = recordManager1.getManagedTrees();

        assertEquals( 1, managedBTrees.size() );
        assertTrue( managedBTrees.contains( "test" ) );

        BTree<Long, String> btree1 = recordManager1.getManagedTree( "test" );

        assertNotNull( btree1 );
        assertEquals( btree.getComparator().getClass().getName(), btree1.getComparator().getClass().getName() );
        assertEquals( btree.getFile(), btree1.getFile() );
        assertEquals( btree.getKeySerializer().getClass().getName(), btree1.getKeySerializer().getClass().getName() );
        assertEquals( btree.getName(), btree1.getName() );
        assertEquals( btree.getNbElems(), btree1.getNbElems() );
        assertEquals( btree.getPageSize(), btree1.getPageSize() );
        assertEquals( btree.getRevision(), btree1.getRevision() );
        assertEquals( btree.getValueSerializer().getClass().getName(), btree1.getValueSerializer().getClass().getName() );

        // Check the stored element
        assertTrue( btree1.hasKey( 1L ) );
        assertFalse( btree1.hasKey( 3L ) );
        assertTrue( btree1.hasKey( 5L ) );
        assertEquals( "V1", btree1.get( 1L ) );
        assertEquals( "V5", btree1.get( 5L ) );

        // Check that we can get a value from each revision
        // revision 1
        assertFalse( btree.hasKey( rev1, 1L ) );
        assertTrue( btree.hasKey( rev1, 3L ) );
        assertFalse( btree.hasKey( rev1, 5L ) );

        // revision 2
        assertTrue( btree.hasKey( rev2, 1L ) );
        assertTrue( btree.hasKey( rev2, 3L ) );
        assertFalse( btree.hasKey( rev2, 5L ) );

        // revision 3
        assertTrue( btree.hasKey( rev3, 1L ) );
        assertTrue( btree.hasKey( rev3, 3L ) );
        assertTrue( btree.hasKey( rev3, 5L ) );

        // revision 4
        assertTrue( btree.hasKey( rev4, 1L ) );
        assertFalse( btree.hasKey( rev4, 3L ) );
        assertTrue( btree.hasKey( rev4, 5L ) );
    }


    /**
     * Test with BTrees containing duplicate keys
     */
    @Test
    public void testBTreesDuplicateKeys() throws IOException, BTreeAlreadyManagedException,
        KeyNotFoundException
    {
        int pageSize = 8;
        int numKeys = 2;
        String name = "duplicateTree";

        BTree<Long, String> dupsTree = new BTree( name, null, new LongSerializer(), new StringSerializer(), pageSize,
            true );

        recordManager1.manage( dupsTree );

        for ( long i = 0; i < numKeys; i++ )
        {
            for ( int k = 0; k < pageSize + 1; k++ )
            {
                dupsTree.insert( i, String.valueOf( k ) );
            }
        }

        // Now, try to reload the file back
        openRecordManagerAndBtree();

        dupsTree = recordManager1.getManagedTree( name );

        //        Cursor<Long, String> cursor1 = dupsTree.browse();
        //        while( cursor1.hasNext() )
        //        {
        //            System.out.println( cursor1.next() );
        //        }
        //        cursor1.close();

        for ( long i = 0; i < numKeys; i++ )
        {
            BTree<String, String> values = dupsTree.getValues( i );
            //            Cursor<String, String> cursor = values.browse();
            //            while( cursor.hasNext() )
            //            {
            //                System.out.println( cursor.next() );
            //            }
            //            cursor.close();

            for ( int k = 0; k < pageSize + 1; k++ )
            {
                assertTrue( values.hasKey( String.valueOf( k ) ) );
            }
        }
    }
}
