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
79    public String toString() {
80        return getClass().getName();
81    }
82
83    private static void assertArrayEquals(char[] expected, char[] 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            char[] expected = new char[] { };
95
96            Writer out = create();
97            out.close();
98            assertArrayEquals(expected, getChars());
99        }
100
101        public void sinkTestWriteZeroChars() throws Exception {
102            char[] expected = new char[] { };
103
104            Writer out = create();
105            char[] a = new char[1024];
106            out.write(a, 1000, 0);
107            out.write(a, 0, 0);
108            out.write(new char[] { });
109
110            out.close();
111            assertArrayEquals(expected, getChars());
112        }
113
114        public void sinkTestWriteCharByChar() throws Exception {
115            char[] expected = "EFGCDECBA".toCharArray();
116
117            Writer out = create();
118            for (char c : expected) {
119                out.write(c);
120            }
121
122            out.close();
123            assertArrayEquals(expected, getChars());
124        }
125
126        public void sinkTestWriteArray() throws Exception {
127            char[] expected = "EFGCDECBA".toCharArray();
128
129            Writer out = create();
130
131            out.write("EF".toCharArray());
132            out.write("GCDE".toCharArray());
133            out.write("CBA".toCharArray());
134
135            out.close();
136            assertArrayEquals(expected, getChars());
137        }
138
139        public void sinkTestWriteOffset() throws Exception {
140            char[] expected = "EFGCDECBA".toCharArray();
141            Writer out = create();
142
143            char[] a = new char[1024];
144            a[1000] = 'E';
145            a[1001] = 'F';
146            out.write(a, 1000, 2);
147
148            char[] b = new char[1024];
149            b[1020] = 'G';
150            b[1021] = 'C';
151            b[1022] = 'D';
152            b[1023] = 'E';
153            out.write(b, 1020, 4);
154
155            char[] c = new char[1024];
156            c[0] = 'C';
157            c[1] = 'B';
158            c[2] = 'A';
159            out.write(c, 0, 3);
160
161            out.close();
162            assertArrayEquals(expected, getChars());
163        }
164
165        public void sinkTestWriteLargeArray() throws Exception {
166            Random dice = new Random();
167            char[] expected = new char[(1024 * 1024) + 1]; // 2 MB + 1 char
168            for (int c = 0; c < expected.length; c++) {
169                expected[c] = (char) ('A' + dice.nextInt(26));
170            }
171
172            Writer out = create();
173            out.write(expected);
174            out.close();
175
176            assertArrayEquals(expected, getChars());
177        }
178
179        public void sinkTestWriteAfterClose() throws Exception {
180            char[] expectedChars = "EF".toCharArray();
181            Writer out = create();
182
183            out.write(expectedChars);
184            out.close();
185
186            try {
187                out.write("GCDE".toCharArray());
188                fail("expected already closed exception");
189            } catch (IOException expected) {
190            }
191
192            assertArrayEquals(expectedChars, getChars());
193        }
194
195        public void sinkTestWriteAfterCloseSuppressed() throws Exception {
196            Writer out = create();
197            out.write("EF".toCharArray());
198            out.close();
199            out.write("GCDE".toCharArray()); // no exception expected!
200        }
201
202        // adding a new test? Don't forget to update createTests().
203
204        @Override
205        public String getName() {
206            return CharSinkTester.this.toString() + ":" + super.getName();
207        }
208    }
209}
210