/* 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:
* Juan Marin (Boundless) - initial implementation
*/
package org.locationtech.geogig.geotools.plumbing;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.CRS;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.locationtech.geogig.api.Context;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevFeature;
import org.locationtech.geogig.api.RevFeatureType;
import org.locationtech.geogig.api.RevObject;
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.ResolveFeatureType;
import org.locationtech.geogig.api.plumbing.RevObjectParse;
import org.locationtech.geogig.api.porcelain.AddOp;
import org.locationtech.geogig.geotools.cli.porcelain.TestHelper;
import org.locationtech.geogig.repository.WorkingTree;
import org.locationtech.geogig.test.integration.RepositoryTestCase;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
public class ImportOpTest extends RepositoryTestCase {
@Rule
public ExpectedException exception = ExpectedException.none();
@Test
public void testNullDataStore() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setTable("table1");
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testNullTableNotAll() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createEmptyTestFactory().createDataStore(null));
importOp.setAll(false);
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testEmptyTableNotAll() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setTable("");
importOp.setAll(false);
importOp.setDataStore(TestHelper.createEmptyTestFactory().createDataStore(null));
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testEmptyTableAndAll() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setTable("");
importOp.setAll(true);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.call();
}
@Test
public void testTableAndAll() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setTable("table1");
importOp.setAll(true);
importOp.setDataStore(TestHelper.createEmptyTestFactory().createDataStore(null));
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testTableNotFound() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createEmptyTestFactory().createDataStore(null));
importOp.setAll(false);
importOp.setTable("table1");
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testNoFeaturesFound() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createEmptyTestFactory().createDataStore(null));
importOp.setAll(true);
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testTypeNameException() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createFactoryWithGetNamesException().createDataStore(null));
importOp.setAll(false);
importOp.setTable("table1");
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testGetFeatureSourceException() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createFactoryWithGetFeatureSourceException()
.createDataStore(null));
importOp.setAll(false);
importOp.setTable("table1");
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testImportTable() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setAll(false);
importOp.setTable("table1");
RevTree newWorkingTree = importOp.call();
Optional<NodeRef> ref = geogig.command(FindTreeChild.class).setParent(newWorkingTree)
.setChildPath("table1/feature1").setIndex(true).call();
assertTrue(ref.isPresent());
ref = geogig.command(FindTreeChild.class).setParent(newWorkingTree)
.setChildPath("table1/feature2").setIndex(true).call();
assertTrue(ref.isPresent());
}
@Test
public void testImportTableWithNoFeatures() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setAll(false);
importOp.setTable("table4");
importOp.call();
geogig.command(AddOp.class).call();
Optional<RevObject> ft = geogig.command(RevObjectParse.class)
.setRefSpec("WORK_HEAD:table4").call();
assertTrue(ft.isPresent());
}
@Test
public void testImportAll() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setAll(true);
RevTree newWorkingTree = importOp.call();
Optional<NodeRef> ref = geogig.command(FindTreeChild.class).setParent(newWorkingTree)
.setChildPath("table1/feature1").setIndex(true).call();
assertTrue(ref.isPresent());
ref = geogig.command(FindTreeChild.class).setParent(newWorkingTree)
.setChildPath("table1/feature2").setIndex(true).call();
assertTrue(ref.isPresent());
ref = geogig.command(FindTreeChild.class).setParent(newWorkingTree)
.setChildPath("table2/feature3").setIndex(true).call();
assertTrue(ref.isPresent());
}
@Test
public void testImportAllWithDifferentFeatureTypesAndDestPath() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setAll(true);
importOp.setDestinationPath("dest");
importOp.setAdaptToDefaultFeatureType(false);
importOp.call();
Iterator<NodeRef> features = geogig.command(LsTreeOp.class)
.setStrategy(Strategy.DEPTHFIRST_ONLY_FEATURES).call();
ArrayList<NodeRef> list = Lists.newArrayList(features);
assertEquals(4, list.size());
TreeSet<ObjectId> set = Sets.newTreeSet();
for (NodeRef node : list) {
set.add(node.getMetadataId());
}
assertEquals(4, set.size());
for (ObjectId metadataId : set) {
Optional<RevFeatureType> ft = geogig.command(RevObjectParse.class)
.setObjectId(metadataId).call(RevFeatureType.class);
assertTrue(ft.isPresent());
assertEquals("dest", ft.get().getName().getLocalPart());
}
}
@Test
public void testImportAllWithDifferentFeatureTypesAndDestPathAndAdd() throws Exception {
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setCRS(CRS.decode("EPSG:4326"));
builder.add("geom", Point.class);
builder.add("label", String.class);
builder.setName("dest");
SimpleFeatureType type = builder.buildFeatureType();
GeometryFactory gf = new GeometryFactory();
SimpleFeature feature = SimpleFeatureBuilder.build(type,
new Object[] { gf.createPoint(new Coordinate(0, 0)), "feature0" }, "feature");
geogig.getRepository().workingTree().insert("dest", feature);
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setAll(true);
importOp.setOverwrite(false);
importOp.setDestinationPath("dest");
importOp.setAdaptToDefaultFeatureType(false);
importOp.call();
Iterator<NodeRef> features = geogig.command(LsTreeOp.class)
.setStrategy(Strategy.DEPTHFIRST_ONLY_FEATURES).call();
ArrayList<NodeRef> list = Lists.newArrayList(features);
assertEquals(5, list.size());
TreeSet<ObjectId> set = Sets.newTreeSet();
ArrayList<RevFeatureType> ftlist = new ArrayList<RevFeatureType>();
for (NodeRef node : list) {
Optional<RevFeatureType> ft = geogig.command(RevObjectParse.class)
.setObjectId(node.getMetadataId()).call(RevFeatureType.class);
assertTrue(ft.isPresent());
ftlist.add(ft.get());
set.add(node.getMetadataId());
}
assertEquals(4, set.size());
}
@Test
public void testAddUsingOriginalFeatureType() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setTable("table1");
importOp.call();
importOp.setTable("table2");
importOp.setAdaptToDefaultFeatureType(false);
importOp.setDestinationPath("table1");
importOp.setOverwrite(false);
importOp.call();
Iterator<NodeRef> features = geogig.command(LsTreeOp.class)
.setStrategy(Strategy.DEPTHFIRST_ONLY_FEATURES).call();
ArrayList<NodeRef> list = Lists.newArrayList(features);
assertEquals(3, list.size());
TreeSet<ObjectId> set = Sets.newTreeSet();
for (NodeRef node : list) {
set.add(node.getMetadataId());
}
assertEquals(2, set.size());
}
@Test
public void testAlter() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setTable("table1");
importOp.call();
importOp.setTable("table2");
importOp.setDestinationPath("table1");
importOp.setAlter(true);
importOp.call();
Iterator<NodeRef> features = geogig.command(LsTreeOp.class)
.setStrategy(Strategy.DEPTHFIRST_ONLY_FEATURES).call();
ArrayList<NodeRef> list = Lists.newArrayList(features);
assertEquals(3, list.size());
Optional<RevFeature> feature = geogig.command(RevObjectParse.class)
.setRefSpec("WORK_HEAD:table1/feature1").call(RevFeature.class);
assertTrue(feature.isPresent());
ImmutableList<Optional<Object>> values = feature.get().getValues();
assertEquals(2, values.size());
assertTrue(values.get(0).isPresent());
assertFalse(values.get(1).isPresent());
TreeSet<ObjectId> set = Sets.newTreeSet();
for (NodeRef node : list) {
set.add(node.getMetadataId());
}
assertEquals(1, set.size());
Optional<RevFeatureType> featureType = geogig.command(RevObjectParse.class)
.setObjectId(set.iterator().next()).call(RevFeatureType.class);
assertTrue(featureType.isPresent());
assertEquals("table1", featureType.get().getName().getLocalPart());
assertEquals("name", featureType.get().sortedDescriptors().get(1).getName().getLocalPart());
}
@Test
public void testImportWithOverriddenGeomName() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setTable("table1");
importOp.setGeometryNameOverride("my_geom_name");
importOp.call();
Iterator<NodeRef> features = geogig.command(LsTreeOp.class)
.setStrategy(Strategy.DEPTHFIRST_ONLY_FEATURES).call();
ArrayList<NodeRef> list = Lists.newArrayList(features);
assertEquals(2, list.size());
Optional<RevFeatureType> featureType = geogig.command(RevObjectParse.class)
.setObjectId(list.get(0).getMetadataId()).call(RevFeatureType.class);
assertTrue(featureType.isPresent());
assertEquals("table1", featureType.get().getName().getLocalPart());
assertEquals("my_geom_name", featureType.get().sortedDescriptors().get(0).getName()
.getLocalPart());
}
@Test
public void testImportWithOverriddenGeomNameAlredyInUse() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setTable("table1");
importOp.setGeometryNameOverride("label");
try {
importOp.call();
fail("Should throw exception complaining of parameter name already in use");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().startsWith("The provided geom name is already in use"));
}
}
@Test
public void testImportWithFid() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setTable("table3");
importOp.setDestinationPath("table3");
importOp.setFidAttribute("number");
importOp.call();
Optional<RevFeature> feature = geogig.command(RevObjectParse.class)
.setRefSpec("WORK_HEAD:table3/1000").call(RevFeature.class);
assertTrue(feature.isPresent());
}
@Test
public void testDeleteException() throws Exception {
WorkingTree workTree = mock(WorkingTree.class);
Context cmdl = mock(Context.class);
when(cmdl.workingTree()).thenReturn(workTree);
doThrow(new RuntimeException("Exception")).when(workTree).delete(any(String.class));
ImportOp importOp = new ImportOp();
importOp.setContext(cmdl);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setAll(true);
exception.expect(GeoToolsOpException.class);
importOp.call();
}
@Test
public void testAdaptFeatureType() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setTable("shpLikeTable");
importOp.setDestinationPath("table");
importOp.call();
Optional<RevFeature> feature = geogig.command(RevObjectParse.class)
.setRefSpec("WORK_HEAD:table/feature1").call(RevFeature.class);
assertTrue(feature.isPresent());
RevFeatureType originalFeatureType = geogig.command(ResolveFeatureType.class)
.setRefSpec("WORK_HEAD:table/feature1").call().get();
importOp.setTable("shpLikeTable2");
importOp.call();
feature = geogig.command(RevObjectParse.class).setRefSpec("WORK_HEAD:table/feature1")
.call(RevFeature.class);
assertTrue(feature.isPresent());
RevFeatureType featureType = geogig.command(ResolveFeatureType.class)
.setRefSpec("WORK_HEAD:table/feature1").call().get();
assertEquals(originalFeatureType.getId(), featureType.getId());
GeometryFactory gf = new GeometryFactory();
ImmutableList<Optional<Object>> values = feature.get().getValues();
assertEquals(values.get(0).get(), gf.createPoint(new Coordinate(0, 7)));
assertEquals(values.get(1).get(), 3.2);
assertEquals(values.get(2).get(), 1100.0);
importOp.setTable("GeoJsonLikeTable");
importOp.call();
feature = geogig.command(RevObjectParse.class).setRefSpec("WORK_HEAD:table/feature1")
.call(RevFeature.class);
assertTrue(feature.isPresent());
featureType = geogig.command(ResolveFeatureType.class)
.setRefSpec("WORK_HEAD:table/feature1").call().get();
assertEquals(originalFeatureType.getId(), featureType.getId());
values = feature.get().getValues();
assertEquals(values.get(0).get(), gf.createPoint(new Coordinate(0, 8)));
assertEquals(values.get(1).get(), 4.2);
assertEquals(values.get(2).get(), 1200.0);
}
@Test
public void testCannotAdaptFeatureTypeIfCRSChanges() throws Exception {
ImportOp importOp = geogig.command(ImportOp.class);
importOp.setDataStore(TestHelper.createTestFactory().createDataStore(null));
importOp.setTable("GeoJsonLikeTable");
importOp.setDestinationPath("table");
importOp.call();
Optional<RevFeature> feature = geogig.command(RevObjectParse.class)
.setRefSpec("WORK_HEAD:table/feature1").call(RevFeature.class);
assertTrue(feature.isPresent());
importOp.setTable("GeoJsonLikeTable2");
try {
importOp.call();
fail();
} catch (GeoToolsOpException e) {
assertEquals(GeoToolsOpException.StatusCode.INCOMPATIBLE_FEATURE_TYPE, e.statusCode);
}
}
@Override
protected void setUpInternal() throws Exception {
}
}