blob: 9d008ff313bb89831be2c88b8b8d8ebed9fb8b52 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.johnzon.core;
import org.junit.Test;
import javax.json.Json;
import javax.json.JsonValue;
import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonParser;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import static javax.json.JsonValue.ValueType.ARRAY;
import static javax.json.JsonValue.ValueType.OBJECT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class SnippetTest {
@Test
public void simple() {
final JsonValue value = parse("{\"name\":\"string\",\"value\":\"string\",\"type\":\"string\"}");
// This snippet is smaller than the allowed size. It should show in entirety.
assertSnippet(value, "{\"name\":\"string\",\"value\":\"string\",\"type\":\"string\"}", 100);
// This snippet is exactly 50 characters when formatted. We should see no "..." at the end.
assertSnippet(value, "{\"name\":\"string\",\"value\":\"string\",\"type\":\"string\"}", 50);
// This snippet is too large. We should see the "..." at the end.
assertSnippet(value, "{\"name\":\"string\",\"value\":\"stri...", 30);
}
@Test
public void mapOfArray() {
final JsonValue value = parse("{\"name\": [\"red\", \"green\", \"blue\"], \"value\": [\"orange\", \"yellow\", \"purple\"]}");
assertSnippet(value, "{\"name\":[\"red\",\"green\",\"blue\"],\"value\":[\"orange\",\"yellow\",\"purple\"]}", 200);
assertSnippet(value, "{\"name\":[\"red\",\"green\",\"blue\"],\"value\":[\"orange\",\"yellow\",\"purple\"]}", 68);
assertSnippet(value, "{\"name\":[\"red\",\"green\",\"blue\"],\"value\":[\"orange\",\"...", 50);
}
@Test
public void mapOfObject() {
final JsonValue value = parse("{\"name\": {\"name\": \"red\", \"value\": \"green\", \"type\": \"blue\"}," +
" \"value\": {\"name\": \"orange\", \"value\": \"purple\", \"type\": \"yellow\", \"scope\": \"brown\"}}");
assertSnippet(value, "{\"name\":{\"name\":\"red\",\"value\":\"green\",\"type\":\"blue\"}," +
"\"value\":{\"name\":\"orange\",\"value\":\"purple\",\"type\":\"yellow\",\"scope\":\"brown\"}}", 200);
assertSnippet(value, "{\"name\":{\"name\":\"red\",\"value\":\"green\",\"type\":\"blue\"}," +
"\"value\":{\"name\":\"orange\",\"value\":\"purple\",\"type\":\"yellow\",\"scope\":\"brown\"}}", 128);
assertSnippet(value, "{\"name\":{\"name\":\"red\",\"value\":\"green\",\"type\":\"blue\"}," +
"\"value\":{\"name\":\"orange\",\"value\":\"purple\",\"type...", 100);
}
@Test
public void mapOfNestedMaps() {
final JsonValue value = parse("{\"name\": {\"name\": {\"name\": {\"name\": \"red\", \"value\": \"green\", \"type\": \"blue\"}}}}");
assertSnippet(value, "{\"name\":{\"name\":{\"name\":{\"name\":\"red\",\"value\":\"green\",\"type\":\"blue\"}}}}", 100);
assertSnippet(value, "{\"name\":{\"name\":{\"name\":{\"name\":\"red\",\"value\":\"green\",\"type\":\"blue\"}}}}", 71);
assertSnippet(value, "{\"name\":{\"name\":{\"name\":{\"name\":\"red\",\"value\":\"gre...", 50);
}
@Test
public void mapOfString() {
final JsonValue value = parse("{\"name\":\"string\",\"value\":\"string\",\"type\":\"string\"}");
assertSnippet(value, "{\"name\":\"string\",\"value\":\"string\",\"type\":\"string\"}", 100);
assertSnippet(value, "{\"name\":\"string\",\"value\":\"string\",\"type\":\"string\"}", 50);
assertSnippet(value, "{\"name\":\"string\",\"value\":\"stri...", 30);
}
@Test
public void mapOfNumber() {
final JsonValue value = parse("{\"name\":1234,\"value\":5,\"type\":67890,\"scope\":null}");
assertSnippet(value, "{\"name\":1234,\"value\":5,\"type\":67890,\"scope\":null}", 100);
assertSnippet(value, "{\"name\":1234,\"value\":5,\"type\":67890,\"scope\":null}", 49);
assertSnippet(value, "{\"name\":1234,\"value\":5,\"type\":...", 30);
}
@Test
public void mapOfTrue() {
final JsonValue value = parse("{\"name\":true,\"value\":true,\"type\":true,\"scope\":true}");
assertSnippet(value, "{\"name\":true,\"value\":true,\"type\":true,\"scope\":true}", 60);
assertSnippet(value, "{\"name\":true,\"value\":true,\"type\":true,\"scope\":true}", 51);
assertSnippet(value, "{\"name\":true,\"value\":true,\"typ...", 30);
}
@Test
public void mapOfFalse() {
final JsonValue value = parse("{\"name\":false,\"value\":false,\"type\":false}");
assertSnippet(value, "{\"name\":false,\"value\":false,\"type\":false}", 50);
assertSnippet(value, "{\"name\":false,\"value\":false,\"type\":false}", 41);
assertSnippet(value, "{\"name\":false,\"value...", 20);
}
@Test
public void mapOfNull() {
final JsonValue value = parse("{\"name\":null,\"value\":null,\"type\":null,\"scope\":null}");
assertSnippet(value, "{\"name\":null,\"value\":null,\"type\":null,\"scope\":null}", 60);
assertSnippet(value, "{\"name\":null,\"value\":null,\"type\":null,\"scope\":null}", 51);
assertSnippet(value, "{\"name\":null,\"value\":null,\"typ...", 30);
}
@Test
public void arrayOfArray() {
final JsonValue value = parse("[[\"red\",\"green\"], [1,22,333], [{\"r\": 255,\"g\": 165}], [true, false]]");
assertSnippet(value, "[[\"red\",\"green\"],[1,22,333],[{\"r\":255,\"g\":165}],[true,false]]", 100);
assertSnippet(value, "[[\"red\",\"green\"],[1,22,333],[{\"r\":255,\"g\":165}],[true,false]]", 61);
assertSnippet(value, "[[\"red\",\"green\"],[1,22,333],[{\"r\":255,\"g...", 40);
}
@Test
public void arrayOfObject() {
final JsonValue value = parse("[{\"r\": 255,\"g\": \"165\"},{\"g\": 0,\"a\": \"0\"},{\"transparent\": false}]");
assertSnippet(value, "[{\"r\":255,\"g\":\"165\"},{\"g\":0,\"a\":\"0\"},{\"transparent\":false}]", 100);
assertSnippet(value, "[{\"r\":255,\"g\":\"165\"},{\"g\":0,\"a\":\"0\"},{\"transparent\":false}]", 59);
assertSnippet(value, "[{\"r\":255,\"g\":\"165\"},{\"g\":0,\"a...", 30);
}
@Test
public void arrayOfString() {
final JsonValue value = parse("[\"red\", \"green\", \"blue\", \"orange\", \"yellow\", \"purple\"]");
assertSnippet(value, "[\"red\",\"green\",\"blue\",\"orange\",\"yellow\",\"purple\"]", 100);
assertSnippet(value, "[\"red\",\"green\",\"blue\",\"orange\",\"yellow\",\"purple\"]", 49);
assertSnippet(value, "[\"red\",\"green\",\"blue\",\"orange\"...", 30);
}
@Test
public void arrayOfNumber() {
final JsonValue value = parse("[1,22,333,4444,55555,666666,7777777,88888888,999999999]");
assertSnippet(value, "[1,22,333,4444,55555,666666,7777777,88888888,999999999]", 100);
assertSnippet(value, "[1,22,333,4444,55555,666666,7777777,88888888,999999999]", 55);
assertSnippet(value, "[1,22,333,4444,55555,666666,77...", 30);
}
@Test
public void arrayOfTrue() {
final JsonValue value = parse("[true,true,true,true,true,true,true,true]");
assertSnippet(value, "[true,true,true,true,true,true,true,true]", 100);
assertSnippet(value, "[true,true,true,true,true,true,true,true]", 41);
assertSnippet(value, "[true,true,true,true,true,true...", 30);
}
@Test
public void arrayOfFalse() {
final JsonValue value = parse("[false,false,false,false,false,false,false]");
assertSnippet(value, "[false,false,false,false,false,false,false]", 100);
assertSnippet(value, "[false,false,false,false,false,false,false]", 43);
assertSnippet(value, "[false,false,false,false,false...", 30);
}
@Test
public void arrayOfNull() {
final JsonValue value = parse("[null,null,null,null,null,null]");
assertSnippet(value, "[null,null,null,null,null,null]", 50);
assertSnippet(value, "[null,null,null,null,null,null]", 31);
assertSnippet(value, "[null,null,null...", 15);
}
@Test
public void string() {
final JsonValue value = parse("\"This is a \\\"string\\\" with quotes in it. It should be properly escaped.\"");
assertSnippet(value, "\"This is a \\\"string\\\" with quotes in it. It should be properly escaped.\"", 100);
assertSnippet(value, "\"This is a \\\"string\\\" with quotes in it. It should be properly escaped.\"", 73);
assertSnippet(value, "\"This is a \\\"string\\\" with quotes in it. It shoul...", 50);
}
@Test
public void number() {
final JsonValue value = parse("1223334444555556666667777777.88888888999999999");
assertSnippet(value, "1223334444555556666667777777.88888888999999999", 50);
assertSnippet(value, "1223334444555556666667777777.88888888999999999", 46);
assertSnippet(value, "1223334444555556666667777777.8...", 30);
}
@Test
public void trueValue() {
final JsonValue value = parse("true");
assertSnippet(value, "true", 50);
// we don't trim 'true' -- showing users something like 't...' doesn't make much sense
assertSnippet(value, "t...", 1);
}
@Test
public void falseValue() {
final JsonValue value = parse("false");
assertSnippet(value, "false", 50);
// we don't trim 'false' -- showing users something like 'f...' doesn't make much sense
assertSnippet(value, "f...", 1);
}
@Test
public void nullValue() {
final JsonValue value = parse("null");
assertSnippet(value, "null", 50);
// we don't trim 'null' -- showing users something like 'n...' doesn't make much sense
assertSnippet(value, "n...", 1);
}
private JsonValue parse(final String json) {
final JsonParser jsonParser = Json.createParser(new ByteArrayInputStream(json.getBytes()));
return jsonParser.getValue();
}
private void assertSnippet(final JsonValue object, final String expected, final int i) {
final TrackingJsonGeneratorFactory factory = new TrackingJsonGeneratorFactory();
final String actual = new Snippet(i, factory).of(object);
// Assert the resulting string contents
assertEquals(expected, actual);
// Assert the resulting string length
if (expected.endsWith("...")) {
assertEquals(i + 3, actual.length());
} else {
assertTrue(actual.length() <= i);
}
/*
* Close methods are supposed to idempotent and
* safe to call many times, but let's be nice and
* ensure it is only called once.
*/
assertEquals(1, factory.calls.stream()
.filter("close()"::equals)
.count());
/*
* When writing arrays or objects, assert we stopped
* calling write methods on the JsonGenerator once the
* end of the snippet is reached.
*/
if (expected.endsWith("...") && isType(object, ARRAY, OBJECT)) {
/*
* Serialize the json value, truncating nothing
*/
final TrackingJsonGeneratorFactory full = new TrackingJsonGeneratorFactory();
new Snippet(Integer.MAX_VALUE, full).of(object);
/*
* Assert that the calls made in truncated version are less
* than when we serialized the entire json value.
*/
assertTrue(factory.calls.size() < full.calls.size());
}
}
private static boolean isType(final JsonValue value, final JsonValue.ValueType... types) {
for (final JsonValue.ValueType type : types) {
if (value.getValueType() == type) {
return true;
}
}
return false;
}
/**
* Track all calls made to JsonGenerator so we can ensure Snippet is
* not trying to serialize entire Json documents. Despite the output
* of Snippet appearing properly truncated, buffering can still cause
* the entire json value to be serialized.
*
* The only way to test this is not happening is to track all calls
* made to the JsonGenerator and assert they do in fact stop.
*/
public static class TrackingJsonGeneratorFactory extends JsonGeneratorFactoryImpl {
private final List<String> calls = new ArrayList<>();
public TrackingJsonGeneratorFactory() {
super(Collections.EMPTY_MAP);
}
@Override
public JsonGenerator createGenerator(final OutputStream out) {
return new TrackingGenerator(super.createGenerator(out));
}
@Override
public JsonGenerator createGenerator(final Writer writer) {
return new TrackingGenerator(super.createGenerator(writer));
}
class TrackingGenerator implements JsonGenerator {
private final JsonGenerator delegate;
public TrackingGenerator(final JsonGenerator delegate) {
record("<init>");
this.delegate = delegate;
}
private void record(final String method, final Object... args) {
final String argString = Stream.of(args)
.map(Object::toString)
.reduce((s, s2) -> s + "," + s2)
.orElse("");
calls.add(String.format("%s(%s)", method, argString));
}
public List<String> getCalls() {
return calls;
}
@Override
public JsonGenerator writeStartObject() {
record("writeStartObject");
return delegate.writeStartObject();
}
@Override
public JsonGenerator writeStartObject(final String name) {
record("writeStartObject", name);
return delegate.writeStartObject(name);
}
@Override
public JsonGenerator writeStartArray() {
record("writeStartArray");
return delegate.writeStartArray();
}
@Override
public JsonGenerator writeStartArray(final String name) {
record("writeStartArray", name);
return delegate.writeStartArray(name);
}
@Override
public JsonGenerator writeKey(final String name) {
record("writeKey", name);
return delegate.writeKey(name);
}
@Override
public JsonGenerator write(final String name, final JsonValue value) {
record("write", name, value.getValueType());
assertJsonType(value);
return delegate.write(name, value);
}
@Override
public JsonGenerator write(final String name, final String value) {
record("write", name, value);
return delegate.write(name, value);
}
@Override
public JsonGenerator write(final String name, final BigInteger value) {
record("write", name, value);
return delegate.write(name, value);
}
@Override
public JsonGenerator write(final String name, final BigDecimal value) {
record("write", name, value);
return delegate.write(name, value);
}
@Override
public JsonGenerator write(final String name, final int value) {
record("write", name, value);
return delegate.write(name, value);
}
@Override
public JsonGenerator write(final String name, final long value) {
record("write", name, value);
return delegate.write(name, value);
}
@Override
public JsonGenerator write(final String name, final double value) {
record("write", name, value);
return delegate.write(name, value);
}
@Override
public JsonGenerator write(final String name, final boolean value) {
record("write", name, value);
return delegate.write(name, value);
}
@Override
public JsonGenerator writeNull(final String name) {
record("writeNull");
return delegate.writeNull(name);
}
@Override
public JsonGenerator writeEnd() {
record("writeEnd");
return delegate.writeEnd();
}
@Override
public JsonGenerator write(final JsonValue value) {
record("write", value.getValueType());
assertJsonType(value);
return delegate.write(value);
}
@Override
public JsonGenerator write(final String value) {
record("write", value);
return delegate.write(value);
}
@Override
public JsonGenerator write(final BigDecimal value) {
record("write", value);
return delegate.write(value);
}
@Override
public JsonGenerator write(final BigInteger value) {
record("write", value);
return delegate.write(value);
}
@Override
public JsonGenerator write(final int value) {
record("write", value);
return delegate.write(value);
}
@Override
public JsonGenerator write(final long value) {
record("write", value);
return delegate.write(value);
}
@Override
public JsonGenerator write(final double value) {
record("write", value);
return delegate.write(value);
}
@Override
public JsonGenerator write(final boolean value) {
record("write", value);
return delegate.write(value);
}
@Override
public JsonGenerator writeNull() {
record("writeNull");
return delegate.writeNull();
}
@Override
public void close() {
record("close");
delegate.close();
}
@Override
public void flush() {
record("flush");
delegate.flush();
}
/**
* Snippet should not be asking the JsonGenerator to be serializing
* an objet or an array as this would cause the entire portion of
* json to be written even if we need a small chunk.
*/
private void assertJsonType(final JsonValue value) {
if (isType(value, ARRAY, OBJECT)) {
fail("should never be called");
}
}
}
}
}