View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *    https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.io.input;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  import static org.mockito.Mockito.spy;
25  import static org.mockito.Mockito.when;
26  
27  import java.io.FileInputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.InterruptedIOException;
31  import java.nio.file.StandardOpenOption;
32  import java.util.concurrent.ExecutorService;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.TimeUnit;
35  
36  import org.junit.jupiter.api.BeforeEach;
37  import org.junit.jupiter.api.Test;
38  import org.junit.jupiter.api.Timeout;
39  
40  /**
41   * Tests {@link ReadAheadInputStream}. This class was ported and adapted from Apache Spark commit 933dc6cb7b3de1d8ccaf73d124d6eb95b947ed19 where it was called
42   * {@code ReadAheadInputStreamSuite}.
43   */
44  class ReadAheadInputStreamTest extends AbstractInputStreamTest {
45  
46      @SuppressWarnings("resource")
47      @BeforeEach
48      void setUpInputStreams() throws IOException {
49          inputStreams = new InputStream[] {
50                  // Tests equal and aligned buffers of wrapped an outer stream.
51                  new ReadAheadInputStream(new BufferedFileChannelInputStream(InputPath, 8 * 1024), 8 * 1024),
52                  // Tests aligned buffers, wrapped bigger than outer.
53                  new ReadAheadInputStream(new BufferedFileChannelInputStream(InputPath, 3 * 1024), 2 * 1024),
54                  // Tests aligned buffers, wrapped smaller than outer.
55                  new ReadAheadInputStream(new BufferedFileChannelInputStream(InputPath, 2 * 1024), 3 * 1024),
56                  // Tests unaligned buffers, wrapped bigger than outer.
57                  new ReadAheadInputStream(new BufferedFileChannelInputStream(InputPath, 321), 123),
58                  // Tests unaligned buffers, wrapped smaller than outer.
59                  new ReadAheadInputStream(new BufferedFileChannelInputStream(InputPath, 123), 321),
60                  //
61                  // Tests equal and aligned buffers of wrapped an outer stream.
62                  ReadAheadInputStream.builder().setInputStream(new BufferedFileChannelInputStream(InputPath, 8 * 1024)).setBufferSize(8 * 1024).get(),
63                  // Tests aligned buffers, wrapped bigger than outer.
64                  ReadAheadInputStream.builder().setInputStream(new BufferedFileChannelInputStream(InputPath, 3 * 1024)).setBufferSize(2 * 1024).get(),
65                  // Tests aligned buffers, wrapped smaller than outer.
66                  ReadAheadInputStream.builder().setInputStream(new BufferedFileChannelInputStream(InputPath, 2 * 1024)).setBufferSize(3 * 1024).get(),
67                  // Tests unaligned buffers, wrapped bigger than outer.
68                  ReadAheadInputStream.builder().setInputStream(new BufferedFileChannelInputStream(InputPath, 321)).setBufferSize(123).get(),
69                  // Tests unaligned buffers, wrapped smaller than outer.
70                  ReadAheadInputStream.builder().setInputStream(new BufferedFileChannelInputStream(InputPath, 123)).setBufferSize(321).get(),
71                  ReadAheadInputStream.builder().setPath(InputPath).setOpenOptions(StandardOpenOption.READ).get() };
72      }
73  
74      @Test
75      @Timeout(value = 30, unit = TimeUnit.SECONDS)
76      synchronized void testCloseInterrupt() throws IOException, InterruptedException {
77          try (ReadAheadInputStream inputStream = ReadAheadInputStream.builder()
78                  // @formatter:off
79                  .setPath(InputPath)
80                  .get()) {
81                  // @formatter:on
82              final ReadAheadInputStream spy = spy(inputStream);
83              when(spy.shutdownAwait()).thenThrow(InterruptedException.class);
84              Thread.currentThread().interrupt();
85              assertInstanceOf(InterruptedException.class, assertThrows(InterruptedIOException.class, spy::close).getCause());
86              assertTrue(Thread.interrupted());
87          }
88      }
89  
90      @Test
91      void testClosePlusExecutorService() throws IOException {
92          final ExecutorService externalExecutor = Executors.newSingleThreadExecutor();
93          // We use an outer try-with-resources for only the test fixture instead of combining it with the ReadAheadInputStream allocation.
94          try (FileInputStream inputStream = new FileInputStream("src/test/resources/org/apache/commons/io/FileUtilsTestDataLF.bin")) {
95              try {
96                  try (ReadAheadInputStream rais = ReadAheadInputStream.builder().setInputStream(inputStream).setExecutorService(externalExecutor).get()) {
97                      assertEquals('1', rais.read());
98                  }
99                  Thread.yield();
100                 // The underlying FileInputStream should be closed since ReadAheadInputStream is a FilterInputStream.
101                 assertThrows(IOException.class, inputStream::available);
102                 assertThrows(IOException.class, inputStream::read);
103                 // The caller remains responsible for shutting down the executor.
104             } finally {
105                 // TODO ExecutorService implements AutoCloseable in Java 19+.
106                 externalExecutor.shutdown();
107             }
108         }
109     }
110 
111 }