From 0540a360acd815cf963e284f204cab5612bd05cf Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Mon, 25 Mar 2024 13:57:01 +0100 Subject: [PATCH] Adding SharedFileInputStream tests Signed-off-by: Jorge Bescos Gascon --- .../mail/util/SharedFileInputStream.java | 40 ++++---- .../mail/util/SharedFileInputStreamTest.java | 96 ++++++++++++++++++- .../jakarta/mail/util/sharedinputstream.txt | 10 ++ 3 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 api/src/test/resources/jakarta/mail/util/sharedinputstream.txt diff --git a/api/src/main/java/jakarta/mail/util/SharedFileInputStream.java b/api/src/main/java/jakarta/mail/util/SharedFileInputStream.java index bd4d9981..0ef818f8 100644 --- a/api/src/main/java/jakarta/mail/util/SharedFileInputStream.java +++ b/api/src/main/java/jakarta/mail/util/SharedFileInputStream.java @@ -76,7 +76,7 @@ public class SharedFileInputStream extends BufferedInputStream */ static class SharedFile implements AutoCloseable { private int cnt; - private RandomAccessFile in; + RandomAccessFile in; SharedFile(String file) throws IOException { this.in = new RandomAccessFile(file, "r"); @@ -256,10 +256,10 @@ private int read1(byte[] b, int off, int len) throws IOException { int avail = count - pos; if (avail <= 0) { if (false) { - /* If the requested length is at least as large as the buffer, and - if there is no mark/reset activity, do not bother to copy the - bytes into the local buffer. In this way buffered streams will - cascade harmlessly. */ + /* If the requested length is at least as large as the buffer, and + if there is no mark/reset activity, do not bother to copy the + bytes into the local buffer. In this way buffered streams will + cascade harmlessly. */ if (len >= buf.length && markpos < 0) { // XXX - seek, update bufpos - how? return in.read(b, off, len); @@ -328,10 +328,10 @@ public synchronized long skip(long n) throws IOException { if (avail <= 0) { // If no mark position set then don't keep in buffer - /* + /* if (markpos <0) return in.skip(n); - */ + */ // Fill in buffer to save bytes for reset fill(); @@ -439,7 +439,7 @@ public void close() throws IOException { @Override public long getPosition() { //System.out.println("getPosition: start " + start + " pos " + pos -// + " bufpos " + bufpos + " = " + (bufpos + pos - start)); +// + " bufpos " + bufpos + " = " + (bufpos + pos - start)); if (in == null) throw new RuntimeException("Stream closed"); return bufpos + pos - start; @@ -473,18 +473,18 @@ public synchronized InputStream newStream(long start, long end) { // for testing... /* public static void main(String[] argv) throws Exception { - SharedFileInputStream is = new SharedFileInputStream(argv[0]); - java.util.Random r = new java.util.Random(); - int b; - while ((b = is.read()) >= 0) { - System.out.write(b); - if (r.nextDouble() < 0.3) { - InputStream is2 = is.newStream(is.getPosition(), -1); - int b2; - while ((b2 = is2.read()) >= 0) - ; - } - } + SharedFileInputStream is = new SharedFileInputStream(argv[0]); + java.util.Random r = new java.util.Random(); + int b; + while ((b = is.read()) >= 0) { + System.out.write(b); + if (r.nextDouble() < 0.3) { + InputStream is2 = is.newStream(is.getPosition(), -1); + int b2; + while ((b2 = is2.read()) >= 0) + ; + } + } } */ } diff --git a/api/src/test/java/jakarta/mail/util/SharedFileInputStreamTest.java b/api/src/test/java/jakarta/mail/util/SharedFileInputStreamTest.java index a1f707bb..bc56ca00 100644 --- a/api/src/test/java/jakarta/mail/util/SharedFileInputStreamTest.java +++ b/api/src/test/java/jakarta/mail/util/SharedFileInputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -16,13 +16,22 @@ package jakarta.mail.util; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import jakarta.mail.util.SharedFileInputStream.SharedFile; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.RandomAccessFile; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.reflect.Field; +import java.util.concurrent.TimeoutException; -import static org.junit.Assert.fail; +import org.junit.Test; /** * Please note: @@ -62,4 +71,85 @@ public void testGrandChild() throws Exception { file.delete(); } } + + @Test + public void testOpenIfOneOpened() throws Exception { + File file = new File(SharedFileInputStreamTest.class.getResource("/jakarta/mail/util/sharedinputstream.txt").toURI()); + SharedFileInputStream in0 = null; + SharedFileInputStream in1 = null; + try (SharedFileInputStream in = new SharedFileInputStream(file)) { + in0 = (SharedFileInputStream) in.newStream(0, 6); + in1 = (SharedFileInputStream) in.newStream(6, 12); + } + RandomAccessFile ra0 = getRandomAccessFile(in0); + RandomAccessFile ra1 = getRandomAccessFile(in1); + // It is the same instance + assertEquals(ra0, ra1); + // RandomAccessFile still be open + in1.close(); + assertEquals(false, isClosed(ra1)); + in0.close(); + // All SharedFileInputStream are closed, so RandomAccessFile gets closed too + assertEquals(true, isClosed(ra1)); + } + + @Test + public void testGC() throws Exception { + File file = new File(SharedFileInputStreamTest.class.getResource("/jakarta/mail/util/sharedinputstream.txt").toURI()); + SharedFileInputStream in = new SharedFileInputStream(file); + GCUtil gcUtil = new GCUtil(in); + SharedFileInputStream in0 = (SharedFileInputStream) in.newStream(0, 6); + in.close(); + in = null; + gcUtil.waitTillGCed(1000); + gcUtil = new GCUtil(in0); + SharedFileInputStream in1 = (SharedFileInputStream) in0.newStream(6, 12); + assertEquals("test0\n", new String(in0.readAllBytes())); + in0.close(); + in0 = null; + gcUtil.waitTillGCed(1000); + assertEquals("test1\n", new String(in1.readAllBytes())); + in1.close(); + } + + private RandomAccessFile getRandomAccessFile(SharedFileInputStream in) throws Exception { + Field f1 = SharedFileInputStream.class.getDeclaredField("sf"); + f1.setAccessible(true); + SharedFile rin = (SharedFile) f1.get(in); + RandomAccessFile rf = rin.in; + return rf; + } + + private boolean isClosed(RandomAccessFile rf) throws Exception { + try { + rf.readByte(); + return false; + } catch (IOException e) { + return true; + } + } + + private static class GCUtil { + + private final ReferenceQueue rq = new ReferenceQueue<>(); + private final PhantomReference phantomRef; + + private GCUtil(Object ref) { + phantomRef = new PhantomReference<>(ref, rq); + } + + private void waitTillGCed(long timeout) throws Exception { + Reference gced; + long time = 0; + long sleep = 100; + while ((gced = rq.poll()) != phantomRef) { + Thread.sleep(sleep); + time = time + sleep; + if (time >= timeout) { + throw new TimeoutException("Instance not GCed after " + timeout + " millis"); + } + System.gc(); + } + } + } } \ No newline at end of file diff --git a/api/src/test/resources/jakarta/mail/util/sharedinputstream.txt b/api/src/test/resources/jakarta/mail/util/sharedinputstream.txt new file mode 100644 index 00000000..7f136131 --- /dev/null +++ b/api/src/test/resources/jakarta/mail/util/sharedinputstream.txt @@ -0,0 +1,10 @@ +test0 +test1 +test2 +test3 +test4 +test5 +test6 +test7 +test8 +test9 \ No newline at end of file