summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java191
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidatorTest.java207
-rwxr-xr-xconfig/pom.xml5
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java4
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java9
-rw-r--r--config/src/test/java/com/yahoo/config/subscription/util/JsonHelper.java28
-rw-r--r--document/pom.xml11
-rw-r--r--document/src/main/java/com/yahoo/document/json/JsonWriter.java1
-rw-r--r--document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java4
-rw-r--r--document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java2
-rw-r--r--testutil/pom.xml10
-rw-r--r--testutil/src/main/java/com/yahoo/test/json/JsonTestHelper.java (renamed from document/src/test/java/com/yahoo/document/json/JsonTestHelper.java)8
-rw-r--r--vespa-http-client/pom.xml6
-rw-r--r--vespa-http-client/src/test/java/com/yahoo/vespa/http/client/runner/JsonReaderTest.java11
15 files changed, 436 insertions, 62 deletions
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java
index 2a4e231dee4..5b3ce7136d5 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankingConstant.java
@@ -26,6 +26,7 @@ public class RankingConstant {
public String getName() { return name; }
public String getFileName() { return fileName; }
public String getFileReference() { return fileRef; }
+ public TensorType getTensorType() { return tensorType; }
public String getType() { return tensorType.toString(); }
public void validate() {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java
new file mode 100644
index 00000000000..0485750bc16
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidator.java
@@ -0,0 +1,191 @@
+package com.yahoo.vespa.model.application.validation;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.google.common.base.Joiner;
+import com.yahoo.tensor.TensorType;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * @author Vegard Sjonfjell
+ */
+public class ConstantTensorJsonValidator {
+ static private final JsonFactory jsonFactory = new JsonFactory();
+ private TensorType tensorType;
+ private JsonParser parser;
+
+ public static class InvalidConstantTensor extends RuntimeException {
+ public InvalidConstantTensor(JsonParser parser, String message) {
+ super(message + " " + parser.getCurrentLocation().toString());
+ }
+
+ public InvalidConstantTensor(JsonParser parser, Exception base) {
+ super("Failed to parse JSON stream " + parser.getCurrentLocation().toString(), base);
+ }
+ }
+
+ @FunctionalInterface
+ private static interface SubroutineThrowingIOException {
+ void invoke() throws IOException;
+ }
+
+ private void wrapIOException(SubroutineThrowingIOException lambda) {
+ try {
+ lambda.invoke();
+ } catch (IOException e) {
+ throw new InvalidConstantTensor(parser, e);
+ }
+ }
+
+ public ConstantTensorJsonValidator(Reader tensorFile, TensorType tensorType) {
+ wrapIOException(() -> {
+ this.parser = jsonFactory.createParser(tensorFile);
+ this.tensorType = tensorType;
+ });
+ }
+
+ public void validate() {
+ wrapIOException(() -> {
+ assertNextTokenIs(JsonToken.START_OBJECT);
+ assertNextTokenIs(JsonToken.FIELD_NAME);
+ assertFieldNameIs("cells");
+
+ assertNextTokenIs(JsonToken.START_ARRAY);
+
+ while (parser.nextToken() != JsonToken.END_ARRAY) {
+ validateTensorCell(tensorType.dimensions());
+ }
+
+ assertNextTokenIs(JsonToken.END_OBJECT);
+ });
+ }
+
+ private void validateTensorCell(Collection<TensorType.Dimension> tensorDimensions) {
+ wrapIOException(() -> {
+ assertCurrentTokenIs(JsonToken.START_OBJECT);
+
+ final List<String> fieldNameCandidates = new ArrayList<>(Arrays.asList("address", "value"));
+ for (int i = 0; i < 2; i++) {
+ assertNextTokenIs(JsonToken.FIELD_NAME);
+ final String fieldName = parser.getCurrentName();
+
+ if (fieldNameCandidates.contains(fieldName)) {
+ fieldNameCandidates.remove(fieldName);
+
+ if (fieldName.equals("address")) {
+ validateTensorAddress(tensorDimensions);
+ } else if (fieldName.equals("value")) {
+ validateTensorValue();
+ }
+ } else {
+ throw new InvalidConstantTensor(parser, "Only \"address\" or \"value\" fields are permitted within a cell object");
+ }
+ }
+
+ assertNextTokenIs(JsonToken.END_OBJECT);
+ });
+ }
+
+ private void validateTensorAddress(Collection<TensorType.Dimension> tensorDimensions) throws IOException {
+ assertNextTokenIs(JsonToken.START_OBJECT);
+
+ final Map<String, TensorType.Dimension> tensorDimensionsMapping = tensorDimensions.stream()
+ .collect(Collectors.toMap(TensorType.Dimension::name, Function.identity()));
+
+ // Iterate within the address key, value pairs
+ while ((parser.nextToken() != JsonToken.END_OBJECT)) {
+ assertCurrentTokenIs(JsonToken.FIELD_NAME);
+
+ final String dimensionName = parser.getCurrentName();
+ TensorType.Dimension dimension = tensorDimensionsMapping.get(dimensionName);
+ if (dimension == null) {
+ throw new InvalidConstantTensor(parser, String.format("Tensor dimension with name \"%s\" does not exist", parser.getCurrentName()));
+ }
+
+ tensorDimensionsMapping.remove(dimensionName);
+ validateTensorCoordinate(dimension);
+ }
+
+ if (!tensorDimensionsMapping.isEmpty()) {
+ throw new InvalidConstantTensor(parser, String.format("Tensor address missing dimension(s): %s", Joiner.on(", ").join(tensorDimensionsMapping.keySet())));
+ }
+ }
+
+ /*
+ * Tensor coordinates are always strings. Coordinates for a mapped dimension can be any string,
+ * but those for indexed dimensions needs to be able to be interpreted as integers, and,
+ * additionally, those for indexed bounded dimensions needs to fall within the dimension size.
+ */
+ private void validateTensorCoordinate(TensorType.Dimension dimension) throws IOException {
+ assertNextTokenIs(JsonToken.VALUE_STRING);
+
+ if (dimension instanceof TensorType.IndexedBoundDimension) {
+ validateBoundedCoordinate((TensorType.IndexedBoundDimension) dimension);
+ } else if (dimension instanceof TensorType.IndexedUnboundDimension) {
+ validateUnboundedCoordinate(dimension);
+ }
+ }
+
+ private void validateBoundedCoordinate(TensorType.IndexedBoundDimension dimension) {
+ wrapIOException(() -> {
+ try {
+ final int value = Integer.parseInt(parser.getValueAsString());
+ if (value >= dimension.size().get()) {
+ throwCoordinateOutsideBoundedDimension(parser.getValueAsString(), dimension.name());
+ }
+ } catch (NumberFormatException e) {
+ throwCoordinateOutsideBoundedDimension(parser.getValueAsString(), dimension.name());
+ }
+ });
+ }
+
+ private void throwCoordinateOutsideBoundedDimension(String value, String dimensionName) {
+ throw new InvalidConstantTensor(parser, String.format("Coordinate \"%s\" not within limits of bounded dimension %s", value, dimensionName));
+ }
+
+ private void validateUnboundedCoordinate(TensorType.Dimension dimension) {
+ wrapIOException(() -> {
+ try {
+ Integer.parseInt(parser.getValueAsString());
+ } catch (NumberFormatException e) {
+ throw new InvalidConstantTensor(parser, String.format("Coordinate \"%s\" for dimension %s is not an integer", parser.getValueAsString(), dimension.name()));
+ }
+ });
+ }
+
+ private void validateTensorValue() throws IOException {
+ assertNextTokenIs(JsonToken.VALUE_NUMBER_FLOAT);
+ }
+
+ private void assertCurrentTokenIs(JsonToken wantedToken) {
+ assertTokenIs(parser.getCurrentToken(), wantedToken);
+ }
+
+ private void assertNextTokenIs(JsonToken wantedToken) throws IOException {
+ assertTokenIs(parser.nextToken(), wantedToken);
+ }
+
+ private void assertTokenIs(JsonToken token, JsonToken wantedToken) {
+ if (token != wantedToken) {
+ throw new InvalidConstantTensor(parser, String.format("Expected JSON token %s, but got %s", wantedToken.toString(), token.toString()));
+ }
+ }
+
+ private void assertFieldNameIs(String wantedFieldName) throws IOException {
+ final String actualFieldName = parser.getCurrentName();
+
+ if (!actualFieldName.equals(wantedFieldName)) {
+ throw new InvalidConstantTensor(parser, String.format("Expected field name \"%s\", got \"%s\"", wantedFieldName, actualFieldName));
+ }
+ }
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidatorTest.java
new file mode 100644
index 00000000000..e43b1597f59
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ConstantTensorJsonValidatorTest.java
@@ -0,0 +1,207 @@
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.tensor.TensorType;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.Reader;
+import java.io.StringReader;
+
+import static com.yahoo.test.json.JsonTestHelper.inputJson;
+import static com.yahoo.vespa.model.application.validation.ConstantTensorJsonValidator.InvalidConstantTensor;
+
+public class ConstantTensorJsonValidatorTest {
+ private static Reader inputJsonToReader(String... lines) {
+ return new StringReader(inputJson(lines));
+ }
+
+ private static void validateTensorJson(TensorType tensorType, Reader jsonTensorReader) {
+ ConstantTensorJsonValidator validator = new ConstantTensorJsonValidator(jsonTensorReader, tensorType);
+ validator.validate();
+ }
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void ensure_that_unbounded_tensor_works() {
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[], y[])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': '99999', 'y': '47' },",
+ " 'value': 9932.0",
+ " }",
+ " ]",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_bounded_tensor_within_limits_works() {
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[5], y[10])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': '3', 'y': '2' },",
+ " 'value': 2.0",
+ " }",
+ " ]",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_multiple_cells_work() {
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[], y[])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': '3', 'y': '2' },",
+ " 'value': 2.0",
+ " },",
+ " {",
+ " 'address': { 'x': '2', 'y': '0' },",
+ " 'value': 4.5",
+ " }",
+ " ]",
+ "}"));
+ }
+
+
+ @Test
+ public void ensure_that_no_cells_work() {
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[], y[])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': []",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_bounded_tensor_outside_limits_is_disallowed() {
+ expectedException.expect(InvalidConstantTensor.class);
+ expectedException.expectMessage("Coordinate \"9\" not within limits of bounded dimension x");
+
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[5], y[10])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': '9', 'y': '2' },",
+ " 'value': 1e47",
+ " }",
+ " ]",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_mapped_tensor_works() {
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x{}, y{})"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': 'andrei', 'y': 'bjarne' },",
+ " 'value': 2.0",
+ " }",
+ " ]",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_non_integer_strings_in_address_points_are_disallowed() {
+ expectedException.expect(InvalidConstantTensor.class);
+ expectedException.expectMessage("Coordinate \"a\" for dimension x is not an integer");
+
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': 'a' },",
+ " 'value': 47.0",
+ " }",
+ " ]",
+ "}"));
+
+ }
+
+ @Test
+ public void ensure_that_missing_coordinates_fail() {
+ expectedException.expect(InvalidConstantTensor.class);
+ expectedException.expectMessage("Tensor address missing dimension(s): y, z");
+
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[], y[], z[])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': '3' },",
+ " 'value': 99.3",
+ " }",
+ " ]",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_extra_dimensions_are_disallowed() {
+ expectedException.expect(InvalidConstantTensor.class);
+ expectedException.expectMessage("Tensor dimension with name \"z\" does not exist");
+
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[], y[])"),
+ inputJsonToReader(
+ "{",
+ " 'cells': [",
+ " {",
+ " 'address': { 'x': '3', 'y': '2', 'z': '4' },",
+ " 'value': 99.3",
+ " }",
+ " ]",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_invalid_json_fails() {
+ expectedException.expect(InvalidConstantTensor.class);
+ expectedException.expectMessage("Failed to parse JSON stream");
+
+ validateTensorJson(
+ TensorType.fromSpec("tensor(x[], y[])"),
+ inputJsonToReader(
+ "{",
+ " cells': [",
+ " {",
+ " 'address': { 'x': '3' 'y': '2' }",
+ " 'value': 2.0",
+ " }",
+ " ",
+ "}"));
+ }
+
+ @Test
+ public void ensure_that_invalid_json_not_in_tensor_format_fails() {
+ expectedException.expect(InvalidConstantTensor.class);
+ expectedException.expectMessage("Expected field name \"cells\", got \"stats\"");
+
+ validateTensorJson(TensorType.fromSpec("tensor(x[], y[])"),
+ inputJsonToReader(
+ "{",
+ " 'stats': {",
+ " 'grandma-surprise': true,",
+ " 'points': 47",
+ " }",
+ "}"));
+ }
+} \ No newline at end of file
diff --git a/config/pom.xml b/config/pom.xml
index 73c8cd5b5ad..3518426a7bd 100755
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -62,11 +62,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>uk.co.datumedge</groupId>
- <artifactId>hamcrest-json</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>13.0.1</version>
diff --git a/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java b/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java
index 7ad7144a1c8..2bc1a10b729 100644
--- a/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/CfgConfigPayloadBuilderTest.java
@@ -11,8 +11,8 @@ import org.junit.Test;
import java.util.Arrays;
import java.util.List;
-import static com.yahoo.config.subscription.util.JsonHelper.assertJsonEquals;
-import static com.yahoo.config.subscription.util.JsonHelper.inputJson;
+import static com.yahoo.test.json.JsonTestHelper.assertJsonEquals;
+import static com.yahoo.test.json.JsonTestHelper.inputJson;
import static org.junit.Assert.assertEquals;
/**
diff --git a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java
index d3713eaa401..342e50821f4 100644
--- a/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java
+++ b/config/src/test/java/com/yahoo/config/subscription/ConfigInstanceSerializerTest.java
@@ -1,22 +1,21 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.subscription;
-import com.yahoo.foo.ArraytypesConfig;
import com.yahoo.config.ConfigInstance;
+import com.yahoo.foo.ArraytypesConfig;
+import com.yahoo.foo.MaptypesConfig;
import com.yahoo.foo.SimpletypesConfig;
import com.yahoo.foo.SpecialtypesConfig;
import com.yahoo.foo.StructtypesConfig;
-import com.yahoo.foo.MaptypesConfig;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
-
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import static com.yahoo.config.subscription.util.JsonHelper.assertJsonEquals;
-import static com.yahoo.config.subscription.util.JsonHelper.inputJson;
+import static com.yahoo.test.json.JsonTestHelper.assertJsonEquals;
+import static com.yahoo.test.json.JsonTestHelper.inputJson;
import static org.junit.Assert.fail;
/**
diff --git a/config/src/test/java/com/yahoo/config/subscription/util/JsonHelper.java b/config/src/test/java/com/yahoo/config/subscription/util/JsonHelper.java
deleted file mode 100644
index 27ac1a1278c..00000000000
--- a/config/src/test/java/com/yahoo/config/subscription/util/JsonHelper.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.config.subscription.util;
-
-import com.google.common.base.Joiner;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
-
-/**
- * @author Vegard Sjonfjell
- */
-public class JsonHelper {
- /**
- * Convenience method to input JSON without escaping double quotes and newlines
- * Each parameter represents a line of JSON encoded data
- * The lines are joined with newline and single quotes are replaced with double quotes
- */
- public static String inputJson(String... lines) {
- return Joiner.on("\n").join(lines).replaceAll("'", "\"");
- }
-
- /**
- * Structurally compare two JSON encoded strings
- */
- public static void assertJsonEquals(String inputJson, String expectedJson) {
- assertThat(inputJson, sameJSONAs(expectedJson));
- }
-}
diff --git a/document/pom.xml b/document/pom.xml
index 529107407ca..71713b27050 100644
--- a/document/pom.xml
+++ b/document/pom.xml
@@ -48,11 +48,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>uk.co.datumedge</groupId>
- <artifactId>hamcrest-json</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>com.yahoo.vespa</groupId>
<artifactId>config</artifactId>
<version>${project.version}</version>
@@ -83,6 +78,12 @@
<classifier>no_aop</classifier>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>testutil</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/document/src/main/java/com/yahoo/document/json/JsonWriter.java b/document/src/main/java/com/yahoo/document/json/JsonWriter.java
index 626c97a958a..420a6bb6669 100644
--- a/document/src/main/java/com/yahoo/document/json/JsonWriter.java
+++ b/document/src/main/java/com/yahoo/document/json/JsonWriter.java
@@ -112,6 +112,7 @@ public class JsonWriter implements DocumentWriter {
Map.Entry<Field, FieldValue> entry = i.next();
entry.getValue().serialize(entry.getKey(), this);
}
+
generator.writeEndObject();
generator.writeEndObject();
generator.flush();
diff --git a/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java b/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
index b3deae547ab..de483186d6c 100644
--- a/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
+++ b/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
@@ -19,8 +19,8 @@ import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import static com.yahoo.document.json.JsonTestHelper.assertJsonEquals;
-import static com.yahoo.document.json.JsonTestHelper.inputJson;
+import static com.yahoo.test.json.JsonTestHelper.assertJsonEquals;
+import static com.yahoo.test.json.JsonTestHelper.inputJson;
/**
* @author Vegard Sjonfjell
diff --git a/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java b/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
index 466a915f83f..206ab8e30f0 100644
--- a/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
+++ b/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
@@ -59,7 +59,7 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
-import static com.yahoo.document.json.JsonTestHelper.inputJson;
+import static com.yahoo.test.json.JsonTestHelper.inputJson;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
diff --git a/testutil/pom.xml b/testutil/pom.xml
index 0d2aa8a4dbf..bd46d2e4c16 100644
--- a/testutil/pom.xml
+++ b/testutil/pom.xml
@@ -15,6 +15,11 @@
<description>Library of useful Hamcrest matchers.</description>
<dependencies>
<dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>compile</scope>
@@ -25,6 +30,11 @@
<scope>compile</scope>
</dependency>
<dependency>
+ <groupId>uk.co.datumedge</groupId>
+ <artifactId>hamcrest-json</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>
diff --git a/document/src/test/java/com/yahoo/document/json/JsonTestHelper.java b/testutil/src/main/java/com/yahoo/test/json/JsonTestHelper.java
index 09e6a74e68a..a8e8e562b2d 100644
--- a/document/src/test/java/com/yahoo/document/json/JsonTestHelper.java
+++ b/testutil/src/main/java/com/yahoo/test/json/JsonTestHelper.java
@@ -1,8 +1,9 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.document.json;
+package com.yahoo.test.json;
import com.google.common.base.Joiner;
-import static org.hamcrest.MatcherAssert.assertThat;
+import org.hamcrest.MatcherAssert;
+
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
/**
@@ -23,7 +24,6 @@ public class JsonTestHelper {
* Structurally compare two JSON encoded strings
*/
public static void assertJsonEquals(String inputJson, String expectedJson) {
- assertThat(inputJson, sameJSONAs(expectedJson));
+ MatcherAssert.assertThat(inputJson, sameJSONAs(expectedJson));
}
-
}
diff --git a/vespa-http-client/pom.xml b/vespa-http-client/pom.xml
index 9e2325e8016..563e4406fe9 100644
--- a/vespa-http-client/pom.xml
+++ b/vespa-http-client/pom.xml
@@ -83,6 +83,12 @@
<artifactId>airline</artifactId>
<version>0.6</version>
</dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>testutil</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<plugins>
diff --git a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/runner/JsonReaderTest.java b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/runner/JsonReaderTest.java
index ab7dca0d5fb..eba8791bbb7 100644
--- a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/runner/JsonReaderTest.java
+++ b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/runner/JsonReaderTest.java
@@ -1,7 +1,6 @@
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.http.client.runner;
-import com.google.common.base.Joiner;
import com.yahoo.vespa.http.client.FeedClient;
import com.yahoo.vespa.http.client.core.JsonReader;
import org.junit.Test;
@@ -13,6 +12,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import static com.yahoo.test.json.JsonTestHelper.inputJson;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
@@ -260,13 +260,4 @@ public class JsonReaderTest {
assertThat(session.documentIds.size(), is(1));
assertThat(session.documentIds.get(0), is("id:foo:music:doc:foo:bar"));
}
-
- /**
- * Convenience method to input JSON without escaping double quotes and newlines
- * Each parameter represents a line of JSON encoded data
- * The lines are joined with newline and single quotes are replaced with double quotes
- */
- static String inputJson(String... lines) {
- return Joiner.on("\n").join(lines).replaceAll("'", "\"");
- }
} \ No newline at end of file