/** * Copyright 2013 Benjamin Lerer * * 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 io.horizondb.db.btree; import io.horizondb.db.btree.BTree; import io.horizondb.db.btree.InternalNode; import io.horizondb.db.btree.Node; import io.horizondb.db.btree.NodeProxy; import io.horizondb.db.btree.OnDiskNodeManager; import io.horizondb.io.files.FileUtils; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static io.horizondb.db.btree.AssertNodes.assertInternalNode; import static io.horizondb.db.btree.AssertNodes.assertLeafNodeContains; import static io.horizondb.db.btree.AssertNodes.assertLeafNodeEmpty; public class OnDiskNodeManagerTest { /** * The test directory. */ private Path testDirectory; /** * The test file. */ private Path testFile; @Before public void setUp() throws IOException { this.testDirectory = Files.createTempDirectory(this.getClass().getSimpleName()); this.testFile = this.testDirectory.resolve("test.b3"); } @After public void tearDown() throws IOException { FileUtils.forceDelete(this.testDirectory); this.testDirectory = null; } @Test @SuppressWarnings({ "boxing" }) public void testSingleInsertionWithOnlyARootNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertLeafNodeContains(btree.getRoot(), 2, "B"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionWithOnlyARootNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertLeafNodeContains(btree.getRoot(), 1, "A", 2, "B", 3, "C", 4, "D"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertingExistingKeyAndOnlyARootNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 4); btree.insert(2, "B"); btree.insert(3, "D"); btree.insert(1, "A"); assertLeafNodeContains(btree.getRoot(), 1, "A", 2, "B", 3, "D"); btree.insert(3, "C"); assertLeafNodeContains(btree.getRoot(), 1, "A", 2, "B", 3, "C"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertLeafNodeContains(btree.getRoot(), 1, "A", 2, "B", 3, "C"); } } @Test @SuppressWarnings({ "boxing" }) public void testGetWithOnlyARootNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertEquals("C", btree.get(3)); assertEquals("B", btree.get(2)); assertEquals("A", btree.get(1)); assertNull(btree.get(5)); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionAfterRestartWithOnlyARootNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(5, "E"); btree.insert(3, "C"); btree.insert(1, "A"); assertLeafNodeContains(btree.getRoot(), 1, "A", 3, "C", 5, "E"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); assertLeafNodeContains(btree.getRoot(), 1, "A", 2, "B", 3, "C", 5, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testIteratorWithOnlyARootNodeAndFullScan() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); KeyValueIterator<Integer, String> iterator = btree.iterator(0, 5); assertNextContains(iterator, 1, "A"); assertNextContains(iterator, 2, "B"); assertNextContains(iterator, 3, "C"); assertNextContains(iterator, 4, "D"); assertFalse(iterator.next()); } } @Test @SuppressWarnings({ "boxing" }) public void testIteratorWithOnlyARootNodeAndPartialScan() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); KeyValueIterator<Integer, String> iterator = btree.iterator(2, 3); assertNextContains(iterator, 2, "B"); assertNextContains(iterator, 3, "C"); assertFalse(iterator.next()); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionWithOnlyARootNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.delete(1); assertLeafNodeContains(btree.getRoot(), 2, "B", 3, "C", 4, "D"); btree.delete(3); assertLeafNodeContains(btree.getRoot(), 2, "B", 4, "D"); btree.delete(2); assertLeafNodeContains(btree.getRoot(), 4, "D"); btree.delete(2); assertLeafNodeContains(btree.getRoot(), 4, "D"); btree.delete(4); assertLeafNodeEmpty(btree.getRoot()); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertLeafNodeEmpty(btree.getRoot()); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionAfterRestartWithOnlyARootNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.delete(1); assertLeafNodeContains(btree.getRoot(), 2, "B", 3, "C", 4, "D"); btree.delete(3); assertLeafNodeContains(btree.getRoot(), 2, "B", 4, "D"); btree.delete(2); assertLeafNodeContains(btree.getRoot(), 4, "D"); btree.delete(2); assertLeafNodeContains(btree.getRoot(), 4, "D"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.delete(4); assertLeafNodeEmpty(btree.getRoot()); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionWithExistingKeyAndInternalNodes() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "F"); btree.insert(6, "E"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 4, "D", 6, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionWithRootNodeSplit() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "E"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 4, "D", 6, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionWithRootNodeSplitAfterRestart() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(6, "E"); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 4, "D", 6, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionWithInternalNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "E"); btree.delete(4); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 6, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testGetWithOneInternalNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(5, "E"); assertEquals("B", btree.get(2)); assertEquals("C", btree.get(3)); assertEquals("E", btree.get(5)); assertEquals("F", btree.get(6)); assertNull(btree.get(10)); assertNull(btree.get(1)); } } @Test @SuppressWarnings({ "boxing" }) public void testGetWithOneInternalNodeAfterRestart() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(5, "E"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 4); assertEquals("B", btree.get(2)); assertEquals("C", btree.get(3)); assertEquals("E", btree.get(5)); assertEquals("F", btree.get(6)); assertNull(btree.get(10)); assertNull(btree.get(1)); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionWithInternalNodeAfterRestart() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "E"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.delete(4); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 6, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfFirstKeyOfLeafNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "E"); btree.delete(3); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 4); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 4, "D", 6, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testIteratorWithInternalNodesAndPartialScanOverTwoNodes() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "F"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); KeyValueIterator<Integer, String> iterator = btree.iterator(2, 3); assertNextContains(iterator, 2, "B"); assertNextContains(iterator, 3, "C"); assertFalse(iterator.next()); } } @Test @SuppressWarnings({ "boxing" }) public void testIteratorWithInternalNodesAndPartialScanOverOneNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "F"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); KeyValueIterator<Integer, String> iterator = btree.iterator(0, 1); assertNextContains(iterator, 1, "A"); assertFalse(iterator.next()); } } @Test @SuppressWarnings({ "boxing" }) public void testIteratorWithInternalNodesAndFullScan() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "F"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); KeyValueIterator<Integer, String> iterator = btree.iterator(0, 10); assertNextContains(iterator, 1, "A"); assertNextContains(iterator, 2, "B"); assertNextContains(iterator, 3, "C"); assertNextContains(iterator, 4, "D"); assertNextContains(iterator, 6, "F"); assertFalse(iterator.next()); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfFirstKeyOfLeafNodeAfterRestart() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); btree.insert(6, "E"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.delete(3); assertInternalNode(btree.getRoot(), 1, 4); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 4, "D", 6, "E"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionWithRootNodeSplitAndBranchingFactor4() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 4); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(1, "A"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 4); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 4, "D"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionOnLeftLeafNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(5, "E"); btree.insert(1, "A"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 4); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 4, "D", 5, "E", 6, "F"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionOnRightLeafNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(5, "E"); btree.insert(7, "G"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 2, 4); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 2, "B", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 4, "D", 5, "E", 6, "F", 7, "G"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionOnRightLeafNodeWithSplit() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(9, "I"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 2, 4, 6); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 2, "B", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 4, "D", 5, "E"); assertLeafNodeContains(internalNode.getChild(2), 6, "F", 7, "G", 9, "I"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionOnLeftLeafNodeWithSplit() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(2, "B"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(7, "G"); btree.insert(9, "I"); btree.insert(1, "A"); btree.insert(5, "E"); btree.insert(4, "D"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 3, 6); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 4, "D", 5, "E"); assertLeafNodeContains(internalNode.getChild(2), 6, "F", 7, "G", 9, "I"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionOnLeafNodeWithLeafAndParentFull() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(16, "P"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(17, "Q"); btree.insert(18, "R"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 10); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 5, 8); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(internalNode.getChild(1), 5, "E", 6, "F", 7, "G"); assertLeafNodeContains(internalNode.getChild(2), 8, "H", 9, "I"); assertInternalNode(root.getChild(1), 10, 14, 16); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 10, "J", 11, "K", 12, "L"); assertLeafNodeContains(internalNode.getChild(1), 14, "N", 15, "O"); assertLeafNodeContains(internalNode.getChild(2), 16, "P", 17, "Q", 18, "R"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionOnLeafNodeWithLeafAndParentFullAfterRestart() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(16, "P"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(17, "Q"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(18, "R"); assertInternalNode(btree.getRoot(), 1, 10); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 5, 8); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(internalNode.getChild(1), 5, "E", 6, "F", 7, "G"); assertLeafNodeContains(internalNode.getChild(2), 8, "H", 9, "I"); assertInternalNode(root.getChild(1), 10, 14, 16); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 10, "J", 11, "K", 12, "L"); assertLeafNodeContains(internalNode.getChild(1), 14, "N", 15, "O"); assertLeafNodeContains(internalNode.getChild(2), 16, "P", 17, "Q", 18, "R"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertWith3LevelDepth() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 3); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(16, "P"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(13, "M"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 3); assertInternalNode(btree.getRoot(), 1, 8, 14); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); InternalNode<Integer, String> internalNodeDepth1 = toInternalNode(root.getChild(0)); assertInternalNode(internalNodeDepth1, 1, 5); InternalNode<Integer, String> internalNodeDepth2 = toInternalNode(internalNodeDepth1.getChild(0)); assertInternalNode(internalNodeDepth2, 1, 2, 3); assertLeafNodeContains(internalNodeDepth2.getChild(0), 1, "A"); assertLeafNodeContains(internalNodeDepth2.getChild(1), 2, "B"); assertLeafNodeContains(internalNodeDepth2.getChild(2), 3, "C", 4, "D"); internalNodeDepth2 = toInternalNode(internalNodeDepth1.getChild(1)); assertInternalNode(internalNodeDepth2, 5, 6); assertLeafNodeContains(internalNodeDepth2.getChild(0), 5, "E"); assertLeafNodeContains(internalNodeDepth2.getChild(1), 6, "F", 7, "G"); internalNodeDepth1 = toInternalNode(root.getChild(1)); assertInternalNode(internalNodeDepth1, 8, 10); internalNodeDepth2 = toInternalNode(internalNodeDepth1.getChild(0)); assertInternalNode(internalNodeDepth2, 8, 9); assertLeafNodeContains(internalNodeDepth2.getChild(0), 8, "H"); assertLeafNodeContains(internalNodeDepth2.getChild(1), 9, "I"); internalNodeDepth2 = toInternalNode(internalNodeDepth1.getChild(1)); assertInternalNode(internalNodeDepth2, 10, 11, 12); assertLeafNodeContains(internalNodeDepth2.getChild(0), 10, "J"); assertLeafNodeContains(internalNodeDepth2.getChild(1), 11, "K"); assertLeafNodeContains(internalNodeDepth2.getChild(2), 12, "L", 13, "M"); internalNodeDepth1 = toInternalNode(root.getChild(2)); assertInternalNode(internalNodeDepth1, 14, 16); internalNodeDepth2 = toInternalNode(internalNodeDepth1.getChild(0)); assertInternalNode(internalNodeDepth2, 14, 15); assertLeafNodeContains(internalNodeDepth2.getChild(0), 14, "N"); assertLeafNodeContains(internalNodeDepth2.getChild(1), 15, "O"); internalNodeDepth2 = toInternalNode(internalNodeDepth1.getChild(1)); assertInternalNode(internalNodeDepth2, 16, 17); assertLeafNodeContains(internalNodeDepth2.getChild(0), 16, "P"); assertLeafNodeContains(internalNodeDepth2.getChild(1), 17, "Q", 18, "R"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertionOnLeafNodeWithLeafFullAndParentNot() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(13, "M"); btree.insert(14, "N"); btree.insert(20, "T"); btree.insert(22, "V"); btree.insert(23, "W"); btree.insert(6, "F"); btree.insert(8, "H"); btree.insert(7, "G"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(15, "O"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(24, "X"); btree.insert(25, "Y"); btree.insert(5, "E"); btree.insert(9, "I"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 8, 15); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 3, 6); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 4, "D", 5, "E"); assertLeafNodeContains(internalNode.getChild(2), 6, "F", 7, "G"); assertInternalNode(root.getChild(1), 8, 10, 13); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 8, "H", 9, "I"); assertLeafNodeContains(internalNode.getChild(1), 10, "J", 11, "K", 12, "L"); assertLeafNodeContains(internalNode.getChild(2), 13, "M", 14, "N"); assertInternalNode(root.getChild(2), 15, 20, 23); internalNode = toInternalNode(root.getChild(2)); assertLeafNodeContains(internalNode.getChild(0), 15, "O", 17, "Q", 18, "R"); assertLeafNodeContains(internalNode.getChild(1), 20, "T", 22, "V"); assertLeafNodeContains(internalNode.getChild(2), 23, "W", 24, "X", 25, "Y"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertWithInternalNodeSplitWithParentNotFull() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(13, "M"); btree.insert(14, "N"); btree.insert(20, "T"); btree.insert(22, "V"); btree.insert(23, "W"); btree.insert(6, "F"); btree.insert(8, "H"); btree.insert(7, "G"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(15, "O"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(24, "X"); btree.insert(25, "Y"); btree.insert(18, "S"); btree.insert(26, "Z"); btree.insert(27, "AA"); btree.insert(28, "BB"); btree.insert(29, "CC"); btree.insert(30, "DD"); btree.insert(31, "EE"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 15, 25); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 6, 13); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 7, "G", 8, "H"); assertLeafNodeContains(internalNode.getChild(2), 13, "M", 14, "N"); assertInternalNode(root.getChild(1), 15, 20, 23); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 15, "O", 17, "Q", 18, "R", 18, "S"); assertLeafNodeContains(internalNode.getChild(1), 20, "T", 22, "V"); assertLeafNodeContains(internalNode.getChild(2), 23, "W", 24, "X"); assertInternalNode(root.getChild(2), 25, 27, 29); internalNode = toInternalNode(root.getChild(2)); assertLeafNodeContains(internalNode.getChild(0), 25, "Y", 26, "Z"); assertLeafNodeContains(internalNode.getChild(1), 27, "AA", 28, "BB"); assertLeafNodeContains(internalNode.getChild(2), 29, "CC", 30, "DD", 31, "EE"); } } @Test @SuppressWarnings({ "boxing" }) public void testInsertAfterRestartWithInternalNodeSplitWithParentNotFull() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(13, "M"); btree.insert(14, "N"); btree.insert(20, "T"); btree.insert(22, "V"); btree.insert(23, "W"); btree.insert(6, "F"); btree.insert(8, "H"); btree.insert(7, "G"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(15, "O"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(24, "X"); btree.insert(25, "Y"); btree.insert(18, "S"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(26, "Z"); btree.insert(27, "AA"); btree.insert(28, "BB"); btree.insert(29, "CC"); btree.insert(30, "DD"); btree.insert(31, "EE"); assertInternalNode(btree.getRoot(), 1, 15, 25); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 6, 13); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 7, "G", 8, "H"); assertLeafNodeContains(internalNode.getChild(2), 13, "M", 14, "N"); assertInternalNode(root.getChild(1), 15, 20, 23); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 15, "O", 17, "Q", 18, "R", 18, "S"); assertLeafNodeContains(internalNode.getChild(1), 20, "T", 22, "V"); assertLeafNodeContains(internalNode.getChild(2), 23, "W", 24, "X"); assertInternalNode(root.getChild(2), 25, 27, 29); internalNode = toInternalNode(root.getChild(2)); assertLeafNodeContains(internalNode.getChild(0), 25, "Y", 26, "Z"); assertLeafNodeContains(internalNode.getChild(1), 27, "AA", 28, "BB"); assertLeafNodeContains(internalNode.getChild(2), 29, "CC", 30, "DD", 31, "EE"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletingKeysWhichAreWithinTheRoot() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(13, "M"); btree.insert(14, "N"); btree.insert(20, "T"); btree.insert(22, "V"); btree.insert(23, "W"); btree.insert(6, "F"); btree.insert(8, "H"); btree.insert(7, "G"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(15, "O"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(24, "X"); btree.insert(25, "Y"); btree.insert(18, "S"); btree.delete(15); btree.delete(1); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 2, 17); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 2, 6, 13); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 7, "G", 8, "H"); assertLeafNodeContains(internalNode.getChild(2), 13, "M", 14, "N"); assertInternalNode(root.getChild(1), 17, 20, 23); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 17, "Q", 18, "R", 18, "S"); assertLeafNodeContains(internalNode.getChild(1), 20, "T", 22, "V"); assertLeafNodeContains(internalNode.getChild(2), 23, "W", 24, "X", 25, "Y"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionWithInternalNodeRebalancingFromTheRightNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(16, "P"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(20, "T"); btree.insert(21, "U"); btree.insert(13, "M"); btree.insert(23, "W"); btree.insert(24, "X"); btree.insert(25, "Y"); btree.insert(26, "Z"); btree.insert(27, "AA"); btree.insert(28, "BB"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(19, "S"); btree.delete(25); btree.delete(4); btree.delete(2); btree.delete(7); btree.delete(5); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 14, 21); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 6, 10); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 8, "H", 9, "I"); assertLeafNodeContains(internalNode.getChild(2), 10, "J", 11, "K", 12, "L", 13, "M"); assertInternalNode(root.getChild(1), 14, 16, 18); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 14, "N", 15, "O"); assertLeafNodeContains(internalNode.getChild(1), 16, "P", 17, "Q"); assertLeafNodeContains(internalNode.getChild(2), 18, "R", 19, "S", 20, "T"); assertInternalNode(root.getChild(2), 21, 24, 27); internalNode = toInternalNode(root.getChild(2)); assertLeafNodeContains(internalNode.getChild(0), 21, "U", 23, "W"); assertLeafNodeContains(internalNode.getChild(1), 24, "X", 26, "Z"); assertLeafNodeContains(internalNode.getChild(2), 27, "AA", 28, "BB"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionAfterRestartWithInternalNodeRebalancingFromTheRightNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(16, "P"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(20, "T"); btree.insert(21, "U"); btree.insert(13, "M"); btree.insert(23, "W"); btree.insert(24, "X"); btree.insert(25, "Y"); btree.insert(26, "Z"); btree.insert(27, "AA"); btree.insert(28, "BB"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(19, "S"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.delete(25); btree.delete(4); btree.delete(2); btree.delete(7); btree.delete(5); assertInternalNode(btree.getRoot(), 1, 14, 21); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 6, 10); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 8, "H", 9, "I"); assertLeafNodeContains(internalNode.getChild(2), 10, "J", 11, "K", 12, "L", 13, "M"); assertInternalNode(root.getChild(1), 14, 16, 18); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 14, "N", 15, "O"); assertLeafNodeContains(internalNode.getChild(1), 16, "P", 17, "Q"); assertLeafNodeContains(internalNode.getChild(2), 18, "R", 19, "S", 20, "T"); assertInternalNode(root.getChild(2), 21, 24, 27); internalNode = toInternalNode(root.getChild(2)); assertLeafNodeContains(internalNode.getChild(0), 21, "U", 23, "W"); assertLeafNodeContains(internalNode.getChild(1), 24, "X", 26, "Z"); assertLeafNodeContains(internalNode.getChild(2), 27, "AA", 28, "BB"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeleteWithInternalNodeRebalancingFromTheLeftNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(16, "P"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(20, "T"); btree.insert(21, "U"); btree.insert(13, "M"); btree.insert(23, "W"); btree.insert(24, "X"); btree.insert(25, "Y"); btree.insert(26, "Z"); btree.insert(27, "AA"); btree.insert(28, "BB"); btree.insert(17, "Q"); btree.insert(18, "R"); btree.insert(19, "S"); btree.insert(22, "V"); btree.delete(27); btree.delete(24); btree.delete(22); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 10, 18); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertInternalNode(root.getChild(0), 1, 5, 8); InternalNode<Integer, String> internalNode = toInternalNode(root.getChild(0)); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(internalNode.getChild(1), 5, "E", 6, "F", 7, "G"); assertLeafNodeContains(internalNode.getChild(2), 8, "H", 9, "I"); assertInternalNode(root.getChild(1), 10, 14, 16); internalNode = toInternalNode(root.getChild(1)); assertLeafNodeContains(internalNode.getChild(0), 10, "J", 11, "K", 12, "L", 13, "M"); assertLeafNodeContains(internalNode.getChild(1), 14, "N", 15, "O"); assertLeafNodeContains(internalNode.getChild(2), 16, "P", 17, "Q"); assertInternalNode(root.getChild(2), 18, 21, 26); internalNode = toInternalNode(root.getChild(2)); assertLeafNodeContains(internalNode.getChild(0), 18, "R", 19, "S", 20, "T"); assertLeafNodeContains(internalNode.getChild(1), 21, "U", 23, "W", 25, "Y"); assertLeafNodeContains(internalNode.getChild(2), 26, "Z", 28, "BB"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyFromLeafNodeWithRedistributionOnRightNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(10, "J"); btree.delete(4); btree.delete(5); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 6, 8); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 7, "G"); assertLeafNodeContains(internalNode.getChild(2), 8, "H", 9, "I", 10, "J"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyFromLeafNodeWithFirstKeyDeletedAndRedistribution() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(10, "J"); btree.delete(4); btree.delete(5); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 6, 8); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 7, "G"); assertLeafNodeContains(internalNode.getChild(2), 8, "H", 9, "I", 10, "J"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyAfterRestartFromLeafNodeWithFirstKeyDeletedAndRedistribution() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(10, "J"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.delete(4); btree.delete(5); assertInternalNode(btree.getRoot(), 1, 6, 8); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 7, "G"); assertLeafNodeContains(internalNode.getChild(2), 8, "H", 9, "I", 10, "J"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyFromLeafNodeWithRedistributionOnLeftNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); btree.insert(10, "J"); btree.delete(8); btree.delete(9); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 4, 7); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B", 3, "C"); assertLeafNodeContains(internalNode.getChild(1), 4, "D", 5, "E", 6, "F"); assertLeafNodeContains(internalNode.getChild(2), 7, "G", 10, "J"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyFromLeafNodeWithMergeOnTheRight() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); btree.delete(3); btree.delete(8); btree.delete(5); btree.delete(1); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 2, 7); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 2, "B", 4, "D", 6, "F"); assertLeafNodeContains(internalNode.getChild(1), 7, "G", 9, "I"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyFromLeafNodeWithMergeOnTheLeft() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); btree.delete(3); btree.delete(8); btree.delete(5); btree.delete(4); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 6); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 6, "F", 7, "G", 9, "I"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyFromLeafNodeWithMergeAndNoRightNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); btree.delete(4); btree.delete(8); btree.delete(5); btree.delete(7); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 6, "F", 9, "I"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionOfKeyAfterRestartFromLeafNodeWithMergeAndNoRightNode() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(4, "D"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(3, "C"); btree.insert(6, "F"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.delete(4); btree.delete(8); btree.delete(5); btree.delete(7); assertInternalNode(btree.getRoot(), 1, 3); InternalNode<Integer, String> internalNode = toInternalNode(btree.getRoot()); assertLeafNodeContains(internalNode.getChild(0), 1, "A", 2, "B"); assertLeafNodeContains(internalNode.getChild(1), 3, "C", 6, "F", 9, "I"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionWithLeafNodeMergeAndInternalNodeMerge() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(13, "M"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(16, "P"); btree.insert(17, "Q"); btree.delete(12); btree.delete(5); btree.delete(10); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); assertInternalNode(btree.getRoot(), 1, 6, 8, 11, 15); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertLeafNodeContains(root.getChild(0), 1, "A", 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(root.getChild(1), 6, "F", 7, "G"); assertLeafNodeContains(root.getChild(2), 8, "H", 9, "I"); assertLeafNodeContains(root.getChild(3), 11, "K", 13, "M", 14, "N"); assertLeafNodeContains(root.getChild(4), 15, "O", 16, "P", 17, "Q"); } } @Test @SuppressWarnings({ "boxing" }) public void testDeletionAfterRestartWithLeafNodeMergeAndInternalNodeMerge() throws IOException { try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.insert(1, "A"); btree.insert(2, "B"); btree.insert(8, "H"); btree.insert(9, "I"); btree.insert(13, "M"); btree.insert(14, "N"); btree.insert(15, "O"); btree.insert(5, "E"); btree.insert(7, "G"); btree.insert(6, "F"); btree.insert(3, "C"); btree.insert(4, "D"); btree.insert(10, "J"); btree.insert(11, "K"); btree.insert(12, "L"); btree.insert(16, "P"); btree.insert(17, "Q"); } try (OnDiskNodeManager<Integer, String> manager = new OnDiskNodeManager<>("test", this.testFile, IntegerAndStringNodeWriter.FACTORY, IntegerAndStringNodeReader.FACTORY)) { BTree<Integer, String> btree = new BTree<>(manager, 5); btree.delete(12); btree.delete(5); btree.delete(10); assertInternalNode(btree.getRoot(), 1, 6, 8, 11, 15); InternalNode<Integer, String> root = toInternalNode(btree.getRoot()); assertLeafNodeContains(root.getChild(0), 1, "A", 2, "B", 3, "C", 4, "D"); assertLeafNodeContains(root.getChild(1), 6, "F", 7, "G"); assertLeafNodeContains(root.getChild(2), 8, "H", 9, "I"); assertLeafNodeContains(root.getChild(3), 11, "K", 13, "M", 14, "N"); assertLeafNodeContains(root.getChild(4), 15, "O", 16, "P", 17, "Q"); } } /** * Asserts that the key and the value of the next record returned by the iterator are equals to * the specified ones. * * @param iterator the iterator to check * @param key the expected key * @param value the expected value * @throws IOException if an I/O problem occurs */ private static void assertNextContains(KeyValueIterator<Integer, String> iterator, Integer key, String value) throws IOException { assertTrue(iterator.next()); assertEquals(key, iterator.getKey()); assertEquals(value, iterator.getValue()); } /** * Converts the specified node into an internal node. * * @param node the node to convert * @return an internal node. * @throws IOException if an I/O problem occurs. */ private static InternalNode<Integer, String> toInternalNode(Node<Integer, String> node) throws IOException { Node<Integer, String> n = node; if (n instanceof NodeProxy) { n = ((NodeProxy<Integer, String>) n).loadNode(); } return (InternalNode<Integer, String>) n; } }