diff --git a/pom.xml b/pom.xml
index f3d4e57..ba8fd7f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,6 +53,14 @@
1.0
+
+
+ bintray-fraunhoferiosb-Maven
+ bintray
+ http://dl.bintray.com/fraunhoferiosb/Maven
+
+
+
org.opengis.cite.teamengine
@@ -77,7 +85,7 @@
org.json
json
- 20090211
+ 20171018
org.apache.httpcomponents
@@ -88,6 +96,11 @@
joda-time
joda-time
+
+ de.fraunhofer.iosb.ilt
+ SensorThingsClient
+ 0.14
+
diff --git a/src/main/java/org/opengis/cite/sta10/SuiteFixtureListener.java b/src/main/java/org/opengis/cite/sta10/SuiteFixtureListener.java
index 41d5f42..e8ad8c1 100644
--- a/src/main/java/org/opengis/cite/sta10/SuiteFixtureListener.java
+++ b/src/main/java/org/opengis/cite/sta10/SuiteFixtureListener.java
@@ -65,7 +65,7 @@ void processSuiteParameters(ISuite suite) {
String iutParam = params.get(TestRunArg.IUT.toString());
- String response = checkServiceRootUri(iutParam);
+ String response = checkServiceRootUri(iutParam, params);
if (!response.equals("")) {
throw new IllegalArgumentException(
response);
@@ -100,7 +100,7 @@ void registerClientComponent(ISuite suite) {
* @param rootUri The root URL for the service under test
* @return If the root URL of the service is not compliant to SensorThings API, it will return the reason it is not compliant. Otherwise it returns empty String.
*/
- private String checkServiceRootUri(String rootUri) {
+ private String checkServiceRootUri(String rootUri, Map params) {
rootUri = rootUri.trim();
if (rootUri.lastIndexOf('/') == rootUri.length() - 1) {
rootUri = rootUri.substring(0, rootUri.length() - 1);
@@ -181,56 +181,55 @@ private String checkServiceRootUri(String rootUri) {
if (!nameUrl.equals(rootUri + "/Things")) {
return "The URL for Things in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("Things");
addedLinks.put(name, true);
break;
case "Locations":
if (!nameUrl.equals(rootUri + "/Locations")) {
return "The URL for Locations in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("Locations");
addedLinks.put(name, true);
break;
case "HistoricalLocations":
if (!nameUrl.equals(rootUri + "/HistoricalLocations")) {
return "The URL for HistoricalLocations in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("HistoricalLocations");
addedLinks.put(name, true);
break;
case "Datastreams":
if (!nameUrl.equals(rootUri + "/Datastreams")) {
return "The URL for Datastreams in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("Datastreams");
addedLinks.put(name, true);
break;
+ case "MultiDatastreams":
+ if (!nameUrl.equals(rootUri + "/MultiDatastreams")) {
+ return "The URL for Datastreams in Service Root URI is not compliant to SensorThings API.";
+ }
+ addedLinks.put(name, true);
+ params.put("hasMultiDatastream", "true");
+ break;
case "Sensors":
if (!nameUrl.equals(rootUri + "/Sensors")) {
return "The URL for Sensors in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("Sensors");
addedLinks.put(name, true);
break;
case "Observations":
if (!nameUrl.equals(rootUri + "/Observations")) {
return "The URL for Observations in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("Observations");
addedLinks.put(name, true);
break;
case "ObservedProperties":
if (!nameUrl.equals(rootUri + "/ObservedProperties")) {
return "The URL for ObservedProperties in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("ObservedProperties");
addedLinks.put(name, true);
break;
case "FeaturesOfInterest":
if (!nameUrl.equals(rootUri + "/FeaturesOfInterest")) {
return "The URL for FeaturesOfInterest in Service Root URI is not compliant to SensorThings API.";
}
- addedLinks.remove("FeaturesOfInterest");
addedLinks.put(name, true);
break;
case "MultiDatastreams":
diff --git a/src/main/java/org/opengis/cite/sta10/multiDatastreamExtension/EntityTypeMds.java b/src/main/java/org/opengis/cite/sta10/multiDatastreamExtension/EntityTypeMds.java
new file mode 100644
index 0000000..04075a5
--- /dev/null
+++ b/src/main/java/org/opengis/cite/sta10/multiDatastreamExtension/EntityTypeMds.java
@@ -0,0 +1,105 @@
+package org.opengis.cite.sta10.multiDatastreamExtension;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * List of entity types in SensorThings API.
+ */
+public enum EntityTypeMds {
+ THING("Thing", "Things"),
+ LOCATION("Location", "Locations"),
+ SENSOR("Sensor", "Sensors"),
+ OBSERVED_PROPERTY("ObservedProperty", "ObservedProperties"),
+ OBSERVATION("Observation", "Observations"),
+ DATASTREAM("Datastream", "Datastreams"),
+ MULTI_DATASTREAM("MultiDatastream", "MultiDatastreams"),
+ FEATURE_OF_INTEREST("FeatureOfInterest", "FeaturesOfInterest"),
+ HISTORICAL_LOCATION("HistoricalLocation", "HistoricalLocations");
+ public final String singular;
+ public final String plural;
+ private final List properties = new ArrayList<>();
+ private final List relations = new ArrayList<>();
+
+ private static final Map NAMES_MAP = new HashMap<>();
+ private static final Set NAMES_PLURAL = new HashSet<>();
+
+ static {
+ THING.addProperties("name", "description");
+ THING.addRelations(DATASTREAM.plural, MULTI_DATASTREAM.plural, HISTORICAL_LOCATION.plural, LOCATION.plural);
+
+ LOCATION.addProperties("name", "description", "encodingType", "location");
+ LOCATION.addRelations(HISTORICAL_LOCATION.plural, THING.plural);
+
+ SENSOR.addProperties("name", "description", "encodingType", "metadata");
+ SENSOR.addRelations(DATASTREAM.plural, MULTI_DATASTREAM.plural);
+
+ OBSERVED_PROPERTY.addProperties("name", "definition", "description");
+ OBSERVED_PROPERTY.addRelations(DATASTREAM.plural, MULTI_DATASTREAM.plural);
+
+ OBSERVATION.addProperties("phenomenonTime", "result", "resultTime");
+ OBSERVATION.addRelations(DATASTREAM.singular, MULTI_DATASTREAM.singular, FEATURE_OF_INTEREST.singular);
+
+ DATASTREAM.addProperties("name", "description", "unitOfMeasurement", "observationType");
+ DATASTREAM.addRelations(THING.singular, SENSOR.singular, OBSERVED_PROPERTY.singular, OBSERVATION.plural);
+
+ MULTI_DATASTREAM.addProperties("name", "description", "unitOfMeasurements", "observationType", "multiObservationDataTypes");
+ MULTI_DATASTREAM.addRelations(THING.singular, SENSOR.singular, OBSERVED_PROPERTY.plural, OBSERVATION.plural);
+
+ FEATURE_OF_INTEREST.addProperties("name", "description", "encodingType", "feature");
+ FEATURE_OF_INTEREST.addRelations(OBSERVATION.plural);
+
+ HISTORICAL_LOCATION.addProperties("time");
+ HISTORICAL_LOCATION.addRelations(THING.singular, LOCATION.plural);
+
+ for (EntityTypeMds entityType : EntityTypeMds.values()) {
+ NAMES_MAP.put(entityType.singular, entityType);
+ NAMES_MAP.put(entityType.plural, entityType);
+ NAMES_PLURAL.add(entityType.plural);
+ }
+ }
+
+ public static EntityTypeMds getForRelation(String relation) {
+ EntityTypeMds entityType = NAMES_MAP.get(relation);
+ if (entityType == null) {
+ throw new IllegalArgumentException("Unknown relation: " + relation);
+ }
+ return entityType;
+ }
+
+ public static boolean isPlural(String relation) {
+ return NAMES_PLURAL.contains(relation);
+ }
+
+ private EntityTypeMds(String singular, String plural) {
+ this.singular = singular;
+ this.plural = plural;
+ }
+
+ public String getRootEntitySet() {
+ return plural;
+ }
+
+ public List getRelations() {
+ return Collections.unmodifiableList(relations);
+ }
+
+ public List getProperties() {
+ return Collections.unmodifiableList(properties);
+ }
+
+ private void addProperties(String... properties) {
+ this.properties.addAll(Arrays.asList(properties));
+ }
+
+ private void addRelations(String... relations) {
+ this.relations.addAll(Arrays.asList(relations));
+ }
+
+}
diff --git a/src/main/java/org/opengis/cite/sta10/multiDatastreamExtension/MultiDatastreamTests.java b/src/main/java/org/opengis/cite/sta10/multiDatastreamExtension/MultiDatastreamTests.java
new file mode 100644
index 0000000..717a8a4
--- /dev/null
+++ b/src/main/java/org/opengis/cite/sta10/multiDatastreamExtension/MultiDatastreamTests.java
@@ -0,0 +1,567 @@
+package org.opengis.cite.sta10.multiDatastreamExtension;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import de.fraunhofer.iosb.ilt.sta.ServiceFailureException;
+import de.fraunhofer.iosb.ilt.sta.model.Datastream;
+import de.fraunhofer.iosb.ilt.sta.model.Entity;
+import de.fraunhofer.iosb.ilt.sta.model.Location;
+import de.fraunhofer.iosb.ilt.sta.model.MultiDatastream;
+import de.fraunhofer.iosb.ilt.sta.model.Observation;
+import de.fraunhofer.iosb.ilt.sta.model.ObservedProperty;
+import de.fraunhofer.iosb.ilt.sta.model.Sensor;
+import de.fraunhofer.iosb.ilt.sta.model.Thing;
+import de.fraunhofer.iosb.ilt.sta.model.ext.EntityList;
+import de.fraunhofer.iosb.ilt.sta.model.ext.UnitOfMeasurement;
+import de.fraunhofer.iosb.ilt.sta.service.SensorThingsService;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import org.geojson.Point;
+import org.opengis.cite.sta10.SuiteAttribute;
+import org.opengis.cite.sta10.util.EntityUtils;
+import org.opengis.cite.sta10.util.HTTPMethods;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.ISuite;
+import org.testng.ITestContext;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.internal.junit.ArrayComparisonFailure;
+
+/**
+ * Some odd tests.
+ *
+ * @author Hylke van der Schaaf
+ */
+public class MultiDatastreamTests {
+
+ /**
+ * The logger for this class.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(MultiDatastreamTests.class);
+ private static String rootUri;
+ private static SensorThingsService service;
+ private static final List THINGS = new ArrayList<>();
+ private static final List LOCATIONS = new ArrayList<>();
+ private static final List SENSORS = new ArrayList<>();
+ private static final List OBSERVED_PROPS = new ArrayList<>();
+ private static final List DATASTREAMS = new ArrayList<>();
+ private static final List MULTIDATASTREAMS = new ArrayList<>();
+ private static final List OBSERVATIONS = new ArrayList<>();
+
+ public MultiDatastreamTests() {
+ }
+
+ @BeforeClass()
+ public void setUp(ITestContext testContext) {
+ LOGGER.info("Setting up class.");
+ ISuite suite = testContext.getSuite();
+ Object obj = suite.getAttribute(SuiteAttribute.LEVEL.getName());
+ if ((null != obj)) {
+ Integer level = Integer.class.cast(obj);
+ Assert.assertTrue(level.intValue() >= 5,
+ "Conformance level 5 will not be checked since ics = " + level);
+ }
+
+ boolean hasMultiDatastream = suite.getXmlSuite().getParameter("hasMultiDatastream") != null;
+ Assert.assertTrue(hasMultiDatastream, "Conformance level 5 not checked since MultiDatastreams not listed in Service Root.");
+
+ rootUri = suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()).toString();
+ rootUri = rootUri.trim();
+ if (rootUri.lastIndexOf('/') == rootUri.length() - 1) {
+ rootUri = rootUri.substring(0, rootUri.length() - 1);
+ }
+ URL url;
+ try {
+ url = new URL(rootUri);
+ service = new SensorThingsService(url);
+ createEntities();
+ } catch (MalformedURLException | URISyntaxException ex) {
+ LOGGER.error("Failed to create service uri.", ex);
+ } catch (ServiceFailureException ex) {
+ LOGGER.error("Failed to create entities.", ex);
+ } catch (Exception ex) {
+ LOGGER.error("Unknown Exception.", ex);
+ }
+ }
+
+ @AfterClass
+ public void tearDown() {
+ LOGGER.info("tearing down class.");
+ try {
+ EntityUtils.deleteAll(service);
+ } catch (ServiceFailureException ex) {
+ LOGGER.error("Failed to clean database.", ex);
+ }
+ }
+
+ /**
+ * Creates some basic non-MultiDatastream entities.
+ *
+ * @throws ServiceFailureException
+ * @throws URISyntaxException
+ */
+ private static void createEntities() throws ServiceFailureException, URISyntaxException {
+ Location location = new Location("Location 1.0", "Location of Thing 1.", "application/vnd.geo+json", new Point(8, 51));
+ service.create(location);
+ LOCATIONS.add(location);
+
+ Thing thing = new Thing("Thing 1", "The first thing.");
+ thing.getLocations().add(location.withOnlyId());
+ service.create(thing);
+ THINGS.add(thing);
+
+ thing = new Thing("Thing 2", "The second thing.");
+ thing.getLocations().add(location.withOnlyId());
+ service.create(thing);
+ THINGS.add(thing);
+
+ Sensor sensor = new Sensor("Sensor 1", "The first sensor.", "text", "Some metadata.");
+ service.create(sensor);
+ SENSORS.add(sensor);
+
+ sensor = new Sensor("Sensor 2", "The second sensor.", "text", "Some metadata.");
+ service.create(sensor);
+ SENSORS.add(sensor);
+
+ ObservedProperty obsProp = new ObservedProperty("ObservedProperty 1", new URI("http://ucom.org/temperature"), "The temperature of the thing.");
+ service.create(obsProp);
+ OBSERVED_PROPS.add(obsProp);
+
+ obsProp = new ObservedProperty("ObservedProperty 2", new URI("http://ucom.org/humidity"), "The humidity of the thing.");
+ service.create(obsProp);
+ OBSERVED_PROPS.add(obsProp);
+
+ Datastream datastream = new Datastream("Datastream 1", "The temperature of thing 1, sensor 1.", "someType", new UnitOfMeasurement("degree celcius", "°C", "ucum:T"));
+ DATASTREAMS.add(datastream);
+ datastream.setThing(THINGS.get(0).withOnlyId());
+ datastream.setSensor(SENSORS.get(0).withOnlyId());
+ datastream.setObservedProperty(OBSERVED_PROPS.get(0).withOnlyId());
+ service.create(datastream);
+
+ datastream = new Datastream("Datastream 2", "The temperature of thing 2, sensor 2.", "someType", new UnitOfMeasurement("degree celcius", "°C", "ucum:T"));
+ DATASTREAMS.add(datastream);
+ datastream.setThing(THINGS.get(1).withOnlyId());
+ datastream.setSensor(SENSORS.get(1).withOnlyId());
+ datastream.setObservedProperty(OBSERVED_PROPS.get(0).withOnlyId());
+ service.create(datastream);
+
+ createObservation(DATASTREAMS.get(0).withOnlyId(), -1);
+ createObservation(DATASTREAMS.get(1).withOnlyId(), 0);
+ }
+
+ private static void createObservation(Datastream ds, double result) throws ServiceFailureException {
+ Observation o = new Observation(result, ds);
+ service.create(o);
+ OBSERVATIONS.add(o);
+ }
+
+ private static void createObservation(MultiDatastream ds, double... result) throws ServiceFailureException {
+ Observation o = new Observation(result, ds);
+ service.create(o);
+ OBSERVATIONS.add(o);
+ }
+
+ private void updateForException(String test, Entity entity) {
+ try {
+ service.update(entity);
+ } catch (ServiceFailureException ex) {
+ return;
+ }
+ Assert.fail(test + " Update did not respond with 400 Bad Request.");
+ }
+
+ private void checkResult(String test, EntityUtils.resultTestResult result) {
+ Assert.assertTrue(result.testOk, test + " " + result.message);
+ }
+
+ private void checkObservedPropertiesFor(MultiDatastream md, ObservedProperty... expectedObservedProps) throws ArrayComparisonFailure, ServiceFailureException {
+ ObservedProperty[] fetchedObservedProps2 = md.observedProperties().query().list().toArray(new ObservedProperty[0]);
+ Assert.assertEquals(expectedObservedProps, fetchedObservedProps2, "Incorrect Observed Properties returned.");
+ }
+
+ @Test(description = "Test MultiDatastream creation.", groups = "level-5", priority = 0)
+ public void testMultiDatastream() throws ServiceFailureException {
+ // Create a MultiDatastream with one ObservedProperty.
+ MultiDatastream md1 = new MultiDatastream();
+ md1.setName("MultiDatastream 1");
+ md1.setDescription("The first test MultiDatastream.");
+ md1.addUnitOfMeasurement(new UnitOfMeasurement("degree celcius", "°C", "ucum:T"));
+
+ List dataTypes1 = new ArrayList<>();
+ dataTypes1.add("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
+ md1.setMultiObservationDataTypes(dataTypes1);
+
+ md1.setThing(THINGS.get(0).withOnlyId());
+ md1.setSensor(SENSORS.get(0).withOnlyId());
+
+ EntityList observedProperties = new EntityList<>(de.fraunhofer.iosb.ilt.sta.model.EntityType.OBSERVED_PROPERTIES);
+ observedProperties.add(OBSERVED_PROPS.get(0).withOnlyId());
+ md1.setObservedProperties(observedProperties);
+
+ service.create(md1);
+ MULTIDATASTREAMS.add(md1);
+
+ // Create a MultiDatastream with two different ObservedProperties.
+ MultiDatastream md2 = new MultiDatastream();
+ md2.setName("MultiDatastream 2");
+ md2.setDescription("The second test MultiDatastream.");
+ md2.addUnitOfMeasurement(new UnitOfMeasurement("degree celcius", "°C", "ucum:T"));
+ md2.addUnitOfMeasurement(new UnitOfMeasurement("percent", "%", "ucum:%"));
+
+ List dataTypes2 = new ArrayList<>();
+ dataTypes2.add("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
+ dataTypes2.add("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
+ md2.setMultiObservationDataTypes(dataTypes2);
+
+ md2.setThing(THINGS.get(0).withOnlyId());
+ md2.setSensor(SENSORS.get(0).withOnlyId());
+
+ EntityList observedProperties2 = new EntityList<>(de.fraunhofer.iosb.ilt.sta.model.EntityType.OBSERVED_PROPERTIES);
+ observedProperties2.add(OBSERVED_PROPS.get(0).withOnlyId());
+ observedProperties2.add(OBSERVED_PROPS.get(1).withOnlyId());
+ md2.setObservedProperties(observedProperties2);
+
+ service.create(md2);
+ MULTIDATASTREAMS.add(md2);
+
+ // Create a MultiDatastream with two different ObservedProperties, in the opposite order.
+ MultiDatastream md3 = new MultiDatastream();
+ md3.setName("MultiDatastream 3");
+ md3.setDescription("The third test MultiDatastream.");
+ md3.addUnitOfMeasurement(new UnitOfMeasurement("percent", "%", "ucum:%"));
+ md3.addUnitOfMeasurement(new UnitOfMeasurement("degree celcius", "°C", "ucum:T"));
+
+ List dataTypes3 = new ArrayList<>();
+ dataTypes3.add("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
+ dataTypes3.add("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
+ md3.setMultiObservationDataTypes(dataTypes3);
+
+ md3.setThing(THINGS.get(0).withOnlyId());
+ md3.setSensor(SENSORS.get(0).withOnlyId());
+
+ EntityList observedProperties3 = new EntityList<>(de.fraunhofer.iosb.ilt.sta.model.EntityType.OBSERVED_PROPERTIES);
+ observedProperties3.add(OBSERVED_PROPS.get(1).withOnlyId());
+ observedProperties3.add(OBSERVED_PROPS.get(0).withOnlyId());
+ md3.setObservedProperties(observedProperties3);
+
+ service.create(md3);
+ MULTIDATASTREAMS.add(md3);
+
+ // Create a MultiDatastream with two of the same ObservedProperties.
+ MultiDatastream md4 = new MultiDatastream();
+ md4.setName("MultiDatastream 4");
+ md4.setDescription("The fourth test MultiDatastream.");
+ md4.addUnitOfMeasurement(new UnitOfMeasurement("degree celcius", "°C", "ucum:T"));
+ md4.addUnitOfMeasurement(new UnitOfMeasurement("degree celcius", "°C", "ucum:T"));
+
+ List dataTypes4 = new ArrayList<>();
+ dataTypes4.add("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
+ dataTypes4.add("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
+ md4.setMultiObservationDataTypes(dataTypes4);
+
+ md4.setThing(THINGS.get(0).withOnlyId());
+ md4.setSensor(SENSORS.get(1).withOnlyId());
+
+ EntityList observedProperties4 = new EntityList<>(de.fraunhofer.iosb.ilt.sta.model.EntityType.OBSERVED_PROPERTIES);
+ observedProperties4.add(OBSERVED_PROPS.get(0).withOnlyId());
+ observedProperties4.add(OBSERVED_PROPS.get(0).withOnlyId());
+ md4.setObservedProperties(observedProperties4);
+
+ service.create(md4);
+ MULTIDATASTREAMS.add(md4);
+ }
+
+ @Test(description = "Test creation of Observations in MultiDatastream.", groups = "level-5", priority = 1)
+ public void testObservationInMultiDatastream() throws ServiceFailureException {
+ createObservation(MULTIDATASTREAMS.get(0).withOnlyId(), 1);
+ createObservation(MULTIDATASTREAMS.get(0).withOnlyId(), 2);
+ createObservation(MULTIDATASTREAMS.get(0).withOnlyId(), 3);
+
+ createObservation(MULTIDATASTREAMS.get(1).withOnlyId(), 4, 1);
+ createObservation(MULTIDATASTREAMS.get(1).withOnlyId(), 5, 2);
+ createObservation(MULTIDATASTREAMS.get(1).withOnlyId(), 6, 3);
+
+ createObservation(MULTIDATASTREAMS.get(2).withOnlyId(), 7, 4);
+ createObservation(MULTIDATASTREAMS.get(2).withOnlyId(), 8, 5);
+ createObservation(MULTIDATASTREAMS.get(2).withOnlyId(), 9, 6);
+
+ createObservation(MULTIDATASTREAMS.get(3).withOnlyId(), 10, 7);
+ createObservation(MULTIDATASTREAMS.get(3).withOnlyId(), 11, 8);
+ createObservation(MULTIDATASTREAMS.get(3).withOnlyId(), 12, 9);
+ }
+
+ @Test(description = "Test creation of incorrect Observations in MultiDatastream.", groups = "level-5", priority = 2)
+ public void testObservationInMultiDatastreamIncorrect() throws ServiceFailureException {
+ boolean failed = false;
+ try {
+ Observation o = new Observation(1, MULTIDATASTREAMS.get(1).withOnlyId());
+ service.create(o);
+ } catch (ServiceFailureException e) {
+ failed = true;
+ }
+ if (!failed) {
+ Assert.fail("Service should have rejected posting non-array result to a multidatastream.");
+ }
+
+ failed = false;
+ try {
+ createObservation(MULTIDATASTREAMS.get(0).withOnlyId(), 1, 2);
+ } catch (ServiceFailureException e) {
+ failed = true;
+ }
+ if (!failed) {
+ Assert.fail("Service should have rejected posting 2 results to a multidatastream with only 1 observed property.");
+ }
+ failed = false;
+ try {
+ createObservation(MULTIDATASTREAMS.get(1).withOnlyId(), 1);
+ } catch (ServiceFailureException e) {
+ failed = true;
+ }
+ if (!failed) {
+ Assert.fail("Service should have rejected posting 1 result to a multidatastream with 2 observed properties.");
+ }
+ }
+
+ private JsonNode getJsonObject(String urlString) {
+ Map responseMap = HTTPMethods.doGet(urlString);
+ String response = responseMap.get("response").toString();
+ int responseCode = Integer.parseInt(responseMap.get("response-code").toString());
+ Assert.assertEquals(responseCode, 200, "Error getting Observations using Data Array: Code " + responseCode);
+
+ JsonNode json;
+ try {
+ json = new ObjectMapper().readTree(response);
+ } catch (IOException ex) {
+ Assert.fail("Server returned malformed JSON for request: " + urlString, ex);
+ return null;
+ }
+ if (!json.isObject()) {
+ Assert.fail("Server did not return a JSON object for request: " + urlString);
+ }
+ return json;
+ }
+
+ private JsonNode getJsonValue(String urlString) {
+ JsonNode json = getJsonObject(urlString);
+ JsonNode value = json.get("value");
+ if (value == null || !value.isArray()) {
+ Assert.fail("value field is not an array for request: " + urlString);
+ }
+ return value;
+ }
+
+ private void entitiesHaveOneOf(JsonNode value, String EntityName, String... properties) {
+ for (JsonNode valueItem : value) {
+ if (!valueItem.isObject()) {
+ Assert.fail("item in " + EntityName + " array is not an object.");
+ return;
+ }
+ for (String property : properties) {
+ if (valueItem.has(property)) {
+ return;
+ }
+ }
+ Assert.fail("item in " + EntityName + " array does not contain any of " + Arrays.toString(properties));
+ }
+ }
+
+ @Test(description = "Test LowLevel JSON.", groups = "level-5", priority = 2)
+ public void testJson() throws ServiceFailureException {
+ JsonNode json = getJsonValue(rootUri + "/Things");
+ entitiesHaveOneOf(json, "Things", "MultiDatastreams@iot.navigationLink");
+ json = getJsonValue(rootUri + "/Sensors");
+ entitiesHaveOneOf(json, "Sensors", "MultiDatastreams@iot.navigationLink");
+ json = getJsonValue(rootUri + "/ObservedProperties");
+ entitiesHaveOneOf(json, "ObservedProperties", "MultiDatastreams@iot.navigationLink");
+ json = getJsonValue(rootUri + "/Observations");
+ entitiesHaveOneOf(json, "Observations", "MultiDatastream@iot.navigationLink", "Datastream@iot.navigationLink");
+ json = getJsonValue(rootUri + "/MultiDatastreams");
+ for (String property : EntityTypeMds.MULTI_DATASTREAM.getProperties()) {
+ entitiesHaveOneOf(json, "MultiDatastreams", property);
+ }
+ for (String relation : EntityTypeMds.MULTI_DATASTREAM.getRelations()) {
+ entitiesHaveOneOf(json, "MultiDatastreams", relation + "@iot.navigationLink");
+ }
+ }
+
+ @Test(description = "Test if all Datastreams and MultiDatastreams are linked to Thing 1.", groups = "level-5", priority = 2)
+ public void testMultiDatastreamThings() throws ServiceFailureException {
+ // Check if all Datastreams and MultiDatastreams are linked to Thing 1.
+ Thing fetchedThing = service.things().find(THINGS.get(0).getId());
+ EntityList fetchedDatastreams = fetchedThing.datastreams().query().list();
+ checkResult("Check Datastreams linked to Thing 1.", EntityUtils.resultContains(fetchedDatastreams, DATASTREAMS.get(0)));
+ EntityList fetchedMultiDatastreams = fetchedThing.multiDatastreams().query().list();
+ checkResult("Check MultiDatastreams linked to Thing 1.", EntityUtils.resultContains(fetchedMultiDatastreams, new ArrayList<>(MULTIDATASTREAMS)));
+ }
+
+ @Test(description = "Test if all Datastreams and MultiDatastreams are linked to Sensor 1.", groups = "level-5", priority = 3)
+ public void testMultiDatastreamSensors() throws ServiceFailureException {
+ // Check if all Datastreams and MultiDatastreams are linked to Sensor 1.
+ Sensor fetchedSensor = service.sensors().find(SENSORS.get(0).getId());
+ EntityList fetchedDatastreams = fetchedSensor.datastreams().query().list();
+ checkResult("Check Datastreams linked to Sensor 1.", EntityUtils.resultContains(fetchedDatastreams, DATASTREAMS.get(0)));
+ EntityList fetchedMultiDatastreams = fetchedSensor.multiDatastreams().query().list();
+ checkResult(
+ "Check MultiDatastreams linked to Sensor 1.",
+ EntityUtils.resultContains(fetchedMultiDatastreams, getFromList(MULTIDATASTREAMS, 0, 1, 2)));
+ }
+
+ @Test(description = "Test if all Datastreams and MultiDatastreams are linked to ObservedProperty 1.", groups = "level-5", priority = 4)
+ public void testMultiDatastreamObservedProperties1() throws ServiceFailureException {
+ // Check if all Datastreams and MultiDatastreams are linked to ObservedProperty 1.
+ ObservedProperty fetchedObservedProp = service.observedProperties().find(OBSERVED_PROPS.get(0).getId());
+ EntityList fetchedDatastreams = fetchedObservedProp.datastreams().query().list();
+ checkResult(
+ "Check Datastreams linked to ObservedProperty 1.",
+ EntityUtils.resultContains(fetchedDatastreams, getFromList(DATASTREAMS, 0, 1)));
+ EntityList fetchedMultiDatastreams = fetchedObservedProp.multiDatastreams().query().list();
+ checkResult(
+ "Check MultiDatastreams linked to ObservedProperty 1.",
+ EntityUtils.resultContains(fetchedMultiDatastreams, new ArrayList<>(MULTIDATASTREAMS)));
+ }
+
+ @Test(description = "Test if MultiDatastreams 2 and 3 are linked to ObservedProperty 2.", groups = "level-5", priority = 5)
+ public void testMultiDatastreamObservedProperties2() throws ServiceFailureException {
+ // Check if MultiDatastreams 2 and 3 are linked to ObservedProperty 2.
+ ObservedProperty fetchedObservedProp = service.observedProperties().find(OBSERVED_PROPS.get(1).getId());
+ EntityList fetchedDatastreams = fetchedObservedProp.datastreams().query().list();
+ checkResult(
+ "Check Datastreams linked to ObservedProperty 2.",
+ EntityUtils.resultContains(fetchedDatastreams, new ArrayList<>()));
+ EntityList fetchedMultiDatastreams = fetchedObservedProp.multiDatastreams().query().list();
+ checkResult(
+ "Check MultiDatastreams linked to ObservedProperty 2.",
+ EntityUtils.resultContains(fetchedMultiDatastreams, getFromList(MULTIDATASTREAMS, 1, 2)));
+ }
+
+ @Test(description = "First Observation should have a Datastream but not a MultiDatasteam.", groups = "level-5", priority = 6)
+ public void testObservationLinks1() throws ServiceFailureException {
+ // First Observation should have a Datastream but not a MultiDatasteam.
+ Observation fetchedObservation = service.observations().find(OBSERVATIONS.get(0).getId());
+ Datastream fetchedDatastream = fetchedObservation.getDatastream();
+ Assert.assertEquals(fetchedDatastream, DATASTREAMS.get(0), "Observation has wrong or no Datastream");
+ MultiDatastream fetchedMultiDatastream = fetchedObservation.getMultiDatastream();
+ Assert.assertEquals(fetchedMultiDatastream, null, "Observation should not have a MultiDatastream");
+ }
+
+ @Test(description = "Second Observation should not have a Datastream but a MultiDatasteam.", groups = "level-5", priority = 7)
+ public void testObservationLinks2() throws ServiceFailureException {
+ // Second Observation should not have a Datastream but a MultiDatasteam.
+ Observation fetchedObservation = service.observations().find(OBSERVATIONS.get(2).getId());
+ Datastream fetchedDatastream = fetchedObservation.getDatastream();
+ Assert.assertEquals(fetchedDatastream, null, "Observation should not have a Datastream");
+ MultiDatastream fetchedMultiDatastream = fetchedObservation.getMultiDatastream();
+ Assert.assertEquals(fetchedMultiDatastream, MULTIDATASTREAMS.get(0), "Observation has wrong or no MultiDatastream");
+ }
+
+ @Test(description = "Test if the MultiDatastreams have the correct ObservedProperties in the correct order.", groups = "level-5", priority = 8)
+ public void testObservedPropertyOrder() throws ServiceFailureException {
+ // Check if the MultiDatastreams have the correct ObservedProperties in the correct order.
+ checkObservedPropertiesFor(MULTIDATASTREAMS.get(0), OBSERVED_PROPS.get(0));
+ checkObservedPropertiesFor(MULTIDATASTREAMS.get(1), OBSERVED_PROPS.get(0), OBSERVED_PROPS.get(1));
+ checkObservedPropertiesFor(MULTIDATASTREAMS.get(2), OBSERVED_PROPS.get(1), OBSERVED_PROPS.get(0));
+ checkObservedPropertiesFor(MULTIDATASTREAMS.get(3), OBSERVED_PROPS.get(0), OBSERVED_PROPS.get(0));
+ }
+
+ @Test(description = "Try to give Observation 1 a MultiDatastream without removing the Datastream. Should give an error.", groups = "level-5", priority = 9)
+ public void testIncorrectObservation() throws ServiceFailureException {
+ // Try to give Observation 1 a MultiDatastream without removing the Datastream. Should give an error.
+ Observation modifiedObservation = OBSERVATIONS.get(0).withOnlyId();
+ modifiedObservation.setMultiDatastream(MULTIDATASTREAMS.get(0).withOnlyId());
+ updateForException("Linking Observation to Datastream AND MultiDatastream.", modifiedObservation);
+ }
+
+ @Test(description = "Try to add a MultiDatastream to an ObservedProperty. Should give an error.", groups = "level-5", priority = 10)
+ public void testIncorrectObservedProperty() throws ServiceFailureException {
+ // Try to add a MultiDatastream to an ObservedProperty. Should give an error.
+ ObservedProperty modifiedObservedProp = OBSERVED_PROPS.get(1).withOnlyId();
+ modifiedObservedProp.getMultiDatastreams().add(MULTIDATASTREAMS.get(0).withOnlyId());
+ updateForException("Linking MultiDatastream to Observed property.", modifiedObservedProp);
+ }
+
+ @Test(description = "Check MultiDatastream(x)/Observations works.", groups = "level-5", priority = 11)
+ public void testFetchObservationsByMultiDatastream() throws ServiceFailureException {
+ EntityList observations = MULTIDATASTREAMS.get(0).observations().query().list();
+ checkResult(
+ "Looking for all observations",
+ EntityUtils.resultContains(observations, getFromList(OBSERVATIONS, 2, 3, 4)));
+
+ observations = MULTIDATASTREAMS.get(1).observations().query().list();
+ checkResult(
+ "Looking for all observations",
+ EntityUtils.resultContains(observations, getFromList(OBSERVATIONS, 5, 6, 7)));
+
+ observations = MULTIDATASTREAMS.get(2).observations().query().list();
+ checkResult(
+ "Looking for all observations",
+ EntityUtils.resultContains(observations, getFromList(OBSERVATIONS, 8, 9, 10)));
+
+ observations = MULTIDATASTREAMS.get(3).observations().query().list();
+ checkResult(
+ "Looking for all observations",
+ EntityUtils.resultContains(observations, getFromList(OBSERVATIONS, 11, 12, 13)));
+ }
+
+ @Test(description = "Check if all observations are there.", groups = "level-5", priority = 11)
+ public void testObservations() throws ServiceFailureException {
+ // Check if all observations are there.
+ EntityList fetchedObservations = service.observations().query().list();
+ checkResult(
+ "Looking for all observations",
+ EntityUtils.resultContains(fetchedObservations, new ArrayList<>(OBSERVATIONS)));
+ }
+
+ @Test(description = "Deleting ObservedProperty 2 should delete MultiDatastream 2 and 3 and their Observations.", groups = "level-5", priority = 12)
+ public void testDeleteObservedProperty() throws ServiceFailureException {
+ // Deleting ObservedProperty 2 should delete MultiDatastream 2 and 3 and their Observations.
+ service.delete(OBSERVED_PROPS.get(1));
+ EntityList fetchedMultiDatastreams = service.multiDatastreams().query().list();
+ checkResult(
+ "Checking if MultiDatastreams are automatically deleted.",
+ EntityUtils.resultContains(fetchedMultiDatastreams, getFromList(MULTIDATASTREAMS, 0, 3)));
+ EntityList fetchedObservations = service.observations().query().list();
+ checkResult(
+ "Checking if Observations are automatically deleted.",
+ EntityUtils.resultContains(fetchedObservations, getFromList(OBSERVATIONS, 0, 1, 2, 3, 4, 11, 12, 13)));
+ }
+
+ @Test(description = "Deleting Sensor 2 should delete MultiDatastream 4", groups = "level-5", priority = 13)
+ public void testDeleteSensor() throws ServiceFailureException {
+ // Deleting Sensor 2 should delete MultiDatastream 4
+ service.delete(SENSORS.get(1));
+ EntityList fetchedMultiDatastreams = service.multiDatastreams().query().list();
+ checkResult(
+ "Checking if MultiDatastreams are automatically deleted.",
+ EntityUtils.resultContains(fetchedMultiDatastreams, getFromList(MULTIDATASTREAMS, 0)));
+ }
+
+ @Test(description = "Deleting Thing 1 should delete the last MultiDatastream.", groups = "level-5", priority = 14)
+ public void testDeleteThing() throws ServiceFailureException {
+ // Deleting Thing 1 should delete the last MultiDatastream.
+ service.delete(THINGS.get(0));
+ EntityList fetchedMultiDatastreams = service.multiDatastreams().query().list();
+ checkResult(
+ "Checking if MultiDatastreams are automatically deleted.",
+ EntityUtils.resultContains(fetchedMultiDatastreams, new ArrayList<>()));
+ }
+
+ public static > List getFromList(List list, int... ids) {
+ List result = new ArrayList<>();
+ for (int i : ids) {
+ result.add(list.get(i));
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/opengis/cite/sta10/sensingCore/Capability1Tests.java b/src/main/java/org/opengis/cite/sta10/sensingCore/Capability1Tests.java
index 3ceab0f..41e9da2 100644
--- a/src/main/java/org/opengis/cite/sta10/sensingCore/Capability1Tests.java
+++ b/src/main/java/org/opengis/cite/sta10/sensingCore/Capability1Tests.java
@@ -16,6 +16,7 @@
import org.opengis.cite.sta10.util.HTTPMethods;
import org.opengis.cite.sta10.util.ServiceURLBuilder;
import org.testng.Assert;
+import org.testng.ISuite;
import org.testng.ITestContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -34,6 +35,7 @@ public class Capability1Tests {
* should be tested
*/
private final int resourcePathLevel = 4;
+ private boolean hasMultiDatastream = false;
/**
* This method will be run before starting the test for this conformance
@@ -44,21 +46,20 @@ public class Capability1Tests {
*/
@BeforeClass
public void obtainTestSubject(ITestContext testContext) {
- Object obj = testContext.getSuite().getAttribute(
- SuiteAttribute.LEVEL.getName());
+ ISuite suite = testContext.getSuite();
+ Object obj = suite.getAttribute(SuiteAttribute.LEVEL.getName());
if ((null != obj)) {
Integer level = Integer.class.cast(obj);
Assert.assertTrue(level.intValue() > 0,
"Conformance level 1 will not be checked since ics = " + level);
}
- rootUri = testContext.getSuite().getAttribute(
- SuiteAttribute.TEST_SUBJECT.getName()).toString();
+ rootUri = suite.getAttribute(SuiteAttribute.TEST_SUBJECT.getName()).toString();
rootUri = rootUri.trim();
if (rootUri.lastIndexOf('/') == rootUri.length() - 1) {
rootUri = rootUri.substring(0, rootUri.length() - 1);
}
-
+ hasMultiDatastream = suite.getXmlSuite().getParameter("hasMultiDatastream") != null;
}
/**
@@ -397,6 +398,9 @@ public void checkServiceRootUri() {
addedLinks.put("Observations", false);
addedLinks.put("ObservedProperties", false);
addedLinks.put("FeaturesOfInterest", false);
+ if (hasMultiDatastream) {
+ addedLinks.put("MultiDatastreams", false);
+ }
for (int i = 0; i < entities.length(); i++) {
JSONObject entity = entities.getJSONObject(i);
try {
@@ -410,42 +414,38 @@ public void checkServiceRootUri() {
switch (name) {
case "Things":
Assert.assertEquals(nameUrl, rootUri + "/Things", "The URL for Things in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("Things");
addedLinks.put(name, true);
break;
case "Locations":
Assert.assertEquals(nameUrl, rootUri + "/Locations", "The URL for Locations in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("Locations");
addedLinks.put(name, true);
break;
case "HistoricalLocations":
Assert.assertEquals(nameUrl, rootUri + "/HistoricalLocations", "The URL for HistoricalLocations in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("HistoricalLocations");
addedLinks.put(name, true);
break;
case "Datastreams":
Assert.assertEquals(nameUrl, rootUri + "/Datastreams", "The URL for Datastreams in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("Datastreams");
+ addedLinks.put(name, true);
+ break;
+ case "MultiDatastreams":
+ Assert.assertEquals(nameUrl, rootUri + "/MultiDatastreams", "The URL for MultiDatastreams in Service Root URI is not compliant to SensorThings API.");
addedLinks.put(name, true);
break;
case "Sensors":
Assert.assertEquals(nameUrl, rootUri + "/Sensors", "The URL for Sensors in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("Sensors");
addedLinks.put(name, true);
break;
case "Observations":
Assert.assertEquals(nameUrl, rootUri + "/Observations", "The URL for Observations in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("Observations");
addedLinks.put(name, true);
break;
case "ObservedProperties":
Assert.assertEquals(nameUrl, rootUri + "/ObservedProperties", "The URL for ObservedProperties in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("ObservedProperties");
addedLinks.put(name, true);
break;
case "FeaturesOfInterest":
Assert.assertEquals(nameUrl, rootUri + "/FeaturesOfInterest", "The URL for FeaturesOfInterest in Service Root URI is not compliant to SensorThings API.");
- addedLinks.remove("FeaturesOfInterest");
addedLinks.put(name, true);
break;
case "MultiDatastreams":
diff --git a/src/main/java/org/opengis/cite/sta10/util/EntityUtils.java b/src/main/java/org/opengis/cite/sta10/util/EntityUtils.java
new file mode 100644
index 0000000..123a96b
--- /dev/null
+++ b/src/main/java/org/opengis/cite/sta10/util/EntityUtils.java
@@ -0,0 +1,112 @@
+package org.opengis.cite.sta10.util;
+
+import de.fraunhofer.iosb.ilt.sta.ServiceFailureException;
+import de.fraunhofer.iosb.ilt.sta.dao.BaseDao;
+import de.fraunhofer.iosb.ilt.sta.model.Entity;
+import de.fraunhofer.iosb.ilt.sta.model.Id;
+import de.fraunhofer.iosb.ilt.sta.model.ext.EntityList;
+import de.fraunhofer.iosb.ilt.sta.service.SensorThingsService;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for comparing results and cleaning the service.
+ *
+ * @author Hylke van der Schaaf
+ */
+public class EntityUtils {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EntityUtils.class.getName());
+
+ /**
+ * Class returned by checks on results. Encapsulates the result of the
+ * check, and the message.
+ */
+ public static class resultTestResult {
+
+ public final boolean testOk;
+ public final String message;
+
+ public resultTestResult(boolean testOk, String message) {
+ this.testOk = testOk;
+ this.message = message;
+ }
+
+ }
+
+ public static resultTestResult resultContains(EntityList extends Entity> result, Entity... entities) {
+ return resultContains(result, new ArrayList(Arrays.asList(entities)));
+ }
+
+ /**
+ * Checks if the list contains all the given entities exactly once.
+ *
+ * @param result the result entities.
+ * @param entityList The expected entities.
+ * @return the test result.
+ */
+ public static resultTestResult resultContains(EntityList extends Entity> result, List extends Entity> entityList) {
+ long count = result.getCount();
+ if (count != -1 && count != entityList.size()) {
+ LOGGER.info("Result count ({}) not equal to expected count ({})", count, entityList.size());
+ return new resultTestResult(false, "Result count " + count + " not equal to expected count (" + entityList.size() + ")");
+ }
+ Iterator extends Entity> it;
+ for (it = result.fullIterator(); it.hasNext();) {
+ Entity next = it.next();
+ Entity inList = findEntityIn(next, entityList);
+ if (!entityList.remove(inList)) {
+ LOGGER.info("Entity with id {} found in result that is not expected.", next.getId());
+ return new resultTestResult(false, "Entity with id " + next.getId() + " found in result that is not expected.");
+ }
+ }
+ if (!entityList.isEmpty()) {
+ LOGGER.info("Expected entity not found in result.");
+ return new resultTestResult(false, entityList.size() + " expected entities not in result.");
+ }
+ return new resultTestResult(true, "Check ok.");
+ }
+
+ public static Entity findEntityIn(Entity entity, List extends Entity> entities) {
+ Id id = entity.getId();
+ for (Entity inList : entities) {
+ if (Objects.equals(inList.getId(), id)) {
+ return inList;
+ }
+ }
+ return null;
+ }
+
+ public static void deleteAll(SensorThingsService sts) throws ServiceFailureException {
+ deleteAll(sts.things());
+ deleteAll(sts.locations());
+ deleteAll(sts.sensors());
+ deleteAll(sts.featuresOfInterest());
+ deleteAll(sts.observedProperties());
+ deleteAll(sts.observations());
+ }
+
+ public static > void deleteAll(BaseDao doa) throws ServiceFailureException {
+ boolean more = true;
+ int count = 0;
+ while (more) {
+ EntityList entities = doa.query().list();
+ if (entities.getCount() > 0) {
+ LOGGER.info("{} to go.", entities.getCount());
+ } else {
+ more = false;
+ }
+ for (T entity : entities) {
+ doa.delete(entity);
+ count++;
+ }
+ }
+ LOGGER.info("Deleted {} using {}.", count, doa.getClass().getName());
+ }
+
+}
diff --git a/src/main/resources/org/opengis/cite/sta10/testng.xml b/src/main/resources/org/opengis/cite/sta10/testng.xml
index a554e10..0c8cf96 100644
--- a/src/main/resources/org/opengis/cite/sta10/testng.xml
+++ b/src/main/resources/org/opengis/cite/sta10/testng.xml
@@ -24,4 +24,9 @@
+
+
+
+
+