/* * Copyright (c) 2014 Oculus Info Inc. http://www.oculusinfo.com/ * * Released under the MIT License. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.oculusinfo.binning.io.serialization.impl; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; import org.junit.Assert; import org.junit.Test; import com.oculusinfo.binning.TileData; import com.oculusinfo.binning.TileIndex; import com.oculusinfo.binning.impl.DenseTileData; import com.oculusinfo.binning.impl.SparseTileData; import com.oculusinfo.binning.io.serialization.SerializationTypeChecker; import com.oculusinfo.binning.io.serialization.TileSerializer; import com.oculusinfo.binning.util.TypeDescriptor; import com.oculusinfo.factory.ConfigurationException; public class KryoSerializationTests extends SerializerTestUtils { private static final Class<?>[] EMPTY = new Class<?>[0]; @Test public void testMetaDataSerialization () throws Exception { TileIndex index = new TileIndex(0, 0, 0, 2, 2); TileData<Double> tile = new DenseTileData<>(index); tile.setBin(0, 0, 1.0); tile.setBin(0, 1, 2.0); tile.setBin(1, 0, 3.0); tile.setBin(1, 1, 4.0); tile.setMetaData("a", "abc"); tile.setMetaData("b", "bcd"); TileSerializer<Double> serializer = new KryoSerializer<Double>(new TypeDescriptor(Double.class)); ByteArrayOutputStream output = new ByteArrayOutputStream(); serializer.serialize(tile, output); output.flush(); output.close(); ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); TileData<Double> received = serializer.deserialize(index, input); Assert.assertEquals(2, received.getMetaDataProperties().size()); Assert.assertTrue(received.getMetaDataProperties().contains("a")); Assert.assertTrue(received.getMetaDataProperties().contains("b")); Assert.assertEquals("abc", received.getMetaData("a")); Assert.assertEquals("bcd", received.getMetaData("b")); } @SafeVarargs final <T> void testRoundTripDense(TypeDescriptor type, Class<?>[] classesToRegister, T... data) throws Exception { TileSerializer<T> serializer = new KryoSerializer<T>(type, classesToRegister); // Create our tile int size = (int) Math.ceil(Math.sqrt(data.length)); TileData<T> input = new DenseTileData<T>(new TileIndex(0, 0, 0, size, size)); for (int y=0; y<size; ++y) { for (int x=0; x<size; ++x) { int i = (x+size*y) % data.length; input.setBin(x, y, data[i]); } } // Send it round-trip through serialization ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.serialize(input, baos); baos.flush(); baos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); TileData<T> output = serializer.deserialize(new TileIndex(1, 1, 1, size, size), bais); // Test to make sure output matches input. Assert.assertEquals(input.getDefinition(), output.getDefinition()); for (int y=0; y<size; ++y) { for (int x=0; x<size; ++x) { Assert.assertEquals(input.getBin(x, y), output.getBin(x, y)); } } } @SafeVarargs final <T> void testRoundTripSparse(TypeDescriptor type, Class<?>[] classesToRegister, T defaultValue, T... data) throws Exception { TileSerializer<T> serializer = new KryoSerializer<T>(type, classesToRegister); // Create our tile int size = (int) Math.ceil(Math.sqrt(data.length*2)); TileData<T> input = new SparseTileData<T>(new TileIndex(0, 0, 0, size, size), defaultValue); int i = 0; for (int y=0; y<size; ++y) { for (int x=0; x<size; ++x) { if (0 == ((x+y)%2) && i < data.length) { input.setBin(x, y, data[i]); ++i; } } } // Send it round-trip through serialization ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.serialize(input, baos); baos.flush(); baos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); TileData<T> output = serializer.deserialize(new TileIndex(1, 1, 1, size, size), bais); // Test to make sure output matches input. Assert.assertEquals(input.getDefinition(), output.getDefinition()); for (int y=0; y<size; ++y) { for (int x=0; x<size; ++x) { Assert.assertEquals(input.getBin(x, y), output.getBin(x, y)); } } } @Test public void testBoolean () throws Exception { testRoundTripDense(new TypeDescriptor(Boolean.class), EMPTY, true, false, true, true, false, true, false, false, true); testRoundTripSparse(new TypeDescriptor(Boolean.class), EMPTY, true, true, false); } @Test public void testInteger () throws Exception { testRoundTripDense(new TypeDescriptor(Integer.class), EMPTY, 0, 1, 4, 9, 16, 25, 36, 49, 64); testRoundTripSparse(new TypeDescriptor(Integer.class), EMPTY, -1, 0, 1, 4, 9); } @Test public void testLong () throws Exception { testRoundTripDense(new TypeDescriptor(Long.class), EMPTY, 0L, 1L, 8L, 27L, 64L, 125L, 216L, 343L, 512L); testRoundTripSparse(new TypeDescriptor(Long.class), EMPTY, -3L, 1L, 8L, 27L, 64L); } @Test public void testFloat () throws Exception { testRoundTripDense(new TypeDescriptor(Float.class), EMPTY, 0.0f, 0.5f, 0.333f, 0.25f, 0.2f, 0.166f, 0.142857f, 0.125f); testRoundTripSparse(new TypeDescriptor(Float.class), EMPTY, -3.5f, 0.0f, 0.5f, 0.333f, 0.25f); } @Test public void testDouble () throws Exception { testRoundTripDense(new TypeDescriptor(Double.class), EMPTY, 0.0, 1.1, 2.4, 3.9, 4.16, 5.25, 6.36, 7.49, 8.64); testRoundTripSparse(new TypeDescriptor(Double.class), EMPTY, -3.5, 1.1, 2.4, 3.9, 4.16); } @Test public void testString () throws Exception { testRoundTripDense(new TypeDescriptor(String.class), EMPTY, "a", "bb", "ccc", "dddd", "eeeee", "ffffff", "ggggggg", "hhhhhhhh"); testRoundTripSparse(new TypeDescriptor(String.class), EMPTY, "invalid", "a", "bb", "ccc", "dddd"); } @Test public void testCustom () throws Exception { testRoundTripDense(new TypeDescriptor(CustomTestData.class), new Class<?>[] {CustomTestData.class}, new CustomTestData(1, 1.1, "one"), new CustomTestData(2, 2.2, "two"), new CustomTestData(3, 3.3, "three"), new CustomTestData(4, 4.4, "four")); testRoundTripSparse(new TypeDescriptor(CustomTestData.class), new Class<?>[] {CustomTestData.class}, new CustomTestData(-1, -1.1, "empty"), new CustomTestData(1, 1.1, "one"), new CustomTestData(2, 2.2, "two"), new CustomTestData(3, 3.3, "three"), new CustomTestData(4, 4.4, "four")); } static class CustomTestData { int _i; double _d; String _s; CustomTestData () { } CustomTestData (int i, double d, String s) { _i = i; _d = d; _s = s; } @Override public boolean equals (Object obj) { if (this == obj) return true; if (!(obj instanceof CustomTestData)) return false; CustomTestData that = (CustomTestData) obj; return (this._i == that._i && this._d == that._d && this._s.equals(that._s)); } } @Test public void testCompression () throws IOException { TileData<Double> data = new DenseTileData<Double>(new TileIndex(0, 0, 0), 1.1); TileSerializer<Double> serializer = new KryoSerializer<Double>(new TypeDescriptor(Double.class)); ByteArrayOutputStream output = new ByteArrayOutputStream(); serializer.serialize(data, output); output.flush(); output.close(); byte[] buffer = output.toByteArray(); Assert.assertTrue(buffer.length < 256*256); } // Make sure the serializer itself is serializable. @Test public void testSerializerSerializability () throws IOException, ClassNotFoundException, ConfigurationException { KryoSerializer<CustomTestData> input = new KryoSerializer<>(new TypeDescriptor(CustomTestData.class), CustomTestData.class); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(input); oos.flush(); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); Object rawOutput = ois.readObject(); // Make sure our output is as correct as we can. Assert.assertTrue(rawOutput instanceof KryoSerializer<?>); KryoSerializer<?> genericOutput = (KryoSerializer<?>) rawOutput; Assert.assertEquals(new TypeDescriptor(CustomTestData.class), genericOutput.getBinTypeDescription()); TileSerializer<CustomTestData> output = SerializationTypeChecker.checkBinClass(genericOutput, CustomTestData.class, new TypeDescriptor(CustomTestData.class)); // Make sure the two versions serialize something identically TileData<CustomTestData> inputData = new DenseTileData<CustomTestData>(new TileIndex(0, 0, 0, 1, 1)); inputData.setBin(0, 0, new CustomTestData(1, 2.0, "3")); baos = new ByteArrayOutputStream(); input.serialize(inputData, baos); baos.flush(); baos.close(); bais = new ByteArrayInputStream(baos.toByteArray()); TileData<CustomTestData> outputData1 = output.deserialize(new TileIndex(0, 0, 0, 1, 1), bais); baos = new ByteArrayOutputStream(); output.serialize(inputData, baos); baos.flush(); baos.close(); bais = new ByteArrayInputStream(baos.toByteArray()); TileData<CustomTestData> outputData2 = input.deserialize(new TileIndex(0, 0, 0, 1, 1), bais); Assert.assertEquals(1, outputData1.getBin(0, 0)._i); Assert.assertEquals(2.0, outputData1.getBin(0, 0)._d, 1E-12); Assert.assertEquals("3", outputData1.getBin(0, 0)._s); Assert.assertEquals(1, outputData2.getBin(0, 0)._i); Assert.assertEquals(2.0, outputData2.getBin(0, 0)._d, 1E-12); Assert.assertEquals("3", outputData2.getBin(0, 0)._s); } @Test public void testBZip () throws Exception { // Create our serializer KryoSerializer<List<Integer>> serializer = new KryoSerializer<>(new TypeDescriptor(List.class, new TypeDescriptor(Integer.class)), KryoSerializer.Codec.BZIP); // Create a large tile int xN = 256; int yN = 256; int zN = 100; TileIndex index = new TileIndex(0, 0, 0, xN, yN); TileData<List<Integer>> input = new DenseTileData<List<Integer>>(index); for (int x=0; x<xN; ++x) { for (int y=0; y<yN; ++y) { List<Integer> bin = new ArrayList<>(zN); for (int z=0; z<zN; ++z) bin.add((int) Math.floor(Math.random()*1024)); input.setBin(x, y, bin); } } // Write it out ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.serialize(input, baos); baos.flush(); baos.close(); // Read it back in ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); TileData<List<Integer>> output = serializer.deserialize(index, bais); assertListTilesEqual(input, output); } @Test public void testGZip () throws Exception { // Create our serializer KryoSerializer<List<Integer>> serializer = new KryoSerializer<>(new TypeDescriptor(List.class, new TypeDescriptor(Integer.class)), KryoSerializer.Codec.GZIP); // Create a large tile int xN = 256; int yN = 256; int zN = 100; TileIndex index = new TileIndex(0, 0, 0, xN, yN); TileData<List<Integer>> input = new DenseTileData<List<Integer>>(index); for (int x=0; x<xN; ++x) { for (int y=0; y<yN; ++y) { List<Integer> bin = new ArrayList<>(zN); for (int z=0; z<zN; ++z) bin.add((int) Math.floor(Math.random()*1024)); input.setBin(x, y, bin); } } // Write it out ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.serialize(input, baos); baos.flush(); baos.close(); // Read it back in ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); TileData<List<Integer>> output = serializer.deserialize(index, bais); assertListTilesEqual(input, output); } @Test public void testDeflate () throws Exception { // Create our serializer KryoSerializer<List<Integer>> serializer = new KryoSerializer<>(new TypeDescriptor(List.class, new TypeDescriptor(Integer.class)), KryoSerializer.Codec.DEFLATE); // Create a large tile int xN = 256; int yN = 256; int zN = 100; TileIndex index = new TileIndex(0, 0, 0, xN, yN); TileData<List<Integer>> input = new DenseTileData<List<Integer>>(index); for (int x=0; x<xN; ++x) { for (int y=0; y<yN; ++y) { List<Integer> bin = new ArrayList<>(zN); for (int z=0; z<zN; ++z) bin.add((int) Math.floor(Math.random()*1024)); input.setBin(x, y, bin); } } // Write it out ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.serialize(input, baos); baos.flush(); baos.close(); // Read it back in ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); TileData<List<Integer>> output = serializer.deserialize(index, bais); assertListTilesEqual(input, output); } }