/* * Copyright 2015 Odnoklassniki Ltd, Mail.Ru Group * * 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 one.nio.serial; import junit.framework.Assert; import junit.framework.TestCase; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.*; import java.util.concurrent.*; public class SerializationTest extends TestCase { private Object clone(Object obj) throws IOException, ClassNotFoundException { CalcSizeStream css = new CalcSizeStream(); css.writeObject(obj); int length = css.count(); byte[] buf = new byte[length]; SerializeStream out = new SerializeStream(buf); out.writeObject(obj); assertEquals(out.count(), length); DeserializeStream in = new DeserializeStream(buf); Object objCopy = in.readObject(); assertEquals(in.count(), length); return objCopy; } private Object cloneViaPersist(Object obj) throws IOException, ClassNotFoundException { PersistStream out = new PersistStream(); out.writeObject(obj); byte[] buf = out.toByteArray(); DeserializeStream in = new DeserializeStream(buf); Object objCopy = in.readObject(); assertEquals(in.count(), buf.length); return objCopy; } private static final Class[] collectionInterfaces = {SortedSet.class, NavigableSet.class, Set.class, Queue.class, List.class}; private static final Class[] mapInterfaces = {SortedMap.class, NavigableMap.class}; private void checkClass(Class<?> cls, Class<?> other) { if (other != cls) { if (Collection.class.isAssignableFrom(cls)) { for (Class<?> iface : collectionInterfaces) { Assert.assertTrue(!iface.isAssignableFrom(cls) || iface.isAssignableFrom(other)); } } if (Map.class.isAssignableFrom(cls)) { for (Class<?> iface : mapInterfaces) { Assert.assertTrue(!iface.isAssignableFrom(cls) || iface.isAssignableFrom(other)); } } } } private void checkSerialize(Object obj) throws IOException, ClassNotFoundException { Object clone1 = clone(obj); checkClass(obj.getClass(), clone1.getClass()); Assert.assertEquals(obj, clone1); Object clone2 = cloneViaPersist(obj); checkClass(obj.getClass(), clone2.getClass()); Assert.assertEquals(obj, clone2); } private void checkSerializeToString(Object obj) throws IOException, ClassNotFoundException { Object objCopy = clone(obj); Assert.assertEquals(obj.toString(), objCopy.toString()); } private String makeString(int length) { char[] s = new char[length]; for (int i = 0; i < length; i++) { s[i] = (char) (i % 10 + '0'); } return new String(s); } private BitSet makeBitSet(int length) { BitSet result = new BitSet(length); result.set(length); return result; } private List makeList(int length) { ArrayList<Integer> result = new ArrayList<Integer>(length * 2); for (int i = 0; i < length; i++) { Integer obj = i; result.add(obj); result.add(obj); } return result; } public void testStrings() throws IOException, ClassNotFoundException { checkSerialize(""); checkSerialize("a"); checkSerialize("\000"); checkSerialize("Short simple sentence!"); checkSerialize("Mix of русские & english языки"); checkSerialize("თავდაცვის"); checkSerialize(makeString(0x7ffe)); checkSerialize(makeString(0x7fff)); checkSerialize(makeString(0x8000)); checkSerialize(makeString(0x8001)); checkSerialize(makeString(0xffff)); checkSerialize(makeString(0x10000)); checkSerialize(makeString(1234567)); } public void testBitSet() throws IOException, ClassNotFoundException { checkSerialize(new BitSet()); checkSerialize(makeBitSet(15)); checkSerialize(makeBitSet(16)); checkSerialize(makeBitSet(17)); checkSerialize(makeBitSet(2000)); checkSerialize(makeBitSet(2047)); checkSerialize(makeBitSet(2048)); checkSerialize(makeBitSet(2049)); checkSerialize(makeBitSet(100000)); } public void testRecursiveRef() throws IOException, ClassNotFoundException { checkSerialize(makeList(0)); checkSerialize(makeList(1)); checkSerialize(makeList(10)); checkSerialize(makeList(32767)); checkSerialize(makeList(32768)); checkSerialize(makeList(50000)); checkSerialize(makeList(65535)); checkSerialize(makeList(65536)); checkSerialize(makeList(200000)); } private enum SimpleEnum { A, B } private enum ComplexEnum { A1, B2, C3 { @Override public String toString() { return "CCC"; } }; final int i = ordinal(); } public void testEnum() throws IOException, ClassNotFoundException { checkSerialize(SimpleEnum.A); checkSerialize(SimpleEnum.B); checkSerialize(ComplexEnum.C3); checkSerialize(ComplexEnum.B2); checkSerialize(ComplexEnum.A1); checkSerialize(new EnumSerializer(SimpleEnum.class)); checkSerialize(new EnumSerializer(ComplexEnum.class)); } public void testUrls() throws IOException, ClassNotFoundException, URISyntaxException { checkSerialize(new URI("socket://192.168.0.1:2222/?param1=value1¶m2=value2")); checkSerialize(new URL("http://www.example.com/somePath/file.txt#anchor")); } public void testExceptions() throws IOException, ClassNotFoundException { Exception e = new NullPointerException(); CalcSizeStream css1 = new CalcSizeStream(); css1.writeObject(e); e.getStackTrace(); CalcSizeStream css2 = new CalcSizeStream(); css2.writeObject(e); Assert.assertEquals(css1.count(), css2.count()); } public void testInetAddress() throws IOException, ClassNotFoundException { checkSerialize(InetAddress.getByName("123.45.67.89")); checkSerialize(InetAddress.getByName("localhost")); checkSerialize(InetAddress.getByAddress(new byte[4])); checkSerialize(InetSocketAddress.createUnresolved("www.example.com", 80)); checkSerialize(new InetSocketAddress(21)); checkSerialize(new InetSocketAddress(InetAddress.getByAddress(new byte[] {8, 8, 8, 8}), 53)); checkSerialize(new InetSocketAddress("google.com", 443)); } public void testBigDecimal() throws IOException, ClassNotFoundException { checkSerialize(new BigInteger("12345678901234567890")); checkSerialize(new BigInteger(-1, new byte[] { 11, 22, 33, 44, 55, 66, 77, 88, 99 })); checkSerialize(new BigDecimal(999.999999999)); checkSerialize(new BigDecimal("88888888888888888.88888888888888888888888")); } public void testStringBuilder() throws IOException, ClassNotFoundException { checkSerializeToString(new StringBuilder()); checkSerializeToString(new StringBuilder("asdasd").append(123).append(true)); checkSerializeToString(new StringBuffer()); checkSerializeToString(new StringBuffer(1000).append(new Object()).append("zzz").append(1234.56789)); } private static class ReadObject1 implements Serializable { private Object[] array = new String[] {"regular", "array"}; private void readObject(ObjectInputStream in) { for (Object o : array) { o.toString(); } } @Override public boolean equals(Object obj) { if (!(obj instanceof ReadObject1)) { return false; } ReadObject1 other = (ReadObject1) obj; return Arrays.equals(array, other.array); } } private static class ReadObject2 implements Serializable { private final Object[] array = new String[] {"final", "field"}; private void readObject(ObjectInputStream in) { for (Object o : array) { o.toString(); } } @Override public boolean equals(Object obj) { if (!(obj instanceof ReadObject2)) { return false; } ReadObject2 other = (ReadObject2) obj; return Arrays.equals(array, other.array); } } public void testCompiledReadObject() throws IOException, ClassNotFoundException { for (int i = 0; i < 20000; i++) { checkSerialize(new ReadObject1()); } for (int i = 0; i < 20000; i++) { checkSerialize(new ReadObject2()); } } private static class User implements Serializable { private long id; private long phone; private String name; public User(long id, long phone, String name) { this.id = id; this.phone = phone; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (id != user.id) return false; if (phone != user.phone) return false; if (!name.equals(user.name)) return false; return true; } @Override public int hashCode() { int result = (int) (id ^ (id >>> 32)); result = 31 * result + (int) (phone ^ (phone >>> 32)); result = 31 * result + name.hashCode(); return result; } } public void testMap() throws IOException, ClassNotFoundException { ConcurrentHashMap<Long, User> users = new ConcurrentHashMap<Long, User>(); users.put(1L, new User(1, 1234567890L, "My Name")); users.put(2L, new User(2, 9876543210L, "Other Name")); checkSerialize(users); } public void testCollections() throws IOException, ClassNotFoundException { Random random = new Random(); ArrayList<Long> list = new ArrayList<Long>(); for (int i = 0; i < 100; i++) { list.add(random.nextLong()); } HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("first", 1); map.put("second", 2); map.put("third", 3); checkSerialize(list); checkSerialize(Arrays.asList(1, 2, 3)); checkSerialize(Collections.emptySet()); checkSerialize(Collections.singleton("abc")); checkSerialize(Collections.singletonMap("Key", "Value")); checkSerialize(Collections.synchronizedSortedSet(new TreeSet<Object>(list))); checkSerialize(Collections.unmodifiableSortedMap(new ConcurrentSkipListMap<Object, Object>())); checkSerialize(EnumSet.noneOf(SimpleEnum.class)); checkSerialize(EnumSet.of(ComplexEnum.C3)); checkSerialize(EnumSet.allOf(ComplexEnum.class)); checkSerialize(new EnumMap<SimpleEnum, Boolean>(Collections.singletonMap(SimpleEnum.A, true))); checkSerialize(Collections.synchronizedSortedMap(new TreeMap<String, Integer>(map))); checkSerialize(list.subList(10, 20)); checkSerialize(map.keySet()); } }