/* Copyright (c) 2012-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.test.integration.repository; import static org.locationtech.geogig.api.NodeRef.appendChild; import java.util.Collection; import java.util.List; import org.junit.Test; import org.locationtech.geogig.api.CommitBuilder; import org.locationtech.geogig.api.Node; import org.locationtech.geogig.api.NodeRef; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.Ref; import org.locationtech.geogig.api.RevCommit; import org.locationtech.geogig.api.RevObject.TYPE; import org.locationtech.geogig.api.RevTree; import org.locationtech.geogig.api.plumbing.FindTreeChild; import org.locationtech.geogig.api.plumbing.LsTreeOp; import org.locationtech.geogig.api.plumbing.LsTreeOp.Strategy; import org.locationtech.geogig.api.plumbing.RevObjectParse; import org.locationtech.geogig.api.plumbing.UpdateRef; import org.locationtech.geogig.api.plumbing.WriteTree; import org.locationtech.geogig.api.plumbing.WriteTree2; import org.locationtech.geogig.api.porcelain.AddOp; import org.locationtech.geogig.repository.StagingArea; import org.locationtech.geogig.repository.WorkingTree; import org.locationtech.geogig.storage.ObjectInserter; import org.locationtech.geogig.test.integration.RepositoryTestCase; import org.opengis.feature.Feature; import org.opengis.feature.simple.SimpleFeature; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; public class IndexTest extends RepositoryTestCase { private StagingArea index; @Override protected void setUpInternal() throws Exception { index = repo.index(); } // two features with the same content and different fid should point to the same object @Test public void testInsertIdenticalObjects() throws Exception { ObjectId oId1 = insertAndAdd(points1); Feature equalContentFeature = feature(pointsType, "DifferentId", ((SimpleFeature) points1) .getAttributes().toArray()); ObjectId oId2 = insertAndAdd(equalContentFeature); // BLOBS.print(repo.getRawObject(insertedId1), System.err); // BLOBS.print(repo.getRawObject(insertedId2), System.err); assertNotNull(oId1); assertNotNull(oId2); assertEquals(oId1, oId2); } // two features with different content should point to different objects @Test public void testInsertNonEqualObjects() throws Exception { ObjectId oId1 = insertAndAdd(points1); ObjectId oId2 = insertAndAdd(points2); assertNotNull(oId1); assertNotNull(oId2); assertFalse(oId1.equals(oId2)); } @Test public void testWriteTree() throws Exception { insertAndAdd(points1); insertAndAdd(lines1); // this new root tree must exist on the repo db, but is not set as the current head. In // fact, it is headless, as there's no commit pointing to it. CommitOp does that. ObjectId newRootTreeId = geogig.command(WriteTree.class) .setOldRoot(tree(repo.getHead().get().getObjectId())).call(); assertNotNull(newRootTreeId); assertFalse(repo.getRootTreeId().equals(newRootTreeId)); // but the index staged root shall be pointing to it // assertEquals(newRootTreeId, index.getStaged().getId()); RevTree tree = repo.getTree(newRootTreeId); // assertEquals(2, tree.size().intValue()); String path = appendChild(pointsName, points1.getIdentifier().getID()); assertTrue(repo.command(FindTreeChild.class).setParent(tree).setChildPath(path).call() .isPresent()); path = appendChild(linesName, lines1.getIdentifier().getID()); assertTrue(repo.command(FindTreeChild.class).setParent(tree).setChildPath(path).call() .isPresent()); // simulate a commit so the repo head points to this new tree ObjectInserter objectInserter = repo.newObjectInserter(); List<ObjectId> parents = ImmutableList.of(); RevCommit commit = new CommitBuilder(geogig.getPlatform()).setTreeId(newRootTreeId) .setParentIds(parents).build(); ObjectId commitId = commit.getId(); objectInserter.insert(commit); Optional<Ref> newHead = geogig.command(UpdateRef.class).setName("refs/heads/master") .setNewValue(commitId).call(); assertTrue(newHead.isPresent()); WorkingTree workTree = repo.workingTree(); workTree.delete(linesName, lines1.getIdentifier().getID()); geogig.command(AddOp.class).call(); newRootTreeId = geogig.command(WriteTree2.class).setOldRoot(tree(newRootTreeId)).call(); // newRootTreeId // = // index.writeTree(newRootTreeId, // new // NullProgressListener()); assertNotNull(newRootTreeId); assertFalse(repo.getRootTreeId().equals(newRootTreeId)); tree = repo.getTree(newRootTreeId); path = appendChild(pointsName, points1.getIdentifier().getID()); assertTrue(repo.command(FindTreeChild.class).setParent(tree).setChildPath(path).call() .isPresent()); path = appendChild(linesName, lines1.getIdentifier().getID()); assertFalse(repo.command(FindTreeChild.class).setParent(tree).setChildPath(path).call() .isPresent()); } private static class TreeNameFilter implements Predicate<NodeRef> { private String treePath; public TreeNameFilter(String treePath) { this.treePath = treePath; } @Override public boolean apply(NodeRef ref) { TYPE type = ref.getType(); String path = ref.path(); return TYPE.TREE.equals(type) && treePath.equals(path); } } @Test public void testWriteEmptyPathAddAll() throws Exception { insert(lines1); WorkingTree workingTree = geogig.getRepository().workingTree(); workingTree.createTypeTree(pointsName, pointsType); List<NodeRef> workHead = toList(geogig.command(LsTreeOp.class).setReference(Ref.WORK_HEAD) .setStrategy(Strategy.DEPTHFIRST).call()); assertEquals(3, workHead.size()); Collection<NodeRef> filtered = Collections2 .filter(workHead, new TreeNameFilter(pointsName)); assertEquals(1, filtered.size()); geogig.command(AddOp.class).call(); List<NodeRef> indexHead = toList(geogig.command(LsTreeOp.class) .setReference(Ref.STAGE_HEAD).setStrategy(Strategy.DEPTHFIRST).call()); assertEquals(3, indexHead.size()); filtered = Collections2.filter(indexHead, new TreeNameFilter(pointsName)); assertEquals(1, filtered.size()); } @Test public void testWriteEmptyPath() throws Exception { WorkingTree workingTree = geogig.getRepository().workingTree(); workingTree.createTypeTree(pointsName, pointsType); workingTree.createTypeTree(linesName, linesType); List<NodeRef> workHead = toList(geogig.command(LsTreeOp.class).setReference(Ref.WORK_HEAD) .setStrategy(Strategy.DEPTHFIRST).call()); assertEquals(2, workHead.size()); Collection<NodeRef> filtered; filtered = Collections2.filter(workHead, new TreeNameFilter(pointsName)); assertEquals(1, filtered.size()); filtered = Collections2.filter(workHead, new TreeNameFilter(linesName)); assertEquals(1, filtered.size()); geogig.command(AddOp.class).addPattern(pointsName).call(); List<NodeRef> indexHead; indexHead = toList(geogig.command(LsTreeOp.class).setReference(Ref.STAGE_HEAD) .setStrategy(Strategy.DEPTHFIRST).call()); assertEquals(1, indexHead.size()); filtered = Collections2.filter(indexHead, new TreeNameFilter(pointsName)); assertEquals(1, filtered.size()); geogig.command(AddOp.class).addPattern(linesName).call(); indexHead = toList(geogig.command(LsTreeOp.class).setReference(Ref.STAGE_HEAD) .setStrategy(Strategy.DEPTHFIRST).call()); assertEquals(2, indexHead.size());// Points and Lines filtered = Collections2.filter(indexHead, new TreeNameFilter(linesName)); assertEquals(1, filtered.size()); } @Test public void testModify() throws Exception { ObjectId oId1 = insertAndAdd(points1); assertNotNull(oId1); assertEquals(oId1, index.findStaged(appendChild(pointsName, idP1)).get().getObjectId()); ObjectId oId1_modified = insertAndAdd(points1_modified); assertNotNull(oId1_modified); assertFalse(oId1.equals(oId1_modified)); assertFalse(index.findStaged(appendChild(pointsName, idP1)).get().getObjectId() .equals(oId1)); assertEquals(oId1_modified, index.findStaged(appendChild(pointsName, idP1)).get() .getObjectId()); } @Test public void testAddMultiple() throws Exception { ObjectId oId1 = insert(points1); ObjectId oId2 = insert(points2); assertNotNull(oId1); assertNotNull(oId2); assertFalse(index.findStaged(appendChild(pointsName, idP1)).isPresent()); assertFalse(index.findStaged(appendChild(pointsName, idP2)).isPresent()); assertEquals(oId1, repo.workingTree().findUnstaged(appendChild(pointsName, idP1)).get() .getObjectId()); assertEquals(oId2, repo.workingTree().findUnstaged(appendChild(pointsName, idP2)).get() .getObjectId()); geogig.command(AddOp.class).call(); assertEquals(oId1, index.findStaged(appendChild(pointsName, idP1)).get().getObjectId()); assertEquals(oId2, index.findStaged(appendChild(pointsName, idP2)).get().getObjectId()); } private Supplier<RevTree> tree(final ObjectId treeId) { Supplier<RevTree> delegate = new Supplier<RevTree>() { @Override public RevTree get() { if (treeId.isNull()) { return RevTree.EMPTY; } return geogig.command(RevObjectParse.class).setObjectId(treeId).call(RevTree.class) .get(); } }; return Suppliers.memoize(delegate); } @Test public void testMultipleStaging() throws Exception { // insert and commit feature1_1 final ObjectId oId1_1 = insertAndAdd(points1); System.err.println("++++++++++++ stage 1: ++++++++++++++++++++"); // staged1.accept(new PrintVisitor(index.getDatabase(), new PrintWriter(System.err))); // check feature1_1 is there assertEquals(oId1_1, index.findStaged(appendChild(pointsName, idP1)).get().getObjectId()); // insert and commit feature1_2, feature1_2 and feature2_1 final ObjectId oId1_2 = insertAndAdd(points2); final ObjectId oId1_3 = insertAndAdd(points3); final ObjectId oId2_1 = insertAndAdd(lines1); System.err.println("++++++++++++ stage 2: ++++++++++++++++++++"); // staged2.accept(new PrintVisitor(index.getDatabase(), new PrintWriter(System.err))); // check feature1_2, feature1_3 and feature2_1 Optional<Node> treeChild; assertNotNull(treeChild = index.findStaged(appendChild(pointsName, idP2))); assertTrue(treeChild.isPresent()); assertEquals(oId1_2, treeChild.get().getObjectId()); assertNotNull(treeChild = index.findStaged(appendChild(pointsName, idP3))); assertTrue(treeChild.isPresent()); assertEquals(oId1_3, treeChild.get().getObjectId()); assertNotNull(treeChild = index.findStaged(appendChild(linesName, idL1))); assertTrue(treeChild.isPresent()); assertEquals(oId2_1, treeChild.get().getObjectId()); // as well as feature1_1 from the previous commit assertNotNull(treeChild = index.findStaged(appendChild(pointsName, idP1))); assertTrue(treeChild.isPresent()); assertEquals(oId1_1, treeChild.get().getObjectId()); // delete feature1_1, feature1_3, and feature2_1 assertTrue(deleteAndAdd(points1)); assertTrue(deleteAndAdd(points3)); assertTrue(deleteAndAdd(lines1)); // and insert feature2_2 final ObjectId oId2_2 = insertAndAdd(lines2); System.err.println("++++++++++++ stage 3: ++++++++++++++++++++"); // staged3.accept(new PrintVisitor(index.getDatabase(), new PrintWriter(System.err))); // and check only points2 and lines2 remain (i.e. its oids are set to NULL) assertFalse(index.findStaged(appendChild(pointsName, idP1)).isPresent()); assertFalse(index.findStaged(appendChild(pointsName, idP3)).isPresent()); assertFalse(index.findStaged(appendChild(linesName, idL1)).isPresent()); assertEquals(oId1_2, index.findStaged(appendChild(pointsName, idP2)).get().getObjectId()); assertEquals(oId2_2, index.findStaged(appendChild(linesName, idL2)).get().getObjectId()); } @Test public void testWriteTree2() throws Exception { // insert and commit feature1_1 final ObjectId oId1_1 = insertAndAdd(points1); final ObjectId newRepoTreeId1; { newRepoTreeId1 = geogig.command(WriteTree2.class) .setOldRoot(tree(repo.getHead().get().getObjectId())).call(); // assertEquals(index.getDatabase().getStagedRootRef().getObjectId(), newRepoTreeId1); RevTree newRepoTree = repo.getTree(newRepoTreeId1); System.err.println("++++++++++ new repo tree 1: " + newRepoTreeId1 + " ++++++++++++"); // check feature1_1 is there assertEquals(oId1_1, repo.getTreeChild(newRepoTree, appendChild(pointsName, idP1)) .get().getObjectId()); } // insert and add (stage) points2, points3, and lines1 final ObjectId oId1_2 = insertAndAdd(points2); final ObjectId oId1_3 = insertAndAdd(points3); final ObjectId oId2_1 = insertAndAdd(lines1); {// simulate a commit so the repo head points to this new tree ObjectInserter objectInserter = repo.newObjectInserter(); List<ObjectId> parents = ImmutableList.of(); RevCommit commit = new CommitBuilder().setTreeId(newRepoTreeId1).setParentIds(parents) .build(); ObjectId commitId = commit.getId(); objectInserter.insert(commit); Optional<Ref> newHead = geogig.command(UpdateRef.class).setName("refs/heads/master") .setNewValue(commitId).call(); assertTrue(newHead.isPresent()); } final ObjectId newRepoTreeId2; { // write comparing the the previously generated tree instead of the repository HEAD, as // it was not updated (no commit op was performed) newRepoTreeId2 = geogig.command(WriteTree2.class).setOldRoot(tree(newRepoTreeId1)) .call(); // assertEquals(index.getDatabase().getStagedRootRef().getObjectId(), newRepoTreeId2); System.err.println("++++++++ new root 2:" + newRepoTreeId2 + " ++++++++++"); RevTree newRepoTree = repo.getTree(newRepoTreeId2); // check feature1_2, feature1_2 and feature2_1 Optional<Node> treeChild; assertNotNull(treeChild = repo.getTreeChild(newRepoTree, appendChild(pointsName, idP2))); assertEquals(oId1_2, treeChild.get().getObjectId()); assertNotNull(treeChild = repo.getTreeChild(newRepoTree, appendChild(pointsName, idP3))); assertEquals(oId1_3, treeChild.get().getObjectId()); assertNotNull(treeChild = repo.getTreeChild(newRepoTree, appendChild(linesName, idL1))); assertEquals(oId2_1, treeChild.get().getObjectId()); // as well as feature1_1 from the previous commit assertNotNull(treeChild = repo.getTreeChild(newRepoTree, appendChild(pointsName, idP1))); assertEquals(oId1_1, treeChild.get().getObjectId()); } {// simulate a commit so the repo head points to this new tree ObjectInserter objectInserter = repo.newObjectInserter(); List<ObjectId> parents = ImmutableList.of(); RevCommit commit = new CommitBuilder().setTreeId(newRepoTreeId2).setParentIds(parents) .build(); ObjectId commitId = commit.getId(); objectInserter.insert(commit); Optional<Ref> newHead = geogig.command(UpdateRef.class).setName("refs/heads/master") .setNewValue(commitId).call(); assertTrue(newHead.isPresent()); } // delete feature1_1, feature1_3, and feature2_1 assertTrue(deleteAndAdd(points1)); assertTrue(deleteAndAdd(points3)); assertTrue(deleteAndAdd(lines1)); // and insert feature2_2 final ObjectId oId2_2 = insertAndAdd(lines2); final ObjectId newRepoTreeId3; { // write comparing the the previously generated tree instead of the repository HEAD, as // it was not updated (no commit op was performed) newRepoTreeId3 = geogig.command(WriteTree2.class).setOldRoot(tree(newRepoTreeId2)) .call(); // assertEquals(index.getDatabase().getStagedRootRef().getObjectId(), newRepoTreeId3); System.err.println("++++++++ new root 3:" + newRepoTreeId3 + " ++++++++++"); RevTree newRepoTree = repo.getTree(newRepoTreeId3); // and check only feature1_2 and feature2_2 remain assertFalse(repo.getTreeChild(newRepoTree, appendChild(pointsName, idP1)).isPresent()); assertFalse(repo.getTreeChild(newRepoTree, appendChild(pointsName, idP3)).isPresent()); assertFalse(repo.getTreeChild(newRepoTree, appendChild(linesName, idL3)).isPresent()); assertEquals(oId1_2, repo.getTreeChild(newRepoTree, appendChild(pointsName, idP2)) .get().getObjectId()); assertEquals(oId2_2, repo.getTreeChild(newRepoTree, appendChild(linesName, idL2)).get() .getObjectId()); } } @Test public void testAddEmptyTree() throws Exception { WorkingTree workingTree = geogig.getRepository().workingTree(); workingTree.createTypeTree(pointsName, pointsType); geogig.command(AddOp.class).setUpdateOnly(false).call(); assertTrue(index.findStaged(pointsName).isPresent()); } }