/* * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may not * use this file except in compliance with the License. A copy of the License is * located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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 com.amazonaws.protocol.json; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Date; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.amazonaws.util.StringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import software.amazon.ion.IonException; import software.amazon.ion.IonSystem; import software.amazon.ion.system.IonBinaryWriterBuilder; import software.amazon.ion.system.IonSystemBuilder; import com.fasterxml.jackson.core.JsonToken; /** * Tests that data written by the {@link SdkIonGenerator} is correctly read * by the {@link IonParser}. For additional stand-alone testing of the * {@link IonParser}, see {@link IonParserTest}. */ @RunWith(Parameterized.class) public class IonRoundtripTest { private enum Data { NULL { @Override public void generate(SdkIonGenerator generator) { // Is this the only way to write a null value? generator.writeValue((String)null); generator.writeValue((BigInteger)null); generator.writeValue((BigDecimal)null); generator.writeValue((Date)null); generator.writeValue((ByteBuffer)null); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); assertEquals(JsonToken.VALUE_NULL, parser.nextToken()); } }, BOOL { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(true); generator.writeValue(false); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_TRUE, parser.nextToken()); assertEquals(true, parser.getBooleanValue()); assertEquals(JsonToken.VALUE_FALSE, parser.nextToken()); assertEquals(false, parser.getBooleanValue()); } }, SHORT { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(0); // There's no writeValue(byte) method, but there is writeValue(short)... generator.writeValue(Byte.MAX_VALUE); generator.writeValue(Byte.MIN_VALUE); generator.writeValue(Short.MAX_VALUE); generator.writeValue(Short.MIN_VALUE); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(0, parser.getIntValue()); assertEquals(0, parser.getLongValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Byte.MAX_VALUE, parser.getByteValue()); assertEquals((short)Byte.MAX_VALUE, parser.getShortValue()); assertEquals((int)Byte.MAX_VALUE, parser.getIntValue()); assertEquals((long)Byte.MAX_VALUE, parser.getLongValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Byte.MIN_VALUE, parser.getByteValue()); assertEquals((short)Byte.MIN_VALUE, parser.getShortValue()); assertEquals((int)Byte.MIN_VALUE, parser.getIntValue()); assertEquals((long)Byte.MIN_VALUE, parser.getLongValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Short.MAX_VALUE, parser.getShortValue()); assertEquals((int)Short.MAX_VALUE, parser.getIntValue()); assertEquals((long)Short.MAX_VALUE, parser.getLongValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Short.MIN_VALUE, parser.getShortValue()); assertEquals((int)Short.MIN_VALUE, parser.getIntValue()); assertEquals((long)Short.MIN_VALUE, parser.getLongValue()); } }, INT { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(Integer.MAX_VALUE); generator.writeValue(Integer.MIN_VALUE); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Integer.MAX_VALUE, parser.getIntValue()); assertEquals((long)Integer.MAX_VALUE, parser.getLongValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Integer.MIN_VALUE, parser.getIntValue()); assertEquals((long)Integer.MIN_VALUE, parser.getLongValue()); } }, LONG { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(Long.MAX_VALUE); generator.writeValue(Long.MIN_VALUE); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Long.MAX_VALUE, parser.getLongValue()); assertEquals(BigInteger.valueOf(Long.MAX_VALUE), parser.getBigIntegerValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(Long.MIN_VALUE, parser.getLongValue()); assertEquals(BigInteger.valueOf(Long.MIN_VALUE), parser.getBigIntegerValue()); } }, BIG_INTEGER { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE)); generator.writeValue(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE)); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE), parser.getBigIntegerValue()); try { parser.getLongValue(); } catch (IonException e1) { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE), parser.getBigIntegerValue()); try { parser.getLongValue(); } catch (IonException e2) { return; } } throw new AssertionError("number shouldn't fit in a long"); } }, FLOAT { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(Float.MAX_VALUE); generator.writeValue(Float.MIN_VALUE); generator.writeValue(-Float.MAX_VALUE); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(Float.MAX_VALUE, parser.getFloatValue(), 1e-9); assertEquals((double)Float.MAX_VALUE, parser.getDoubleValue(), 1e-9); assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(Float.MIN_VALUE, parser.getFloatValue(), 1e-9); assertEquals((double)Float.MIN_VALUE, parser.getDoubleValue(), 1e-9); assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(-Float.MAX_VALUE, parser.getFloatValue(), 1e-9); assertEquals((double)-Float.MAX_VALUE, parser.getDoubleValue(), 1e-9); } }, DOUBLE { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(Double.MAX_VALUE); generator.writeValue(Double.MIN_VALUE); generator.writeValue(-Double.MAX_VALUE); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(Double.MAX_VALUE, parser.getDoubleValue(), 1e-9); assertEquals(BigDecimal.valueOf(Double.MAX_VALUE), parser.getDecimalValue()); assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(Float.MIN_VALUE, parser.getDoubleValue(), 1e-9); assertEquals(BigDecimal.valueOf(Double.MIN_VALUE), parser.getDecimalValue()); assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(-Double.MAX_VALUE, parser.getDoubleValue(), 1e-9); assertEquals(BigDecimal.valueOf(-Double.MAX_VALUE), parser.getDecimalValue()); } }, BIG_DECIMAL { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(BigDecimal.valueOf(Double.MAX_VALUE).add(BigDecimal.ONE)); generator.writeValue(BigDecimal.valueOf(-Double.MAX_VALUE).subtract(BigDecimal.ONE)); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(BigDecimal.valueOf(Double.MAX_VALUE).add(BigDecimal.ONE), parser.getDecimalValue()); assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); assertEquals(BigDecimal.valueOf(-Double.MAX_VALUE).subtract(BigDecimal.ONE), parser.getDecimalValue()); } }, TIMESTAMP { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(new Date(0)); // Note: dates too far in the future are rejected by Ion generator.writeValue(new Date(Integer.MAX_VALUE)); generator.writeValue(new Date(Integer.MIN_VALUE)); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); assertEquals(new Date(0), parser.getEmbeddedObject()); assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); assertEquals(new Date(Integer.MAX_VALUE), parser.getEmbeddedObject()); assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); assertEquals(new Date(Integer.MIN_VALUE), parser.getEmbeddedObject()); } }, BYTES { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(ByteBuffer.wrap("foobar".getBytes(StringUtils.UTF8))); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken()); assertEquals(ByteBuffer.wrap("foobar".getBytes(StringUtils.UTF8)), parser.getEmbeddedObject()); } }, EMPTY_STRUCT { @Override public void generate(SdkIonGenerator generator) { generator.writeStartObject(); generator.writeEndObject(); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.START_OBJECT, parser.nextToken()); assertEquals(JsonToken.END_OBJECT, parser.nextToken()); } }, EMPTY_LIST { @Override public void generate(SdkIonGenerator generator) { generator.writeStartArray(); generator.writeEndArray(); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.START_ARRAY, parser.nextToken()); assertEquals(JsonToken.END_ARRAY, parser.nextToken()); } }, STRUCT { @Override public void generate(SdkIonGenerator generator) { generator.writeStartObject(); generator.writeFieldName("int"); generator.writeValue(1); generator.writeFieldName("string"); generator.writeValue("foo"); generator.writeFieldName("bool"); generator.writeValue(false); generator.writeEndObject(); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.START_OBJECT, parser.nextToken()); assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); assertEquals("int", parser.getText()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(1, parser.getIntValue()); assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); assertEquals("string", parser.getText()); assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); assertEquals("foo", parser.getText()); assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); assertEquals("bool", parser.getText()); assertEquals(JsonToken.VALUE_FALSE, parser.nextToken()); assertEquals(false, parser.getBooleanValue()); assertEquals(JsonToken.END_OBJECT, parser.nextToken()); } }, LIST { @Override public void generate(SdkIonGenerator generator) { generator.writeStartArray(); generator.writeValue(1); generator.writeValue("foo"); generator.writeValue(true); generator.writeEndArray(); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.START_ARRAY, parser.nextToken()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(1, parser.getIntValue()); assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); assertEquals("foo", parser.getText()); assertEquals(JsonToken.VALUE_TRUE, parser.nextToken()); assertEquals(true, parser.getBooleanValue()); assertEquals(JsonToken.END_ARRAY, parser.nextToken()); } }, STRUCT_IN_LIST { @Override public void generate(SdkIonGenerator generator) { generator.writeStartArray(); STRUCT.generate(generator); generator.writeEndArray(); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.START_ARRAY, parser.nextToken()); STRUCT.parse(parser); assertEquals(JsonToken.END_ARRAY, parser.nextToken()); } }, LIST_IN_STRUCT { @Override public void generate(SdkIonGenerator generator) { generator.writeStartObject(); generator.writeFieldName("list"); LIST.generate(generator); generator.writeEndObject(); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.START_OBJECT, parser.nextToken()); assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); assertEquals("list", parser.getText()); LIST.parse(parser); assertEquals(JsonToken.END_OBJECT, parser.nextToken()); } }, STRUCT_SKIP { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(42); STRUCT.generate(generator); generator.writeValue("foo"); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(42, parser.getIntValue()); assertEquals(JsonToken.START_OBJECT, parser.nextToken()); parser.skipChildren(); assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); assertEquals("foo", parser.getText()); } }, LIST_SKIP { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(42); LIST.generate(generator); generator.writeValue("foo"); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(42, parser.getIntValue()); assertEquals(JsonToken.START_ARRAY, parser.nextToken()); parser.skipChildren(); assertEquals(JsonToken.END_ARRAY, parser.getCurrentToken()); assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); assertEquals("foo", parser.getText()); } }, NESTED_SKIP { @Override public void generate(SdkIonGenerator generator) { generator.writeValue(42); LIST_IN_STRUCT.generate(generator); generator.writeValue("foo"); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(42, parser.getIntValue()); assertEquals(JsonToken.START_OBJECT, parser.nextToken()); parser.skipChildren(); assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); assertEquals("foo", parser.getText()); } }, NESTED_INNER_SKIP { @Override public void generate(SdkIonGenerator generator) { generator.writeStartArray(); generator.writeValue(42); STRUCT.generate(generator); generator.writeValue("foo"); generator.writeEndArray(); } @Override public void parse(IonParser parser) throws IOException { assertEquals(JsonToken.START_ARRAY, parser.nextToken()); assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(42, parser.getIntValue()); assertEquals(JsonToken.START_OBJECT, parser.nextToken()); parser.skipChildren(); assertEquals(JsonToken.END_OBJECT, parser.getCurrentToken()); assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); assertEquals("foo", parser.getText()); assertEquals(JsonToken.END_ARRAY, parser.nextToken()); } }; public abstract void generate(SdkIonGenerator generator); public abstract void parse(IonParser parser) throws IOException; } @Parameters public static Collection<Object[]> data() { List<Object[]> parameters = new ArrayList<Object[]>(); for (Data data : Data.values()) { parameters.add(new Object[]{ data }); } return parameters; } private static final IonSystem SYSTEM = IonSystemBuilder.standard().build(); private final Data data; public IonRoundtripTest(Data data) { this.data = data; } @Test public void testRoundtrip() throws IOException { SdkIonGenerator generator = SdkIonGenerator.create(IonBinaryWriterBuilder.standard(), "foo"); data.generate(generator); IonParser parser = new IonParser(SYSTEM.newReader(generator.getBytes()), false); data.parse(parser); assertNull(parser.nextToken()); // Asserts data was read fully. assertFalse(parser.hasCurrentToken()); assertFalse(parser.isClosed()); parser.close(); assertTrue(parser.isClosed()); } }