/* * Copyright 2012-2017 the original author or authors. * * 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 org.springframework.boot.loader.data; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Queue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.springframework.boot.loader.data.RandomAccessData.ResourceAccess; import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link RandomAccessDataFile}. * * @author Phillip Webb */ public class RandomAccessDataFileTests { private static final byte[] BYTES; static { BYTES = new byte[256]; for (int i = 0; i < BYTES.length; i++) { BYTES[i] = (byte) i; } } @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private File tempFile; private RandomAccessDataFile file; private InputStream inputStream; @Before public void setup() throws Exception { this.tempFile = this.temporaryFolder.newFile(); FileOutputStream outputStream = new FileOutputStream(this.tempFile); outputStream.write(BYTES); outputStream.close(); this.file = new RandomAccessDataFile(this.tempFile); this.inputStream = this.file.getInputStream(ResourceAccess.PER_READ); } @After public void cleanup() throws Exception { this.inputStream.close(); this.file.close(); } @Test public void fileNotNull() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("File must not be null"); new RandomAccessDataFile(null); } @Test public void fileExists() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("File must exist"); new RandomAccessDataFile(new File("/does/not/exist")); } @Test public void fileNotNullWithConcurrentReads() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("File must not be null"); new RandomAccessDataFile(null, 1); } @Test public void fileExistsWithConcurrentReads() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("File must exist"); new RandomAccessDataFile(new File("/does/not/exist"), 1); } @Test public void inputStreamRead() throws Exception { for (int i = 0; i <= 255; i++) { assertThat(this.inputStream.read()).isEqualTo(i); } } @Test public void inputStreamReadNullBytes() throws Exception { this.thrown.expect(NullPointerException.class); this.thrown.expectMessage("Bytes must not be null"); this.inputStream.read(null); } @Test public void inputStreamReadNullBytesWithOffset() throws Exception { this.thrown.expect(NullPointerException.class); this.thrown.expectMessage("Bytes must not be null"); this.inputStream.read(null, 0, 1); } @Test public void inputStreamReadBytes() throws Exception { byte[] b = new byte[256]; int amountRead = this.inputStream.read(b); assertThat(b).isEqualTo(BYTES); assertThat(amountRead).isEqualTo(256); } @Test public void inputStreamReadOffsetBytes() throws Exception { byte[] b = new byte[7]; this.inputStream.skip(1); int amountRead = this.inputStream.read(b, 2, 3); assertThat(b).isEqualTo(new byte[] { 0, 0, 1, 2, 3, 0, 0 }); assertThat(amountRead).isEqualTo(3); } @Test public void inputStreamReadMoreBytesThanAvailable() throws Exception { byte[] b = new byte[257]; int amountRead = this.inputStream.read(b); assertThat(b).startsWith(BYTES); assertThat(amountRead).isEqualTo(256); } @Test public void inputStreamReadPastEnd() throws Exception { this.inputStream.skip(255); assertThat(this.inputStream.read()).isEqualTo(0xFF); assertThat(this.inputStream.read()).isEqualTo(-1); assertThat(this.inputStream.read()).isEqualTo(-1); } @Test public void inputStreamReadZeroLength() throws Exception { byte[] b = new byte[] { 0x0F }; int amountRead = this.inputStream.read(b, 0, 0); assertThat(b).isEqualTo(new byte[] { 0x0F }); assertThat(amountRead).isEqualTo(0); assertThat(this.inputStream.read()).isEqualTo(0); } @Test public void inputStreamSkip() throws Exception { long amountSkipped = this.inputStream.skip(4); assertThat(this.inputStream.read()).isEqualTo(4); assertThat(amountSkipped).isEqualTo(4L); } @Test public void inputStreamSkipMoreThanAvailable() throws Exception { long amountSkipped = this.inputStream.skip(257); assertThat(this.inputStream.read()).isEqualTo(-1); assertThat(amountSkipped).isEqualTo(256L); } @Test public void inputStreamSkipPastEnd() throws Exception { this.inputStream.skip(256); long amountSkipped = this.inputStream.skip(1); assertThat(amountSkipped).isEqualTo(0L); } @Test public void subsectionNegativeOffset() throws Exception { this.thrown.expect(IndexOutOfBoundsException.class); this.file.getSubsection(-1, 1); } @Test public void subsectionNegativeLength() throws Exception { this.thrown.expect(IndexOutOfBoundsException.class); this.file.getSubsection(0, -1); } @Test public void subsectionZeroLength() throws Exception { RandomAccessData subsection = this.file.getSubsection(0, 0); assertThat(subsection.getInputStream(ResourceAccess.PER_READ).read()) .isEqualTo(-1); } @Test public void subsectionTooBig() throws Exception { this.file.getSubsection(0, 256); this.thrown.expect(IndexOutOfBoundsException.class); this.file.getSubsection(0, 257); } @Test public void subsectionTooBigWithOffset() throws Exception { this.file.getSubsection(1, 255); this.thrown.expect(IndexOutOfBoundsException.class); this.file.getSubsection(1, 256); } @Test public void subsection() throws Exception { RandomAccessData subsection = this.file.getSubsection(1, 1); assertThat(subsection.getInputStream(ResourceAccess.PER_READ).read()) .isEqualTo(1); } @Test public void inputStreamReadPastSubsection() throws Exception { RandomAccessData subsection = this.file.getSubsection(1, 2); InputStream inputStream = subsection.getInputStream(ResourceAccess.PER_READ); assertThat(inputStream.read()).isEqualTo(1); assertThat(inputStream.read()).isEqualTo(2); assertThat(inputStream.read()).isEqualTo(-1); } @Test public void inputStreamReadBytesPastSubsection() throws Exception { RandomAccessData subsection = this.file.getSubsection(1, 2); InputStream inputStream = subsection.getInputStream(ResourceAccess.PER_READ); byte[] b = new byte[3]; int amountRead = inputStream.read(b); assertThat(b).isEqualTo(new byte[] { 1, 2, 0 }); assertThat(amountRead).isEqualTo(2); } @Test public void inputStreamSkipPastSubsection() throws Exception { RandomAccessData subsection = this.file.getSubsection(1, 2); InputStream inputStream = subsection.getInputStream(ResourceAccess.PER_READ); assertThat(inputStream.skip(3)).isEqualTo(2L); assertThat(inputStream.read()).isEqualTo(-1); } @Test public void inputStreamSkipNegative() throws Exception { assertThat(this.inputStream.skip(-1)).isEqualTo(0L); } @Test public void getFile() throws Exception { assertThat(this.file.getFile()).isEqualTo(this.tempFile); } @Test public void concurrentReads() throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(20); List<Future<Boolean>> results = new ArrayList<>(); for (int i = 0; i < 100; i++) { results.add(executorService.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { InputStream subsectionInputStream = RandomAccessDataFileTests.this.file .getSubsection(0, 256) .getInputStream(ResourceAccess.PER_READ); byte[] b = new byte[256]; subsectionInputStream.read(b); return Arrays.equals(b, BYTES); } })); } for (Future<Boolean> future : results) { assertThat(future.get()).isTrue(); } } @Test public void close() throws Exception { this.file.getInputStream(ResourceAccess.PER_READ).read(); this.file.close(); Field filePoolField = RandomAccessDataFile.class.getDeclaredField("filePool"); filePoolField.setAccessible(true); Object filePool = filePoolField.get(this.file); Field filesField = filePool.getClass().getDeclaredField("files"); filesField.setAccessible(true); Queue<?> queue = (Queue<?>) filesField.get(filePool); assertThat(queue.size()).isEqualTo(0); } }