/*
* 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 com.addthis.hydra.data.tree.nonconcurrent;
import com.addthis.basis.util.ClosableIterator;
import com.addthis.basis.util.LessFiles;
import com.addthis.hydra.data.tree.DataTreeNode;
import com.addthis.hydra.data.tree.TreeCommonParameters;
import com.addthis.hydra.data.tree.concurrent.TreeBuilder;
import com.addthis.hydra.store.db.CloseOperation;
import com.addthis.hydra.store.nonconcurrent.NonConcurrentPage;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class TestNonConcurrentTree {
private static final Logger log = LoggerFactory.getLogger(TestNonConcurrentTree.class);
private static final CloseOperation close = CloseOperation.TEST;
private File makeTemporaryDirectory() throws IOException {
final File temp;
temp = File.createTempFile("temp", Long.toString(System.nanoTime()));
if (!(temp.delete())) {
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
}
if (!(temp.mkdir())) {
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
}
return temp;
}
@Test
public void getOrCreateOneThread() throws Exception {
log.info("getOrCreateOneThread");
File dir = makeTemporaryDirectory();
try {
NonConcurrentTree tree = new NonConcurrentTree(dir, 100, 100, NonConcurrentPage.NonConcurrentPageFactory.singleton);
NonConcurrentTreeNode root = tree.getRootNode();
for (int i = 0; i < 1000; i++) {
NonConcurrentTreeNode node = tree.getOrCreateNode(root, Integer.toString(i), null);
assertNotNull(node);
assertEquals(Integer.toString(i), node.getName());
}
for (int i = 0; i < 1000; i++) {
NonConcurrentTreeNode node = tree.getNode(root, Integer.toString(i), true);
assertNotNull(node);
assertEquals(Integer.toString(i), node.getName());
}
tree.close(false, close);
} finally {
if (dir != null) {
LessFiles.deleteDir(dir);
}
}
}
@Test
public void deleteOneThread() throws Exception {
log.info("recursiveDeleteOneThread");
File dir = makeTemporaryDirectory();
try {
NonConcurrentTree tree = new NonConcurrentTree(dir, 100, 100, NonConcurrentPage.NonConcurrentPageFactory.singleton);
NonConcurrentTreeNode root = tree.getRootNode();
NonConcurrentTreeNode parent = tree.getOrCreateNode(root, "hello", null);
for (int i = 0; i < 100_000; i++) {
NonConcurrentTreeNode child = tree.getOrCreateNode(parent, Integer.toString(i), null);
assertNotNull(child);
assertEquals(Integer.toString(i), child.getName());
parent = child;
}
assertEquals(1, root.getNodeCount());
tree.deleteNode(root, "hello");
assertEquals(0, root.getNodeCount());
tree.close(false, close);
} finally {
if (dir != null) {
LessFiles.deleteDir(dir);
}
}
}
@Test
public void iterateAndDeleteFast() throws Exception {
iterateAndDelete(10);
}
private void iterateAndDelete(int numElements) throws Exception {
log.info("iterateAndDelete {}", numElements);
File dir = makeTemporaryDirectory();
try {
NonConcurrentTree tree = new TreeBuilder(dir).maxPageSize(5).singleThreadedTree();
NonConcurrentTreeNode root = tree.getRootNode();
for (int i = 0; i < numElements; i++) {
NonConcurrentTreeNode node = tree.getOrCreateNode(root, Integer.toString(i), null);
assertNotNull(node);
assertEquals(Integer.toString(i), node.getName());
NonConcurrentTreeNode child = tree.getOrCreateNode(node, Integer.toString(i), null);
assertNotNull(child);
}
Random rng = new Random();
ClosableIterator<DataTreeNode> iterator = tree.getIterator();
try {
int counter = 0;
while (iterator.hasNext()) {
DataTreeNode node = iterator.next();
if (rng.nextFloat() < 0.8) {
String name = node.getName();
tree.deleteNode(root, name);
counter++;
}
}
log.info("Deleted " + (((float) counter) / numElements * 100.0) + " % of nodes");
} finally {
iterator.close();
}
tree.close(false, close);
} finally {
if (dir != null) {
LessFiles.deleteDir(dir);
}
}
}
@Test
public void maximumNodeIdentifier() throws Exception {
File dir = makeTemporaryDirectory();
try {
NonConcurrentTree tree = new TreeBuilder(dir).singleThreadedTree();
NonConcurrentTreeNode root = tree.getRootNode();
for (int i = 0; i < 1000; i++) {
NonConcurrentTreeNode node = tree.getOrCreateNode(root, Integer.toString(i), null);
assertNotNull(node);
assertEquals(Integer.toString(i), node.getName());
NonConcurrentTreeNode child = tree.getOrCreateNode(node, Integer.toString(i), null);
child.release();
node.release();
}
assertTrue(tree.setNextNodeDB(Integer.MAX_VALUE));
for (int i = 1000; i < 2000; i++) {
NonConcurrentTreeNode node = tree.getOrCreateNode(root, Integer.toString(i), null);
assertNotNull(node);
assertEquals(Integer.toString(i), node.getName());
NonConcurrentTreeNode child = tree.getOrCreateNode(node, Integer.toString(i), null);
child.release();
node.release();
}
tree.close(false, close);
} finally {
if (dir != null) {
LessFiles.deleteDir(dir);
}
}
}
@Test
public void recursiveDelete() throws Exception {
log.info("recursiveDelete");
File dir = makeTemporaryDirectory();
try {
NonConcurrentTree tree = new TreeBuilder(dir).singleThreadedTree();
NonConcurrentTreeNode root = tree.getRootNode();
NonConcurrentTreeNode parent = tree.getOrCreateNode(root, "0", null);
for (int j = 0; j < TreeCommonParameters.cleanQMax; j++) {
NonConcurrentTreeNode child = tree.getOrCreateNode(parent, Integer.toString(j), null);
assertNotNull(child);
assertEquals(Integer.toString(j), child.getName());
parent.release();
parent = child;
}
parent.release();
tree.deleteNode(root, "0");
assertEquals(0, root.getNodeCount());
tree.close(false, close);
} finally {
if (dir != null) {
LessFiles.deleteDir(dir);
}
}
}
}