/* Copyright (c) 2013-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 java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
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 com.google.common.base.Strings;
public class TreeDifferenceTest extends Assert {
/**
* Tree made of the following nodes "<path> -> <treeId> (<metadataId>)"
*
* <pre>
* <code>
* roads -> a1 (null)
* roads/highways -> a2 (d1)
* roads/streets -> a3 (d2)
* buildings -> a4 (null)
* buildings/stores -> a5 (d3)
* buildings/unknown -> a6 (d4)
* buildings/towers -> a7 (d5) //deleted in rightTree
* </code>
* </pre>
*/
private MutableTree leftTree;
/**
* Tree made of the following nodes "<path> -> <treeId> (<metadataId>)"
*
* <pre>
* <code>
* roads -> a11 (null) // id changed
* roads/highways -> a2 (d1) // not changed
* roads/streetsRenamed -> a3 (d2) // renamed, same id and mdid
* buildings -> a41 (null) // id changed
* buildings/stores -> a5 (d31) // only metadataId changed
* buildings/knownUnknown -> a61 (d41) // renamed, changed, and new metadataid. Counts as a whole new tree.
* admin -> c1 (d5) // new tree
* admin/area -> c2 (d6) // new tree
* admin/line -> c3 (d7) // new tree
* </code>
* </pre>
*/
private MutableTree rightTree;
private TreeDifference treeDifference;
@Before
public void setUp() {
leftTree = MutableTree.createFromRefs(id("abc"),//
tree("roads", "a1", null), //
tree("roads/highways", "a2", "d1"),//
tree("roads/streets", "a3", "d2"), //
tree("buildings", "a4", null),//
tree("buildings/stores", "a5", "d3"),//
tree("buildings/unknown", "a6", "d4"),//
tree("buildings/towers", "a7", "d5")//
);
rightTree = MutableTree.createFromRefs(id("abc2"),//
tree("roads", "a11", null), //
tree("roads/highways", "a2", "d1"),//
tree("roads/streetsRenamed", "a3", "d2"), //
tree("buildings", "a41", null),//
tree("buildings/stores", "a5", "d31"),//
tree("buildings/knownUnknown", "a61", "d41"),//
tree("admin", "c1", "d5"),//
tree("admin/area", "c2", "d6"),//
tree("admin/line", "c3", "d7")//
);
treeDifference = TreeDifference.create(leftTree, rightTree);
}
@Test
public void testCreate() {
assertNotNull(treeDifference);
assertSame(leftTree, treeDifference.getLeftTree());
assertSame(rightTree, treeDifference.getRightTree());
}
@Test
public void testInverse() {
TreeDifference inverse = treeDifference.inverse();
assertNotNull(inverse);
assertNotSame(treeDifference, inverse);
assertSame(leftTree, inverse.getRightTree());
assertSame(rightTree, inverse.getLeftTree());
}
/**
* Only {@code roads/streetsRenamed} is purely a rename. {@code buildings/unknown} is renamed to
* {@code buildings/kownUnknown} but it also points to a different tree, so there's no way to
* know it was renamed
*/
@Test
public void testFindRenames() {
Map<NodeRef, NodeRef> renames = treeDifference.findRenames();
assertNotNull(renames);
assertEquals(1, renames.size());
Entry<NodeRef, NodeRef> e = renames.entrySet().iterator().next();
NodeRef oldRef = e.getKey();
NodeRef newRef = e.getValue();
assertEquals(tree("roads/streets", "a3", "d2"), oldRef);
assertEquals(tree("roads/streetsRenamed", "a3", "d2"), newRef);
}
@Test
public void testFindNewTrees() {
Set<NodeRef> newTrees = treeDifference.findNewTrees();
// buildings/knownUnknown, admin, admin/area, admin/line
assertEquals(4, newTrees.size());
assertTrue(newTrees.contains(tree("buildings/knownUnknown", "a61", "d41")));
assertTrue(newTrees.contains(tree("admin", "c1", "d5")));
assertTrue(newTrees.contains(tree("admin/area", "c2", "d6")));
assertTrue(newTrees.contains(tree("admin/line", "c3", "d7")));
}
@Test
public void testFindDeletes() {
Set<NodeRef> deletes = treeDifference.findDeletes();
// buildings/towers, buildings/unknown
assertEquals(2, deletes.size());
assertTrue(deletes.contains(tree("buildings/towers", "a7", "d5")));
assertTrue(deletes.contains(tree("buildings/unknown", "a6", "d4")));
}
@Test
public void testFindChanges() {
Map<NodeRef, NodeRef> changes = treeDifference.findChanges();
assertNotNull(changes);
assertEquals(changes.toString(), 2, changes.size());
NodeRef l1 = tree("roads", "a1", null);
NodeRef r1 = tree("roads", "a11", null);
NodeRef l2 = tree("buildings", "a4", null);
NodeRef r2 = tree("buildings", "a41", null);
assertEquals(r1, changes.get(l1));
assertEquals(r2, changes.get(l2));
}
@Test
public void testFindPureMetadataChanges() {
// buildings/stores -> a5 (d31) // only metadataId changed
Map<NodeRef, NodeRef> metadataChanges = treeDifference.findPureMetadataChanges();
assertNotNull(metadataChanges);
assertEquals(1, metadataChanges.size());
NodeRef left = tree("buildings/stores", "a5", "d3");
NodeRef right = tree("buildings/stores", "a5", "d31");
assertEquals(right, metadataChanges.get(left));
}
private NodeRef tree(String path, String treeId, @Nullable String metadataId) {
ObjectId mdId = metadataId == null ? null : id(metadataId);
return tree(path, id(treeId), mdId);
}
private NodeRef tree(String path, ObjectId treeId, @Nullable ObjectId metadataId) {
String parentPath = NodeRef.parentPath(path);
String name = NodeRef.nodeFromPath(path);
Node node = treeNode(name, treeId, metadataId);
return new NodeRef(node, parentPath, ObjectId.NULL);
}
private Node treeNode(String name, ObjectId treeId, @Nullable ObjectId metadataId) {
if (metadataId == null) {
metadataId = ObjectId.NULL;
}
Node node = Node.create(name, treeId, metadataId, TYPE.TREE, null);
return node;
}
private static ObjectId id(String hash) {
hash = Strings.padEnd(hash, 2 * ObjectId.NUM_BYTES, '0');
return ObjectId.valueOf(hash);
}
}