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.TestCase; 21import junit.framework.TestSuite; 22 23import java.io.ByteArrayOutputStream; 24import java.io.IOException; 25import java.io.OutputStream; 26 27/** 28 * Tests behaviour common to wrapping and filtering implementations of {@link 29 * OutputStream}. 30 */ 31public 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 public String toString() { 77 return getClass().getName(); 78 } 79 80 private class WrapperSinkTester extends SinkTester { 81 private ClosableByteArrayOutputStream delegate; 82 83 @Override public OutputStream create() throws Exception { 84 delegate = new ClosableByteArrayOutputStream(); 85 return WrapperTester.this.create(delegate); 86 } 87 88 @Override public byte[] getBytes() throws Exception { 89 return WrapperTester.this.decode(delegate.bytesOut.toByteArray()); 90 } 91 92 @Override public String toString() { 93 return WrapperTester.this.toString(); 94 } 95 } 96 97 public class WrapperTestCase extends TestCase { 98 99 private WrapperTestCase(String name) { 100 super(name); 101 } 102 103 @Override public String getName() { 104 return WrapperTester.this.toString() + ":" + super.getName(); 105 } 106 107 public void wrapperTestFlushThrowsViaFlushSuppressed() throws Exception { 108 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 109 OutputStream o = create(delegate); 110 o.write(new byte[] { 8, 6, 7, 5 }); 111 o.write(new byte[] { 3, 0, 9 }); 112 o.flush(); 113 assertTrue(delegate.flushed); 114 } 115 116 public void wrapperTestFlushThrowsViaCloseSuppressed() throws Exception { 117 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 118 OutputStream o = create(delegate); 119 o.write(new byte[] { 8, 6, 7, 5 }); 120 o.write(new byte[] { 3, 0, 9 }); 121 o.close(); 122 assertTrue(delegate.flushed); 123 } 124 125 public void wrapperTestFlushThrowsViaFlush() throws Exception { 126 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 127 128 OutputStream o = create(delegate); 129 try { 130 // any of these is permitted to flush 131 o.write(new byte[] { 8, 6, 7, 5 }); 132 o.write(new byte[] { 3, 0, 9 }); 133 o.flush(); 134 assertTrue(delegate.flushed); 135 fail("flush exception ignored"); 136 } catch (IOException expected) { 137 assertEquals("Flush failed" , expected.getMessage()); 138 } 139 } 140 141 public void wrapperTestFlushThrowsViaClose() throws Exception { 142 FailOnFlushOutputStream delegate = new FailOnFlushOutputStream(); 143 144 OutputStream o = create(delegate); 145 try { 146 // any of these is permitted to flush 147 o.write(new byte[] { 8, 6, 7, 5 }); 148 o.write(new byte[] { 3, 0, 9 }); 149 o.close(); 150 assertTrue(delegate.flushed); 151 fail("flush exception ignored"); 152 } catch (IOException expected) { 153 assertEquals("Flush failed" , expected.getMessage()); 154 } 155 156 try { 157 o.write(new byte[] { 4, 4, 5 }); 158 fail("expected already closed exception"); 159 } catch (IOException expected) { 160 } 161 } 162 163 public void wrapperTestCloseThrows() throws Exception { 164 FailOnCloseOutputStream delegate = new FailOnCloseOutputStream(); 165 OutputStream o = create(delegate); 166 try { 167 o.close(); 168 assertTrue(delegate.closed); 169 fail("close exception ignored"); 170 } catch (IOException expected) { 171 assertEquals("Close failed" , expected.getMessage()); 172 } 173 } 174 175 public void wrapperTestCloseThrowsSuppressed() throws Exception { 176 FailOnCloseOutputStream delegate = new FailOnCloseOutputStream(); 177 OutputStream o = create(delegate); 178 o.close(); 179 assertTrue(delegate.closed); 180 } 181 182 // adding a new test? Don't forget to update createTests(). 183 } 184 185 private static class ClosableByteArrayOutputStream extends OutputStream { 186 private final ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); 187 private boolean closed = false; 188 189 @Override public void close() throws IOException { 190 closed = true; 191 } 192 193 @Override public void write(int oneByte) throws IOException { 194 if (closed) { 195 throw new IOException(); 196 } 197 bytesOut.write(oneByte); 198 } 199 } 200 201 private static class FailOnFlushOutputStream extends OutputStream { 202 boolean flushed = false; 203 boolean closed = false; 204 205 @Override public void write(int oneByte) throws IOException { 206 if (closed) { 207 throw new IOException("Already closed"); 208 } 209 } 210 211 @Override public void close() throws IOException { 212 closed = true; 213 flush(); 214 } 215 216 @Override public void flush() throws IOException { 217 if (!flushed) { 218 flushed = true; 219 throw new IOException("Flush failed"); 220 } 221 } 222 } 223 224 private static class FailOnCloseOutputStream extends ByteArrayOutputStream { 225 boolean closed = false; 226 227 @Override public void close() throws IOException { 228 closed = true; 229 throw new IOException("Close failed"); 230 } 231 } 232} 233