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 public String toString() {
80        return getClass().getName();
81    }
82
83    private static void assertArrayEquals(byte[] expected, byte[] actual) {
84        Assert.assertEquals(Arrays.toString(expected), Arrays.toString(actual));
85    }
86
87    public class SinkTestCase extends TestCase {
88
89        private SinkTestCase(String name) {
90            super(name);
91        }
92
93        public void sinkTestNoWriting() throws Exception {
94            byte[] expected = new byte[] {};
95
96            OutputStream out = create();
97            out.close();
98            assertArrayEquals(expected, getBytes());
99        }
100
101        public void sinkTestWriteZeroBytes() throws Exception {
102            byte[] expected = new byte[] {};
103
104            OutputStream out = create();
105            byte[] a = new byte[1024];
106            out.write(a, 1000, 0);
107            out.write(a, 0, 0);
108            out.write(new byte[] {});
109
110            out.close();
111            assertArrayEquals(expected, getBytes());
112        }
113
114        public void sinkTestWriteByteByByte() throws Exception {
115            byte[] expected = new byte[] { 5, 6, 7, 3, 4, 5, 3, 2, 1 };
116
117            OutputStream out = create();
118            for (byte b : expected) {
119                out.write(b);
120            }
121
122            out.close();
123            assertArrayEquals(expected, getBytes());
124        }
125
126        public void sinkTestWriteArray() throws Exception {
127            byte[] expected = new byte[] {
128                    5, 6,
129                    7, 3, 4, 5,
130                    3, 2, 1
131            };
132
133            OutputStream out = create();
134
135            byte[] a = new byte[] { 5, 6 };
136            out.write(a);
137
138            byte[] b = new byte[] { 7, 3, 4, 5 };
139            out.write(b);
140
141            byte[] c = new byte[] { 3, 2, 1 };
142            out.write(c);
143
144            out.close();
145            assertArrayEquals(expected, getBytes());
146        }
147
148        public void sinkTestWriteOffset() throws Exception {
149            byte[] expected = new byte[] {
150                    5, 6,
151                    7, 3, 4, 5,
152                    3, 2, 1
153            };
154
155            OutputStream out = create();
156
157            byte[] a = new byte[1024];
158            a[1000] = 5;
159            a[1001] = 6;
160            out.write(a, 1000, 2);
161
162            byte[] b = new byte[1024];
163            b[1020] = 7;
164            b[1021] = 3;
165            b[1022] = 4;
166            b[1023] = 5;
167            out.write(b, 1020, 4);
168
169            byte[] c = new byte[1024];
170            c[0] = 3;
171            c[1] = 2;
172            c[2] = 1;
173            out.write(c, 0, 3);
174
175            out.close();
176            assertArrayEquals(expected, getBytes());
177        }
178
179        public void sinkTestWriteLargeArray() throws Exception {
180            byte[] expected = new byte[(1024 * 1024) + 1]; // 1 MB + 1 byte
181            new Random().nextBytes(expected);
182
183            OutputStream out = create();
184            out.write(expected);
185            out.close();
186
187            assertArrayEquals(expected, getBytes());
188        }
189
190        public void sinkTestWriteAfterClose() throws Exception {
191            byte[] expectedBytes = { 5, 6 };
192            OutputStream out = create();
193
194            out.write(expectedBytes);
195            out.close();
196
197            try {
198                out.write(new byte[] { 7, 3, 4, 5 });
199                fail("expected already closed exception");
200            } catch (IOException expected) {
201            }
202
203            assertArrayEquals(expectedBytes, getBytes());
204        }
205
206        public void sinkTestWriteAfterCloseSuppressed() throws Exception {
207            OutputStream out = create();
208            out.write(new byte[] { 5, 6 });
209            out.close();
210            out.write(new byte[] { 7, 3, 4, 5 }); // no exception expected!
211        }
212
213        // adding a new test? Don't forget to update createTests().
214
215        @Override public String getName() {
216            return SinkTester.this.toString() + ":" + super.getName();
217        }
218    }
219}
220