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();
+ }
+}