1984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson/* 2984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * Copyright (C) 2010 Google Inc. 3984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * 4984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 5984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * you may not use this file except in compliance with the License. 6984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * You may obtain a copy of the License at 7984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * 8984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 9984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * 10984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * Unless required by applicable law or agreed to in writing, software 11984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 12984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * See the License for the specific language governing permissions and 14984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson * limitations under the License. 15984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson */ 16984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 17b416ef5dc224630af2b9393a15ae120b27e4864aJesse Wilsonpackage libcore.util; 18984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 19984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilsonimport java.io.ByteArrayInputStream; 20984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilsonimport java.io.ByteArrayOutputStream; 21984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilsonimport java.io.IOException; 22984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilsonimport java.io.ObjectInputStream; 23984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilsonimport java.io.ObjectOutputStream; 2410078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilsonimport static junit.framework.Assert.assertEquals; 2510078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilsonimport static junit.framework.Assert.assertTrue; 2610078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilsonimport static junit.framework.Assert.fail; 27984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilsonimport junit.framework.AssertionFailedError; 28984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 29b416ef5dc224630af2b9393a15ae120b27e4864aJesse Wilsonpublic class SerializationTester<T> { 30984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson private final String golden; 31984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson private final T value; 32984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 33b416ef5dc224630af2b9393a15ae120b27e4864aJesse Wilson public SerializationTester(T value, String golden) { 34984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson this.golden = golden; 35984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson this.value = value; 36984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 37984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 3819a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson /** 3919a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson * Returns true if {@code a} and {@code b} are equal. Override this if 4019a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson * {@link Object#equals} isn't appropriate or sufficient for this tester's 4119a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson * value type. 4219a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson */ 4319a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson protected boolean equals(T a, T b) { 4419a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson return a.equals(b); 4519a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson } 4619a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson 478033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson /** 488033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson * Verifies that {@code deserialized} is valid. Implementations of this 498033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson * method may mutate {@code deserialized}. 508033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson */ 5110078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson protected void verify(T deserialized) throws Exception {} 52984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 53984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson public void test() { 54984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson try { 55984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson if (golden == null || golden.length() == 0) { 56984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson fail("No golden value supplied! Consider using this: " 57984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson + hexEncode(serialize(value))); 58984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 59984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 60984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson @SuppressWarnings("unchecked") // deserialize should return the proper type 61984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson T deserialized = (T) deserialize(hexDecode(golden)); 6219a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson assertTrue("User-constructed value doesn't equal deserialized golden value", 6319a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson equals(value, deserialized)); 64984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 65984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson @SuppressWarnings("unchecked") // deserialize should return the proper type 66984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson T reserialized = (T) deserialize(serialize(value)); 6719a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson assertTrue("User-constructed value doesn't equal itself, reserialized", 6819a270e90b1e992c1f6639f355ae13564c2f3a6aJesse Wilson equals(value, reserialized)); 698033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson 708033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson // just a sanity check! if this fails, verify() is probably broken 718033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson verify(value); 728033ba2bd4b8eab11e67738ba4d1390e1fb72111Jesse Wilson verify(deserialized); 73984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson verify(reserialized); 74984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 75984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } catch (Exception e) { 76984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson Error failure = new AssertionFailedError(); 77984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson failure.initCause(e); 78984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson throw failure; 79984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 80984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 81984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 821ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson private static byte[] serialize(Object object) throws IOException { 83984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson ByteArrayOutputStream out = new ByteArrayOutputStream(); 84984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson new ObjectOutputStream(out).writeObject(object); 85984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson return out.toByteArray(); 86984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 87984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 881ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException { 89984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes)); 90984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson Object result = in.readObject(); 91984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson assertEquals(-1, in.read()); 92984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson return result; 93984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 94984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 951ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson private static String hexEncode(byte[] bytes) { 96984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson StringBuilder result = new StringBuilder(bytes.length * 2); 97984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson for (byte b : bytes) { 98984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson result.append(String.format("%02x", b)); 99984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 100984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson return result.toString(); 101984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 102984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 1031ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson private static byte[] hexDecode(String s) { 104984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson byte[] result = new byte[s.length() / 2]; 105984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson for (int i = 0; i < result.length; i++) { 106984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson result[i] = (byte) Integer.parseInt(s.substring(i*2, i*2 + 2), 16); 107984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 108984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson return result; 109984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson } 110984dc62f58d1f9611ebccc2598f714c15242a6ebJesse Wilson 11110078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson /** 11210078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson * Returns a serialized-and-deserialized copy of {@code object}. 11310078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson */ 11410078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson public static Object reserialize(Object object) throws IOException, ClassNotFoundException { 11510078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson return deserialize(serialize(object)); 11610078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson } 11710078dabb17441ce2721a8e5e10f275c5d0a426aJesse Wilson 1181ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson public static String serializeHex(Object object) throws IOException { 1191ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson return hexEncode(serialize(object)); 1201ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson } 1211ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson 1221ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson public static Object deserializeHex(String hex) throws IOException, ClassNotFoundException { 1231ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson return deserialize(hexDecode(hex)); 1241ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson } 1251ffc4b2e242d1ba40ceb30b21510f0f26bd5aaa2Jesse Wilson} 126