Added emulation for OutputStream and ByteArrayOutputStream Added emulation for the following Java IO classes based on the Android libcore implementation: - OutputStream - ByteArrayOutputStream Added corresponding unit tests. Change-Id: I1a56c1629bdca278d616a4e16651a8d34984628a Review-Link: https://gwt-review.googlesource.com/#/c/12950/
diff --git a/user/super/com/google/gwt/emul/java/io/ByteArrayOutputStream.java b/user/super/com/google/gwt/emul/java/io/ByteArrayOutputStream.java new file mode 100644 index 0000000..ca3a166 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/io/ByteArrayOutputStream.java
@@ -0,0 +1,233 @@ +// CHECKSTYLE_OFF: Copyrighted to the Android Open Source Project. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// CHECKSTYLE_ON + +package java.io; + +/** + * A specialized {@link OutputStream} for class for writing content to an + * (internal) byte array. As bytes are written to this stream, the byte array + * may be expanded to hold more bytes. When the writing is considered to be + * finished, a copy of the byte array can be requested from the class. + * + * @see ByteArrayInputStream + */ +public class ByteArrayOutputStream extends OutputStream { + /** + * The byte array containing the bytes written. + */ + protected byte[] buf; + + /** + * The number of bytes written. + */ + protected int count; + + /** + * Constructs a new ByteArrayOutputStream with a default size of 32 bytes. + * If more than 32 bytes are written to this instance, the underlying byte + * array will expand. + */ + public ByteArrayOutputStream() { + buf = new byte[32]; + } + + /** + * Constructs a new {@code ByteArrayOutputStream} with a default size of + * {@code size} bytes. If more than {@code size} bytes are written to this + * instance, the underlying byte array will expand. + * + * @param size + * initial size for the underlying byte array, must be + * non-negative. + * @throws IllegalArgumentException + * if {@code size} < 0. + */ + public ByteArrayOutputStream(int size) { + if (size >= 0) { + buf = new byte[size]; + } else { + throw new IllegalArgumentException("size < 0"); + } + } + + /** + * Closes this stream. This releases system resources used for this stream. + * + * @throws IOException + * if an error occurs while attempting to close this stream. + */ + @Override + public void close() throws IOException { + /** + * Although the spec claims "A closed stream cannot perform output + * operations and cannot be reopened.", this implementation must do + * nothing. + */ + super.close(); + } + + private void expand(int i) { + /* Can the buffer handle @i more bytes, if not expand it */ + if (count + i <= buf.length) { + return; + } + + byte[] newbuf = new byte[(count + i) * 2]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + + /** + * Resets this stream to the beginning of the underlying byte array. All + * subsequent writes will overwrite any bytes previously stored in this + * stream. + */ + public void reset() { + count = 0; + } + + /** + * Returns the total number of bytes written to this stream so far. + * + * @return the number of bytes written to this stream. + */ + public int size() { + return count; + } + + /** + * Returns the contents of this ByteArrayOutputStream as a byte array. Any + * changes made to the receiver after returning will not be reflected in the + * byte array returned to the caller. + * + * @return this stream's current contents as a byte array. + */ + public byte[] toByteArray() { + byte[] newArray = new byte[count]; + System.arraycopy(buf, 0, newArray, 0, count); + return newArray; + } + + /** + * Returns the contents of this ByteArrayOutputStream as a string. Any + * changes made to the receiver after returning will not be reflected in the + * string returned to the caller. + * + * @return this stream's current contents as a string. + */ + + @Override + public String toString() { + return new String(buf, 0, count); + } + + /** + * Returns the contents of this ByteArrayOutputStream as a string. Each byte + * {@code b} in this stream is converted to a character {@code c} using the + * following function: + * {@code c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))}. This method is + * deprecated and either {@link #toString()} or {@link #toString(String)} + * should be used. + * + * @param hibyte + * the high byte of each resulting Unicode character. + * @return this stream's current contents as a string with the high byte set + * to {@code hibyte}. + * @deprecated Use {@link #toString()} instead. + */ + @Deprecated + public String toString(int hibyte) { + char[] newBuf = new char[size()]; + for (int i = 0; i < newBuf.length; i++) { + newBuf[i] = (char) (((hibyte & 0xff) << 8) | (buf[i] & 0xff)); + } + return new String(newBuf); + } + + /** + * Returns the contents of this ByteArrayOutputStream as a string converted + * according to the encoding declared in {@code charsetName}. + * + * @param charsetName + * a string representing the encoding to use when translating + * this stream to a string. + * @return this stream's current contents as an encoded string. + * @throws UnsupportedEncodingException + * if the provided encoding is not supported. + */ + public String toString(String charsetName) throws UnsupportedEncodingException { + return new String(buf, 0, count, charsetName); + } + + /** + * Writes {@code count} bytes from the byte array {@code buffer} starting at + * offset {@code index} to this stream. + * + * @param buffer + * the buffer to be written. + * @param offset + * the initial position in {@code buffer} to retrieve bytes. + * @param len + * the number of bytes of {@code buffer} to write. + * @throws NullPointerException + * if {@code buffer} is {@code null}. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code len < 0}, or if + * {@code offset + len} is greater than the length of + * {@code buffer}. + */ + @Override + public void write(byte[] buffer, int offset, int len) { + IOUtils.checkOffsetAndCount(buffer, offset, len); + if (len == 0) { + return; + } + expand(len); + System.arraycopy(buffer, offset, buf, this.count, len); + this.count += len; + } + + /** + * Writes the specified byte {@code oneByte} to the OutputStream. Only the + * low order byte of {@code oneByte} is written. + * + * @param oneByte + * the byte to be written. + */ + @Override + public void write(int oneByte) { + if (count == buf.length) { + expand(1); + } + buf[count++] = (byte) oneByte; + } + + /** + * Takes the contents of this stream and writes it to the output stream + * {@code out}. + * + * @param out + * an OutputStream on which to write the contents of this stream. + * @throws IOException + * if an error occurs while writing to {@code out}. + */ + public void writeTo(OutputStream out) throws IOException { + out.write(buf, 0, count); + } +}
diff --git a/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java b/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java index 65f6a3b..e185e60 100644 --- a/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java +++ b/user/super/com/google/gwt/emul/java/io/FilterOutputStream.java
@@ -23,4 +23,7 @@ public FilterOutputStream(OutputStream out) { } + @Override + public void write(int oneByte) throws IOException { + } }
diff --git a/user/super/com/google/gwt/emul/java/io/Flushable.java b/user/super/com/google/gwt/emul/java/io/Flushable.java new file mode 100644 index 0000000..9d236c4 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/io/Flushable.java
@@ -0,0 +1,36 @@ +// CHECKSTYLE_OFF: Copyrighted to the Android Open Source Project. +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// CHECKSTYLE_ON + +package java.io; + +/** + * Defines an interface for classes that can (or need to) be flushed, typically + * before some output processing is considered to be finished and the object + * gets closed. + */ +public interface Flushable { + /** + * Flushes the object by writing out any buffered data to the underlying + * output. + * + * @throws IOException + * if there are any issues writing the data. + */ + void flush() throws IOException; +}
diff --git a/user/super/com/google/gwt/emul/java/io/OutputStream.java b/user/super/com/google/gwt/emul/java/io/OutputStream.java index f735723..1331844 100644 --- a/user/super/com/google/gwt/emul/java/io/OutputStream.java +++ b/user/super/com/google/gwt/emul/java/io/OutputStream.java
@@ -1,22 +1,131 @@ +// CHECKSTYLE_OFF: Copyrighted to the Android Open Source Project. /* - * Copyright 2006 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ +// CHECKSTYLE_ON + package java.io; +import static com.google.gwt.core.shared.impl.InternalPreconditions.checkNotNull; + /** - * @skip + * A writable sink for bytes. + * + * <p>Most clients will use output streams that write data to the file system + * ({@link FileOutputStream}), the network ({@link java.net.Socket#getOutputStream()}/{@link + * java.net.HttpURLConnection#getOutputStream()}), or to an in-memory byte array + * ({@link ByteArrayOutputStream}). + * + * <p>Use {@link OutputStreamWriter} to adapt a byte stream like this one into a + * character stream. + * + * <p>Most clients should wrap their output stream with {@link + * BufferedOutputStream}. Callers that do only bulk writes may omit buffering. + * + * <h3>Subclassing OutputStream</h3> + * Subclasses that decorate another output stream should consider subclassing + * {@link FilterOutputStream}, which delegates all calls to the target output + * stream. + * + * <p>All output stream subclasses should override <strong>both</strong> {@link + * #write(int)} and {@link #write(byte[],int,int) write(byte[],int,int)}. The + * three argument overload is necessary for bulk access to the data. This is + * much more efficient than byte-by-byte access. + * + * @see InputStream + * + * <p>The implementation provided by this class behaves as described in the Java + * API documentation except for {@link write(int)} which throws an exception of + * type {@link java.lang.UnsupportedOperationException} instead of being + * abstract. */ -public abstract class OutputStream { +public abstract class OutputStream implements Closeable, Flushable { + + /** + * Default constructor. + */ + public OutputStream() { + } + + /** + * Closes this stream. Implementations of this method should free any + * resources used by the stream. This implementation does nothing. + * + * @throws IOException + * if an error occurs while closing this stream. + */ + public void close() throws IOException { + /* empty */ + } + + /** + * Flushes this stream. Implementations of this method should ensure that + * any buffered data is written out. This implementation does nothing. + * + * @throws IOException + * if an error occurs while flushing this stream. + */ + public void flush() throws IOException { + /* empty */ + } + + /** + * Equivalent to {@code write(buffer, 0, buffer.length)}. + */ + public void write(byte[] buffer) throws IOException { + // Note that GWT will throw a JavascriptException rather than a NullPointerException if we + // skip this check and the buffer array is null. This way we ensure that this implementation + // behaves in the same way as the classes that are emulated. + checkNotNull(buffer); + write(buffer, 0, buffer.length); + } + + /** + * Writes {@code count} bytes from the byte array {@code buffer} starting at + * position {@code offset} to this stream. + * + * @param buffer + * the buffer to be written. + * @param offset + * the start position in {@code buffer} from where to get bytes. + * @param count + * the number of bytes from {@code buffer} to write to this + * stream. + * @throws IOException + * if an error occurs while writing to this stream. + * @throws IndexOutOfBoundsException + * if {@code offset < 0} or {@code count < 0}, or if + * {@code offset + count} is bigger than the length of + * {@code buffer}. + */ + public void write(byte[] buffer, int offset, int count) throws IOException { + IOUtils.checkOffsetAndCount(buffer, offset, count); + for (int i = offset; i < offset + count; i++) { + write(buffer[i]); + } + } + + /** + * Writes a single byte to this stream. Only the least significant byte of + * the integer {@code oneByte} is written to the stream. + * + * @param oneByte + * the byte to be written. + * @throws IOException + * if an error occurs while writing to this stream. + */ + public abstract void write(int oneByte) throws IOException; }
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java index 0f447bc..43a4db2 100644 --- a/user/test/com/google/gwt/emultest/EmulSuite.java +++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -16,7 +16,9 @@ package com.google.gwt.emultest; import com.google.gwt.emultest.java.io.ByteArrayInputStreamTest; +import com.google.gwt.emultest.java.io.ByteArrayOutputStreamTest; import com.google.gwt.emultest.java.io.InputStreamTest; +import com.google.gwt.emultest.java.io.OutputStreamTest; import com.google.gwt.emultest.java.lang.BooleanTest; import com.google.gwt.emultest.java.lang.ByteTest; import com.google.gwt.emultest.java.lang.CharacterTest; @@ -57,7 +59,9 @@ // $JUnit-BEGIN$ //-- java.io suite.addTestSuite(ByteArrayInputStreamTest.class); + suite.addTestSuite(ByteArrayOutputStreamTest.class); suite.addTestSuite(InputStreamTest.class); + suite.addTestSuite(OutputStreamTest.class); //-- java.lang suite.addTestSuite(BooleanTest.class); suite.addTestSuite(ByteTest.class);
diff --git a/user/test/com/google/gwt/emultest/java/io/ByteArrayOutputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/ByteArrayOutputStreamTest.java new file mode 100644 index 0000000..c7cced3 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java/io/ByteArrayOutputStreamTest.java
@@ -0,0 +1,152 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java.io; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Unit test for the {@link java.io.ByteArrayOutputStream} emulated class. + */ +public class ByteArrayOutputStreamTest extends OutputStreamBaseTest { + + private static ByteArrayOutputStream outputStream; + + @Override + protected OutputStream createDefaultOutputStream() { + outputStream = new ByteArrayOutputStream(); + return outputStream; + } + + @Override + protected byte[] getBytesWritten() { + return outputStream != null ? outputStream.toByteArray() : null; + } + + public void testClose() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // should do nothing (including not throwing an exception). + outputStream.close(); + } + + public void testFlush() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + // should do nothing (including not throwing an exception). + outputStream.flush(); + } + + public void testReset() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2); + + outputStream.write(TEST_ARRAY); + + byte[] actualBytes = outputStream.toByteArray(); + assertTrue(Arrays.equals(TEST_ARRAY, actualBytes)); + + outputStream.reset(); + + final byte[] expectedBytes = new byte[] { 101, 102 }; + outputStream.write(expectedBytes); + + actualBytes = outputStream.toByteArray(); + assertTrue(Arrays.equals(expectedBytes, actualBytes)); + } + + public void testSize() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + assertEquals(0, outputStream.size()); + + outputStream.write(128); + assertEquals(1, outputStream.size()); + + outputStream.write(TEST_ARRAY); + assertEquals(1 + TEST_ARRAY.length, outputStream.size()); + + outputStream.write(TEST_ARRAY, 1, 2); + assertEquals(3 + TEST_ARRAY.length, outputStream.size()); + + outputStream.reset(); + assertEquals(0, outputStream.size()); + + outputStream.write(128); + assertEquals(1, outputStream.size()); + } + + public void testToStringUsingEmptyStream() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + final String actualString = outputStream.toString(); + assertTrue(actualString.isEmpty()); + } + + public void testToStringUsingNonEmptyStream() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + final byte[] values = new byte[] { + (byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f + }; + outputStream.write(values); + final String actualString = outputStream.toString(); + assertEquals("Hello", actualString); + } + + public void testToStringWithHighByteAndEmptyStream() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + final String actualString = outputStream.toString(0x01); + assertTrue(actualString.isEmpty()); + } + + public void testToStringWithHighByteAndNonEmptyStream() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + final byte[] values = new byte[] { + (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 + }; + outputStream.write(values); + final String actualString = outputStream.toString(0x01); + final String expectedString = new String(new char[] { + (char) 0x0100, (char) 0x0101, (char) 0x0102, (char) 0x0103, (char) 0x0104 + }); + assertEquals(expectedString, actualString); + } + + public void testToStringWithCharsetNameAndEmptyStream() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + final String actualString = outputStream.toString("UTF-8"); + assertTrue(actualString.isEmpty()); + } + + public void testToStringWithCharsetNameAndNonEmptyStream() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1); + final String expectedString = "Hello"; + + outputStream.write(expectedString.getBytes("UTF-8")); + final String actualString = outputStream.toString("UTF-8"); + assertEquals(expectedString, actualString); + } + + public void testWriteSingleValues() throws IOException { + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2); + assertEquals(0, outputStream.size()); + + for (int i = 0; i < 3; i++) { + outputStream.write(TEST_ARRAY[i]); + assertEquals(i + 1, outputStream.size()); + } + final byte[] expectedBytes = Arrays.copyOf(TEST_ARRAY, 3); + final byte[] actualBytes = outputStream.toByteArray(); + assertTrue(Arrays.equals(expectedBytes, actualBytes)); + } +}
diff --git a/user/test/com/google/gwt/emultest/java/io/OutputStreamBaseTest.java b/user/test/com/google/gwt/emultest/java/io/OutputStreamBaseTest.java new file mode 100644 index 0000000..e7b02a9 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java/io/OutputStreamBaseTest.java
@@ -0,0 +1,145 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java.io; + +import com.google.gwt.junit.client.GWTTestCase; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Class for reusing tests that are commong to {@link java.io.OutputStream} and its subclasses. + */ +public abstract class OutputStreamBaseTest extends GWTTestCase { + + protected static final byte[] TEST_ARRAY = new byte[] { 10, 20, 30, 40, 50 }; + + /** + * Factory method for creating a stream object. + * + * @return output stream object to be tested. + */ + protected abstract OutputStream createDefaultOutputStream(); + + /** + * Retrieves the array of bytes written by the seam. + * + * @return bytes written by the stream. + */ + protected abstract byte[] getBytesWritten(); + + /** + * Sets module name so that javascript compiler can operate. + */ + @Override + public String getModuleName() { + return "com.google.gwt.emultest.EmulSuite"; + } + + public void testWriteArrayUsingNullArrayObject() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + try { + outputStream.write(null, 0, 1); + fail("should have thrown NullPointerException"); + } catch (NullPointerException expected) { + } + } + + public void testWriteArrayUsingNegativeOffsetValue() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + try { + outputStream.write(TEST_ARRAY, -1, 1); + fail("should have thrown IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException expected) { + } + } + + public void testWriteArrayUsingNegativeLengthValue() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + try { + outputStream.write(TEST_ARRAY, 0, -1); + fail("should have thrown IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException expected) { + } + } + + public void testWriteArrayUsingAnInvalidRange() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + try { + outputStream.write(TEST_ARRAY, 1, TEST_ARRAY.length); + fail("should have thrown IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException expected) { + } + } + + public void testWriteArrayZeroLength() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + outputStream.write(TEST_ARRAY, 0, 0); + assertEquals(0, getBytesWritten().length); + } + + public void testWriteArrayZeroOffset() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + outputStream.write(TEST_ARRAY, 0, TEST_ARRAY.length); + assertTrue(Arrays.equals(TEST_ARRAY, getBytesWritten())); + } + + public void testWriteArrayFirstBytesOnly() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + outputStream.write(TEST_ARRAY, 0, TEST_ARRAY.length - 2); + + final byte[] expected = Arrays.copyOf(TEST_ARRAY, TEST_ARRAY.length - 2); + assertTrue(Arrays.equals(expected, getBytesWritten())); + } + + public void testWriteArrayLastBytesOnly() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + outputStream.write(TEST_ARRAY, 2, TEST_ARRAY.length - 2); + + final byte[] expected = Arrays.copyOfRange(TEST_ARRAY, 2, TEST_ARRAY.length); + assertTrue(Arrays.equals(expected, getBytesWritten())); + } + + public void testWriteArrayMiddleBytesOnly() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + outputStream.write(TEST_ARRAY, 2, TEST_ARRAY.length - 4); + + final byte[] expected = Arrays.copyOfRange(TEST_ARRAY, 2, TEST_ARRAY.length - 2); + assertTrue(Arrays.equals(expected, getBytesWritten())); + } + + public void testWriteArrayUsingNullArrayObjectAndNoOffset() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + try { + outputStream.write(null); + fail("should have thrown NullPointerException"); + } catch (NullPointerException expected) { + } + } + + public void testWriteArrayZeroBytesNoOffset() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + outputStream.write(new byte[0]); + assertEquals(0, getBytesWritten().length); + } + + public void testWriteArrayNoOffset() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + outputStream.write(TEST_ARRAY); + assertTrue(Arrays.equals(TEST_ARRAY, getBytesWritten())); + } +}
diff --git a/user/test/com/google/gwt/emultest/java/io/OutputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/OutputStreamTest.java new file mode 100644 index 0000000..c5f8370 --- /dev/null +++ b/user/test/com/google/gwt/emultest/java/io/OutputStreamTest.java
@@ -0,0 +1,65 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.emultest.java.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.LinkedList; + +/** + * Unit test for the {@link java.io.OutputStream} emulated class. + */ +public class OutputStreamTest extends OutputStreamBaseTest { + + private static LinkedList<Byte> outputBytes; + + @Override + protected void gwtSetUp() throws Exception { + super.gwtSetUp(); + outputBytes = new LinkedList<>(); + } + + @Override + protected OutputStream createDefaultOutputStream() { + return new OutputStream() { + @Override public void write(int b) { + outputBytes.add((byte) b); + } + }; + } + + @Override + protected byte[] getBytesWritten() { + byte[] bytesWritten = new byte[outputBytes.size()]; + int i = 0; + for (Byte nextByte : outputBytes) { + bytesWritten[i++] = nextByte; + } + return bytesWritten; + } + + public void testDefaultBehaviorOfClose() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + // should do nothing (including not throwing an exception). + outputStream.close(); + } + + public void testDefaultBehaviorOfFlush() throws IOException { + final OutputStream outputStream = createDefaultOutputStream(); + // should do nothing (including not throwing an exception). + outputStream.flush(); + } +}