/* 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.geotools.data;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.NameImpl;
import org.geotools.geometry.jts.GeometryBuilder;
import org.junit.Test;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.Ref;
import org.locationtech.geogig.api.plumbing.LsTreeOp;
import org.locationtech.geogig.api.plumbing.LsTreeOp.Strategy;
import org.locationtech.geogig.api.porcelain.BranchCreateOp;
import org.locationtech.geogig.api.porcelain.CommitOp;
import org.locationtech.geogig.geotools.data.GeoGigDataStore.ChangeType;
import org.locationtech.geogig.test.integration.RepositoryTestCase;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import com.google.common.base.Function;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
public class GeoGigDataStoreTest extends RepositoryTestCase {
private GeoGigDataStore dataStore;
@Override
protected void setUpInternal() throws Exception {
dataStore = new GeoGigDataStore(geogig);
}
@Override
protected void tearDownInternal() throws Exception {
dataStore.dispose();
dataStore = null;
}
@Test
public void testDispose() {
assertTrue(geogig.isOpen());
dataStore.dispose();
assertFalse(geogig.isOpen());
}
private List<String> getTypeNames(String head) {
Iterator<NodeRef> typeTrees = geogig.command(LsTreeOp.class)
.setStrategy(Strategy.TREES_ONLY).setReference(head).call();
List<String> typeNames = Lists.newArrayList(Iterators.transform(typeTrees,
new Function<NodeRef, String>() {
@Override
public String apply(NodeRef input) {
return input.name();
}
}));
return typeNames;
}
@Test
public void testCreateSchema() throws IOException {
final SimpleFeatureType featureType = super.linesType;
dataStore.createSchema(featureType);
List<String> typeNames;
typeNames = getTypeNames(Ref.HEAD);
assertEquals(1, typeNames.size());
assertEquals(linesName, typeNames.get(0));
dataStore.createSchema(super.pointsType);
typeNames = getTypeNames(Ref.HEAD);
assertEquals(2, typeNames.size());
assertTrue(typeNames.contains(linesName));
assertTrue(typeNames.contains(pointsName));
try {
dataStore.createSchema(super.pointsType);
fail("Expected IOException on existing type");
} catch (IOException e) {
assertTrue(e.getMessage().contains("already exists"));
}
}
@Test
public void testCreateSchemaOnBranch() throws IOException {
final String branchName = "testBranch";
geogig.command(BranchCreateOp.class).setName(branchName).setOrphan(true).call();
dataStore.setHead(branchName);
final SimpleFeatureType featureType = super.linesType;
dataStore.createSchema(featureType);
List<String> typeNames;
typeNames = getTypeNames(Ref.HEAD);
assertTrue(typeNames.isEmpty());
typeNames = getTypeNames(branchName);
assertEquals(1, typeNames.size());
assertEquals(linesName, typeNames.get(0));
dataStore.createSchema(super.pointsType);
typeNames = getTypeNames(Ref.HEAD);
assertTrue(typeNames.isEmpty());
typeNames = getTypeNames(branchName);
assertEquals(2, typeNames.size());
assertTrue(typeNames.contains(linesName));
assertTrue(typeNames.contains(pointsName));
}
@Test
public void testGetNames() throws Exception {
assertEquals(0, dataStore.getNames().size());
insertAndAdd(points1);
assertEquals(0, dataStore.getNames().size());
commit();
assertEquals(1, dataStore.getNames().size());
insertAndAdd(lines1);
assertEquals(1, dataStore.getNames().size());
commit();
assertEquals(2, dataStore.getNames().size());
List<Name> names = dataStore.getNames();
// ContentDataStore doesn't support native namespaces
// assertTrue(names.contains(RepositoryTestCase.linesTypeName));
// assertTrue(names.contains(RepositoryTestCase.pointsTypeName));
assertTrue(names.contains(new NameImpl(pointsName)));
assertTrue(names.contains(new NameImpl(linesName)));
}
@Test
public void testGetTypeNames() throws Exception {
assertEquals(0, dataStore.getTypeNames().length);
insertAndAdd(lines1);
assertEquals(0, dataStore.getTypeNames().length);
commit();
assertEquals(1, dataStore.getTypeNames().length);
insertAndAdd(points1);
assertEquals(1, dataStore.getTypeNames().length);
commit();
assertEquals(2, dataStore.getTypeNames().length);
List<String> simpleNames = Arrays.asList(dataStore.getTypeNames());
assertTrue(simpleNames.contains(linesName));
assertTrue(simpleNames.contains(pointsName));
}
@Test
public void testGetSchemaName() throws Exception {
try {
dataStore.getSchema(RepositoryTestCase.linesTypeName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(e.getMessage(), e.getMessage().contains("does not exist"));
}
insertAndAdd(lines1);
try {
dataStore.getSchema(RepositoryTestCase.linesTypeName);
fail("Expected IOE as type hasn't been committed");
} catch (IOException e) {
assertTrue(e.getMessage().contains("does not exist"));
}
commit();
SimpleFeatureType lines = dataStore.getSchema(RepositoryTestCase.linesTypeName);
assertEquals(super.linesType, lines);
try {
dataStore.getSchema(RepositoryTestCase.pointsTypeName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(true);
}
insertAndAdd(points1);
commit();
SimpleFeatureType points = dataStore.getSchema(RepositoryTestCase.pointsTypeName);
assertEquals(super.pointsType, points);
}
private ObjectId commit() {
org.locationtech.geogig.api.RevCommit c = geogig.command(CommitOp.class).call();
return c.getId();
}
@Test
public void testGetSchemaProvidedNamespace() throws Exception {
String namespace = "http://www.geogig.org/test";
dataStore.setNamespaceURI(namespace);
insertAndAdd(lines1);
commit();
SimpleFeatureType lines = dataStore.getSchema(RepositoryTestCase.linesTypeName);
Name expectedName = new NameImpl(namespace, linesName);
assertEquals(expectedName, lines.getName());
assertEquals(super.linesType.getAttributeDescriptors(), lines.getAttributeDescriptors());
insertAndAdd(points1);
commit();
SimpleFeatureType points = dataStore.getSchema(RepositoryTestCase.pointsTypeName);
assertEquals(new NameImpl(namespace, pointsName), points.getName());
assertEquals(super.pointsType.getAttributeDescriptors(), points.getAttributeDescriptors());
}
@Test
public void testGetSchemaString() throws Exception {
try {
dataStore.getSchema(RepositoryTestCase.linesName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(true);
}
insertAndAdd(lines1);
commit();
SimpleFeatureType lines = dataStore.getSchema(RepositoryTestCase.linesName);
assertEquals(super.linesType.getAttributeDescriptors(), lines.getAttributeDescriptors());
try {
dataStore.getSchema(RepositoryTestCase.pointsName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(true);
}
insertAndAdd(points1);
commit();
SimpleFeatureType points = dataStore.getSchema(RepositoryTestCase.pointsName);
assertEquals(super.pointsType, points);
}
@Test
public void testGetFeatureSourceName() throws Exception {
try {
dataStore.getFeatureSource(RepositoryTestCase.linesTypeName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(true);
}
SimpleFeatureSource source;
insertAndAdd(lines1);
try {
dataStore.getFeatureSource(RepositoryTestCase.linesTypeName);
fail("Expected IOE as feature typ is not committed yet");
} catch (IOException e) {
assertTrue(e.getMessage().contains("does not exist"));
}
commit();
source = dataStore.getFeatureSource(RepositoryTestCase.linesTypeName);
assertTrue(source instanceof GeogigFeatureStore);
try {
dataStore.getFeatureSource(RepositoryTestCase.pointsTypeName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(true);
}
insertAndAdd(points1);
commit();
source = dataStore.getFeatureSource(RepositoryTestCase.pointsTypeName);
assertTrue(source instanceof GeogigFeatureStore);
}
@Test
public void testGetFeatureSourceString() throws Exception {
try {
dataStore.getFeatureSource(RepositoryTestCase.linesName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(true);
}
SimpleFeatureSource source;
insertAndAdd(lines1);
commit();
source = dataStore.getFeatureSource(RepositoryTestCase.linesName);
assertTrue(source instanceof GeogigFeatureStore);
try {
dataStore.getFeatureSource(RepositoryTestCase.pointsName);
fail("Expected IOException");
} catch (IOException e) {
assertTrue(true);
}
insertAndAdd(points1);
commit();
source = dataStore.getFeatureSource(RepositoryTestCase.pointsName);
assertTrue(source instanceof GeogigFeatureStore);
}
@Test
public void testFeatureWriterAppend() throws Exception {
dataStore.createSchema(linesType);
Transaction tx = new DefaultTransaction();
FeatureWriter<SimpleFeatureType, SimpleFeature> fw = dataStore.getFeatureWriterAppend(
linesTypeName.getLocalPart(), tx);
LineString line = new GeometryBuilder().lineString(0, 0, 1, 1);
SimpleFeature f = (SimpleFeature) fw.next();
f.setAttribute("sp", "foo");
f.setAttribute("ip", 10);
f.setAttribute("pp", line);
fw.write();
fw.close();
tx.commit();
FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore
.getFeatureSource(linesTypeName);
assertEquals(1, source.getCount(null));
FeatureReader<SimpleFeatureType, SimpleFeature> r = dataStore.getFeatureReader(new Query(
linesTypeName.getLocalPart()), Transaction.AUTO_COMMIT);
assertTrue(r.hasNext());
f = r.next();
assertEquals("foo", f.getAttribute("sp"));
assertEquals(10, f.getAttribute("ip"));
assertTrue(line.equals((Geometry) f.getAttribute("pp")));
}
@Test
public void testGetDiffFeatureSource() throws Exception {
insertAndAdd(points1);
insertAndAdd(lines1, lines2);
final ObjectId c1 = commit();
insertAndAdd(points2);
final ObjectId c2 = commit();
deleteAndAdd(points2);
final ObjectId c3 = commit();
insertAndAdd(points1_modified);
final ObjectId c4 = commit();
testDiffFeatures(ObjectId.NULL, c1, 1/* added */, 0/* removed */, 0/* modified */);
testDiffFeatures(c1, c2, 1, 0, 0);
testDiffFeatures(c2, c1, 0, 1, 0);
testDiffFeatures(ObjectId.NULL, c2, 2, 0, 0);
testDiffFeatures(c3, c4, 0, 0, 1);
testDiffFeatures(c4, c3, 0, 0, 1);
testDiffFeatures(c2, c4, 0, 1, 1);
testDiffFeatures(c4, c2, 1, 0, 1);
}
private void testDiffFeatures(ObjectId oldRoot, ObjectId newRoot, int expectedAdded,
int expectedRemoved, int expectedChanged) throws IOException {
dataStore.setHead(newRoot.toString());
List<String> fids;
SimpleFeatureCollection features;
ChangeType changeType = ChangeType.ADDED;
features = dataStore.getDiffFeatureSource(pointsName, oldRoot.toString(), changeType)
.getFeatures();
fids = toIdList(features);
assertEquals(changeType + fids.toString(), expectedAdded, fids.size());
assertEquals(changeType + fids.toString(), expectedAdded, features.size());
changeType = ChangeType.REMOVED;
features = dataStore.getDiffFeatureSource(pointsName, oldRoot.toString(), changeType)
.getFeatures();
fids = toIdList(features);
assertEquals(changeType + fids.toString(), expectedRemoved, fids.size());
assertEquals(changeType + fids.toString(), expectedRemoved, features.size());
changeType = ChangeType.CHANGED_NEW;
features = dataStore.getDiffFeatureSource(pointsName, oldRoot.toString(), changeType)
.getFeatures();
fids = toIdList(features);
assertEquals(changeType + fids.toString(), expectedChanged, fids.size());
assertEquals(changeType + fids.toString(), expectedChanged, features.size());
changeType = ChangeType.CHANGED_OLD;
features = dataStore.getDiffFeatureSource(pointsName, oldRoot.toString(), changeType)
.getFeatures();
fids = toIdList(features);
assertEquals(changeType + fids.toString(), expectedChanged, fids.size());
assertEquals(changeType + fids.toString(), expectedChanged, features.size());
}
private List<String> toIdList(SimpleFeatureCollection features) {
List<SimpleFeature> list = toList(features);
return Lists.transform(list, new Function<SimpleFeature, String>() {
@Override
public String apply(SimpleFeature f) {
return f.getID();
}
});
}
private List<SimpleFeature> toList(SimpleFeatureCollection features) {
List<SimpleFeature> list = new ArrayList<>();
try (SimpleFeatureIterator it = features.features()) {
while (it.hasNext()) {
list.add(it.next());
}
}
return list;
}
}