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 18 package org.apache.harmony.testframework; 19 20 import junit.framework.TestCase; 21 import junit.framework.TestSuite; 22 23 import java.io.ByteArrayOutputStream; 24 import java.io.IOException; 25 import java.io.OutputStream; 26 27 /** 28 * Tests behaviour common to wrapping and filtering implementations of {@link 29 * OutputStream}. 30 */ 31 public abstract class WrapperTester { 32 33 private boolean throwsExceptions = true; 34 35 /** 36 * Creates a new output stream that receives one stream of bytes, optionally 37 * transforms it, and emits another stream of bytes to {@code delegate}. 38 */ 39 public abstract OutputStream create(OutputStream delegate) throws Exception; 40 41 /** 42 * Decodes the bytes received by the delegate into their original form: the 43 * bytes originally received by this wrapper. 44 */ 45 public abstract byte[] decode(byte[] delegateBytes) throws Exception; 46 47 /** 48 * Configures whether the stream is expected to throw exceptions when an 49 * error is encountered. Classes like {@code PrintStream} report errors via 50 * an API method instead. 51 */ 52 public WrapperTester setThrowsExceptions(boolean throwsExceptions) { 53 this.throwsExceptions = throwsExceptions; 54 return this; 55 } 56 57 public final TestSuite createTests() { 58 TestSuite result = new TestSuite(); 59 result.addTest(new WrapperSinkTester() 60 .setThrowsExceptions(throwsExceptions) 61 .createTests()); 62 63 if (throwsExceptions) { 64 result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaFlush")); 65 result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaClose")); 66 result.addTest(new WrapperTestCase("wrapperTestCloseThrows")); 67 } else { 68 result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaFlushSuppressed")); 69 result.addTest(new WrapperTestCase("wrapperTestFlushThrowsViaCloseSuppressed")); 70 result.addTest(new WrapperTestCase("wrapperTestCloseThrowsSuppressed")); 71 } 72 73 return result; 74 } 75 76 @Override 77 public String toString() { 78 return getClass().getName(); 79 } 80 81 private class WrapperSinkTester extends SinkTester { 82 private ClosableByteArrayOutputStream delegate; 83 84 @Override 85 public OutputStream create() throws Exception { 86 delegate = new ClosableByteArrayOutputStream(); 87 return WrapperTester.this.create(delegate); 88 } 89 90 @Override 91 public byte[] getBytes() throws Exception { 92 return WrapperTester.this.decode(delegate.bytesOut.toByteArray()); 93 } 94 95 @Override 96 public String toString() { 97 return WrapperTester.this.toString(); 98 } 99 } 100 101 public class WrapperTestCase extends TestCase { 102 103 private WrapperTestCase(String name) { 104 super(name); 105 } 106 107 @Override 108 public String getName() { 109 return WrapperTester.this.toString() + ":" + super.getName(); 110 } 111 112 public void wrapperTestFlushThrowsViaFlushSuppressed() throws Exception { 113 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 114 OutputStream o = create(delegate); 115 o.write(new byte[] { 8, 6, 7, 5 }); 116 o.write(new byte[] { 3, 0, 9 }); 117 o.flush(); 118 assertTrue(delegate.flushed); 119 } 120 121 public void wrapperTestFlushThrowsViaCloseSuppressed() throws Exception { 122 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 123 OutputStream o = create(delegate); 124 o.write(new byte[] { 8, 6, 7, 5 }); 125 o.write(new byte[] { 3, 0, 9 }); 126 o.close(); 127 assertTrue(delegate.flushed); 128 } 129 130 public void wrapperTestFlushThrowsViaFlush() throws Exception { 131 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 132 133 OutputStream o = create(delegate); 134 try { 135 // any of these is permitted to flush 136 o.write(new byte[] { 8, 6, 7, 5 }); 137 o.write(new byte[] { 3, 0, 9 }); 138 o.flush(); 139 assertTrue(delegate.flushed); 140 fail("flush exception ignored"); 141 } catch (IOException expected) { 142 assertEquals("Flush failed", expected.getMessage()); 143 } 144 } 145 146 public void wrapperTestFlushThrowsViaClose() throws Exception { 147 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 148 149 OutputStream o = create(delegate); 150 try { 151 // any of these is permitted to flush 152 o.write(new byte[] { 8, 6, 7, 5 }); 153 o.write(new byte[] { 3, 0, 9 }); 154 o.close(); 155 assertTrue(delegate.flushed); 156 fail("flush exception ignored"); 157 } catch (IOException expected) { 158 assertEquals("Flush failed", expected.getMessage()); 159 } 160 161 try { 162 o.write(new byte[] { 4, 4, 5 }); 163 fail("expected already closed exception"); 164 } catch (IOException expected) { 165 } 166 } 167 168 public void wrapperTestCloseThrows() throws Exception { 169 FailOnCloseOutputStream delegate = new FailOnCloseOutputStream(); 170 OutputStream o = create(delegate); 171 try { 172 o.close(); 173 assertTrue(delegate.closed); 174 fail("close exception ignored"); 175 } catch (IOException expected) { 176 assertEquals("Close failed", expected.getMessage()); 177 } 178 } 179 180 public void wrapperTestCloseThrowsSuppressed() throws Exception { 181 FailOnCloseOutputStream delegate = new FailOnCloseOutputStream(); 182 OutputStream o = create(delegate); 183 o.close(); 184 assertTrue(delegate.closed); 185 } 186 187 // adding a new test? Don't forget to update createTests(). 188 } 189 190 private static class ClosableByteArrayOutputStream extends OutputStream { 191 private final ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 192 private boolean closed = false; 193 194 @Override 195 public void close() throws IOException { 196 closed = true; 197 } 198 199 @Override 200 public void write(int oneByte) throws IOException { 201 if (closed) { 202 throw new IOException(); 203 } 204 bytesOut.write(oneByte); 205 } 206 } 207 208 private static class FailOnFlushOutputStream extends OutputStream { 209 boolean flushed = false; 210 boolean closed = false; 211 212 @Override 213 public void write(int oneByte) throws IOException { 214 if (closed) { 215 throw new IOException("Already closed"); 216 } 217 } 218 219 @Override 220 public void close() throws IOException { 221 closed = true; 222 flush(); 223 } 224 225 @Override 226 public void flush() throws IOException { 227 if (!flushed) { 228 flushed = true; 229 throw new IOException("Flush failed"); 230 } 231 } 232 } 233 234 private static class FailOnCloseOutputStream extends ByteArrayOutputStream { 235 boolean closed = false; 236 237 @Override 238 public void close() throws IOException { 239 closed = true; 240 throw new IOException("Close failed"); 241 } 242 } 243 } 244