/* * Copyright 2011 Future Systems * * 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.krakenapps.btree; import java.io.File; import java.io.IOException; import org.junit.After; import org.junit.Test; import org.krakenapps.btree.types.CompositeKey; import org.krakenapps.btree.types.IntegerKey; import org.krakenapps.btree.types.IntegerValue; import org.krakenapps.btree.types.IntegerValueFactory; import static org.junit.Assert.*; public class BtreeTest { private File file = new File("btree.dat"); private Schema schema; private Btree btree; public void setup(int pageSize) throws IOException { setup(pageSize, new Class<?>[] { Integer.class }); } public void setup(int pageSize, Class<?>... keyTypes) throws IOException { file.delete(); schema = new Schema(pageSize, keyTypes); schema.setRowValueFactory(new IntegerValueFactory()); btree = BtreeImpl.create(file, schema); btree.setRowValueFactory(new IntegerValueFactory()); } @After public void teardown() throws IOException { btree.close(); file.delete(); btree = null; } @Test public void testSimpleInsert() throws IOException { setup(80); PageFile pf = btree.getPageFile(); assertEquals(1, pf.getPageCount()); assertEquals(1, pf.getRootPage()); RowEntry value = btree.get(new IntegerKey(1)); assertNull(value); // three row can be added to 76 byte page for (int i = 1; i <= 3; i++) { btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); value = btree.get(new IntegerKey(i)); assertEquals(new IntegerValue(i * 100), value); } } @Test public void testSimpleSplit() throws IOException { setup(80); PageFile pf = btree.getPageFile(); assertEquals(1, pf.getPageCount()); assertEquals(1, pf.getRootPage()); RowEntry value = btree.get(new IntegerKey(1)); assertNull(value); // three row can be added to 76 byte page for (int i = 1; i <= 7; i++) { btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); value = btree.get(new IntegerKey(i)); assertEquals(new IntegerValue(i * 100), value); } } @Test public void testDescInsert() throws IOException { setup(80); PageFile pf = btree.getPageFile(); assertEquals(1, pf.getPageCount()); assertEquals(1, pf.getRootPage()); RowEntry value = btree.get(new IntegerKey(1)); assertNull(value); for (int i = 100; i > 0; i--) { btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); value = btree.get(new IntegerKey(i)); assertEquals(new IntegerValue(i * 100), value); } } @Test public void testRandomOrder() throws IOException { setup(80); // 1, 3, 4, 6, 7, 8, 9, 10, 18 btree.insert(new IntegerKey(6), new IntegerValue(600)); btree.insert(new IntegerKey(7), new IntegerValue(700)); btree.insert(new IntegerKey(8), new IntegerValue(800)); btree.insert(new IntegerKey(9), new IntegerValue(900)); btree.insert(new IntegerKey(10), new IntegerValue(1000)); btree.insert(new IntegerKey(3), new IntegerValue(300)); btree.insert(new IntegerKey(18), new IntegerValue(1800)); btree.insert(new IntegerKey(4), new IntegerValue(400)); btree.insert(new IntegerKey(1), new IntegerValue(100)); btree.sync(); assertEquals(new IntegerValue(600), btree.get(new IntegerKey(6))); assertEquals(new IntegerValue(700), btree.get(new IntegerKey(7))); assertEquals(new IntegerValue(800), btree.get(new IntegerKey(8))); assertEquals(new IntegerValue(900), btree.get(new IntegerKey(9))); assertEquals(new IntegerValue(1000), btree.get(new IntegerKey(10))); assertEquals(new IntegerValue(300), btree.get(new IntegerKey(3))); assertEquals(new IntegerValue(1800), btree.get(new IntegerKey(18))); assertEquals(new IntegerValue(400), btree.get(new IntegerKey(4))); assertEquals(new IntegerValue(100), btree.get(new IntegerKey(1))); } @Test public void testDuplicates() throws IOException { setup(80); for (int i = 1; i <= 5; i++) { btree.insert(new IntegerKey(5 + i), new IntegerValue(i)); } btree.insert(new IntegerKey(3), new IntegerValue(300)); btree.insert(new IntegerKey(18), new IntegerValue(800)); btree.insert(new IntegerKey(4), new IntegerValue(400)); btree.insert(new IntegerKey(1), new IntegerValue(11)); btree.insert(new IntegerKey(1), new IntegerValue(12)); btree.insert(new IntegerKey(1), new IntegerValue(13)); btree.insert(new IntegerKey(1), new IntegerValue(14)); btree.insert(new IntegerKey(1), new IntegerValue(15)); btree.insert(new IntegerKey(1), new IntegerValue(16)); btree.insert(new IntegerKey(1), new IntegerValue(17)); btree.sync(); // seven 1s, 3, 4, [6, 7, 8, 9, 10], 18 // count all 1s over several pages assertEquals(7, count1s(Cursor.ASC)); assertEquals(7, count1s(Cursor.DESC)); // check contiguous numbers for (int i = 1; i <= 5; i++) { assertEquals(new IntegerValue(i), btree.get(new IntegerKey(5 + i))); } // check other out-of-order numbers assertEquals(new IntegerValue(300), btree.get(new IntegerKey(3))); assertEquals(new IntegerValue(800), btree.get(new IntegerKey(18))); assertEquals(new IntegerValue(400), btree.get(new IntegerKey(4))); } private int count1s(int cursorType) throws IOException { int count = 0; IntegerKey searchKey = new IntegerKey(1); Cursor cursor = btree.openCursor(searchKey, cursorType); do { if (searchKey.equals(cursor.getKey())) { count++; } else break; } while (cursor.next()); cursor.close(); return count; } @Test public void testIteration() throws IOException { setup(80); int index = 1; for (int i = 1; i <= 10; i++) btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); // test ascending iteration Cursor c = btree.openCursor(Cursor.ASC); do { assertEquals(new IntegerKey(index), c.getKey()); assertEquals(new IntegerValue(index * 100), c.getValue()); index++; } while (c.next()); c.close(); // test descending iteration index = 10; c = btree.openCursor(Cursor.DESC); do { assertEquals(new IntegerKey(index), c.getKey()); assertEquals(new IntegerValue(index * 100), c.getValue()); index--; } while (c.next()); c.close(); } @Test public void testCompositeKey() throws IOException { setup(100, String.class, Integer.class); CompositeKey k2 = new CompositeKey("test", 2); btree.insert(k2, new IntegerValue(200)); CompositeKey k3 = new CompositeKey("test", 3); btree.insert(k3, new IntegerValue(300)); CompositeKey k1 = new CompositeKey("test", 1); btree.insert(k1, new IntegerValue(100)); assertEquals(new IntegerValue(100), btree.get(k1)); assertEquals(new IntegerValue(200), btree.get(k2)); assertEquals(new IntegerValue(300), btree.get(k3)); Cursor c = btree.openCursor(Cursor.ASC); assertEquals(new IntegerValue(100), c.getValue()); c.close(); c = btree.openCursor(Cursor.DESC); assertEquals(new IntegerValue(300), c.getValue()); c.close(); } @Test public void testSimpleDelete() throws IOException { setup(80); int index = 1; for (int i = 1; i <= 10; i++) btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); // delete 2 of 3 elements of last (rightmost) page btree.delete(new IntegerKey(8)); btree.delete(new IntegerKey(10)); Cursor c = btree.openCursor(Cursor.ASC); do { assertEquals(new IntegerKey(index), c.getKey()); assertEquals(new IntegerValue(index * 100), c.getValue()); index++; if (index == 8 || index == 10) index++; } while (c.next()); c.close(); } @Test public void testDeleteLastPage() throws IOException { setup(80); for (int i = 1; i <= 10; i++) btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); // delete all elements of last (rightmost) page btree.delete(new IntegerKey(9)); btree.delete(new IntegerKey(10)); btree.delete(new IntegerKey(8)); // assert int index = 1; Cursor c = btree.openCursor(Cursor.ASC); do { assertEquals(new IntegerKey(index), c.getKey()); assertEquals(new IntegerValue(index * 100), c.getValue()); index++; } while (c.next()); c.close(); } @Test public void testDeleteLeftmostPage() throws IOException { setup(80); for (int i = 1; i <= 10; i++) btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); btree.delete(new IntegerKey(1)); int index = 2; // test 2~10 Cursor c = btree.openCursor(Cursor.ASC); do { assertEquals(new IntegerKey(index), c.getKey()); assertEquals(new IntegerValue(index * 100), c.getValue()); index++; } while (c.next()); c.close(); } @Test public void testDeleteLeftmostTree() throws IOException { setup(80); for (int i = 1; i <= 10; i++) btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); btree.delete(new IntegerKey(1)); btree.delete(new IntegerKey(2)); btree.delete(new IntegerKey(3)); int index = 4; // test 4~10 Cursor c = btree.openCursor(Cursor.ASC); do { assertEquals(new IntegerKey(index), c.getKey()); assertEquals(new IntegerValue(index * 100), c.getValue()); index++; } while (c.next()); c.close(); } @Test public void testDeleteRightChildKey() throws IOException { setup(80); for (int i = 1; i <= 10; i++) btree.insert(new IntegerKey(i), new IntegerValue(i * 100)); // bug case: delete 3, but 1 and 2 also be inaccessible. btree.delete(new IntegerKey(3)); btree.sync(); trace(); // int index = 1; Cursor c = btree.openCursor(Cursor.ASC); do { // should be 1, 2, 5, 6, 8 System.out.println(c.getKey() + " " + c.getValue()); // assertEquals(new IntegerKey(index), c.getKey()); // assertEquals(new IntegerValue(index * 100), c.getValue()); // index++; } while (c.next()); c.close(); } private void trace() throws IOException { PageFile pf = btree.getPageFile(); System.out.println("ROOT=" + pf.getRootPage()); for (int i = 1; i <= pf.getPageCount(); i++) { Page p = pf.read(i); System.out.println("Page " + i + ", Type=" + p.getFlag() + ", RightChild=" + p.getRightChildPage() + ", Right=" + p.getRightPage() + ", Upper=" + p.getUpperPage()); for (int r = 0; r < p.getRecordCount(); r++) { System.out.println(p.getKey(r) + ", value=" + p.getValue(r)); } System.out.println(); } } }