1/*
2 * Copyright (C) 2010 The Android Open Source Project
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
17package libcore.java.io;
18
19import java.io.ByteArrayInputStream;
20import java.io.ByteArrayOutputStream;
21import java.io.IOException;
22import java.io.ObjectInputStream;
23import java.io.ObjectOutputStream;
24import java.io.Serializable;
25import java.util.Arrays;
26import java.util.List;
27import junit.framework.TestCase;
28
29public final class ObjectOutputStreamTest extends TestCase {
30    public void testLongString() throws Exception {
31        // Most modified UTF-8 is limited to 64KiB, but serialized strings can have an 8-byte
32        // length, so this should never throw java.io.UTFDataFormatException...
33        StringBuilder sb = new StringBuilder();
34        for (int i = 0; i < 64*1024 * 2; ++i) {
35            sb.append('a');
36        }
37        String s = sb.toString();
38        ObjectOutputStream os = new ObjectOutputStream(new ByteArrayOutputStream());
39        os.writeObject(s);
40    }
41
42    public static class CallsCloseInWriteObjectMethod implements Serializable {
43        private String message;
44
45        public CallsCloseInWriteObjectMethod(String message) {
46            this.message = message;
47        }
48
49        private void writeObject(ObjectOutputStream oos) throws IOException {
50            oos.writeObject(message);
51            oos.close();
52        }
53
54        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
55            message = (String) ois.readObject();
56        }
57
58        @Override
59        public boolean equals(Object o) {
60            if (this == o) {
61                return true;
62            }
63            if (o == null || getClass() != o.getClass()) {
64                return false;
65            }
66
67            CallsCloseInWriteObjectMethod that = (CallsCloseInWriteObjectMethod) o;
68
69            return message.equals(that.message);
70        }
71
72        @Override
73        public int hashCode() {
74            return message.hashCode();
75        }
76    }
77
78    // http://b/28159133
79    public void testCloseInWriteObject() throws Exception {
80        String hello = "Hello";
81        CallsCloseInWriteObjectMethod object = new CallsCloseInWriteObjectMethod(hello);
82        // This reproduces the problem in http://b/28159133 as follows:
83        //   the list class gets handle N
84        //   the object closes the ObjectOutputStream and clears the handle table
85        //   the hello gets handle N
86        //   the reuse of hello has a reference to handle N
87        // When it is deserialized the list contains object, hello, Arrays.asList().getClass()
88        // instead of object, hello, hello.
89        List<Serializable> input = Arrays.asList(object, hello, hello);
90        @SuppressWarnings("unchecked")
91        List<CallsCloseInWriteObjectMethod> output = (List<CallsCloseInWriteObjectMethod>)
92                roundTrip(input);
93
94        assertEquals(input, output);
95    }
96
97    private Serializable roundTrip(Object object)
98            throws IOException, ClassNotFoundException {
99        ByteArrayOutputStream baos = new ByteArrayOutputStream();
100        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
101            oos.writeObject(object);
102        }
103
104        Serializable read;
105        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
106        try (ObjectInputStream ois = new ObjectInputStream(bais)) {
107            read = (Serializable) ois.readObject();
108        }
109        return read;
110    }
111}
112