package org.limewire.collection;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import junit.framework.Test;
import org.limewire.util.BaseTestCase;
public class TreeStorageTest extends BaseTestCase {
public TreeStorageTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(TreeStorageTest.class);
}
/**
* Generator that does simple XOR.
*/
private final NodeGenerator xorGen = new NodeGenerator() {
public byte [] generate(byte [] left, byte [] right) {
byte [] ret = new byte[left.length];
for (int i = 0; i < left.length ; i ++)
ret[i] = (byte)(left[i] ^ right[i]);
return ret;
}
};
private static Map<Integer, byte[]> createRandomTree(int size, NodeGenerator gen) {
final int startOffset = (0x1 << TreeStorage.log2Ceil(size));
Map<Integer, byte[]> tree = new HashMap<Integer, byte[]>();
Random r = new Random();
for (int i = 0; i < size ; i++) {
byte [] b = new byte[20];
r.nextBytes(b);
tree.put(i+startOffset, b);
}
for (int i = TreeStorage.log2Ceil(size); i >= 1; i--) {
int offset = 0x1 << i;
for (int j = offset ; j < Math.min(offset * 2, offset + size); j+=2) {
byte [] left = tree.get(j);
if (left == null)
continue;
byte [] right = tree.get(j+1);
byte [] data = right == null ? left : gen.generate(left, right);
tree.put(j / 2, data);
}
}
return tree;
}
public void testFileToNodeId() throws Exception {
TreeStorage storage = new TreeStorage(null, new NodeGenerator.NullGenerator(), 8);
assertEquals(8, storage.fileToNodeId(0));
assertEquals(15, storage.fileToNodeId(7));
try {
storage.fileToNodeId(8);
fail("id out of range");
} catch (IllegalArgumentException expected){}
storage = new TreeStorage(null, new NodeGenerator.NullGenerator(), 11);
assertEquals(16, storage.fileToNodeId(0));
assertEquals(23, storage.fileToNodeId(7));
try {
storage.fileToNodeId(11);
fail("id out of range");
} catch (IllegalArgumentException expected){}
storage = new TreeStorage(null, new NodeGenerator.NullGenerator(), 1);
}
/**
* Test basic functionality.
*/
public void testGeneral() throws Exception {
int leafs = 8;
// create a random tree
Map<Integer, byte[]> tree = createRandomTree(leafs, xorGen);
// test the TreeStorage
TreeStorage storage = new TreeStorage(tree.get(1), xorGen, leafs);
assertEquals(1, storage.getVerifiedNodes().size());
assertEquals(0, storage.getUsedNodes().size());
assertTrue(storage.getVerifiedNodes().contains(1)); // root always there
// add nodes 3, 4, and 5
// 3 alone does not get verified
storage.add(3, tree.get(3));
assertEquals(1,storage.getVerifiedNodes().size());
// adding four does change things
storage.add(4, tree.get(4));
assertEquals(1,storage.getVerifiedNodes().size());
// when we add 5 all nodes become verified
storage.add(5, tree.get(5));
assertTrue(storage.getVerifiedNodes().contains(3));
assertTrue(storage.getVerifiedNodes().contains(4));
assertTrue(storage.getVerifiedNodes().contains(5));
// when we add 8 and 9 node 4 will disappear
storage.add(8, tree.get(8));
assertFalse(storage.getVerifiedNodes().contains(8));
assertTrue(storage.getVerifiedNodes().contains(4));
storage.add(9, tree.get(9));
assertTrue(storage.getVerifiedNodes().contains(8));
assertTrue(storage.getVerifiedNodes().contains(9));
assertFalse(storage.getVerifiedNodes().contains(4));
// if we use 8 and 9 they will disappear
// and 4 will appear
storage.used(8);
assertTrue(storage.getVerifiedNodes().contains(8));
assertTrue(storage.getVerifiedNodes().contains(9));
assertFalse(storage.getVerifiedNodes().contains(4));
storage.used(9);
assertTrue(storage.getVerifiedNodes().contains(4));
assertFalse(storage.getVerifiedNodes().contains(8));
assertFalse(storage.getVerifiedNodes().contains(9));
assertTrue(storage.getUsedNodes().contains(4));
// if we use 5 then 4 and 5 will disappear
// and 2 will appear
assertFalse(storage.getVerifiedNodes().contains(2));
storage.used(5);
assertFalse(storage.getVerifiedNodes().contains(4));
assertFalse(storage.getVerifiedNodes().contains(5));
assertTrue(storage.getVerifiedNodes().contains(2));
assertTrue(storage.getUsedNodes().contains(2));
assertTrue(Arrays.equals(tree.get(2),storage.get(2)));
}
public void testBottomLeafs() throws Exception {
Map<Integer, byte[]> tree = createRandomTree(7, xorGen);
TreeStorage storage = new TreeStorage(tree.get(1),xorGen, 7);
// add all the bottom leafs
for (int i = 8; i < 15; i++) {
storage.add(i, tree.get(i));
}
// now use them all
for (int i = 8; i < 15; i++)
storage.used(i);
assertEquals(1,storage.getVerifiedNodes().size());
assertEquals(1,storage.getUsedNodes().size());
assertTrue(storage.getVerifiedNodes().containsAll(storage.getUsedNodes()));
assertTrue(storage.getVerifiedNodes().contains(1));
}
public void testAdvancedUsage() throws Exception {
// more advanced usage - some nodes get used while others are verified
// 12 nodes, leafs are 16-28
Map<Integer, byte[]> tree = createRandomTree(12, xorGen);
TreeStorage storage = new TreeStorage(tree.get(1),xorGen, 12);
// add 3, 5, 9, 16
storage.add(3, tree.get(3));
storage.add(5, tree.get(5));
storage.add(9, tree.get(9));
storage.add(16, tree.get(16));
assertFalse(storage.getVerifiedNodes().contains(3));
assertFalse(storage.getVerifiedNodes().contains(5));
assertFalse(storage.getVerifiedNodes().contains(9));
assertFalse(storage.getVerifiedNodes().contains(16));
// add broken 17, nothing changes
assertFalse(storage.add(17, tree.get(16)));
assertFalse(storage.getVerifiedNodes().contains(3));
assertFalse(storage.getVerifiedNodes().contains(5));
assertFalse(storage.getVerifiedNodes().contains(9));
assertFalse(storage.getVerifiedNodes().contains(16));
// add real 17, they all become verified
assertTrue(storage.add(17, tree.get(17)));
// assert storage.add(17, tree.get(17));
assertTrue(storage.getVerifiedNodes().contains(3));
assertTrue(storage.getVerifiedNodes().contains(5));
assertTrue(storage.getVerifiedNodes().contains(9));
assertTrue(storage.getVerifiedNodes().contains(16));
assertTrue(storage.getVerifiedNodes().contains(17));
assertEquals(6, storage.getVerifiedNodes().size());
// use 3, 5, 9, 16
storage.used(3);
storage.used(5);
storage.used(9);
storage.used(16);
assertEquals(6, storage.getVerifiedNodes().size());
// use 17 and only the root remains
storage.used(17);
assertEquals(1,storage.getVerifiedNodes().size());
assertEquals(1,storage.getUsedNodes().size());
assertTrue(storage.getVerifiedNodes().containsAll(storage.getUsedNodes()));
assertTrue(storage.getVerifiedNodes().contains(1));
}
public void testNodeToFileId() throws Exception {
TreeStorage ts = new TreeStorage(null, new NodeGenerator.NullGenerator(), 10);
int [] full = ts.nodeToFileId(1);
assertEquals(0,full[0]);
assertEquals(9,full[1]);
int [] half = ts.nodeToFileId(2);
assertEquals(0,half[0]);
assertEquals(7,half[1]);
int [] last2 = ts.nodeToFileId(3);
assertEquals(8,last2[0]);
assertEquals(9,last2[1]);
int [] just1 = ts.nodeToFileId(17);
assertEquals(1,just1[0]);
assertEquals(1,just1[1]);
just1 = ts.nodeToFileId(25);
assertEquals(9,just1[0]);
assertEquals(9,just1[1]);
}
}