Package org.locationtech.geogig.api.plumbing

Source Code of org.locationtech.geogig.api.plumbing.WriteTree2Test

/* 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;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;

import javax.annotation.Nullable;

import org.junit.Test;
import org.locationtech.geogig.api.Bucket;
import org.locationtech.geogig.api.CommitBuilder;
import org.locationtech.geogig.api.GeoGIG;
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.RevFeature;
import org.locationtech.geogig.api.RevFeatureBuilder;
import org.locationtech.geogig.api.RevFeatureType;
import org.locationtech.geogig.api.RevFeatureTypeImpl;
import org.locationtech.geogig.api.RevObject.TYPE;
import org.locationtech.geogig.api.RevTree;
import org.locationtech.geogig.api.RevTreeBuilder;
import org.locationtech.geogig.api.RevTreeImpl;
import org.locationtech.geogig.api.SymRef;
import org.locationtech.geogig.api.plumbing.LsTreeOp.Strategy;
import org.locationtech.geogig.api.plumbing.diff.MutableTree;
import org.locationtech.geogig.repository.SpatialOps;
import org.locationtech.geogig.storage.ObjectDatabase;
import org.locationtech.geogig.storage.StagingDatabase;
import org.locationtech.geogig.test.integration.RepositoryTestCase;
import org.opengis.feature.Feature;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.io.ParseException;

public class WriteTree2Test extends RepositoryTestCase {

    private static final String EMPTY_ID = RevTree.EMPTY_TREE_ID.toString();

    private WriteTree2 command;

    private GeoGIG geogig;

    private StagingDatabase indexDb;

    private ObjectDatabase objectDb;

    private RevTree leftTree;

    private RevTree rightTree;

    @Override
    protected void setUpInternal() throws Exception {
        geogig = getGeogig();
        command = geogig.command(WriteTree2.class);
        indexDb = geogig.getRepository().stagingDatabase();
        objectDb = geogig.getRepository().objectDatabase();
    }

    @Override
    public void tearDownInternal() {
        if (objectDb != null) {
            objectDb.close();
        }
        if (indexDb != null) {
            indexDb.close();
        }
    }

    @Test
    public void testEmptyRepo() {
        ObjectId root = command.call();
        assertNotNull(root);
        assertEquals(RevTree.EMPTY_TREE_ID, root);
    }

    @Test
    public void testEmptyRepoSingleStagedTree() {
        rightTree = createStageHeadTree(//
        indexTree("roads", "a1", "d1", 10)//
        );

        ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);
        // print(newRepoRoot);

        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);
        ImmutableMap<String, NodeRef> refsByPath = getTreeRefsByPath(newRepoRoot);
        assertEquals(1, refsByPath.size());
        assertTrue(refsByPath.keySet().contains("roads"));
    }

    @Test
    public void testRename() {
        leftTree = createHeadTree(//
        repoTree("roads", EMPTY_ID, null, 0) //
        );
        rightTree = createStageHeadTree(//
        indexTree("roadsRenamed", EMPTY_ID, null, 0) //
        );

        ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);
        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getTreeRefsByPath(newRepoRoot);
        assertEquals(1, refsByPath.size());
        assertTrue(refsByPath.containsKey("roadsRenamed"));
    }

    @Test
    public void testRenameNested() {
        leftTree = createHeadTree(//
                repoTree("roads", EMPTY_ID, null, 0), //
                repoTree("roads/highways", "a2", "d1", 2),//
                repoTree("roads/streets", "a3", "d2", 2) //
        );
        rightTree = createStageHeadTree(//
                indexTree("roads", EMPTY_ID, null, 0), //
                indexTree("roads/highways", "a2", "d1", 2),//
                indexTree("roads/streetsRenamed", "a3", "d2", 2) //
        );

        ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);
        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getTreeRefsByPath(newRepoRoot);
        assertEquals(3, refsByPath.size());
        assertTrue(refsByPath.containsKey("roads"));
        assertTrue(refsByPath.containsKey("roads/highways"));
        assertTrue(refsByPath.containsKey("roads/streetsRenamed"));
    }

    @Test
    public void testNoChanges() {
        leftTree = createHeadTree(//
                repoTree("buildings", EMPTY_ID, null, 0),//
                repoTree("buildings/stores", "a5", "d3", 5),//
                repoTree("buildings/unknown", "a6", "d4", 5),//
                repoTree("buildings/towers", "a7", "d5", 5)//
        );
        rightTree = createStageHeadTree(//
                repoTree("buildings", EMPTY_ID, null, 0),//
                repoTree("buildings/stores", "a5", "d3", 5),//
                repoTree("buildings/unknown", "a6", "d4", 5),//
                repoTree("buildings/towers", "a7", "d5", 5)//
        );

        ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);
        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getTreeRefsByPath(newRepoRoot);
        assertEquals(4, refsByPath.size());
        Set<String> expected = set("buildings", "buildings/stores", "buildings/unknown",
                "buildings/towers");
        assertEquals(expected, refsByPath.keySet());
    }

    @Test
    public void testMetadataIdChangeOnly() {
        leftTree = createHeadTree(//
                repoTree("buildings", EMPTY_ID, null, 0),//
                repoTree("buildings/stores", "a5", "d3", 5)// old md id is d3
        );
        rightTree = createStageHeadTree(//
                indexTree("buildings", EMPTY_ID, null, 0),//
                indexTree("buildings/stores", "a5", "d31", 5)// new md id is d31
        );

        ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);
        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);
        ImmutableMap<String, NodeRef> refsByPath = getTreeRefsByPath(newRepoRoot);
        assertEquals(set("buildings", "buildings/stores"), refsByPath.keySet());
        assertEquals(id("d31"), refsByPath.get("buildings/stores").getMetadataId());
    }

    @Test
    public void testDeleteAll() {
        leftTree = createHeadTree(//
                repoTree("roads", EMPTY_ID, null, 0), //
                repoTree("roads/highways", "a2", "d1", 10),//
                repoTree("roads/streets", "a3", "d2", 10), //
                repoTree("buildings", EMPTY_ID, null, 0),//
                repoTree("buildings/stores", "a5", "d3", 5),//
                repoTree("buildings/unknown", "a6", "d4", 5),//
                repoTree("buildings/towers", "a7", "d5", 5)//
        );
        rightTree = createStageHeadTree();

        final ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getTreeRefsByPath(newRepoRoot);
        assertEquals(set(), refsByPath.keySet());
    }

    @Test
    public void testDeletes() {
        leftTree = createHeadTree(//
                repoTree("roads", EMPTY_ID, null, 0), //
                repoTree("roads/highways", "a2", "d1", 10),//
                repoTree("roads/streets", "a3", "d2", 10), //
                repoTree("buildings", EMPTY_ID, null, 0),//
                repoTree("buildings/stores", "a5", "d3", 5),//
                repoTree("buildings/unknown", "a6", "d4", 5),//
                repoTree("buildings/towers", "a7", "d5", 5)//
        );
        rightTree = createStageHeadTree(//
                indexTree("roads", EMPTY_ID, null, 0), //
                indexTree("roads/highways", "a2", "d1", 10),//
                indexTree("buildings", EMPTY_ID, null, 0),//
                indexTree("buildings/stores", "a5", "d31", 5)//
        );

        final ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);

        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getTreeRefsByPath(newRepoRoot);
        assertEquals(set("roads", "roads/highways", "buildings", "buildings/stores"),
                refsByPath.keySet());
    }

    @Test
    public void testSimpleChanges() {
        leftTree = createHeadTree(//
                repoTree("roads", "a1", "d1", 1), //
                repoTree("buildings", EMPTY_ID, null, 0)//
        );
        rightTree = createStageHeadTree(//
                repoTree("roads", "a11", "d1", 2), //
                repoTree("buildings", "a41", null, 1)//
        );
        // print(leftTree.getId());
        // print(rightTree.getId());
        final ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);
        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getRefsByPath(newRepoRoot, true);

        Set<String> expected = set("roads", "roads/roads.0", "roads/roads.1", "buildings",
                "buildings/buildings.0");
        ImmutableSet<String> actual = refsByPath.keySet();

        assertEquals(expected, actual);
    }

    @Test
    public void testNestedChanges() {
        leftTree = createHeadTree(//
                repoTree("roads", EMPTY_ID, null, 0), //
                repoTree("roads/highways", EMPTY_ID, "d1", 0),//
                repoTree("roads/streets", "a3", "d2", 1), //
                repoTree("buildings", EMPTY_ID, null, 0),//
                repoTree("buildings/stores", EMPTY_ID, "d3", 0),//
                repoTree("buildings/unknown", "a6", "d4", 1)//
        );
        rightTree = createStageHeadTree(//
                indexTree("roads", EMPTY_ID, null, 0), //
                indexTree("roads/highways", "a21", "d1", 1),// 1 added
                indexTree("roads/streets", EMPTY_ID, "d2", 0), // 1 removed
                indexTree("buildings", EMPTY_ID, null, 0),//
                indexTree("buildings/stores", "a51", "d3", 2),// 2 added
                indexTree("buildings/unknown", "a6", "d4", 1)// not changed
        );

        final ObjectId newRepoRoot = command.call();
        assertNotNull(newRepoRoot);
        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getRefsByPath(newRepoRoot, true);

        Set<String> expected = set("roads", "roads/highways", "roads/highways/highways.0",
                "roads/streets", "buildings", "buildings/stores", "buildings/stores/stores.0",
                "buildings/stores/stores.1", "buildings/unknown", "buildings/unknown/unknown.0");
        ImmutableSet<String> actual = refsByPath.keySet();

        assertEquals(expected, actual);
    }

    @Test
    public void testAllKindsOfChanges() {
        leftTree = createHeadTree(//
                repoTree("roads", EMPTY_ID, null, 0), //
                repoTree("roads/highways", "a2", "d1", 1),//
                repoTree("roads/streets", "a3", "d2", 1), //
                repoTree("buildings", "a4", null, 2),// mixed tree, contains features and subtrees
                repoTree("buildings/stores", "a5", "d3", 1),//
                repoTree("buildings/unknown", "a6", "d4", 1),//
                repoTree("buildings/towers", "a7", "d5", 5)//
        );
        rightTree = createStageHeadTree(//
                indexTree("roads", EMPTY_ID, null, 0), //
                indexTree("roads/highways", "a21", "d1", 2),// 1 feature added
                indexTree("roads/streetsRenamed", "a3", "d2", 1), // tree renamed
                indexTree("buildings", "a41", null, 1),// 1 feature removed
                indexTree("buildings/stores", "a5", "d31", 1),// only metadata changed
                indexTree("buildings/knownUnknown", "a61", "d41", 2),// renamed, changed tree and
                                                                     // metadata
                // buildings/towers removed completely
                indexTree("admin", "c1", "d5", 2),// new mixed tree, contains features and subtrees
                indexTree("admin/area", "c2", "d6", 1),//
                indexTree("admin/line", EMPTY_ID, "d7", 0)//
        );

        final ObjectId newRepoRoot = command.call();
        // print(newRepoRoot);
        assertNotNull(newRepoRoot);
        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        ImmutableMap<String, NodeRef> refsByPath = getRefsByPath(newRepoRoot, true);
        Set<String> paths = Sets.newTreeSet();
        paths.addAll(refsByPath.keySet());

        Set<String> expected = set(//
                "roads",//
                "roads/highways",//
                "roads/highways/highways.0",//
                "roads/highways/highways.1",//
                "roads/streetsRenamed",//
                "roads/streetsRenamed/streets.0",//
                "buildings",//
                "buildings/buildings.0",//
                "buildings/stores",//
                "buildings/stores/stores.0",//
                "buildings/knownUnknown",//
                "buildings/knownUnknown/knownUnknown.0",//
                "buildings/knownUnknown/knownUnknown.1",//
                "admin",//
                "admin/admin.0",//
                "admin/admin.1",//
                "admin/area",//
                "admin/area/area.0",//
                "admin/line"//
        );
        assertEquals(expected, paths);
    }

    @Test
    public void testPathFilteringTopLevelTree() {
        leftTree = createHeadTree(//
                repoTree("roads", "a1", null, 2), //
                repoTree("roads/highways", "a2", "d1", 1),//
                repoTree("roads/streets", "a3", "d2", 2),//
                repoTree("buildings", "a4", "d3", 2)// deleted tree completely
        );
        rightTree = createStageHeadTree(//
                repoTree("roads", "a11", null, 1), // deleted 1 feature
                repoTree("roads/highways", "a21", "d1", 3),// added 2 features
                repoTree("roads/streets", "a31", "d2", 1) // removed 1 feature
        );

        MapDifference<String, NodeRef> difference;
        Set<String> onlyOnLeft;
        Set<String> onlyOnRight;
        Set<String> entriesInCommon;

        difference = runWithPathFilter(leftTree, rightTree, "roads");
        onlyOnLeft = difference.entriesOnlyOnLeft().keySet();
        onlyOnRight = difference.entriesOnlyOnRight().keySet();
        entriesInCommon = difference.entriesInCommon().keySet();
        Set<String> entriesDiffering = difference.entriesDiffering().keySet();

        assertEquals(set("buildings", "buildings/buildings.0", "buildings/buildings.1"), onlyOnLeft);
        assertEquals(set(), onlyOnRight);
        assertEquals(
                set("roads", "roads/roads.0", "roads/streets/streets.0",
                        "roads/highways/highways.0", "roads/highways/highways.2",
                        "roads/highways/highways.1", "roads/highways", "roads/streets"),
                entriesInCommon);
        assertEquals(set(), entriesDiffering);

    }

    @Test
    public void testPathFilteringSingleFeature() {
        leftTree = createHeadTree(//
                repoTree("roads", "a1", null, 2), //
                repoTree("roads/highways", "a2", "d1", 1)//
        );
        rightTree = createStageHeadTree(//
                repoTree("roads", "a11", null, 1), // deleted 1 feature
                repoTree("roads/highways", "a21", "d1", 3)// added 2 features
        );

        MapDifference<String, NodeRef> difference;
        Set<String> onlyOnLeft;
        Set<String> onlyOnRight;

        difference = runWithPathFilter(leftTree, rightTree, "roads/roads.1");
        onlyOnLeft = difference.entriesOnlyOnLeft().keySet();
        onlyOnRight = difference.entriesOnlyOnRight().keySet();

        assertEquals(set(), onlyOnLeft);
        assertEquals(set("roads/highways/highways.1", "roads/highways/highways.2"), onlyOnRight);
        assertEquals(set("roads/highways/highways.0", "roads/roads.0"), difference
                .entriesInCommon().keySet());
    }

    @Test
    public void testPathFilteringDeletedTreeButCommitSingleChange() {
        leftTree = createHeadTree(//
                repoTree("roads", "a1", null, 1), //
                repoTree("highways", "a2", "d1", 2)//
        );
        rightTree = createStageHeadTree(//
        repoTree("roads", "a1", null, 1)
        // deleted highways
        );

        MapDifference<String, NodeRef> difference;
        Set<String> onlyOnNewTree;
        Set<String> onlyStaged;

        difference = runWithPathFilter(leftTree, rightTree, "highways/highways.1");
        onlyOnNewTree = difference.entriesOnlyOnLeft().keySet();
        onlyStaged = difference.entriesOnlyOnRight().keySet();
        Set<String> differing = difference.entriesDiffering().keySet();
        Set<String> inCommon = difference.entriesInCommon().keySet();

        assertEquals(set("roads", "roads/roads.0"), inCommon);
        assertEquals(set("highways", "highways/highways.0"), onlyOnNewTree);
        assertEquals(set(), differing);
        assertEquals(set(), onlyStaged);
    }

    @Test
    public void testFilteredAddsFirstCommit() {
        leftTree = createHeadTree();
        rightTree = createStageHeadTree(//
                repoTree("points", "a1", null, 3), //
                repoTree("lines", "b1", null, 2) //
        );

        MapDifference<String, NodeRef> difference;
        Set<String> onlyOnLeft;
        Set<String> onlyOnRight;
        Set<String> entriesDiffering;
        Set<String> entriesInCommon;

        difference = runWithPathFilter(leftTree, rightTree, "points/points.1", "points/points.2");
        onlyOnLeft = difference.entriesOnlyOnLeft().keySet();
        onlyOnRight = difference.entriesOnlyOnRight().keySet();
        entriesDiffering = difference.entriesDiffering().keySet();
        entriesInCommon = difference.entriesInCommon().keySet();

        assertEquals(set(), onlyOnLeft);
        assertEquals(set("points"), entriesDiffering);
        assertEquals(set("points/points.0", "lines", "lines/lines.0", "lines/lines.1"), onlyOnRight);
        assertEquals(set("points/points.1", "points/points.2"), entriesInCommon);

        difference = runWithPathFilter(leftTree, rightTree, "lines/lines.1", "badFilter");
        onlyOnLeft = difference.entriesOnlyOnLeft().keySet();
        onlyOnRight = difference.entriesOnlyOnRight().keySet();
        entriesDiffering = difference.entriesDiffering().keySet();
        entriesInCommon = difference.entriesInCommon().keySet();

        assertEquals(set(), onlyOnLeft);
        assertEquals(
                set("lines/lines.0", "points", "points/points.0", "points/points.1",
                        "points/points.2"), onlyOnRight);
        assertEquals(set("lines"), entriesDiffering);
        assertEquals(set("lines/lines.1"), entriesInCommon);
    }

    /**
     * @return the differences between the given right tree(staged) and the resulting tree after
     *         running {@link WriteTree2} with the given filters. The result's left refs are the
     *         ones in the new tree, and the right refs the same as given in the rightTree
     */
    private MapDifference<String, NodeRef> runWithPathFilter(RevTree leftTree, RevTree rightTree,
            String... filters) {

        // print(leftTree.getId());
        // print(rightTree.getId());

        final ObjectId newRepoRoot = command.setPathFilter(Arrays.asList(filters)).call();
        assertNotNull(newRepoRoot);
        // print(newRepoRoot);

        // check all blobs have been moved from the index to the object database
        verifyRepositoryTree(NodeRef.ROOT, newRepoRoot);

        final boolean includeFeatures = true;
        ImmutableMap<String, NodeRef> stagedRefs = getRefsByPath(rightTree.getId(), includeFeatures);

        ImmutableMap<String, NodeRef> resultRefs = getRefsByPath(newRepoRoot, includeFeatures);

        MapDifference<String, NodeRef> difference = Maps.difference(resultRefs, stagedRefs);

        return difference;
    }

    private ImmutableMap<String, NodeRef> getTreeRefsByPath(ObjectId newRepoRoot) {
        Iterator<NodeRef> iterator = geogig.command(LsTreeOp.class)
                .setReference(newRepoRoot.toString()).setStrategy(Strategy.DEPTHFIRST_ONLY_TREES)
                .call();
        Function<NodeRef, String> keyFunction = new Function<NodeRef, String>() {
            @Override
            public String apply(NodeRef input) {
                return input.path();
            }
        };
        ImmutableMap<String, NodeRef> refsByPath = Maps.uniqueIndex(iterator, keyFunction);
        return refsByPath;
    }

    private ImmutableMap<String, NodeRef> getRefsByPath(ObjectId repoRoot, boolean includeFeatures) {

        Strategy strategy = includeFeatures ? Strategy.DEPTHFIRST : Strategy.DEPTHFIRST_ONLY_TREES;
        Iterator<NodeRef> iterator = geogig.command(LsTreeOp.class)
                .setReference(repoRoot.toString()).setStrategy(strategy).call();
        Function<NodeRef, String> keyFunction = new Function<NodeRef, String>() {
            @Override
            public String apply(NodeRef input) {
                return input.path();
            }
        };
        ImmutableMap<String, NodeRef> refsByPath = Maps.uniqueIndex(iterator, keyFunction);
        return refsByPath;
    }

    private void print(ObjectId treeId) {
        System.err.println(treeId);
        Iterator<NodeRef> iterator = geogig.command(LsTreeOp.class).setReference(treeId.toString())
                .setStrategy(Strategy.DEPTHFIRST).call();
        while (iterator.hasNext()) {
            print(iterator.next());
        }
    }

    private void print(NodeRef ref) {
        System.err.printf("\t%s '%s' -> %s (%s)\n", ref.getType().toString().charAt(0), ref.path(),
                ref.objectId(), ref.getNode().getMetadataId());
    }

    private void verifyRepositoryTree(String path, ObjectId repoTreeId) {
        ObjectDatabase objectDb = this.objectDb;

        verifyTree(objectDb, path, repoTreeId);
    }

    private void verifyTree(ObjectDatabase objectDb, String path, ObjectId repoTreeId) {
        assertTrue(String.format("tree '%s' (%s) is not present", path, repoTreeId),
                objectDb.exists(repoTreeId));

        RevTree tree = objectDb.getTree(repoTreeId);

        Iterator<Node> children = tree.children();
        while (children.hasNext()) {
            final Node node = children.next();
            if (TYPE.TREE.equals(node.getType())) {
                path = NodeRef.appendChild(path, node.getName());
                ObjectId objectId = node.getObjectId();
                verifyRepositoryTree(path, objectId);
            } else if (TYPE.FEATURE.equals(node.getType())) {
                verifyFeature(node);
            } else {
                throw new IllegalStateException(node.getType().toString());
            }
            verifyMetadata(node);
        }
        if (tree.buckets().isPresent()) {
            ImmutableCollection<Bucket> buckets = tree.buckets().get().values();
            for (Bucket b : buckets) {
                ObjectId bucketTreeId = b.id();
                verifyRepositoryTree(path + "/" + bucketTreeId.toString().substring(0, 8),
                        bucketTreeId);
            }
        }
    }

    private void verifyFeature(Node node) {
        ObjectId objectId = node.getObjectId();
        assertTrue("feature " + node.getName() + " -> " + objectId + " is not present in objectDb",
                objectDb.exists(objectId));
    }

    private void verifyMetadata(Node node) {
        if (node.getMetadataId().isPresent()) {
            ObjectId mdId = node.getMetadataId().get();
            String msg = "RevFeatureType " + mdId + " is not present (from " + node.getName() + ")";
            assertTrue(msg, objectDb.exists(mdId));
        }
    }

    private RevTree createHeadTree(NodeRef... treeRefs) {
        RevTree root = createFromRefs(objectDb, treeRefs);
        objectDb.put(root);
        CommitBuilder cb = new CommitBuilder(geogig.getPlatform());
        ObjectId treeId = root.getId();

        RevCommit commit = cb.setTreeId(treeId).setCommitter("Gabriel Roldan")
                .setAuthor("Gabriel Roldan").build();
        objectDb.put(commit);

        SymRef head = (SymRef) geogig.command(RefParse.class).setName(Ref.HEAD).call().get();
        final String currentBranch = head.getTarget();

        geogig.command(UpdateRef.class).setName(currentBranch).setNewValue(commit.getId()).call();

        verifyRepositoryTree(NodeRef.ROOT, treeId);
        verifyTreeStructure(treeId, treeRefs);

        return root;
    }

    private void verifyTreeStructure(ObjectId treeId, NodeRef... treeRefs) {
        Function<NodeRef, String> function = new Function<NodeRef, String>() {
            @Override
            public String apply(NodeRef input) {
                return input.path();
            }
        };
        Set<String> expectedPaths = ImmutableSet.copyOf(Iterables.transform(
                Arrays.asList(treeRefs), function));

        ImmutableMap<String, NodeRef> refs = getTreeRefsByPath(treeId);

        assertEquals(expectedPaths, refs.keySet());
    }

    private RevTree createStageHeadTree(NodeRef... treeRefs) {
        RevTree root = createFromRefs(indexDb, treeRefs);
        geogig.command(UpdateRef.class).setName(Ref.STAGE_HEAD).setNewValue(root.getId()).call();
        return root;
    }

    private RevTree createFromRefs(ObjectDatabase targetDb, NodeRef... treeRefs) {
        MutableTree mutableTree = MutableTree.createFromRefs(RevTree.EMPTY_TREE_ID, treeRefs);
        RevTree tree = mutableTree.build(indexDb, targetDb);
        return tree;
    }

    private NodeRef indexTree(String path, String id, String mdId, int numFeatures) {
        return tree(indexDb, path, id, mdId, numFeatures);
    }

    private NodeRef repoTree(String path, String id, String mdId, int numFeatures) {
        return tree(objectDb, path, id, mdId, numFeatures);
    }

    /**
     * Creates a tree reference for testing, forcing the specified id and metadata id, and with the
     * specified number of features (zero or more).
     * <p>
     * Note the tree is saved to the specified database only if its a leaf tree (more than zero
     * features), in order for the {@link #createFromRefs} method to be able of saving the parent
     */
    private NodeRef tree(ObjectDatabase db, String path, String id, String mdId, int numFeatures) {
        Preconditions.checkArgument(numFeatures != 0 || EMPTY_ID.equals(id),
                "for zero features trees use RevTree.EMPTY_TREE_ID");
        final ObjectId treeId = id(id);
        final ObjectId metadataId = id(mdId);
        final String feturePrefix = NodeRef.nodeFromPath(path);
        RevTreeBuilder b = new RevTreeBuilder(db);
        if (numFeatures > 0) {
            for (int i = 0; i < numFeatures; i++) {
                Node fn = feature(db, feturePrefix, i);
                b.put(fn);
            }
        }
        RevTree fakenId = forceTreeId(b, treeId);
        if (!db.exists(fakenId.getId())) {
            db.put(fakenId);
        }
        if (!metadataId.isNull()) {
            RevFeatureType fakeType = new RevFeatureTypeImpl(metadataId, pointsType);
            if (!db.exists(fakeType.getId())) {
                db.put(fakeType);
            }
        }

        String name = NodeRef.nodeFromPath(path);
        String parent = NodeRef.parentPath(path);

        Envelope bounds = SpatialOps.boundsOf(fakenId);
        Node node = Node.create(name, treeId, metadataId, TYPE.TREE, bounds);
        return new NodeRef(node, parent, ObjectId.NULL);
    }

    private RevTree forceTreeId(RevTreeBuilder b, ObjectId treeId) {
        RevTree tree = b.build();
        RevTree fakenId = RevTreeImpl.create(treeId, tree.size(), tree);
        return fakenId;
    }

    private String point(int i) {
        return "POINT(" + i + " " + i + ")";
    }

    private Node feature(ObjectDatabase db, String idPrefix, int index) {
        final String id = idPrefix + "." + index;
        final Feature feature;
        try {
            feature = super.feature(pointsType, id, id, index, point(index));
        } catch (ParseException e) {
            throw Throwables.propagate(e);
        }
        RevFeature revFeature = RevFeatureBuilder.build(feature);
        db.put(revFeature);
        Envelope bounds = (Envelope) feature.getBounds();
        return Node.create(id, revFeature.getId(), ObjectId.NULL, TYPE.FEATURE, bounds);
    }

    private static ObjectId id(@Nullable String partialHash) {
        if (partialHash == null) {
            return ObjectId.NULL;
        }
        partialHash = Strings.padEnd(partialHash, 2 * ObjectId.NUM_BYTES, '0');
        return ObjectId.valueOf(partialHash);
    }

    private ImmutableSet<String> set(String... contents) {
        if (contents == null) {
            return ImmutableSet.of();
        }
        return ImmutableSet.copyOf(contents);
    }
}
TOP

Related Classes of org.locationtech.geogig.api.plumbing.WriteTree2Test

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.