#705 No longer closes streams we didn't open

(cherry picked from commit ab08ec1e0d699963fde7dc14e9a3cf32b9d10ea1)
This commit is contained in:
Harald Kuhr 2022-10-18 20:25:14 +02:00
parent 73a58266be
commit a98224e652
9 changed files with 86 additions and 63 deletions

View File

@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.stream; package com.twelvemonkeys.imageio.stream;
import javax.imageio.stream.ImageInputStreamImpl; import javax.imageio.stream.ImageInputStreamImpl;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@ -52,6 +53,10 @@ import static java.lang.Math.max;
* for shorter reads, like single byte or bit reads. * for shorter reads, like single byte or bit reads.
*/ */
final class BufferedChannelImageInputStream extends ImageInputStreamImpl { final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
private static final Closeable CLOSEABLE_STUB = new Closeable() {
@Override public void close() {}
};
static final int DEFAULT_BUFFER_SIZE = 8192; static final int DEFAULT_BUFFER_SIZE = 8192;
private ByteBuffer byteBuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); private ByteBuffer byteBuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
@ -63,6 +68,7 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
private final byte[] integralCacheArray = integralCache.array(); private final byte[] integralCacheArray = integralCache.array();
private SeekableByteChannel channel; private SeekableByteChannel channel;
private Closeable closeable;
/** /**
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code File}. * Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code File}.
@ -86,49 +92,62 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
* @throws SecurityException if a security manager is installed, and it denies read access to the file. * @throws SecurityException if a security manager is installed, and it denies read access to the file.
*/ */
public BufferedChannelImageInputStream(final Path file) throws IOException { public BufferedChannelImageInputStream(final Path file) throws IOException {
this(FileChannel.open(notNull(file, "file"), StandardOpenOption.READ)); this(FileChannel.open(notNull(file, "file"), StandardOpenOption.READ), true);
} }
/** /**
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code RandomAccessFile}. * Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code RandomAccessFile}.
* <p>
* Closing this stream will close the {@code RandomAccessFile}.
* </p>
* *
* @param file a {@code RandomAccessFile} to read from. * @param file a {@code RandomAccessFile} to read from.
* @throws IllegalArgumentException if {@code file} is {@code null}. * @throws IllegalArgumentException if {@code file} is {@code null}.
*/ */
public BufferedChannelImageInputStream(final RandomAccessFile file) { public BufferedChannelImageInputStream(final RandomAccessFile file) {
// Assumption: Closing the FileChannel will also close its backing RandomAccessFile // Closing the RAF is inconsistent, but emulates the behavior of javax.imageio.stream.FileImageInputStream
// (it does in the OpenJDK implementation, and it makes sense, although I can't see this is documented behaviour). this(notNull(file, "file").getChannel(), true);
this(notNull(file, "file").getChannel());
} }
/** /**
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code FileInputStream}. * Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code FileInputStream}.
* <p> * <p>
* Closing this stream will close the {@code FileInputStream}. * Closing this stream will <em>not</em> close the {@code FileInputStream}.
* </p> * </p>
* *
* @param inputStream a {@code FileInputStream} to read from. * @param inputStream a {@code FileInputStream} to read from.
* @throws IllegalArgumentException if {@code inputStream} is {@code null}. * @throws IllegalArgumentException if {@code inputStream} is {@code null}.
*/ */
public BufferedChannelImageInputStream(final FileInputStream inputStream) { public BufferedChannelImageInputStream(final FileInputStream inputStream) {
// Assumption: Closing the FileChannel will also close its backing FileInputStream (it does in the OpenJDK implementation, although I can't see this is documented). this(notNull(inputStream, "inputStream").getChannel(), false);
this(notNull(inputStream, "inputStream").getChannel());
} }
/** /**
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code SeekableByteChannel}. * Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code SeekableByteChannel}.
* <p> * <p>
* Closing this stream will close the {@code SeekableByteChannel}. * Closing this stream will <em>not</em> close the {@code SeekableByteChannel}.
* </p> * </p>
* *
* @param channel a {@code SeekableByteChannel} to read from. * @param channel a {@code SeekableByteChannel} to read from.
* @throws IllegalArgumentException if {@code channel} is {@code null}. * @throws IllegalArgumentException if {@code channel} is {@code null}.
*/ */
public BufferedChannelImageInputStream(final SeekableByteChannel channel) { public BufferedChannelImageInputStream(final SeekableByteChannel channel) {
this(notNull(channel, "channel"), false);
}
/**
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code Cache}.
* <p>
* Closing this stream will close the {@code Cache}.
* </p>
*
* @param cache a {@code SeekableByteChannel} to read from.
* @throws IllegalArgumentException if {@code channel} is {@code null}.
*/
BufferedChannelImageInputStream(final Cache cache) {
this(notNull(cache, "cache"), true);
}
private BufferedChannelImageInputStream(final SeekableByteChannel channel, boolean closeChannelOnClose) {
this.channel = notNull(channel, "channel"); this.channel = notNull(channel, "channel");
this.closeable = closeChannelOnClose ? this.channel : CLOSEABLE_STUB;
} }
@SuppressWarnings("BooleanMethodIsAlwaysInverted") @SuppressWarnings("BooleanMethodIsAlwaysInverted")
@ -246,8 +265,14 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
buffer = null; buffer = null;
byteBuffer = null; byteBuffer = null;
channel.close();
channel = null; channel = null;
try {
closeable.close();
}
finally {
closeable = null;
}
} }
// Need to override the readShort(), readInt() and readLong() methods, // Need to override the readShort(), readInt() and readLong() methods,
@ -315,9 +340,9 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
public void flushBefore(final long pos) throws IOException { public void flushBefore(final long pos) throws IOException {
super.flushBefore(pos); super.flushBefore(pos);
if (channel instanceof MemoryCache) { if (channel instanceof Cache) {
// In case of memory cache, free up memory // In case of memory cache, free up memory
((MemoryCache) channel).flushBefore(pos); ((Cache) channel).flushBefore(pos);
} }
} }
} }

View File

@ -53,7 +53,7 @@ public final class BufferedInputStreamImageInputStreamSpi extends ImageInputStre
} }
// Otherwise, create a cache for backwards seeking // Otherwise, create a cache for backwards seeking
return new BufferedChannelImageInputStream(useCacheFile ? new DiskCache(channel, cacheDir): new MemoryCache(channel)); return new BufferedChannelImageInputStream(useCacheFile ? new FileCache(channel, cacheDir) : new MemoryCache(channel));
} }
throw new IllegalArgumentException("Expected input of type InputStream: " + input); throw new IllegalArgumentException("Expected input of type InputStream: " + input);

View File

@ -0,0 +1,7 @@
package com.twelvemonkeys.imageio.stream;
import java.nio.channels.SeekableByteChannel;
interface Cache extends SeekableByteChannel {
void flushBefore(long pos);
}

View File

@ -26,19 +26,19 @@ import static java.nio.file.StandardOpenOption.WRITE;
// the usual {@link #read read} and {@link #write write} methods. From the // the usual {@link #read read} and {@link #write write} methods. From the
// standpoint of performance it is generally only worth mapping relatively // standpoint of performance it is generally only worth mapping relatively
// large files into memory. // large files into memory.
final class DiskCache implements SeekableByteChannel { final class FileCache implements Cache {
final static int BLOCK_SIZE = 1 << 13; final static int BLOCK_SIZE = 1 << 13;
private final FileChannel cache; private final FileChannel cache;
private final ReadableByteChannel channel; private final ReadableByteChannel channel;
// TODO: Perhaps skip this constructor? // TODO: Perhaps skip this constructor?
DiskCache(InputStream stream, File cacheDir) throws IOException { FileCache(InputStream stream, File cacheDir) throws IOException {
// Stream will be closed with channel, documented behavior // Stream will be closed with channel, documented behavior
this(Channels.newChannel(notNull(stream, "stream")), cacheDir); this(Channels.newChannel(notNull(stream, "stream")), cacheDir);
} }
public DiskCache(ReadableByteChannel channel, File cacheDir) throws IOException { public FileCache(ReadableByteChannel channel, File cacheDir) throws IOException {
this.channel = notNull(channel, "channel"); this.channel = notNull(channel, "channel");
isTrue(cacheDir == null || cacheDir.isDirectory(), cacheDir, "%s is not a directory"); isTrue(cacheDir == null || cacheDir.isDirectory(), cacheDir, "%s is not a directory");
@ -65,12 +65,7 @@ final class DiskCache implements SeekableByteChannel {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
try { cache.close();
cache.close();
}
finally {
channel.close();
}
} }
@Override @Override
@ -110,5 +105,8 @@ final class DiskCache implements SeekableByteChannel {
public SeekableByteChannel truncate(long size) { public SeekableByteChannel truncate(long size) {
throw new NonWritableChannelException(); throw new NonWritableChannelException();
} }
@Override public void flushBefore(long pos) {
}
} }

View File

@ -13,7 +13,7 @@ import java.util.List;
import static com.twelvemonkeys.lang.Validate.notNull; import static com.twelvemonkeys.lang.Validate.notNull;
import static java.lang.Math.min; import static java.lang.Math.min;
public final class MemoryCache implements SeekableByteChannel { final class MemoryCache implements Cache {
final static int BLOCK_SIZE = 1 << 13; final static int BLOCK_SIZE = 1 << 13;
@ -78,12 +78,7 @@ public final class MemoryCache implements SeekableByteChannel {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
try { cache.clear();
cache.clear();
}
finally {
channel.close();
}
} }
@Override @Override
@ -135,7 +130,8 @@ public final class MemoryCache implements SeekableByteChannel {
throw new NonWritableChannelException(); throw new NonWritableChannelException();
} }
void flushBefore(long pos) { @Override
public void flushBefore(long pos) {
if (pos < start) { if (pos < start) {
throw new IndexOutOfBoundsException("pos < flushed position"); throw new IndexOutOfBoundsException("pos < flushed position");
} }

View File

@ -80,7 +80,7 @@ public final class URLImageInputStreamSpi extends ImageInputStreamSpi {
// Otherwise revert to cached // Otherwise revert to cached
InputStream urlStream = url.openStream(); InputStream urlStream = url.openStream();
return new BufferedChannelImageInputStream(useCacheFile ? new DiskCache(urlStream, cacheDir) : new MemoryCache(urlStream)); return new BufferedChannelImageInputStream(useCacheFile ? new FileCache(urlStream, cacheDir) : new MemoryCache(urlStream));
} }
throw new IllegalArgumentException("Expected input of type URL: " + input); throw new IllegalArgumentException("Expected input of type URL: " + input);

View File

@ -57,7 +57,7 @@ import static org.mockito.Mockito.verify;
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$ * @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
*/ */
// TODO: Remove this test, and instead test the disk cache directly! // TODO: Remove this test, and instead test the disk cache directly!
public class BufferedChannelImageInputStreamDiskCacheTest { public class BufferedChannelImageInputStreamFileCacheTest {
private final Random random = new Random(170984354357234566L); private final Random random = new Random(170984354357234566L);
private InputStream randomDataToInputStream(byte[] data) { private InputStream randomDataToInputStream(byte[] data) {
@ -68,7 +68,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
@Test @Test
public void testCreate() throws IOException { public void testCreate() throws IOException {
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(new ByteArrayInputStream(new byte[0]), null))) { try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(new ByteArrayInputStream(new byte[0]), null))) {
assertEquals("Stream length should be unknown", -1, stream.length()); assertEquals("Stream length should be unknown", -1, stream.length());
} }
} }
@ -76,7 +76,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
@Test @Test
public void testCreateNullStream() throws IOException { public void testCreateNullStream() throws IOException {
try { try {
new DiskCache((InputStream) null, null); new FileCache((InputStream) null, null);
fail("Expected IllegalArgumentException"); fail("Expected IllegalArgumentException");
} }
catch (IllegalArgumentException expected) { catch (IllegalArgumentException expected) {
@ -90,7 +90,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
@Test @Test
public void testCreateNullChannel() throws IOException { public void testCreateNullChannel() throws IOException {
try { try {
new DiskCache((ReadableByteChannel) null, null); new FileCache((ReadableByteChannel) null, null);
fail("Expected IllegalArgumentException"); fail("Expected IllegalArgumentException");
} }
catch (IllegalArgumentException expected) { catch (IllegalArgumentException expected) {
@ -106,7 +106,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
byte[] data = new byte[1024 * 1024]; byte[] data = new byte[1024 * 1024];
InputStream input = randomDataToInputStream(data); InputStream input = randomDataToInputStream(data);
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
assertEquals("Stream length should be unknown", -1, stream.length()); assertEquals("Stream length should be unknown", -1, stream.length());
for (byte value : data) { for (byte value : data) {
@ -122,7 +122,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
byte[] data = new byte[1024 * 1024]; byte[] data = new byte[1024 * 1024];
InputStream input = randomDataToInputStream(data); InputStream input = randomDataToInputStream(data);
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
assertEquals("Stream length should be unknown", -1, stream.length()); assertEquals("Stream length should be unknown", -1, stream.length());
byte[] result = new byte[1024]; byte[] result = new byte[1024];
@ -141,7 +141,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
byte[] data = new byte[1024 * 14]; byte[] data = new byte[1024 * 14];
InputStream input = randomDataToInputStream(data); InputStream input = randomDataToInputStream(data);
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
assertEquals("Stream length should be unknown", -1, stream.length()); assertEquals("Stream length should be unknown", -1, stream.length());
byte[] result = new byte[7]; byte[] result = new byte[7];
@ -159,7 +159,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
byte[] data = new byte[1024 * 18]; byte[] data = new byte[1024 * 18];
InputStream input = randomDataToInputStream(data); InputStream input = randomDataToInputStream(data);
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
assertEquals("Stream length should be unknown", -1, stream.length()); assertEquals("Stream length should be unknown", -1, stream.length());
byte[] result = new byte[9]; byte[] result = new byte[9];
@ -180,7 +180,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
byte[] data = new byte[256]; byte[] data = new byte[256];
InputStream input = randomDataToInputStream(data); InputStream input = randomDataToInputStream(data);
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
assertEquals("Stream length should be unknown", -1, stream.length()); assertEquals("Stream length should be unknown", -1, stream.length());
byte[] buffer = new byte[data.length * 2]; byte[] buffer = new byte[data.length * 2];
@ -198,7 +198,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
try (ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
for (int i = 1; i <= 64; i++) { for (int i = 1; i <= 64; i++) {
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit()); assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
} }
@ -212,7 +212,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
try (ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
for (int i = 1; i <= 64; i++) { for (int i = 1; i <= 64; i++) {
stream.seek(0); stream.seek(0);
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i)); assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
@ -228,7 +228,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
try (ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
for (int i = 1; i <= 60; i++) { for (int i = 1; i <= 60; i++) {
stream.seek(0); stream.seek(0);
stream.setBitOffset(i % 8); stream.setBitOffset(i % 8);
@ -244,7 +244,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
InputStream input = randomDataToInputStream(bytes); InputStream input = randomDataToInputStream(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 2; i++) { for (int i = 0; i < bytes.length / 2; i++) {
@ -282,7 +282,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
InputStream input = randomDataToInputStream(bytes); InputStream input = randomDataToInputStream(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 4; i++) { for (int i = 0; i < bytes.length / 4; i++) {
@ -320,7 +320,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
InputStream input = randomDataToInputStream(bytes); InputStream input = randomDataToInputStream(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 8; i++) { for (int i = 0; i < bytes.length / 8; i++) {
@ -357,7 +357,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
byte[] bytes = new byte[9]; byte[] bytes = new byte[9];
InputStream input = randomDataToInputStream(bytes); InputStream input = randomDataToInputStream(bytes);
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
stream.seek(1000); stream.seek(1000);
assertEquals(-1, stream.read()); assertEquals(-1, stream.read());
@ -406,11 +406,11 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
@Test @Test
public void testClose() throws IOException { public void testClose() throws IOException {
// Create wrapper stream // Create wrapper stream
InputStream mock = mock(InputStream.class); Cache cache = mock(Cache.class);
ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(mock, null)); ImageInputStream stream = new BufferedChannelImageInputStream(cache);
stream.close(); stream.close();
verify(mock, only()).close(); verify(cache, only()).close();
} }
@Test @Test
@ -422,7 +422,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
byte[] bytes = new byte[size]; byte[] bytes = new byte[size];
InputStream input = randomDataToInputStream(bytes); InputStream input = randomDataToInputStream(bytes);
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) { try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
byte[] result = new byte[size]; byte[] result = new byte[size];
int head = stream.read(result, 0, 12); // Provoke a buffered read int head = stream.read(result, 0, 12); // Provoke a buffered read
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read

View File

@ -406,11 +406,11 @@ public class BufferedChannelImageInputStreamMemoryCacheTest {
@Test @Test
public void testClose() throws IOException { public void testClose() throws IOException {
// Create wrapper stream // Create wrapper stream
InputStream mock = mock(InputStream.class); Cache cache = mock(Cache.class);
ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(mock)); ImageInputStream stream = new BufferedChannelImageInputStream(cache);
stream.close(); stream.close();
verify(mock, only()).close(); verify(cache, only()).close();
} }
@Test @Test

View File

@ -47,7 +47,7 @@ import java.util.Random;
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals; import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
/** /**
@ -403,14 +403,11 @@ public class BufferedChannelImageInputStreamTest {
@Test @Test
public void testCloseChannel() throws IOException { public void testCloseChannel() throws IOException {
// NOTE: As the stream-based constructor is chained to the channel-based one, SeekableByteChannel channel = mock(SeekableByteChannel.class);
// we'll rely on the fact that closing the channel will close the stream. ImageInputStream stream = new BufferedChannelImageInputStream(channel);
SeekableByteChannel mock = mock(SeekableByteChannel.class);
ImageInputStream stream = new BufferedChannelImageInputStream(mock);
stream.close(); stream.close();
verify(mock, only()).close(); verify(channel, never()).close();
} }
@Test @Test