Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2010 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package libcore.util;
     18 
     19 import java.io.ByteArrayInputStream;
     20 import java.io.ByteArrayOutputStream;
     21 import java.io.IOException;
     22 import java.io.ObjectInputStream;
     23 import java.io.ObjectOutputStream;
     24 import static junit.framework.Assert.assertEquals;
     25 import static junit.framework.Assert.assertTrue;
     26 import static junit.framework.Assert.fail;
     27 import junit.framework.AssertionFailedError;
     28 
     29 public class SerializationTester<T> {
     30     private final String golden;
     31     private final T value;
     32 
     33     public SerializationTester(T value, String golden) {
     34         this.golden = golden;
     35         this.value = value;
     36     }
     37 
     38     /**
     39      * Returns true if {@code a} and {@code b} are equal. Override this if
     40      * {@link Object#equals} isn't appropriate or sufficient for this tester's
     41      * value type.
     42      */
     43     protected boolean equals(T a, T b) {
     44         return a.equals(b);
     45     }
     46 
     47     /**
     48      * Verifies that {@code deserialized} is valid. Implementations of this
     49      * method may mutate {@code deserialized}.
     50      */
     51     protected void verify(T deserialized) throws Exception {}
     52 
     53     public void test() {
     54         try {
     55             if (golden == null || golden.length() == 0) {
     56                 fail("No golden value supplied! Consider using this: "
     57                         + hexEncode(serialize(value)));
     58             }
     59 
     60             @SuppressWarnings("unchecked") // deserialize should return the proper type
     61             T deserialized = (T) deserialize(hexDecode(golden));
     62             assertTrue("User-constructed value doesn't equal deserialized golden value",
     63                     equals(value, deserialized));
     64 
     65             @SuppressWarnings("unchecked") // deserialize should return the proper type
     66             T reserialized = (T) deserialize(serialize(value));
     67             assertTrue("User-constructed value doesn't equal itself, reserialized",
     68                     equals(value, reserialized));
     69 
     70             // just a sanity check! if this fails, verify() is probably broken
     71             verify(value);
     72             verify(deserialized);
     73             verify(reserialized);
     74 
     75         } catch (Exception e) {
     76             Error failure = new AssertionFailedError();
     77             failure.initCause(e);
     78             throw failure;
     79         }
     80     }
     81 
     82     private static byte[] serialize(Object object) throws IOException {
     83         ByteArrayOutputStream out = new ByteArrayOutputStream();
     84         new ObjectOutputStream(out).writeObject(object);
     85         return out.toByteArray();
     86     }
     87 
     88     private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
     89         ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
     90         Object result = in.readObject();
     91         assertEquals(-1, in.read());
     92         return result;
     93     }
     94 
     95     private static String hexEncode(byte[] bytes) {
     96         StringBuilder result = new StringBuilder(bytes.length * 2);
     97         for (byte b : bytes) {
     98             result.append(String.format("%02x", b));
     99         }
    100         return result.toString();
    101     }
    102 
    103     private static byte[] hexDecode(String s) {
    104         byte[] result = new byte[s.length() / 2];
    105         for (int i = 0; i < result.length; i++) {
    106             result[i] = (byte) Integer.parseInt(s.substring(i*2, i*2 + 2), 16);
    107         }
    108         return result;
    109     }
    110 
    111     /**
    112      * Returns a serialized-and-deserialized copy of {@code object}.
    113      */
    114     public static Object reserialize(Object object) throws IOException, ClassNotFoundException {
    115         return deserialize(serialize(object));
    116     }
    117 
    118     public static String serializeHex(Object object) throws IOException {
    119         return hexEncode(serialize(object));
    120     }
    121 
    122     public static Object deserializeHex(String hex) throws IOException, ClassNotFoundException {
    123         return deserialize(hexDecode(hex));
    124     }
    125 }
    126