/*********************************************************************************************************************** * * Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu) * * Licensed 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 eu.stratosphere.api.common.typeutils; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Arrays; import junit.framework.Assert; import org.apache.commons.lang3.SerializationException; import org.apache.commons.lang3.SerializationUtils; import org.junit.Test; import eu.stratosphere.core.memory.DataInputView; import eu.stratosphere.core.memory.DataOutputView; /** * Abstract test base for serializers. */ public abstract class SerializerTestBase<T> { protected abstract TypeSerializer<T> createSerializer(); protected abstract int getLength(); protected abstract Class<T> getTypeClass(); protected abstract T[] getTestData(); // -------------------------------------------------------------------------------------------- @Test public void testInstantiate() { try { TypeSerializer<T> serializer = getSerializer(); T instance = serializer.createInstance(); assertNotNull("The created instance must not be null.", instance); Class<T> type = getTypeClass(); assertNotNull("The test is corrupt: type class is null.", type); assertEquals("Type of the instantiated object is wrong.", type, instance.getClass()); } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testGetLength() { try { TypeSerializer<T> serializer = getSerializer(); assertEquals(getLength(), serializer.getLength()); } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testCopyIntoNewElements() { try { TypeSerializer<T> serializer = getSerializer(); T[] testData = getData(); for (T datum : testData) { T copy = serializer.copy(datum, serializer.createInstance()); deepEquals("Copied element is not equal to the original element.", datum, copy); } } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testCopyIntoReusedElements() { try { TypeSerializer<T> serializer = getSerializer(); T[] testData = getData(); T target = serializer.createInstance(); for (T datum : testData) { T copy = serializer.copy(datum, target); deepEquals("Copied element is not equal to the original element.", datum, copy); target = copy; } } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testSerializeIndividually() { try { TypeSerializer<T> serializer = getSerializer(); T[] testData = getData(); for (T value : testData) { TestOutputView out = new TestOutputView(); serializer.serialize(value, out); TestInputView in = out.getInputView(); assertTrue("No data available during deserialization.", in.available() > 0); T deserialized = serializer.deserialize(serializer.createInstance(), in); deepEquals("Deserialized value if wrong.", value, deserialized); assertTrue("Trailing data available after deserialization.", in.available() == 0); } } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testSerializeIndividuallyReusingValues() { try { TypeSerializer<T> serializer = getSerializer(); T[] testData = getData(); T reuseValue = serializer.createInstance(); for (T value : testData) { TestOutputView out = new TestOutputView(); serializer.serialize(value, out); TestInputView in = out.getInputView(); assertTrue("No data available during deserialization.", in.available() > 0); T deserialized = serializer.deserialize(reuseValue, in); deepEquals("Deserialized value if wrong.", value, deserialized); assertTrue("Trailing data available after deserialization.", in.available() == 0); reuseValue = deserialized; } } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testSerializeAsSequence() { try { TypeSerializer<T> serializer = getSerializer(); T[] testData = getData(); TestOutputView out = new TestOutputView(); for (T value : testData) { serializer.serialize(value, out); } TestInputView in = out.getInputView(); T reuseValue = serializer.createInstance(); int num = 0; while (in.available() > 0) { T deserialized = serializer.deserialize(reuseValue, in); deepEquals("Deserialized value if wrong.", testData[num], deserialized); reuseValue = deserialized; num++; } assertEquals("Wrong number of elements deserialized.", testData.length, num); } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testSerializedCopyIndividually() { try { TypeSerializer<T> serializer = getSerializer(); T[] testData = getData(); for (T value : testData) { TestOutputView out = new TestOutputView(); serializer.serialize(value, out); TestInputView source = out.getInputView(); TestOutputView target = new TestOutputView(); serializer.copy(source, target); TestInputView toVerify = target.getInputView(); assertTrue("No data available copying.", toVerify.available() > 0); T deserialized = serializer.deserialize(serializer.createInstance(), toVerify); deepEquals("Deserialized value if wrong.", value, deserialized); assertTrue("Trailing data available after deserialization.", toVerify.available() == 0); } } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testSerializedCopyAsSequence() { try { TypeSerializer<T> serializer = getSerializer(); T[] testData = getData(); TestOutputView out = new TestOutputView(); for (T value : testData) { serializer.serialize(value, out); } TestInputView source = out.getInputView(); TestOutputView target = new TestOutputView(); for (int i = 0; i < testData.length; i++) { serializer.copy(source, target); } TestInputView toVerify = target.getInputView(); int num = 0; while (toVerify.available() > 0) { T deserialized = serializer.deserialize(serializer.createInstance(), toVerify); deepEquals("Deserialized value if wrong.", testData[num], deserialized); num++; } assertEquals("Wrong number of elements copied.", testData.length, num); } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } @Test public void testSerializabilityAndEquals() { try { TypeSerializer<T> ser1 = getSerializer(); TypeSerializer<T> ser2; try { ser2 = SerializationUtils.clone(ser1); } catch (SerializationException e) { fail("The serializer is not serializable."); return; } assertEquals("The copy of the serializer is not equal to the original one.", ser1, ser2); } catch (Exception e) { System.err.println(e.getMessage()); e.printStackTrace(); fail("Exception in test: " + e.getMessage()); } } // -------------------------------------------------------------------------------------------- protected void deepEquals(String message, T should, T is) { if (should.getClass().isArray()) { if (should instanceof boolean[]) { Assert.assertTrue(message, Arrays.equals((boolean[]) should, (boolean[]) is)); } else if (should instanceof byte[]) { assertArrayEquals(message, (byte[]) should, (byte[]) is); } else if (should instanceof short[]) { assertArrayEquals(message, (short[]) should, (short[]) is); } else if (should instanceof int[]) { assertArrayEquals(message, (int[]) should, (int[]) is); } else if (should instanceof long[]) { assertArrayEquals(message, (long[]) should, (long[]) is); } else if (should instanceof float[]) { assertArrayEquals(message, (float[]) should, (float[]) is, 0.0f); } else if (should instanceof double[]) { assertArrayEquals(message, (double[]) should, (double[]) is, 0.0); } else if (should instanceof char[]) { assertArrayEquals(message, (char[]) should, (char[]) is); } else { assertArrayEquals(message, (Object[]) should, (Object[]) is); } } else { assertEquals(message, should, is); } } // -------------------------------------------------------------------------------------------- private TypeSerializer<T> getSerializer() { TypeSerializer<T> serializer = createSerializer(); if (serializer == null) { throw new RuntimeException("Test case corrupt. Returns null as serializer."); } return serializer; } private T[] getData() { T[] data = getTestData(); if (data == null) { throw new RuntimeException("Test case corrupt. Returns null as test data."); } return data; } // -------------------------------------------------------------------------------------------- private static final class TestOutputView extends DataOutputStream implements DataOutputView { public TestOutputView() { super(new ByteArrayOutputStream(4096)); } public TestInputView getInputView() { ByteArrayOutputStream baos = (ByteArrayOutputStream) out; return new TestInputView(baos.toByteArray()); } @Override public void skipBytesToWrite(int numBytes) throws IOException { for (int i = 0; i < numBytes; i++) { write(0); } } @Override public void write(DataInputView source, int numBytes) throws IOException { byte[] buffer = new byte[numBytes]; source.readFully(buffer); write(buffer); } } private static final class TestInputView extends DataInputStream implements DataInputView { public TestInputView(byte[] data) { super(new ByteArrayInputStream(data)); } @Override public void skipBytesToRead(int numBytes) throws IOException { while (numBytes > 0) { int skipped = skipBytes(numBytes); numBytes -= skipped; } } } }