/* Copyright (c) 2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.api.plumbing.diff;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.locationtech.geogig.api.Bucket;
import org.locationtech.geogig.api.Node;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevObject.TYPE;
import org.locationtech.geogig.api.RevTree;
import org.locationtech.geogig.api.RevTreeBuilder;
import org.locationtech.geogig.storage.ObjectDatabase;
import org.locationtech.geogig.storage.memory.HeapObjectDatabse;
/**
*
*/
public class DiffCountConsumerTest extends Assert {
/**
* All original feature noderefs have this objectid
*/
private static final ObjectId FAKE_FEATURE_ID = ObjectId
.forString("1100000000000000000000000000000000000000");
/**
* All changed feature noderefs have this objectid
*/
private static final ObjectId FAKE_FEATURE_ID_CHANGED = ObjectId
.forString("2200000000000000000000000000000000000000");
private ObjectDatabase odb;
private RevTree childrenFeatureTree;
/** single level tree with 2 * {@link RevTree#NORMALIZED_SIZE_LIMIT} feature references */
private RevTree bucketsFeatureTree;
private RevTree childrenFeatureTypesTree;
RevTreeBuilder childTree1;
RevTreeBuilder childTree2;
@Before
public void setUp() {
odb = new HeapObjectDatabse();
odb.open();
{
RevTreeBuilder builder = createFeaturesTree("", 10);
this.childrenFeatureTree = builder.build();
}
{
RevTreeBuilder rootBuilder = new RevTreeBuilder(odb);
childTree1 = createFeaturesTree("tree1", 10);
createFeatureTypesTree(rootBuilder, "tree1", childTree1);
childTree2 = createFeaturesTree("tree2", 5);
createFeatureTypesTree(rootBuilder, "tree2", childTree2);
childrenFeatureTypesTree = rootBuilder.build();
}
{
RevTreeBuilder builder = createFeaturesTree("", 2 * RevTree.NORMALIZED_SIZE_LIMIT);
this.bucketsFeatureTree = builder.build();
assertTrue(bucketsFeatureTree.buckets().isPresent());
}
}
private void createFeatureTypesTree(RevTreeBuilder rootBuilder, String treePath,
RevTreeBuilder childBuilder) {
RevTree childTree = childBuilder.build();
odb.put(childTree);
Node childRef = Node.create(treePath, childTree.getId(), ObjectId.NULL, TYPE.TREE, null);
rootBuilder.put(childRef);
}
private DiffObjectCount count(RevTree left, RevTree right) {
PreOrderDiffWalk visitor = new PreOrderDiffWalk(left, right, odb, odb);
DiffCountConsumer consumer = new DiffCountConsumer(odb);
visitor.walk(consumer);
DiffObjectCount count = consumer.get();
return count;
}
@Test
public void testSameTree() {
DiffObjectCount count = count(childrenFeatureTree, childrenFeatureTree);
assertEquals(0, count.featureCount());
assertEquals(0, count.treeCount());
}
@Test
public void testChildrenEmpty() {
assertEquals(childrenFeatureTree.size(), count(childrenFeatureTree, RevTree.EMPTY)
.featureCount());
assertEquals(childrenFeatureTree.size(), count(RevTree.EMPTY, childrenFeatureTree)
.featureCount());
}
@Test
public void testChildrenChildren() {
RevTreeBuilder builder = new RevTreeBuilder(odb, childrenFeatureTree);
RevTree changed = builder.remove("3").build();
assertEquals(1, count(childrenFeatureTree, changed).featureCount());
assertEquals(1, count(changed, childrenFeatureTree).featureCount());
changed = builder.put(
Node.create("new", FAKE_FEATURE_ID, ObjectId.NULL, TYPE.FEATURE, null)).build();
assertEquals(2, count(childrenFeatureTree, changed).featureCount());
assertEquals(2, count(changed, childrenFeatureTree).featureCount());
changed = builder.put(
Node.create("1", FAKE_FEATURE_ID_CHANGED, ObjectId.NULL, TYPE.FEATURE, null))
.build();
assertEquals(3, count(childrenFeatureTree, changed).featureCount());
assertEquals(3, count(changed, childrenFeatureTree).featureCount());
}
@Test
public void testChildrenChildrenNestedTrees() {
RevTreeBuilder rootBuilder = new RevTreeBuilder(odb, childrenFeatureTypesTree);
childTree1.put(featureRef("tree1", 1000));
createFeatureTypesTree(rootBuilder, "tree1", childTree1);
RevTree newRoot = rootBuilder.build();
assertEquals(1, count(childrenFeatureTypesTree, newRoot).featureCount());
childTree2.remove("tree2/2");
createFeatureTypesTree(rootBuilder, "tree2", childTree2);
newRoot = rootBuilder.build();
assertEquals(2, count(childrenFeatureTypesTree, newRoot).featureCount());
childTree2.put(Node.create("tree2/1", FAKE_FEATURE_ID_CHANGED, ObjectId.NULL, TYPE.FEATURE,
null));
createFeatureTypesTree(rootBuilder, "tree2", childTree2);
newRoot = rootBuilder.build();
assertEquals(3, count(childrenFeatureTypesTree, newRoot).featureCount());
}
@Test
public void testBucketBucketAdd() {
RevTreeBuilder builder = new RevTreeBuilder(odb, bucketsFeatureTree);
final int initialSize = (int) bucketsFeatureTree.size();
final int added = 1 + 2 * RevTree.NORMALIZED_SIZE_LIMIT;
for (int i = initialSize; i < (initialSize + added); i++) {
builder.put(featureRef("", i));
}
RevTree changed = builder.build();
assertEquals(initialSize + added, changed.size());
assertEquals(added, count(bucketsFeatureTree, changed).featureCount());
assertEquals(added, count(changed, bucketsFeatureTree).featureCount());
assertEquals(added, count(bucketsFeatureTree, changed).getFeaturesAdded());
assertEquals(0, count(bucketsFeatureTree, changed).getFeaturesChanged());
assertEquals(0, count(bucketsFeatureTree, changed).getFeaturesRemoved());
// invert the comparison
assertEquals(0, count(changed, bucketsFeatureTree).getFeaturesAdded());
assertEquals(added, count(changed, bucketsFeatureTree).getFeaturesRemoved());
assertEquals(0, count(changed, bucketsFeatureTree).getFeaturesChanged());
}
@Test
public void testBucketBucketRemove() {
RevTreeBuilder builder = new RevTreeBuilder(odb, bucketsFeatureTree);
RevTree changed;
changed = builder.remove("3").build();
assertEquals(1, count(bucketsFeatureTree, changed).featureCount());
assertEquals(1, count(changed, bucketsFeatureTree).featureCount());
for (int i = 0; i < RevTree.NORMALIZED_SIZE_LIMIT - 1; i++) {
builder.remove(String.valueOf(i));
}
changed = builder.build();
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT + 1, changed.size());
assertTrue(changed.buckets().isPresent());
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT - 1, count(bucketsFeatureTree, changed)
.featureCount());
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT - 1, count(changed, bucketsFeatureTree)
.featureCount());
builder.remove(String.valueOf(RevTree.NORMALIZED_SIZE_LIMIT + 1));
changed = builder.build();
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT, changed.size());
assertFalse(changed.buckets().isPresent());
}
@Test
public void testBucketBucketChange() {
RevTreeBuilder builder;
RevTree changed;
builder = new RevTreeBuilder(odb, bucketsFeatureTree);
changed = builder.put(
Node.create("1023", FAKE_FEATURE_ID_CHANGED, ObjectId.NULL, TYPE.FEATURE, null))
.build();
DiffObjectCount count = count(bucketsFeatureTree, changed);
assertEquals(1, count.featureCount());
assertEquals(0, count.treeCount());
count = count(changed, bucketsFeatureTree);
assertEquals(1, count.featureCount());
assertEquals(0, count.treeCount());
builder = new RevTreeBuilder(odb, bucketsFeatureTree);
int expected = 0;
for (int i = 0; i < bucketsFeatureTree.size(); i += 2) {
changed = builder.put(
Node.create(String.valueOf(i), FAKE_FEATURE_ID_CHANGED, ObjectId.NULL,
TYPE.FEATURE, null)).build();
expected++;
}
changed = builder.build();
assertEquals(expected, count(bucketsFeatureTree, changed).featureCount());
assertEquals(expected, count(changed, bucketsFeatureTree).featureCount());
assertEquals(expected, count(bucketsFeatureTree, changed).getFeaturesChanged());
assertEquals(expected, count(changed, bucketsFeatureTree).getFeaturesChanged());
assertEquals(0, count(changed, bucketsFeatureTree).getFeaturesAdded());
assertEquals(0, count(changed, bucketsFeatureTree).getFeaturesRemoved());
}
@Test
public void testBucketChildren() {
RevTreeBuilder builder = new RevTreeBuilder(odb, bucketsFeatureTree);
RevTree changed;
for (int i = 0; i < RevTree.NORMALIZED_SIZE_LIMIT; i++) {
builder.remove(String.valueOf(i));
}
changed = builder.build();
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT, changed.size());
assertFalse(changed.buckets().isPresent());
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT, count(bucketsFeatureTree, changed)
.featureCount());
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT, count(changed, bucketsFeatureTree)
.featureCount());
}
@Test
public void testBucketChildrenDeeperBuckets() {
final RevTree deepTree = createFeaturesTree("", 20000 + RevTree.NORMALIZED_SIZE_LIMIT)
.build();
// sanity check
assertTrue(deepTree.buckets().isPresent());
{// sanity check to ensure we're testing with a tree with depth > 1 (i.e. at least two
// levels of buckets)
final int maxDepth = depth(deepTree, 0);
assertTrue(maxDepth > 1);
}
RevTreeBuilder builder = new RevTreeBuilder(odb, deepTree);
{
final int count = (int) (deepTree.size() - RevTree.NORMALIZED_SIZE_LIMIT);
for (int i = 0; i < count; i++) {
String path = String.valueOf(i);
builder.remove(path);
}
}
RevTree changed = builder.build();
assertEquals(RevTree.NORMALIZED_SIZE_LIMIT, changed.size());
// sanity check
assertTrue(changed.features().isPresent());
assertFalse(changed.buckets().isPresent());
final long expected = deepTree.size() - changed.size();
assertEquals(expected, count(deepTree, changed).featureCount());
assertEquals(expected, count(changed, deepTree).featureCount());
}
private int depth(RevTree deepTree, int currDepth) {
if (!deepTree.buckets().isPresent()) {
return currDepth;
}
int depth = currDepth;
for (Bucket bucket : deepTree.buckets().get().values()) {
RevTree bucketTree = odb.get(bucket.id(), RevTree.class);
int d = depth(bucketTree, currDepth + 1);
depth = Math.max(depth, d);
}
return depth;
}
private RevTreeBuilder createFeaturesTree(final String parentPath, final int numEntries) {
RevTreeBuilder tree = new RevTreeBuilder(odb);
for (int i = 0; i < numEntries; i++) {
tree.put(featureRef(parentPath, i));
}
return tree;
}
private Node featureRef(String parentPath, int i) {
String path = NodeRef.appendChild(parentPath, String.valueOf(i));
Node ref = Node.create(path, FAKE_FEATURE_ID, ObjectId.NULL, TYPE.FEATURE, null);
return ref;
}
}