1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.testframework;
19
20import junit.framework.Assert;
21import junit.framework.TestCase;
22import junit.framework.TestSuite;
23
24import java.io.IOException;
25import java.io.OutputStream;
26import java.util.Arrays;
27import java.util.Random;
28
29/**
30 * Tests behaviour common to all implementations of {@link OutputStream}. This
31 * adapts streams that collects untransformed bytes so that they may be tested.
32 */
33public abstract class SinkTester {
34
35    private boolean throwsExceptions = true;
36
37    /**
38     * Creates a new output stream ready to receive an arbitrary number of
39     * bytes. Each time this method is invoked, any previously returned output
40     * streams may be discarded.
41     */
42    public abstract OutputStream create() throws Exception;
43
44    /**
45     * Returns the current set of bytes written to the output stream last
46     * returned by {@link #create}, and releases any resources held by that
47     * stream.
48     */
49    public abstract byte[] getBytes() throws Exception;
50
51    /**
52     * Configures whether the stream is expected to throw exceptions when an
53     * error is encountered. Classes like {@code PrintStream} report errors via
54     * an API method instead.
55     */
56    public SinkTester setThrowsExceptions(boolean throwsExceptions) {
57        this.throwsExceptions = throwsExceptions;
58        return this;
59    }
60
61    public final TestSuite createTests() {
62        TestSuite result = new TestSuite();
63        result.addTest(new SinkTestCase("sinkTestNoWriting"));
64        result.addTest(new SinkTestCase("sinkTestWriteZeroBytes"));
65        result.addTest(new SinkTestCase("sinkTestWriteByteByByte"));
66        result.addTest(new SinkTestCase("sinkTestWriteArray"));
67        result.addTest(new SinkTestCase("sinkTestWriteOffset"));
68        result.addTest(new SinkTestCase("sinkTestWriteLargeArray"));
69
70        if (throwsExceptions) {
71            result.addTest(new SinkTestCase("sinkTestWriteAfterClose"));
72        } else {
73            result.addTest(new SinkTestCase("sinkTestWriteAfterCloseSuppressed"));
74        }
75
76        return result;
77    }
78
79    @Override
80    public String toString() {
81        return getClass().getName();
82    }
83
84    private static void assertArrayEquals(byte[] expected, byte[] actual) {
85        Assert.assertEquals(Arrays.toString(expected), Arrays.toString(actual));
86    }
87
88    public class SinkTestCase extends TestCase {
89
90        private SinkTestCase(String name) {
91            super(name);
92        }
93
94        public void sinkTestNoWriting() throws Exception {
95            byte[] expected = new byte[] { };
96
97            OutputStream out = create();
98            out.close();
99            assertArrayEquals(expected, getBytes());
100        }
101
102        public void sinkTestWriteZeroBytes() throws Exception {
103            byte[] expected = new byte[] { };
104
105            OutputStream out = create();
106            byte[] a = new byte[1024];
107            out.write(a, 1000, 0);
108            out.write(a, 0, 0);
109            out.write(new byte[] { });
110
111            out.close();
112            assertArrayEquals(expected, getBytes());
113        }
114
115        public void sinkTestWriteByteByByte() throws Exception {
116            byte[] expected = new byte[] { 5, 6, 7, 3, 4, 5, 3, 2, 1 };
117
118            OutputStream out = create();
119            for (byte b : expected) {
120                out.write(b);
121            }
122
123            out.close();
124            assertArrayEquals(expected, getBytes());
125        }
126
127        public void sinkTestWriteArray() throws Exception {
128            byte[] expected = new byte[] {
129                    5, 6,
130                    7, 3, 4, 5,
131                    3, 2, 1
132            };
133
134            OutputStream out = create();
135
136            byte[] a = new byte[] { 5, 6 };
137            out.write(a);
138
139            byte[] b = new byte[] { 7, 3, 4, 5 };
140            out.write(b);
141
142            byte[] c = new byte[] { 3, 2, 1 };
143            out.write(c);
144
145            out.close();
146            assertArrayEquals(expected, getBytes());
147        }
148
149        public void sinkTestWriteOffset() throws Exception {
150            byte[] expected = new byte[] {
151                    5, 6,
152                    7, 3, 4, 5,
153                    3, 2, 1
154            };
155
156            OutputStream out = create();
157
158            byte[] a = new byte[1024];
159            a[1000] = 5;
160            a[1001] = 6;
161            out.write(a, 1000, 2);
162
163            byte[] b = new byte[1024];
164            b[1020] = 7;
165            b[1021] = 3;
166            b[1022] = 4;
167            b[1023] = 5;
168            out.write(b, 1020, 4);
169
170            byte[] c = new byte[1024];
171            c[0] = 3;
172            c[1] = 2;
173            c[2] = 1;
174            out.write(c, 0, 3);
175
176            out.close();
177            assertArrayEquals(expected, getBytes());
178        }
179
180        public void sinkTestWriteLargeArray() throws Exception {
181            byte[] expected = new byte[(1024 * 1024) + 1]; // 1 MB + 1 byte
182            new Random().nextBytes(expected);
183
184            OutputStream out = create();
185            out.write(expected);
186            out.close();
187
188            assertArrayEquals(expected, getBytes());
189        }
190
191        public void sinkTestWriteAfterClose() throws Exception {
192            byte[] expectedBytes = { 5, 6 };
193            OutputStream out = create();
194
195            out.write(expectedBytes);
196            out.close();
197
198            try {
199                out.write(new byte[] { 7, 3, 4, 5 });
200                fail("expected already closed exception");
201            } catch (IOException expected) {
202            }
203
204            assertArrayEquals(expectedBytes, getBytes());
205        }
206
207        public void sinkTestWriteAfterCloseSuppressed() throws Exception {
208            OutputStream out = create();
209            out.write(new byte[] { 5, 6 });
210            out.close();
211            out.write(new byte[] { 7, 3, 4, 5 }); // no exception expected!
212        }
213
214        // adding a new test? Don't forget to update createTests().
215
216        @Override
217        public String getName() {
218            return SinkTester.this.toString() + ":" + super.getName();
219        }
220    }
221}
222