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.build;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.io.Closeable;
27  import java.io.File;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.io.RandomAccessFile;
32  import java.io.Reader;
33  import java.io.Writer;
34  import java.nio.ByteBuffer;
35  import java.nio.channels.ReadableByteChannel;
36  import java.nio.channels.SeekableByteChannel;
37  import java.nio.channels.WritableByteChannel;
38  import java.nio.charset.Charset;
39  import java.nio.charset.StandardCharsets;
40  import java.nio.file.Files;
41  import java.nio.file.OpenOption;
42  import java.nio.file.Path;
43  import java.nio.file.StandardOpenOption;
44  import java.util.Objects;
45  
46  import org.apache.commons.io.FileUtils;
47  import org.apache.commons.io.IOUtils;
48  import org.apache.commons.io.build.AbstractOrigin.RandomAccessFileOrigin;
49  import org.junit.jupiter.api.AfterEach;
50  import org.junit.jupiter.api.BeforeEach;
51  import org.junit.jupiter.api.Test;
52  import org.junit.jupiter.api.io.TempDir;
53  import org.junit.jupiter.params.ParameterizedTest;
54  import org.junit.jupiter.params.provider.EnumSource;
55  
56  /**
57   * Tests {@link AbstractOrigin} and subclasses.
58   *
59   * @param <T> the type of instances to build.
60   * @param <B> the type of builder subclass.
61   */
62  public abstract class AbstractOriginTest<T, B extends AbstractOrigin<T, B>> {
63  
64      protected static final String FILE_RES_RO = "/org/apache/commons/io/test-file-20byteslength.bin";
65      protected static final String FILE_NAME_RO = "src/test/resources" + FILE_RES_RO;
66      protected static final String FILE_NAME_RW = AbstractOriginTest.class.getSimpleName() + ".txt";
67      private static final int RO_LENGTH = 20;
68  
69      protected AbstractOrigin<T, B> originRo;
70      protected AbstractOrigin<T, B> originRw;
71  
72      @TempDir
73      protected Path tempPath;
74  
75      @BeforeEach
76      void beforeEach() throws IOException {
77          setOriginRo(newOriginRo());
78          resetOriginRw();
79          setOriginRw(newOriginRw());
80      }
81  
82      private void checkRead(final ReadableByteChannel channel) throws IOException {
83          final ByteBuffer buffer = ByteBuffer.allocate(RO_LENGTH);
84          int read = channel.read(buffer);
85          assertEquals(RO_LENGTH, read);
86          assertArrayEquals(getFixtureByteArray(), buffer.array());
87          // Channel is at EOF
88          buffer.clear();
89          read = channel.read(buffer);
90          assertEquals(-1, read);
91      }
92  
93      private void checkWrite(final WritableByteChannel channel) throws IOException {
94          final ByteBuffer buffer = ByteBuffer.wrap(getFixtureByteArray());
95          final int written = channel.write(buffer);
96          assertEquals(RO_LENGTH, written);
97      }
98  
99      @AfterEach
100     void cleanup() {
101         final T originRo = getOriginRo().get();
102         if (originRo instanceof Closeable) {
103             IOUtils.closeQuietly((Closeable) originRo);
104         }
105         final T originRw = getOriginRw().get();
106         if (originRw instanceof Closeable) {
107             IOUtils.closeQuietly((Closeable) originRw);
108         }
109     }
110 
111     byte[] getFixtureByteArray() throws IOException {
112         return IOUtils.resourceToByteArray(FILE_RES_RO);
113     }
114 
115     String getFixtureString() throws IOException {
116         return IOUtils.resourceToString(FILE_RES_RO, StandardCharsets.UTF_8);
117     }
118 
119     protected AbstractOrigin<T, B> getOriginRo() {
120         return Objects.requireNonNull(originRo, "originRo");
121     }
122 
123     protected AbstractOrigin<T, B> getOriginRw() {
124         return Objects.requireNonNull(originRw, "originRw");
125     }
126 
127     @SuppressWarnings("resource")
128     private boolean isValid(final RandomAccessFile raf) throws IOException {
129         return Objects.requireNonNull(raf).getFD().valid();
130     }
131 
132     protected abstract B newOriginRo() throws IOException;
133 
134     protected abstract B newOriginRw() throws IOException;
135 
136     protected void resetOriginRw() throws IOException {
137         // No-op
138     }
139 
140     protected void setOriginRo(final AbstractOrigin<T, B> origin) {
141         this.originRo = origin;
142     }
143 
144     protected void setOriginRw(final AbstractOrigin<T, B> origin) {
145         this.originRw = origin;
146     }
147 
148     @Test
149     void testGetByteArray() throws IOException {
150         assertArrayEquals(getFixtureByteArray(), getOriginRo().getByteArray());
151     }
152 
153     @Test
154     void testGetByteArrayAt_0_0() throws IOException {
155         assertArrayEquals(new byte[] {}, getOriginRo().getByteArray(0, 0));
156     }
157 
158     @Test
159     void testGetByteArrayAt_0_1() throws IOException {
160         assertArrayEquals(new byte[] { '1' }, getOriginRo().getByteArray(0, 1));
161     }
162 
163     @Test
164     void testGetByteArrayAt_1_1() throws IOException {
165         assertArrayEquals(new byte[] { '2' }, getOriginRo().getByteArray(1, 1));
166     }
167 
168     @Test
169     void testGetCharSequence() throws IOException {
170         final CharSequence charSequence = getOriginRo().getCharSequence(StandardCharsets.UTF_8);
171         assertNotNull(charSequence);
172         assertEquals(getFixtureString(), charSequence.toString());
173     }
174 
175     @Test
176     void testGetFile() throws IOException {
177         testGetFile(getOriginRo().getFile(), RO_LENGTH);
178         FileUtils.touch(getOriginRw().getFile());
179         testGetFile(getOriginRw().getFile(), 0);
180     }
181 
182     private void testGetFile(final File file, final long expectedLen) throws IOException {
183         assertNotNull(file);
184         assertTrue(file.exists(), () -> "File does not exist: " + file);
185         final int length = FileUtils.readFileToByteArray(file).length;
186         assertEquals(length, expectedLen, () -> String.format("File %s, actual length=%,d", file, length));
187     }
188 
189     @Test
190     void testGetInputStream() throws IOException {
191         try (InputStream inputStream = getOriginRo().getInputStream()) {
192             assertNotNull(inputStream);
193             assertArrayEquals(getFixtureByteArray(), IOUtils.toByteArray(inputStream));
194         }
195     }
196 
197     @Test
198     void testGetOutputStream() throws IOException {
199         try (OutputStream output = getOriginRw().getOutputStream()) {
200             assertNotNull(output);
201         }
202     }
203 
204     @Test
205     void testGetPath() throws IOException {
206         testGetPath(getOriginRo().getPath(), RO_LENGTH);
207         FileUtils.touch(getOriginRw().getPath().toFile());
208         testGetPath(getOriginRw().getPath(), 0);
209     }
210 
211     private void testGetPath(final Path path, final long expectedLen) throws IOException {
212         assertNotNull(path);
213         assertTrue(Files.exists(path));
214         final int length = Files.readAllBytes(path).length;
215         assertEquals(length, expectedLen, () -> String.format("Path %s, actual length=%,d", path, length));
216     }
217 
218     @Test
219     void testGetRandomAccessFile() throws IOException {
220         // Default
221         try (RandomAccessFile raf = getOriginRo().getRandomAccessFile()) {
222             assertNotNull(raf);
223             assertTrue(isValid(raf));
224         }
225         final boolean isRafOriginRo = getOriginRo() instanceof RandomAccessFileOrigin;
226         final boolean isRafOriginRw = getOriginRw() instanceof RandomAccessFileOrigin;
227         // Same as above, but underlying resource is now closed.
228         try (RandomAccessFile raf = getOriginRo().getRandomAccessFile()) {
229             assertNotNull(raf);
230             assertFalse(isRafOriginRo && isValid(raf));
231         }
232         // Read
233         try (RandomAccessFile raf = getOriginRo().getRandomAccessFile(StandardOpenOption.READ)) {
234             assertNotNull(raf);
235             assertFalse(isRafOriginRo && isValid(raf));
236         }
237         // Write, first access
238         try (RandomAccessFile raf = getOriginRw().getRandomAccessFile(StandardOpenOption.WRITE)) {
239             assertNotNull(raf);
240             if (isRafOriginRw || getOriginRw().getFile() != null) {
241                 assertTrue(isValid(raf), () -> getOriginRw().toString());
242             } else {
243                 // Can't get there from here.
244                 assertFalse(isValid(raf), () -> getOriginRw().toString());
245             }
246         }
247         // Read, Write, underlying resource is now closed.
248         try (RandomAccessFile raf = getOriginRw().getRandomAccessFile(StandardOpenOption.READ, StandardOpenOption.WRITE)) {
249             assertNotNull(raf);
250             assertFalse(isRafOriginRw && isValid(raf));
251         }
252     }
253 
254     @ParameterizedTest
255     @EnumSource(StandardOpenOption.class)
256     void testGetRandomAccessFile(final OpenOption openOption) throws IOException {
257         // Default
258         try (RandomAccessFile raf = getOriginRw().getRandomAccessFile()) {
259             assertNotNull(raf);
260             assertTrue(isValid(raf));
261         }
262         // Same as above, but underlying resource is now closed.
263         final boolean isRafOrigin = getOriginRw() instanceof RandomAccessFileOrigin;
264         try (RandomAccessFile raf = getOriginRw().getRandomAccessFile()) {
265             assertNotNull(raf);
266             assertFalse(isRafOrigin && isValid(raf));
267         }
268         try (RandomAccessFile raf = getOriginRw().getRandomAccessFile(openOption)) {
269             assertNotNull(raf);
270             assertFalse(isRafOrigin && isValid(raf));
271         }
272         try (RandomAccessFile raf = getOriginRw().getRandomAccessFile(openOption)) {
273             assertNotNull(raf);
274             assertFalse(isRafOrigin && isValid(raf));
275         }
276     }
277 
278     @Test
279     void testGetReadableByteChannel() throws IOException {
280         try (ReadableByteChannel channel = getOriginRo().getChannel(ReadableByteChannel.class, StandardOpenOption.READ)) {
281             final SeekableByteChannel seekable = channel instanceof SeekableByteChannel ? (SeekableByteChannel) channel : null;
282             assertNotNull(channel);
283             assertTrue(channel.isOpen());
284             if (seekable != null) {
285                 assertEquals(0, seekable.position());
286                 assertEquals(RO_LENGTH, seekable.size());
287             }
288             checkRead(channel);
289             if (seekable != null) {
290                 assertEquals(RO_LENGTH, seekable.position());
291             }
292         }
293     }
294 
295     @Test
296     void testGetReader() throws IOException {
297         try (Reader reader = getOriginRo().getReader(Charset.defaultCharset())) {
298             assertNotNull(reader);
299         }
300         setOriginRo(newOriginRo());
301         try (Reader reader = getOriginRo().getReader(null)) {
302             assertNotNull(reader);
303         }
304         setOriginRo(newOriginRo());
305         try (Reader reader = getOriginRo().getReader(StandardCharsets.UTF_8)) {
306             assertNotNull(reader);
307             assertEquals(getFixtureString(), IOUtils.toString(reader));
308         }
309     }
310 
311     @Test
312     void testGetWritableByteChannel() throws IOException {
313         final boolean supportsRead;
314         try (WritableByteChannel channel = getOriginRw().getChannel(WritableByteChannel.class, StandardOpenOption.WRITE)) {
315             supportsRead = channel instanceof ReadableByteChannel;
316             final SeekableByteChannel seekable = channel instanceof SeekableByteChannel ? (SeekableByteChannel) channel : null;
317             assertNotNull(channel);
318             assertTrue(channel.isOpen());
319             if (seekable != null) {
320                 assertEquals(0, seekable.position());
321                 assertEquals(0, seekable.size());
322             }
323             checkWrite(channel);
324             if (seekable != null) {
325                 assertEquals(RO_LENGTH, seekable.position());
326                 assertEquals(RO_LENGTH, seekable.size());
327             }
328         }
329         if (supportsRead) {
330             setOriginRw(newOriginRw());
331             try (ReadableByteChannel channel = getOriginRw().getChannel(ReadableByteChannel.class, StandardOpenOption.READ)) {
332                 assertNotNull(channel);
333                 assertTrue(channel.isOpen());
334                 checkRead(channel);
335             }
336         }
337         setOriginRw(newOriginRw());
338         try (WritableByteChannel channel = getOriginRw().getChannel(WritableByteChannel.class, StandardOpenOption.WRITE)) {
339             final SeekableByteChannel seekable = channel instanceof SeekableByteChannel ? (SeekableByteChannel) channel : null;
340             assertNotNull(channel);
341             assertTrue(channel.isOpen());
342             if (seekable != null) {
343                 seekable.position(RO_LENGTH);
344                 assertEquals(RO_LENGTH, seekable.position());
345                 assertEquals(RO_LENGTH, seekable.size());
346                 // Truncate
347                 final int newSize = RO_LENGTH / 2;
348                 seekable.truncate(newSize);
349                 assertEquals(newSize, seekable.position());
350                 assertEquals(newSize, seekable.size());
351                 // Rewind
352                 seekable.position(0);
353                 assertEquals(0, seekable.position());
354             }
355         }
356     }
357 
358     @Test
359     void testGetWriter() throws IOException {
360         try (Writer writer = getOriginRw().getWriter(Charset.defaultCharset())) {
361             assertNotNull(writer);
362         }
363         setOriginRw(newOriginRw());
364         try (Writer writer = getOriginRw().getWriter(null)) {
365             assertNotNull(writer);
366         }
367     }
368 
369     @Test
370     void testSize() throws IOException {
371         assertEquals(RO_LENGTH, getOriginRo().getByteArray().length);
372     }
373 }