Package org.apache.jdbm

Source Code of org.apache.jdbm.BTreeTest$ObjectTT

/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed 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.jdbm;

import junit.framework.AssertionFailedError;
import junit.framework.TestResult;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.AbstractMap.SimpleEntry;
import java.util.*;
import java.util.concurrent.locks.Lock;

/**
* This class contains all Unit tests for {@link BTree}.
*
* @author Alex Boisvert
*/
public class BTreeTest
        extends TestCaseWithTestFile {

    static final boolean DEBUG = false;

    // the number of threads to be started in the synchronization test
    static final int THREAD_NUMBER = 5;

    // the size of the content of the maps for the synchronization
    // test. Beware that THREAD_NUMBER * THREAD_CONTENT_COUNT < Integer.MAX_VALUE.
    static final int THREAD_CONTENT_SIZE = 150;

    // for how long should the threads run.
    static final int THREAD_RUNTIME = 10 * 1000;

    protected TestResult result_;

    /**
     * Overrides TestCase.run(TestResult), so the errors from threads
     * started from this thread can be added to the testresult. This is
     * shown in
     * http://www.javaworld.com/javaworld/jw-12-2000/jw-1221-junit.html
     *
     * @param result the testresult
     */
    public void run(TestResult result) {
        result_ = result;
        super.run(result);
        result_ = null;
    }

//----------------------------------------------------------------------

    /**
     * Handles the exceptions from other threads, so they are not ignored
     * in the junit test result. This method must be called from every
     * thread's run() method, if any throwables were throws.
     *
     * @param t the throwable (either from an assertEquals, assertTrue,
     *          fail, ... method, or an uncaught exception to be added to the test
     *          result of the junit test.
     */

    protected void handleThreadException(final Throwable t) {
        synchronized (result_) {
            if (t instanceof AssertionFailedError)
                result_.addFailure(this, (AssertionFailedError) t);
            else
                result_.addError(this, t);
        }
    }


    /**
     * Basic tests
     */
    public void testBasics() throws IOException {
        DBAbstract db;
        BTree tree;
        byte[] test, test0, test1, test2, test3;
        byte[] value1, value2;

        test = "test".getBytes();
        test0 = "test0".getBytes();
        test1 = "test1".getBytes();
        test2 = "test2".getBytes();
        test3 = "test3".getBytes();

        value1 = "value1".getBytes();
        value2 = "value2".getBytes();

        if (DEBUG) {
            System.out.println("BTreeTest.testBasics");
        }

        db = newDBCache();
        tree = BTree.createInstance(db, new ByteArrayComparator(),null,null,true);

        tree.insert(test1, value1, false);
        tree.insert(test2, value2, false);

        byte[] result;
        result = (byte[]) tree.get(test0);
        if (result != null) {
            throw new Error("Test0 shouldn't be found");
        }

        result = (byte[]) tree.get(test1);
        if (result == null || ByteArrayComparator.compareByteArray(result, value1) != 0) {
            throw new Error("Invalid value for test1: " + result);
        }

        result = (byte[]) tree.get(test2);
        if (result == null || ByteArrayComparator.compareByteArray(result, value2) != 0) {
            throw new Error("Invalid value for test2: " + result);
        }

        result = (byte[]) tree.get(test3);
        if (result != null) {
            throw new Error("Test3 shouldn't be found");
        }

        db.close();
    }

    /**
     * Basic tests, just use the simple test possibilities of junit (cdaller)
     */
    public void testBasics2() throws IOException {
        DBAbstract db;
        BTree tree;
        byte[] test, test0, test1, test2, test3;
        byte[] value1, value2;

        test = "test".getBytes();
        test0 = "test0".getBytes();
        test1 = "test1".getBytes();
        test2 = "test2".getBytes();
        test3 = "test3".getBytes();

        value1 = "value1".getBytes();
        value2 = "value2".getBytes();

        if (DEBUG)
            System.out.println("BTreeTest.testBasics2");

        db = newDBCache();
        tree = BTree.createInstance(db, new ByteArrayComparator(),null,null,true);

        tree.insert(test1, value1, false);
        tree.insert(test2, value2, false);

        assertEquals(null, tree.get(test0));
        assertEquals(0, ByteArrayComparator.compareByteArray(value1, (byte[]) tree.get(test1)));
        assertEquals(0, ByteArrayComparator.compareByteArray(value2, (byte[]) tree.get(test2)));
        assertEquals(null, (byte[]) tree.get(test3));

        db.close();
    }


    /**
     * Test what happens after the dbager has been closed but the
     * btree is accessed. WHAT SHOULD HAPPEN???????????
     * (cdaller)
     */
    public void testClose()
            throws IOException {
        DBAbstract db;
        BTree tree;
        byte[] test, test0, test1, test2, test3;
        byte[] value1, value2;

        test = "test".getBytes();
        test0 = "test0".getBytes();
        test1 = "test1".getBytes();
        test2 = "test2".getBytes();
        test3 = "test3".getBytes();

        value1 = "value1".getBytes();
        value2 = "value2".getBytes();

        if (DEBUG)
            System.out.println("BTreeTest.testClose");

        db = newDBCache();
        tree = BTree.createInstance(db, new ByteArrayComparator(),null,null,true);

        tree.insert(test1, value1, false);
        tree.insert(test2, value2, false);

        assertEquals(null, tree.get(test0));
        assertEquals(0, ByteArrayComparator.compareByteArray(value1, (byte[]) tree.get(test1)));
        assertEquals(0, ByteArrayComparator.compareByteArray(value2, (byte[]) tree.get(test2)));
        assertEquals(null, (byte[]) tree.get(test3));

        db.close();

        try {
            tree.browse();
            fail("Should throw an IllegalStateException on access on not opened btree");
        } catch (IllegalStateException except) {
            // ignore
        }

        try {
            tree.get(test0);
            fail("Should throw an IllegalStateException on access on not opened btree");
        } catch (IllegalStateException except) {
            // ignore
        }

        try {
            tree.findGreaterOrEqual(test0);
            fail("Should throw an IllegalStateException on access on not opened btree");
        } catch (IllegalStateException except) {
            // ignore
        }

        try {
            tree.insert(test2, value2, false);
            fail("Should throw an IllegalStateException on access on not opened btree");
        } catch (IllegalStateException except) {
            // ignore
        }

        try {
            tree.remove(test0);
            fail("Should throw an IllegalStateException on access on not opened btree");
        } catch (IllegalStateException except) {
            // ignore
        }

        /*
        try {
            tree.size();
            fail( "Should throw an IllegalStateException on access on not opened btree" );
        } catch( IllegalStateException except ) {
            // ignore
        }
        */
    }


    /**
     * Test to insert different objects into one btree. (cdaller)
     */
    public void testInsert()
            throws IOException {
        DBAbstract db;
        BTree tree;

        if (DEBUG)
            System.out.println("BTreeTest.testInsert");

        db = newDBCache();
        tree = BTree.createInstance(db);

        // insert differnt objects and retrieve them
        tree.insert("test1", "value1", false);
        tree.insert("test2", "value2", false);
        tree.insert("one", new Integer(1), false);
        tree.insert("two", new Long(2), false);
        tree.insert("myownobject", new ObjectTT(new Integer(234)), false);

        assertEquals("value2", (String) tree.get("test2"));
        assertEquals("value1", (String) tree.get("test1"));
        assertEquals(new Integer(1), (Integer) tree.get("one"));
        assertEquals(new Long(2), (Long) tree.get("two"));

        // what happens here? must not be replaced, does it return anything?
        // probably yes!
        assertEquals("value1", tree.insert("test1", "value11", false));
        assertEquals("value1", tree.get("test1")); // still the old value?
        assertEquals("value1", tree.insert("test1", "value11", true));
        assertEquals("value11", tree.get("test1")); // now the new value!

        ObjectTT expected_obj = new ObjectTT(new Integer(234));
        ObjectTT btree_obj = (ObjectTT) tree.get("myownobject");
        assertEquals(expected_obj, btree_obj);

        db.close();
    }


    /**
     * Test to remove  objects from the btree. (cdaller)
     */
    public void testRemove()
            throws IOException {
        DBAbstract db;
        BTree tree;

        if (DEBUG) {
            System.out.println("BTreeTest.testRemove");
        }

        db = newDBCache();
        tree = BTree.createInstance(db);

        tree.insert("test1", "value1", false);
        tree.insert("test2", "value2", false);
        assertEquals("value1", (String) tree.get("test1"));
        assertEquals("value2", (String) tree.get("test2"));
        tree.remove("test1");
        assertEquals(null, (String) tree.get("test1"));
        assertEquals("value2", (String) tree.get("test2"));
        tree.remove("test2");
        assertEquals(null, (String) tree.get("test2"));

        int iterations = 1000;

        for (int count = 0; count < iterations; count++) {
            tree.insert("num" + count, new Integer(count), false);
        }

        assertEquals(iterations, tree._entries);

        for (int count = 0; count < iterations; count++) {
            assertEquals(new Integer(count), tree.get("num" + count));
        }

        for (int count = 0; count < iterations; count++) {
            tree.remove("num" + count);
        }

        assertEquals(0, tree._entries);

        db.close();
    }

    /**
     * Test to get differents objects in the btree. (cdaller)
     */
    public void testFind()
            throws IOException {
        DBAbstract db;
        BTree tree;

        if (DEBUG)
            System.out.println("BTreeTest.testFind");

        db = newDBCache();
        tree = BTree.createInstance(db);

        tree.insert("test1", "value1", false);
        tree.insert("test2", "value2", false);

        Object value = tree.get("test1");
        assertTrue(value instanceof String);
        assertEquals("value1", value);

        tree.insert("", "Empty String as key", false);
        assertEquals("Empty String as key", (String) tree.get(""));

        assertEquals(null, (String) tree.get("someoneelse"));

        db.close();
    }

    /**
     * Test deletion of btree from record manager. (kday)
     * <p/>
     * After deletion, the BTree and all of it's BTreeNode children (and their children)
     * should be removed from the recordmanager.
     */
    public void testDelete()
            throws IOException {
        if (DEBUG)
            System.out.println("BTreeTest.testFind");
        DBAbstract db = newDBCache();
        BTree<String, Serializable> tree = BTree.createInstance(db);

        // put enough data into the tree so we definitely have multiple nodes
        for (int count = 1; count <= 1000; count++) {
            tree.insert("num" + count, new Integer(count), false);
            if (count % 100 == 0)
                db.commit();
        }
        List<Long> out = new ArrayList<Long>();
        tree.dumpChildNodeRecIDs(out);
        assertTrue(out.size() > 0);
    }


    /**
     * Test to insert, retrieve and remove a large amount of data. (cdaller)
     */
    public void testLargeDataAmount()
            throws IOException {

        DBAbstract db;
        BTree tree;

        if (DEBUG)
            System.out.println("BTreeTest.testLargeDataAmount");

        db = newDBCache();
        // db = new jdbm.db.BaseRecordManager( "test" );

        tree = BTree.createInstance(db);
        // tree.setSplitPoint( 4 );

        int iterations = 10000;

        // insert data
        for (int count = 0; count < iterations; count++) {
            try {
                assertEquals(null, tree.insert("num" + count, new Integer(count), false));
            } catch (IOException except) {
                except.printStackTrace();
                throw except;
            }
        }

        // get data
        for (int count = 0; count < iterations; count++) {
            assertEquals(new Integer(count), tree.get("num" + count));
        }

        // delete data
        for (int count = 0; count < iterations; count++) {
            assertEquals(new Integer(count), tree.remove("num" + count));
        }

        assertEquals(0, tree._entries);

        db.close();
    }

    public void testRecordListener() throws IOException {
        DBAbstract db = newDBCache();
        BTree<Integer, String> tree = BTree.createInstance(db);
        final List<SimpleEntry<Integer, String>> dels = new ArrayList();
        final List<SimpleEntry<Integer, String>> ins = new ArrayList();
        final List<SimpleEntry<Integer, String>> updNew = new ArrayList();
        final List<SimpleEntry<Integer, String>> updOld = new ArrayList();

        tree.addRecordListener(new RecordListener<Integer, String>() {

            public void recordUpdated(Integer key, String oldValue, String newValue) throws IOException {
                updOld.add(new SimpleEntry<Integer, String>(key, oldValue));
                updNew.add(new SimpleEntry<Integer, String>(key, newValue));
            }

            public void recordRemoved(Integer key, String value) throws IOException {
                dels.add(new SimpleEntry<Integer, String>(key, value));
            }

            public void recordInserted(Integer key, String value) throws IOException {
                ins.add(new SimpleEntry<Integer, String>(key, value));
            }
        });

        //test insert
        tree.insert(11, "aa11", true);
        tree.insert(12, "aa12", true);
        assertTrue(ins.contains(new SimpleEntry(11, "aa11")));
        assertTrue(ins.contains(new SimpleEntry(12, "aa12")));
        assertTrue(ins.size() == 2);
        ins.clear();
        assertTrue(dels.isEmpty());
        assertTrue(updNew.isEmpty());
        assertTrue(updOld.isEmpty());

        //test update
        tree.insert(12, "aa123", true);
        assertTrue(ins.isEmpty());
        assertTrue(dels.isEmpty());
        assertTrue(updOld.contains(new SimpleEntry(12, "aa12")));
        assertTrue(updOld.size() == 1);
        updOld.clear();
        assertTrue(updNew.contains(new SimpleEntry(12, "aa123")));
        assertTrue(updNew.size() == 1);
        updNew.clear();

        //test remove
        tree.remove(11);
        assertTrue(dels.contains(new SimpleEntry(11, "aa11")));
        assertTrue(dels.size() == 1);
        dels.clear();
        assertTrue(ins.isEmpty());
        assertTrue(updOld.isEmpty());
        assertTrue(updNew.isEmpty());

    }


    /**
     * Tests the corner case of deleting all nodes from the tree.  In this case, all BTreeNodes
     * associated with the tree should be removed from the db.
     * <p/>
     * We are also going to test to make sure the db file doesn't grow (leak) if we repeat the
     * process a number of times.
     *
     * @throws Exception
     */
    public void testDeleteAllNodes() throws Exception {


        // we are going to run this test without object cache enabled.  If it is turned on,
        // we will have problems with using a different deserializer for BTreeNodes than the standard
        // serializer.

        String recordManagerBasename = newTestFile();
        String recordManagerDBname = recordManagerBasename + ".d.0";

        long previousdbSize = 0;
        for (int i = 0; i < 5; i++) {
            DBAbstract db = (DBAbstract) DBMaker.openFile(recordManagerBasename).disableCache().make();


                BTree<String, Serializable> tree = BTree.createInstance(db);
                String[] keys = new String[1000];
                for (int count = 0; count < 1000; count++) {
                    keys[count] = "num" + count;
                }

                // put enough data into the tree so we definitely have multiple nodes
                for (int count = 0; count < 1000; count++) {
                    tree.insert(keys[count], new Integer(count), false);
                    if (count % 100 == 0)
                        db.commit();
                }
                db.commit();

                long currentdbSize = new File(recordManagerDBname).length();
                assertTrue("file size too small " + currentdbSize, currentdbSize > 0);


                // now remove it all
                for (int count = 0; count < 1000; count++) {
                    tree.remove(keys[count]);
                    if (count % 100 == 0)
                        db.commit();
                }
                db.commit();

                BTreeNode root = tree.getRoot();
                assertNull(root);



                db.close();
                currentdbSize = new File(recordManagerDBname).length();
                assertTrue("file size too small " + currentdbSize, currentdbSize > 0);
                if (previousdbSize != 0) {
                    assertTrue(currentdbSize == previousdbSize);
                }

        }
    }


    /**
     * Test access from multiple threads. Assertions only work, when the
     * run() method is overridden and the exceptions of the threads are
     * added to the resultset of the TestCase. see run() and
     * handleException().
     */
    public void testMultithreadAccess()
            throws IOException {
        DBAbstract db;
        BTree tree;

        if (DEBUG)
            System.out.println("BTreeTest.testMultithreadAccess");

        db = newDBCache();
        tree = BTree.createInstance(db);

        TestThread[] thread_pool = new TestThread[THREAD_NUMBER];
        String name;
        Map content;

        // create content for the tree, different content for different threads!
        for (int thread_count = 0; thread_count < THREAD_NUMBER; thread_count++) {
            name = "thread" + thread_count;
            content = new TreeMap();
            for (int content_count = 0; content_count < THREAD_CONTENT_SIZE; content_count++) {
                // guarantee, that keys and values do not overleap,
                // otherwise one thread removes some keys/values of
                // other threads!
                content.put(name + "_" + content_count,
                        new Integer(thread_count * THREAD_CONTENT_SIZE + content_count));
            }
            thread_pool[thread_count] = new TestThread(name, tree, content);
            thread_pool[thread_count].start();
        }

        try {
            Thread.sleep(THREAD_RUNTIME);
        } catch (InterruptedException ignore) {
            ignore.printStackTrace();
        }

        // stop threads:
        for (int thread_count = 0; thread_count < THREAD_NUMBER; thread_count++) {
            if (DEBUG) System.out.println("Stop threads");
            thread_pool[thread_count].setStop();
        }
        // wait until the threads really stop:
        try {
            for (int thread_count = 0; thread_count < THREAD_NUMBER; thread_count++) {
                if (DEBUG) System.out.println("Join thread " + thread_count);
                thread_pool[thread_count].join();
                if (DEBUG) System.out.println("Joined thread " + thread_count);
            }
        } catch (InterruptedException ignore) {
            ignore.printStackTrace();
        }
        db.close();
    }


    /**
     * Helper method to 'simulate' the methods of an entry set of the btree.
     */
    protected static boolean containsKey(Object key, BTree btree)
            throws IOException {
        return (btree.get(key) != null);
    }


    /**
     * Helper method to 'simulate' the methods of an entry set of the btree.
     */
    protected static boolean containsValue(Object value, BTree btree)
            throws IOException {
        // we must synchronize on the BTree while browsing
        Lock readLock = btree.getLock().readLock();
        try {
            readLock.lock();
            BTree.BTreeTupleBrowser browser = btree.browse();
            BTree.BTreeTuple tuple = new BTree.BTreeTuple();
            while (browser.getNext(tuple)) {
                if (tuple.value.equals(value))
                    return (true);
            }
        } finally {
            readLock.unlock();
        }
        //    System.out.println("Comparation of '"+value+"' with '"+ tuple.getValue()+"' FAILED");
        return (false);
    }

    /**
     * Helper method to 'simulate' the methods of an entry set of the btree.
     */
    protected static boolean contains(Map.Entry entry, BTree btree)
            throws IOException {
        Object tree_obj = btree.get(entry.getKey());
        if (tree_obj == null) {
            // can't distuingish, if value is null or not found!!!!!!
            return (entry.getValue() == null);
        }
        return (tree_obj.equals(entry.getValue()));
    }


    /**
     * Inner class for testing puroposes only (multithreaded access)
     */
    class TestThread
            extends Thread {
        Map _content;
        BTree _btree;
        volatile boolean _continue = true;
        int THREAD_SLEEP_TIME = 50; // in ms
        String _name;

        TestThread(String name, BTree btree, Map content) {
            _content = content;
            _btree = btree;
            _name = name;
        }

        public void setStop() {
            _continue = false;
        }

        private void action()
                throws IOException {
            Iterator iterator = _content.entrySet().iterator();
            Map.Entry entry;
            if (DEBUG) {
                System.out.println("Thread " + _name + ": fill btree.");
            }
            while (iterator.hasNext()) {
                entry = (Map.Entry) iterator.next();
                assertEquals(null, _btree.insert(entry.getKey(), entry.getValue(), false));
            }

            // as other threads are filling the btree as well, the size
            // of the btree is unknown (but must be at least the size of
            // the content map)
            assertTrue(_content.size() <= _btree._entries);

            iterator = _content.entrySet().iterator();
            if (DEBUG) {
                System.out.println("Thread " + _name + ": iterates btree.");
            }
            while (iterator.hasNext()) {
                entry = (Map.Entry) iterator.next();
                assertEquals(entry.getValue(), _btree.get(entry.getKey()));
                assertTrue(contains(entry, _btree));
                assertTrue(containsKey(entry.getKey(), _btree));
                assertTrue(containsValue(entry.getValue(), _btree));
            }

            iterator = _content.entrySet().iterator();
            Object key;
            if (DEBUG) {
                System.out.println("Thread " + _name + ": removes his elements from the btree.");
            }
            while (iterator.hasNext()) {
                key = ((Map.Entry) iterator.next()).getKey();
                _btree.remove(key);
                assertTrue(!containsKey(key, _btree));
            }
        }

        public void run() {
            if (DEBUG)
                System.out.println("Thread " + _name + ": started.");
            try {
                while (_continue) {
                    action();
                    try {
                        Thread.sleep(THREAD_SLEEP_TIME);
                    } catch (InterruptedException except) {
                        except.printStackTrace();
                    }
                }
            } catch (Throwable t) {
                if (DEBUG) {
                    System.err.println("Thread " + _name + " threw an exception:");
                    t.printStackTrace();
                }
                handleThreadException(t);
            }
            if (DEBUG)
                System.out.println("Thread " + _name + ": stopped.");
        }
    } // end of class TestThread

    static class ObjectTT
            implements Serializable {

        Object _content;

        private ObjectTT() {
            // empty
        }


        public ObjectTT(Object content) {
            _content = content;
        }


        Object getContent() {
            return _content;
        }


        public boolean equals(Object obj) {
            if (!(obj instanceof ObjectTT)) {
                return false;
            }
            return _content.equals(((ObjectTT) obj).getContent());
        }

        public String toString() {
            return ("ObjectTT {content='" + _content + "'}");
        }

    } // ObjectTT
   
    public void testIssue2(){
        //this causes stack overflow
        // https://github.com/jankotek/JDBM3/issues/2
        DB build = DBMaker.openFile(newTestFile()).setMRUCacheSize(100).make();
        Map<String, String> treeMap = build.createTreeMap("treeMap");
        for (int i = 0; i < 100000; i++) {
            treeMap.put(i + "asdddfffffffffffffffffffdgf" + i + "sddfdfsfddddddddddddddddd" + i, "dsfgfg.dfcdfsgfgfffffffffffffffffdddddddddd");
            if (i % 10000 == 0) {
                build.commit();
            }
        }
        build.commit();
        build.close();
    }

}


TOP

Related Classes of org.apache.jdbm.BTreeTest$ObjectTT

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.