Added emulation for InputStream and ByteArrayInputStream

Added emulation for the following Java IO classes based
on the Android libcore implementation:

 - InputStream
 - ByteArrayInputStream

Added corresponding unit tests.

Change-Id: I041552ebfd075c31d4613b1ce09ab375842fbd7d
Review-Link: https://gwt-review.googlesource.com/#/c/12770/
diff --git a/user/super/com/google/gwt/emul/java/io/ByteArrayInputStream.java b/user/super/com/google/gwt/emul/java/io/ByteArrayInputStream.java
new file mode 100644
index 0000000..4093b38
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/io/ByteArrayInputStream.java
@@ -0,0 +1,190 @@
+// 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 InputStream } for reading the contents of a byte array.
+ *
+ * @see ByteArrayOutputStream
+ */
+public class ByteArrayInputStream extends InputStream {
+    /**
+     * The {@code byte} array containing the bytes to stream over.
+     */
+    protected byte[] buf;
+
+    /**
+     * The current position within the byte array.
+     */
+    protected int pos;
+
+    /**
+     * The current mark position. Initially set to 0 or the <code>offset</code>
+     * parameter within the constructor.
+     */
+    protected int mark;
+
+    /**
+     * The total number of bytes initially available in the byte array
+     * {@code buf}.
+     */
+    protected int count;
+
+    /**
+     * Constructs a new {@code ByteArrayInputStream} on the byte array
+     * {@code buf}.
+     *
+     * @param buf
+     *            the byte array to stream over.
+     */
+    public ByteArrayInputStream(byte[] buf) {
+        this.mark = 0;
+        this.buf = buf;
+        this.count = buf.length;
+    }
+
+    /**
+     * Constructs a new {@code ByteArrayInputStream} on the byte array
+     * {@code buf} with the initial position set to {@code offset} and the
+     * number of bytes available set to {@code offset} + {@code length}.
+     *
+     * @param buf
+     *            the byte array to stream over.
+     * @param offset
+     *            the initial position in {@code buf} to start streaming from.
+     * @param length
+     *            the number of bytes available for streaming.
+     */
+    public ByteArrayInputStream(byte[] buf, int offset, int length) {
+        this.buf = buf;
+        pos = offset;
+        mark = offset;
+        count = offset + length > buf.length ? buf.length : offset + length;
+    }
+
+    /**
+     * Returns the number of remaining bytes.
+     *
+     * @return {@code count - pos}
+     */
+    @Override
+    public synchronized int available() {
+        return count - pos;
+    }
+
+    /**
+     * Closes this stream and frees resources associated with this stream.
+     *
+     * @throws IOException
+     *             if an I/O error occurs while closing this stream.
+     */
+    @Override
+    public void close() throws IOException {
+        // Do nothing on close, this matches JDK behavior.
+    }
+
+    /**
+     * Sets a mark position in this ByteArrayInputStream. The parameter
+     * {@code readlimit} is ignored. Sending {@code reset()} will reposition the
+     * stream back to the marked position.
+     *
+     * @param readlimit
+     *            ignored.
+     * @see #markSupported()
+     * @see #reset()
+     */
+    @Override
+    public synchronized void mark(int readlimit) {
+        mark = pos;
+    }
+
+    /**
+     * Indicates whether this stream supports the {@code mark()} and
+     * {@code reset()} methods. Returns {@code true} since this class supports
+     * these methods.
+     *
+     * @return always {@code true}.
+     * @see #mark(int)
+     * @see #reset()
+     */
+    @Override
+    public boolean markSupported() {
+        return true;
+    }
+
+    /**
+     * Reads a single byte from the source byte array and returns it as an
+     * integer in the range from 0 to 255. Returns -1 if the end of the source
+     * array has been reached.
+     *
+     * @return the byte read or -1 if the end of this stream has been reached.
+     */
+    @Override
+    public synchronized int read() {
+        return pos < count ? buf[pos++] & 0xFF : -1;
+    }
+
+    @Override public synchronized int read(byte[] buffer, int byteOffset, int byteCount) {
+        IOUtils.checkOffsetAndCount(buffer, byteOffset, byteCount);
+
+        // Are there any bytes available?
+        if (this.pos >= this.count) {
+            return -1;
+        }
+        if (byteCount == 0) {
+            return 0;
+        }
+
+        int copylen = this.count - pos < byteCount ? this.count - pos : byteCount;
+        System.arraycopy(this.buf, pos, buffer, byteOffset, copylen);
+        pos += copylen;
+        return copylen;
+    }
+
+    /**
+     * Resets this stream to the last marked location. This implementation
+     * resets the position to either the marked position, the start position
+     * supplied in the constructor or 0 if neither has been provided.
+     *
+     * @see #mark(int)
+     */
+    @Override
+    public synchronized void reset() {
+        pos = mark;
+    }
+
+    /**
+     * Skips {@code byteCount} bytes in this InputStream. Subsequent
+     * calls to {@code read} will not return these bytes unless {@code reset} is
+     * used. This implementation skips {@code byteCount} number of bytes in the
+     * target stream. It does nothing and returns 0 if {@code byteCount} is negative.
+     *
+     * @return the number of bytes actually skipped.
+     */
+    @Override
+    public synchronized long skip(long byteCount) {
+        if (byteCount <= 0) {
+            return 0;
+        }
+        int temp = pos;
+        pos = this.count - pos < byteCount ? this.count : (int) (pos + byteCount);
+        return pos - temp;
+    }
+}
diff --git a/user/super/com/google/gwt/emul/java/io/Closeable.java b/user/super/com/google/gwt/emul/java/io/Closeable.java
new file mode 100644
index 0000000..2ef478f
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/io/Closeable.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;
+
+/**
+ * An {@code AutoCloseable} whose close method may throw an {@link IOException}.
+ */
+public interface Closeable extends AutoCloseable {
+
+    /**
+     * Closes the object and release any system resources it holds.
+     *
+     * <p>Although only the first call has any effect, it is safe to call close
+     * multiple times on the same object. This is more lenient than the
+     * overridden {@code AutoCloseable.close()}, which may be called at most
+     * once.
+     */
+    void close() throws IOException;
+}
diff --git a/user/super/com/google/gwt/emul/java/io/IOUtils.java b/user/super/com/google/gwt/emul/java/io/IOUtils.java
new file mode 100644
index 0000000..0d8a42a
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/io/IOUtils.java
@@ -0,0 +1,78 @@
+/*
+ * 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 java.io;
+
+import static com.google.gwt.core.shared.impl.InternalPreconditions.checkNotNull;
+
+/**
+ * Provides a series of utilities to be reused between IO classes.
+ *
+ * TODO(chehayeb): move these checks to InternalPreconditions.
+ */
+final class IOUtils {
+
+  /**
+   * Validates the offset and the byte count for the given array of bytes.
+   *
+   * @param buffer Array of bytes to be checked.
+   * @param byteOffset Starting offset in the array.
+   * @param byteCount Total number of bytes to be accessed.
+   * @throws NullPointerException if the given reference to the buffer is null.
+   * @throws IndexOutOfBoundsException if {@code byteOffset} is negative, {@code byteCount} is
+   *     negative or their sum exceeds the buffer length.
+   */
+  public static void checkOffsetAndCount(byte[] buffer, int byteOffset, int byteCount) {
+    // Ensure we throw a NullPointerException instead of a JavascriptException in case the
+    // given buffer is null.
+    checkNotNull(buffer);
+    checkOffsetAndCount(buffer.length, byteOffset, byteCount);
+  }
+
+  /**
+   * Validates the offset and the byte count for the given array of characters.
+   *
+   * @param buffer Array of characters to be checked.
+   * @param charOffset Starting offset in the array.
+   * @param charCount Total number of characters to be accessed.
+   * @throws NullPointerException if the given reference to the buffer is null.
+   * @throws IndexOutOfBoundsException if {@code charOffset} is negative, {@code charCount} is
+   *     negative or their sum exceeds the buffer length.
+   */
+  public static void checkOffsetAndCount(char[] buffer, int charOffset, int charCount) {
+    // Ensure we throw a NullPointerException instead of a JavascriptException in case the
+    // given buffer is null.
+    checkNotNull(buffer);
+    checkOffsetAndCount(buffer.length, charOffset, charCount);
+  }
+
+  /**
+   * Validates the offset and the byte count for the given array length.
+   *
+   * @param length Length of the array to be checked.
+   * @param offset Starting offset in the array.
+   * @param count Total number of elements to be accessed.
+   * @throws IndexOutOfBoundsException if {@code offset} is negative, {@code count} is negative or
+   *     their sum exceeds the given {@code length}.
+   */
+  private static void checkOffsetAndCount(int length, int offset, int count) {
+    if ((offset < 0) || (count < 0) || ((offset + count) > length)) {
+      throw new IndexOutOfBoundsException();
+    }
+  }
+
+  private IOUtils() {
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/io/InputStream.java b/user/super/com/google/gwt/emul/java/io/InputStream.java
new file mode 100644
index 0000000..6b82150
--- /dev/null
+++ b/user/super/com/google/gwt/emul/java/io/InputStream.java
@@ -0,0 +1,258 @@
+// 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;
+
+import static com.google.gwt.core.shared.impl.InternalPreconditions.checkNotNull;
+
+/**
+ * A readable source of bytes.
+ *
+ * <p>Most clients will use input streams that read data from the file system
+ * ({@link FileInputStream}), the network ({@link java.net.Socket#getInputStream()}/{@link
+ * java.net.HttpURLConnection#getInputStream()}), or from an in-memory byte
+ * array ({@link ByteArrayInputStream}).
+ *
+ * <p>Use {@link InputStreamReader} to adapt a byte stream like this one into a
+ * character stream.
+ *
+ * <p>Most clients should wrap their input stream with {@link
+ * BufferedInputStream}. Callers that do only bulk reads may omit buffering.
+ *
+ * <p>Some implementations support marking a position in the input stream and
+ * resetting back to this position later. Implementations that don't return
+ * false from {@link #markSupported()} and throw an {@link IOException} when
+ * {@link #reset()} is called.
+ *
+ * <h3>Subclassing InputStream</h3>
+ * Subclasses that decorate another input stream should consider subclassing
+ * {@link FilterInputStream}, which delegates all calls to the source input
+ * stream.
+ *
+ * <p>All input stream subclasses should override <strong>both</strong> {@link
+ * #read() read()} and {@link #read(byte[],int,int) read(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 OutputStream
+ */
+public abstract class InputStream extends Object implements Closeable {
+
+    /**
+     * Size of the temporary buffer used when skipping bytes with {@link skip(long)}.
+     */
+    private static final int MAX_SKIP_BUFFER_SIZE = 4096;
+
+    /**
+     * This constructor does nothing. It is provided for signature
+     * compatibility.
+     */
+    public InputStream() {
+        /* empty */
+    }
+
+    /**
+     * Returns an estimated number of bytes that can be read or skipped without blocking for more
+     * input.
+     *
+     * <p>Note that this method provides such a weak guarantee that it is not very useful in
+     * practice.
+     *
+     * <p>Firstly, the guarantee is "without blocking for more input" rather than "without
+     * blocking": a read may still block waiting for I/O to complete&nbsp;&mdash; the guarantee is
+     * merely that it won't have to wait indefinitely for data to be written. The result of this
+     * method should not be used as a license to do I/O on a thread that shouldn't be blocked.
+     *
+     * <p>Secondly, the result is a
+     * conservative estimate and may be significantly smaller than the actual number of bytes
+     * available. In particular, an implementation that always returns 0 would be correct.
+     * In general, callers should only use this method if they'd be satisfied with
+     * treating the result as a boolean yes or no answer to the question "is there definitely
+     * data ready?".
+     *
+     * <p>Thirdly, the fact that a given number of bytes is "available" does not guarantee that a
+     * read or skip will actually read or skip that many bytes: they may read or skip fewer.
+     *
+     * <p>It is particularly important to realize that you <i>must not</i> use this method to
+     * size a container and assume that you can read the entirety of the stream without needing
+     * to resize the container. Such callers should probably write everything they read to a
+     * {@link ByteArrayOutputStream} and convert that to a byte array. Alternatively, if you're
+     * reading from a file, {@link File#length} returns the current length of the file (though
+     * assuming the file's length can't change may be incorrect, reading a file is inherently
+     * racy).
+     *
+     * <p>The default implementation of this method in {@code InputStream} always returns 0.
+     * Subclasses should override this method if they are able to indicate the number of bytes
+     * available.
+     *
+     * @return the estimated number of bytes available
+     * @throws IOException if this stream is closed or an error occurs
+     */
+    public int available() throws IOException {
+        return 0;
+    }
+
+    /**
+     * Closes this stream. Concrete implementations of this class should free
+     * any resources during close. This implementation does nothing.
+     *
+     * @throws IOException
+     *             if an error occurs while closing this stream.
+     */
+    public void close() throws IOException {
+        /* empty */
+    }
+
+    /**
+     * Sets a mark position in this InputStream. The parameter {@code readlimit}
+     * indicates how many bytes can be read before the mark is invalidated.
+     * Sending {@code reset()} will reposition the stream back to the marked
+     * position provided {@code readLimit} has not been surpassed.
+     * <p>
+     * This default implementation does nothing and concrete subclasses must
+     * provide their own implementation.
+     *
+     * @param readlimit
+     *            the number of bytes that can be read from this stream before
+     *            the mark is invalidated.
+     * @see #markSupported()
+     * @see #reset()
+     */
+    public void mark(int readlimit) {
+        /* empty */
+    }
+
+    /**
+     * Indicates whether this stream supports the {@code mark()} and
+     * {@code reset()} methods. The default implementation returns {@code false}.
+     *
+     * @return always {@code false}.
+     * @see #mark(int)
+     * @see #reset()
+     */
+    public boolean markSupported() {
+        return false;
+    }
+
+    /**
+     * Reads a single byte from this stream and returns it as an integer in the
+     * range from 0 to 255. Returns -1 if the end of the stream has been
+     * reached. Blocks until one byte has been read, the end of the source
+     * stream is detected or an exception is thrown.
+     *
+     * @throws IOException
+     *             if the stream is closed or another IOException occurs.
+     */
+    public abstract int read() throws IOException;
+
+    /**
+     * Equivalent to {@code read(buffer, 0, buffer.length)}.
+     */
+    public int read(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);
+        return read(buffer, 0, buffer.length);
+    }
+
+    /**
+     * Reads up to {@code byteCount} bytes from this stream and stores them in
+     * the byte array {@code buffer} starting at {@code byteOffset}.
+     * Returns the number of bytes actually read or -1 if the end of the stream
+     * has been reached.
+     *
+     * @throws IndexOutOfBoundsException
+     *   if {@code byteOffset < 0 || byteCount < 0 || byteOffset + byteCount > buffer.length}.
+     * @throws IOException
+     *             if the stream is closed or another IOException occurs.
+     */
+    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+        IOUtils.checkOffsetAndCount(buffer, byteOffset, byteCount);
+        for (int i = 0; i < byteCount; ++i) {
+            int c;
+            try {
+                if ((c = read()) == -1) {
+                    return i == 0 ? -1 : i;
+                }
+            } catch (IOException e) {
+                if (i != 0) {
+                    return i;
+                }
+                throw e;
+            }
+            buffer[byteOffset + i] = (byte) c;
+        }
+        return byteCount;
+    }
+
+    /**
+     * Resets this stream to the last marked location. Throws an
+     * {@code IOException} if the number of bytes read since the mark has been
+     * set is greater than the limit provided to {@code mark}, or if no mark
+     * has been set.
+     * <p>
+     * This implementation always throws an {@code IOException} and concrete
+     * subclasses should provide the proper implementation.
+     *
+     * @throws IOException
+     *             if this stream is closed or another IOException occurs.
+     */
+    public synchronized void reset() throws IOException {
+        throw new IOException();
+    }
+
+    /**
+     * Skips at most {@code byteCount} bytes in this stream. The number of actual
+     * bytes skipped may be anywhere between 0 and {@code byteCount}. If
+     * {@code byteCount} is negative, this method does nothing and returns 0, but
+     * some subclasses may throw.
+     *
+     * <p>Note the "at most" in the description of this method: this method may
+     * choose to skip fewer bytes than requested. Callers should <i>always</i>
+     * check the return value.
+     *
+     * <p>This default implementation reads bytes into a temporary buffer. Concrete
+     * subclasses should provide their own implementation.
+     *
+     * @return the number of bytes actually skipped.
+     * @throws IOException if this stream is closed or another IOException
+     *             occurs.
+     */
+    public long skip(long byteCount) throws IOException {
+        if (byteCount <= 0) {
+            return 0;
+        }
+        final int bSize = (int) Math.min(MAX_SKIP_BUFFER_SIZE, byteCount);
+        final byte[] b = new byte[bSize];
+        long skipped = 0;
+        while (skipped < byteCount) {
+            final int toRead = (int) Math.min(byteCount - skipped, b.length);
+            final int readCount = read(b, 0, toRead);
+            if (readCount == -1) {
+                break;
+            }
+            skipped += readCount;
+            if (readCount < toRead) {
+                break;
+            }
+        }
+        return skipped;
+    }
+}
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index edce91d..0f447bc 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.emultest;
 
+import com.google.gwt.emultest.java.io.ByteArrayInputStreamTest;
+import com.google.gwt.emultest.java.io.InputStreamTest;
 import com.google.gwt.emultest.java.lang.BooleanTest;
 import com.google.gwt.emultest.java.lang.ByteTest;
 import com.google.gwt.emultest.java.lang.CharacterTest;
@@ -53,6 +55,9 @@
     GWTTestSuite suite = new GWTTestSuite("Tests for com.google.gwt.emul.java");
 
     // $JUnit-BEGIN$
+    //-- java.io
+    suite.addTestSuite(ByteArrayInputStreamTest.class);
+    suite.addTestSuite(InputStreamTest.class);
     //-- java.lang
     suite.addTestSuite(BooleanTest.class);
     suite.addTestSuite(ByteTest.class);
diff --git a/user/test/com/google/gwt/emultest/java/io/ByteArrayInputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/ByteArrayInputStreamTest.java
new file mode 100644
index 0000000..2961e86
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/io/ByteArrayInputStreamTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Unit test for the {@link java.io.ByteArrayInputStream} emulated class.
+ */
+public class ByteArrayInputStreamTest extends InputStreamBaseTest {
+
+  @Override
+  protected InputStream createInputStream(final byte[] expectedBytes) {
+    // note that GWT fails here when trying to use clone().
+    return new ByteArrayInputStream(Arrays.copyOf(expectedBytes, expectedBytes.length));
+  }
+
+  public void testAvailableForEmptyArray() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[0]);
+    final int available = inputStream.available();
+    assertEquals(0, available);
+  }
+
+  public void testAvailableForNonEmptyArray() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES);
+    final int available = inputStream.available();
+    assertEquals(TEST_BYTES.length, available);
+  }
+
+  public void testAvailableForEmptyArrayRange() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES, 1, 0);
+    final int available = inputStream.available();
+    assertEquals(0, available);
+  }
+
+  public void testAvailableForNonEmptyArrayRange() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES, 1, 3);
+    final int available = inputStream.available();
+    assertEquals(3, available);
+  }
+
+  public void testClose() throws IOException {
+    // should do nothing (including not throwing an exception)
+    try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES)) {
+    }
+  }
+
+  public void testMarkSupported() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES);
+    final boolean markSupported = inputStream.markSupported();
+    assertTrue(markSupported);
+  }
+
+  public void testReadSingleValueFromEmptyArray() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[0]);
+    final int c = inputStream.read();
+    assertEquals(-1, c);
+  }
+
+  public void testReadSingleValuesFromNonEmptyArray() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES);
+    for (int i = 0; i < TEST_BYTES.length; i++) {
+      final int c = inputStream.read();
+      assertEquals(TEST_BYTES[i], c);
+
+      final int available = inputStream.available();
+      assertEquals(TEST_BYTES.length - i - 1, available);
+    }
+    // at this point we should have reached the end of the stream.
+    final int c = inputStream.read();
+    assertEquals(-1, c);
+
+    final int available = inputStream.available();
+    assertEquals(0, available);
+  }
+
+  public void testReadSingleValueFromEmptyArrayRange() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES, 1, 0);
+    final int c = inputStream.read();
+    assertEquals(-1, c);
+  }
+
+  public void testReadSingleValuesFromNonEmptyArrayRange() {
+    final int startAt = 1;
+    final int count = 3;
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES, startAt, count);
+    for (int i = 0; i < count; i++) {
+      final int c = inputStream.read();
+      assertEquals(TEST_BYTES[i + startAt], c);
+
+      final int available = inputStream.available();
+      assertEquals(count - i - 1, available);
+    }
+    // at this point we should have reached the end of the stream.
+    final int c = inputStream.read();
+    assertEquals(-1, c);
+
+    final int available = inputStream.available();
+    assertEquals(0, available);
+  }
+
+  public void testResetWithoutInvokingMark() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES);
+
+    inputStream.reset();
+
+    int c = inputStream.read();
+    assertEquals(TEST_BYTES[0], c);
+    c = inputStream.read();
+    assertEquals(TEST_BYTES[1], c);
+  }
+
+  public void testResetAfterInvokingMark() {
+    final ByteArrayInputStream inputStream = new ByteArrayInputStream(TEST_BYTES);
+
+    int c = inputStream.read();
+    assertEquals(TEST_BYTES[0], c);
+
+    inputStream.mark(0);
+
+    c = inputStream.read();
+    assertEquals(TEST_BYTES[1], c);
+    c = inputStream.read();
+    assertEquals(TEST_BYTES[2], c);
+
+    inputStream.reset();
+
+    c = inputStream.read();
+    assertEquals(TEST_BYTES[1], c);
+    c = inputStream.read();
+    assertEquals(TEST_BYTES[2], c);
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java/io/InputStreamBaseTest.java b/user/test/com/google/gwt/emultest/java/io/InputStreamBaseTest.java
new file mode 100644
index 0000000..1b499ec
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/io/InputStreamBaseTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.InputStream;
+import java.util.Arrays;
+
+/**
+ * Class for reusing tests that are common to {@link java.io.InputStream} and its subclasses.
+ */
+public abstract class InputStreamBaseTest extends GWTTestCase {
+
+  protected static final byte[] TEST_BYTES = new byte[] { 10, 20, 30, 40, 50 };
+
+  /**
+   * Sets module name so that javascript compiler can operate.
+   */
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.emultest.EmulSuite";
+  }
+
+  /**
+   * Factory method for creating a stream object.
+   *
+   * @param expectedBytes expected bytes when reading from the stream.
+   * @return input stream object to be tested.
+   */
+  protected abstract InputStream createInputStream(byte[] expectedBytes);
+
+  public void testReadArrayUsingNullArrayObject() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    try {
+      inputStream.read(null, 0, 1);
+      fail("should have thrown NullPointerException");
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testReadArrayUsingNegativeOffsetValue() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[1];
+    try {
+      inputStream.read(b, -1, 1);
+      fail("should have thrown IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  public void testReadArrayUsingNegativeLengthValue() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[1];
+    try {
+      inputStream.read(b, 0, -1);
+      fail("should have thrown IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  public void testReadArrayUsingAnInvalidRange() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[1];
+    try {
+      inputStream.read(b, 1, 1);
+      fail("should have thrown IndexOutOfBoundsException");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
+  public void testReadArrayUsingZeroLength() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[1];
+    int c = inputStream.read(b, 0, 0);
+    assertEquals(0, c);
+    c = inputStream.read(b, 1, 0);
+    assertEquals(0, c);
+  }
+
+  public void testReadArrayFromEmptyStream() throws IOException {
+    final InputStream inputStream = createInputStream(new byte[] {});
+    final byte[] b = new byte[1];
+    final int c = inputStream.read(b, 0, 1);
+    assertEquals(-1, c);
+  }
+
+  public void testReadArrayPartial() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[2];
+    final int result = inputStream.read(b, 1, 1);
+    assertEquals(1, result);
+    assertEquals(b[0], 0);
+    assertEquals(b[1], TEST_BYTES[0]);
+  }
+
+  public void testReadArrayReachEndOfStream() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[TEST_BYTES.length + 5];
+    final int result = inputStream.read(b, 5, TEST_BYTES.length);
+    assertEquals(TEST_BYTES.length, result);
+
+    final byte[] expected = new byte[b.length];
+    System.arraycopy(TEST_BYTES, 0, expected, 5, TEST_BYTES.length);
+    assertTrue(Arrays.equals(expected, b));
+  }
+
+  public void testReadArrayExceedEndOfStream() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[5 + TEST_BYTES.length + 5];
+    final int result = inputStream.read(b, 5, TEST_BYTES.length + 5);
+    assertEquals(TEST_BYTES.length, result);
+
+    final byte[] expected = new byte[b.length];
+    System.arraycopy(TEST_BYTES, 0, expected, 5, TEST_BYTES.length);
+    assertTrue(Arrays.equals(expected, b));
+  }
+
+  public void testReadArrayUsingNullArrayObjectNoOffset() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    try {
+      inputStream.read(null);
+      fail("should have thrown NullPointerException");
+    } catch (NullPointerException expected) {
+    }
+  }
+
+  public void testReadUsingAnEmptyArrayNoOffset() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[0];
+    final int c = inputStream.read(b);
+    assertEquals(0, c);
+  }
+
+  public void testReadArrayFromEmptyStreamNoOffset() throws IOException {
+    final InputStream inputStream = createInputStream(new byte[] {});
+    final byte[] b = new byte[1];
+    final int c = inputStream.read(b);
+    assertEquals(-1, c);
+  }
+
+  public void testReadArrayPartialNoOffset() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[TEST_BYTES.length / 2];
+    final int result = inputStream.read(b);
+    assertEquals(b.length, result);
+    assertTrue(Arrays.equals(Arrays.copyOf(TEST_BYTES, b.length), b));
+  }
+
+  public void testReadArrayReachEndOfStreamNoOffset() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[TEST_BYTES.length];
+    final int result = inputStream.read(b);
+    assertEquals(TEST_BYTES.length, result);
+    assertTrue(Arrays.equals(TEST_BYTES, b));
+  }
+
+  public void testReadArrayExceedEndOfStreamNoOffset() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final byte[] b = new byte[TEST_BYTES.length * 2];
+    final int result = inputStream.read(b);
+    assertEquals(TEST_BYTES.length, result);
+    assertTrue(Arrays.equals(Arrays.copyOf(TEST_BYTES, b.length), b));
+  }
+
+  public void testSkipZeroBytes() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    long result = inputStream.skip(0);
+    assertEquals(0, result);
+  }
+
+  public void testSkipFewBytes() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    long result = inputStream.skip(3);
+    assertEquals(3, result);
+  }
+
+  public void testSkipReachEndOfStream() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    long result = inputStream.skip(TEST_BYTES.length);
+    assertEquals(TEST_BYTES.length, result);
+  }
+
+  public void testSkipExceedEndOfStream() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    long result = inputStream.skip(TEST_BYTES.length + 5);
+    assertEquals(TEST_BYTES.length, result);
+  }
+}
diff --git a/user/test/com/google/gwt/emultest/java/io/InputStreamTest.java b/user/test/com/google/gwt/emultest/java/io/InputStreamTest.java
new file mode 100644
index 0000000..ba491d3
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/io/InputStreamTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.InputStream;
+import java.util.Arrays;
+
+/**
+ * Unit test for the {@link java.io.InputStream} emulated class.
+ */
+public class InputStreamTest extends InputStreamBaseTest {
+
+  @Override
+  protected InputStream createInputStream(final byte[] expectedBytes) {
+    return new InputStream() {
+      // note that GWT fails here when trying to use clone().
+      private final byte[] b = Arrays.copyOf(expectedBytes, expectedBytes.length);
+      private int index = 0;
+      @Override public int read() {
+        int c = -1;
+        if (index < b.length) {
+          c = b[index];
+          index++;
+        }
+        return c;
+      }
+    };
+  }
+
+  public void testDefaultBehaviorOfAvailable() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final int available = inputStream.available();
+    assertEquals(0, available);
+  }
+
+  public void testDefaultBehaviorOfClose() throws IOException {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    // should do nothing (including not throwing an exception)
+    inputStream.close();
+  }
+
+  public void testDefaultBehaviorOfMark() {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    // should do nothing (including not throwing an exception)
+    inputStream.mark(1000);
+  }
+
+  public void testDefaultBehaviorOfMarkSupported() {
+    final InputStream inputStream = createInputStream(TEST_BYTES);
+    final boolean markSupported = inputStream.markSupported();
+    assertFalse(markSupported);
+  }
+
+  public void testDefaultBehaviorOfReset() {
+    final InputStream inputStream = createInputStream(new byte[] {});
+    try {
+      inputStream.reset();
+      fail("should have thrown IOException");
+    } catch (IOException expected) {
+    }
+  }
+}