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.Writer;
26import java.util.Arrays;
27import java.util.Random;
28
29/**
30 * Tests behaviour common to all implementations of {@link Writer}. This adapts
31 * writers that collects untransformed chars so that they may be tested.
32 */
33public abstract class CharSinkTester {
34
35    private boolean throwsExceptions = true;
36
37    /**
38     * Creates a new writer ready to receive an arbitrary number of chars. Each
39     * time this method is invoked, any previously returned writers may be
40     * discarded.
41     */
42    public abstract Writer create() throws Exception;
43
44    /**
45     * Returns the current set of chars written to the writer last returned by
46     * {@link #create}, and releases any resources held by that writer.
47     */
48    public abstract char[] getChars() throws Exception;
49
50    /**
51     * Configures whether the writer is expected to throw exceptions when an
52     * error is encountered. Classes like {@code PrintWriter} report errors via
53     * an API method instead.
54     */
55    public CharSinkTester setThrowsExceptions(boolean throwsExceptions) {
56        this.throwsExceptions = throwsExceptions;
57        return this;
58    }
59
60    public final TestSuite createTests() {
61        TestSuite result = new TestSuite();
62        result.addTest(new SinkTestCase("sinkTestNoWriting"));
63        result.addTest(new SinkTestCase("sinkTestWriteZeroChars"));
64        result.addTest(new SinkTestCase("sinkTestWriteCharByChar"));
65        result.addTest(new SinkTestCase("sinkTestWriteArray"));
66        result.addTest(new SinkTestCase("sinkTestWriteOffset"));
67        result.addTest(new SinkTestCase("sinkTestWriteLargeArray"));
68
69        if (throwsExceptions) {
70            result.addTest(new SinkTestCase("sinkTestWriteAfterClose"));
71        } else {
72            result.addTest(new SinkTestCase("sinkTestWriteAfterCloseSuppressed"));
73        }
74
75        return result;
76    }
77
78    @Override public String toString() {
79        return getClass().getName();
80    }
81
82    private static void assertArrayEquals(char[] expected, char[] actual) {
83        Assert.assertEquals(Arrays.toString(expected), Arrays.toString(actual));
84    }
85
86    public class SinkTestCase extends TestCase {
87
88        private SinkTestCase(String name) {
89            super(name);
90        }
91
92        public void sinkTestNoWriting() throws Exception {
93            char[] expected = new char[] {};
94
95            Writer out = create();
96            out.close();
97            assertArrayEquals(expected, getChars());
98        }
99
100        public void sinkTestWriteZeroChars() throws Exception {
101            char[] expected = new char[] {};
102
103            Writer out = create();
104            char[] a = new char[1024];
105            out.write(a, 1000, 0);
106            out.write(a, 0, 0);
107            out.write(new char[] {});
108
109            out.close();
110            assertArrayEquals(expected, getChars());
111        }
112
113        public void sinkTestWriteCharByChar() throws Exception {
114            char[] expected = "EFGCDECBA".toCharArray();
115
116            Writer out = create();
117            for (char c : expected) {
118                out.write(c);
119            }
120
121            out.close();
122            assertArrayEquals(expected, getChars());
123        }
124
125        public void sinkTestWriteArray() throws Exception {
126            char[] expected = "EFGCDECBA".toCharArray();
127
128            Writer out = create();
129
130            out.write("EF".toCharArray());
131            out.write("GCDE".toCharArray());
132            out.write("CBA".toCharArray());
133
134            out.close();
135            assertArrayEquals(expected, getChars());
136        }
137
138        public void sinkTestWriteOffset() throws Exception {
139            char[] expected = "EFGCDECBA".toCharArray();
140            Writer out = create();
141
142            char[] a = new char[1024];
143            a[1000] = 'E';
144            a[1001] = 'F';
145            out.write(a, 1000, 2);
146
147            char[] b = new char[1024];
148            b[1020] = 'G';
149            b[1021] = 'C';
150            b[1022] = 'D';
151            b[1023] = 'E';
152            out.write(b, 1020, 4);
153
154            char[] c = new char[1024];
155            c[0] = 'C';
156            c[1] = 'B';
157            c[2] = 'A';
158            out.write(c, 0, 3);
159
160            out.close();
161            assertArrayEquals(expected, getChars());
162        }
163
164        public void sinkTestWriteLargeArray() throws Exception {
165            Random dice = new Random();
166            char[] expected = new char[(1024 * 1024) + 1]; // 2 MB + 1 char
167            for (int c = 0; c < expected.length; c++) {
168                expected[c] = (char) ('A' + dice.nextInt(26));
169            }
170
171            Writer out = create();
172            out.write(expected);
173            out.close();
174
175            assertArrayEquals(expected, getChars());
176        }
177
178        public void sinkTestWriteAfterClose() throws Exception {
179            char[] expectedChars = "EF".toCharArray();
180            Writer out = create();
181
182            out.write(expectedChars);
183            out.close();
184
185            try {
186                out.write("GCDE".toCharArray());
187                fail("expected already closed exception");
188            } catch (IOException expected) {
189            }
190
191            assertArrayEquals(expectedChars, getChars());
192        }
193
194        public void sinkTestWriteAfterCloseSuppressed() throws Exception {
195            Writer out = create();
196            out.write("EF".toCharArray());
197            out.close();
198            out.write("GCDE".toCharArray()); // no exception expected!
199        }
200
201        // adding a new test? Don't forget to update createTests().
202
203        @Override public String getName() {
204            return CharSinkTester.this.toString() + ":" + super.getName();
205        }
206    }
207}
208