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