You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TestIOUtils.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.util;
  16. import static org.junit.jupiter.api.Assertions.assertArrayEquals;
  17. import static org.junit.jupiter.api.Assertions.assertEquals;
  18. import static org.junit.jupiter.api.Assertions.assertFalse;
  19. import static org.junit.jupiter.api.Assertions.assertNotNull;
  20. import static org.junit.jupiter.api.Assertions.assertThrows;
  21. import static org.junit.jupiter.api.Assertions.assertTrue;
  22. import java.io.ByteArrayInputStream;
  23. import java.io.EOFException;
  24. import java.io.File;
  25. import java.io.FileInputStream;
  26. import java.io.FileOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.OutputStream;
  30. import java.io.PushbackInputStream;
  31. import java.nio.ByteBuffer;
  32. import java.nio.channels.ReadableByteChannel;
  33. import java.nio.charset.StandardCharsets;
  34. import java.util.Random;
  35. import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
  36. import org.apache.poi.EmptyFileException;
  37. import org.junit.jupiter.api.AfterAll;
  38. import org.junit.jupiter.api.BeforeAll;
  39. import org.junit.jupiter.api.Test;
  40. import org.junit.jupiter.api.parallel.Isolated;
  41. @Isolated // this test changes global static BYTE_ARRAY_MAX_OVERRIDE
  42. final class TestIOUtils {
  43. private static File TMP;
  44. private static final long LENGTH = 300 + new Random().nextInt(9000);
  45. @BeforeAll
  46. public static void setUp() throws IOException {
  47. TMP = File.createTempFile("poi-ioutils-", "");
  48. try (OutputStream os = new FileOutputStream(TMP)) {
  49. for (int i = 0; i < LENGTH; i++) {
  50. os.write(0x01);
  51. }
  52. }
  53. }
  54. @AfterAll
  55. public static void tearDown() {
  56. if (TMP != null) {
  57. assertTrue(TMP.delete());
  58. }
  59. }
  60. private static InputStream data123() {
  61. return new ByteArrayInputStream(new byte[]{1,2,3});
  62. }
  63. @Test
  64. void testPeekFirst8Bytes() throws Exception {
  65. assertArrayEquals("01234567".getBytes(StandardCharsets.UTF_8),
  66. IOUtils.peekFirst8Bytes(new ByteArrayInputStream("0123456789".getBytes(StandardCharsets.UTF_8))));
  67. }
  68. @Test
  69. void testPeekFirst8BytesWithPushbackInputStream() throws Exception {
  70. assertArrayEquals("01234567".getBytes(StandardCharsets.UTF_8),
  71. IOUtils.peekFirst8Bytes(new PushbackInputStream(new ByteArrayInputStream("0123456789".getBytes(StandardCharsets.UTF_8)), 8)));
  72. }
  73. @Test
  74. void testPeekFirst8BytesTooLessAvailable() throws Exception {
  75. assertArrayEquals(new byte[] { 1, 2, 3, 0, 0, 0, 0, 0}, IOUtils.peekFirst8Bytes(data123()));
  76. }
  77. @Test
  78. void testPeekFirst8BytesEmpty() {
  79. assertThrows(EmptyFileException.class, () ->
  80. IOUtils.peekFirst8Bytes(new ByteArrayInputStream(new byte[0])));
  81. }
  82. @Test
  83. void testToByteArray() throws Exception {
  84. assertArrayEquals(new byte[] { 1, 2, 3}, IOUtils.toByteArray(data123()));
  85. }
  86. @Test
  87. void testToByteArrayToSmall() {
  88. assertThrows(IOException.class, () -> IOUtils.toByteArray(data123(), 10));
  89. }
  90. @Test
  91. void testToByteArrayMaxLengthToSmall() {
  92. assertThrows(IOException.class, () -> IOUtils.toByteArray(data123(), 10, 10));
  93. }
  94. @Test
  95. void testToByteArrayNegativeLength() {
  96. assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(data123(), -1));
  97. }
  98. @Test
  99. void testToByteArrayNegativeMaxLength() {
  100. assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(data123(), 10, -1));
  101. }
  102. @Test
  103. void testToByteArrayByteBuffer() {
  104. assertArrayEquals(new byte[] { 1, 2, 3},
  105. IOUtils.toByteArray(ByteBuffer.wrap(new byte[]{1, 2, 3}), 10));
  106. }
  107. @Test
  108. void testToByteArrayByteBufferNonArray() {
  109. ByteBuffer buffer = ByteBuffer.allocate(3);
  110. buffer.put(new byte[] { 1, 2, 3});
  111. buffer.position(0);
  112. assertFalse(buffer.asReadOnlyBuffer().hasArray());
  113. assertEquals(3, buffer.asReadOnlyBuffer().remaining());
  114. assertArrayEquals(new byte[] { 1, 2, 3},
  115. IOUtils.toByteArray(buffer.asReadOnlyBuffer(), 3));
  116. }
  117. @Test
  118. void testToByteArrayByteBufferToSmall() {
  119. assertArrayEquals(new byte[] { 1, 2, 3, 4, 5, 6, 7},
  120. IOUtils.toByteArray(ByteBuffer.wrap(new byte[]{1, 2, 3, 4, 5, 6, 7}), 3));
  121. }
  122. @Test
  123. void testSkipFully() throws IOException {
  124. try (InputStream is = new FileInputStream(TMP)) {
  125. long skipped = IOUtils.skipFully(is, 20000L);
  126. assertEquals(LENGTH, skipped);
  127. }
  128. }
  129. @Test
  130. void testSkipFullyGtIntMax() throws IOException {
  131. try (InputStream is = new FileInputStream(TMP)) {
  132. long skipped = IOUtils.skipFully(is, Integer.MAX_VALUE + 20000L);
  133. assertEquals(LENGTH, skipped);
  134. }
  135. }
  136. @Test
  137. void testSkipFullyByteArray() throws IOException {
  138. UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
  139. try (InputStream is = new FileInputStream(TMP)) {
  140. assertEquals(LENGTH, IOUtils.copy(is, bos));
  141. long skipped = IOUtils.skipFully(bos.toInputStream(), 20000L);
  142. assertEquals(LENGTH, skipped);
  143. }
  144. }
  145. @Test
  146. void testSkipFullyByteArrayGtIntMax() throws IOException {
  147. UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
  148. try (InputStream is = new FileInputStream(TMP)) {
  149. assertEquals(LENGTH, IOUtils.copy(is, bos));
  150. long skipped = IOUtils.skipFully(bos.toInputStream(), Integer.MAX_VALUE + 20000L);
  151. assertEquals(LENGTH, skipped);
  152. }
  153. }
  154. @Test
  155. void testCopyToFile() throws IOException {
  156. File dest = File.createTempFile("poi-ioutils-", "");
  157. try {
  158. try (InputStream is = new FileInputStream(TMP)) {
  159. assertEquals(LENGTH, IOUtils.copy(is, dest));
  160. }
  161. try (FileInputStream strOrig = new FileInputStream(TMP);
  162. FileInputStream strDest = new FileInputStream(dest)) {
  163. byte[] bytesOrig = new byte[(int)LENGTH];
  164. byte[] bytesDest = new byte[(int)LENGTH];
  165. IOUtils.readFully(strOrig, bytesOrig);
  166. IOUtils.readFully(strDest, bytesDest);
  167. assertArrayEquals(bytesOrig, bytesDest);
  168. }
  169. } finally {
  170. assertTrue(dest.delete());
  171. }
  172. }
  173. @Test
  174. void testCopyToInvalidFile() throws IOException {
  175. try (InputStream is = new FileInputStream(TMP)) {
  176. assertThrows(RuntimeException.class,
  177. () -> {
  178. // try with two different paths so we fail on both Unix and Windows
  179. IOUtils.copy(is, new File("/notexisting/directory/structure"));
  180. IOUtils.copy(is, new File("c:\\note&/()\"§=§%&!§$81§0_:;,.-'#*+~`?ß´ß0´ß9243xisting\\directory\\structure"));
  181. });
  182. }
  183. }
  184. @Test
  185. void testSkipFullyBug61294() throws IOException {
  186. long skipped = IOUtils.skipFully(new ByteArrayInputStream(new byte[0]), 1);
  187. assertEquals(-1L, skipped);
  188. }
  189. @Test
  190. void testZeroByte() throws IOException {
  191. long skipped = IOUtils.skipFully((new ByteArrayInputStream(new byte[0])), 100);
  192. assertEquals(-1L, skipped);
  193. }
  194. @Test
  195. void testSkipZero() throws IOException {
  196. try (InputStream is = new FileInputStream(TMP)) {
  197. long skipped = IOUtils.skipFully(is, 0);
  198. assertEquals(0, skipped);
  199. }
  200. }
  201. @Test
  202. void testSkipNegative() throws IOException {
  203. try (InputStream is = new FileInputStream(TMP)) {
  204. assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(is, -1));
  205. }
  206. }
  207. @Test
  208. void testMaxLengthTooLong() throws IOException {
  209. try (InputStream is = new FileInputStream(TMP)) {
  210. assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(is, Integer.MAX_VALUE, 100));
  211. }
  212. }
  213. @Test
  214. void testMaxLengthIgnored() throws IOException {
  215. try (InputStream is = new FileInputStream(TMP)) {
  216. int len = IOUtils.toByteArray(is, 90, Integer.MAX_VALUE).length;
  217. assertEquals(90, len);
  218. len = IOUtils.toByteArray(is, 90, 100).length;
  219. assertEquals(90, len);
  220. len = IOUtils.toByteArray(is, Integer.MAX_VALUE, Integer.MAX_VALUE).length;
  221. assertTrue(len > 300-2*90);
  222. }
  223. }
  224. @Test
  225. void testMaxLengthInvalid() throws IOException {
  226. try (InputStream is = new FileInputStream(TMP)) {
  227. assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(is, 90, 80));
  228. }
  229. }
  230. @Test
  231. void testWonkyInputStream() throws IOException {
  232. long skipped = IOUtils.skipFully(new WonkyInputStream(), 10000);
  233. assertEquals(10000, skipped);
  234. }
  235. @Test
  236. void testSetMaxOverride() throws IOException {
  237. ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
  238. byte[] bytes = IOUtils.toByteArray(stream);
  239. assertNotNull(bytes);
  240. assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
  241. }
  242. @Test
  243. void testSetMaxOverrideLimit() throws IOException {
  244. IOUtils.setByteArrayMaxOverride(30 * 1024 * 1024);
  245. try {
  246. ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
  247. byte[] bytes = IOUtils.toByteArray(stream);
  248. assertNotNull(bytes);
  249. assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
  250. } finally {
  251. IOUtils.setByteArrayMaxOverride(-1);
  252. }
  253. }
  254. @Test
  255. void testSetMaxOverrideOverLimit() {
  256. IOUtils.setByteArrayMaxOverride(2);
  257. try {
  258. ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
  259. assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(stream));
  260. } finally {
  261. IOUtils.setByteArrayMaxOverride(-1);
  262. }
  263. }
  264. @Test
  265. void testSetMaxOverrideWithLength() throws IOException {
  266. ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
  267. byte[] bytes = IOUtils.toByteArray(stream, 3, 100);
  268. assertNotNull(bytes);
  269. assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
  270. }
  271. @Test
  272. void testSetMaxOverrideLimitWithLength() throws IOException {
  273. IOUtils.setByteArrayMaxOverride(30 * 1024 * 1024);
  274. try {
  275. ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
  276. byte[] bytes = IOUtils.toByteArray(stream, 3, 100);
  277. assertNotNull(bytes);
  278. assertEquals("abc", new String(bytes, StandardCharsets.UTF_8));
  279. } finally {
  280. IOUtils.setByteArrayMaxOverride(-1);
  281. }
  282. }
  283. @Test
  284. void testSetMaxOverrideOverLimitWithLength() {
  285. IOUtils.setByteArrayMaxOverride(2);
  286. try {
  287. ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
  288. assertThrows(RecordFormatException.class, () -> IOUtils.toByteArray(stream, 3, 100));
  289. } finally {
  290. IOUtils.setByteArrayMaxOverride(-1);
  291. }
  292. }
  293. @Test
  294. void testSafelyAllocate() {
  295. byte[] bytes = IOUtils.safelyAllocate(30, 200);
  296. assertNotNull(bytes);
  297. assertEquals(30, bytes.length);
  298. }
  299. @Test
  300. void testSafelyAllocateLimit() {
  301. IOUtils.setByteArrayMaxOverride(40);
  302. try {
  303. byte[] bytes = IOUtils.safelyAllocate(30, 200);
  304. assertNotNull(bytes);
  305. assertEquals(30, bytes.length);
  306. } finally {
  307. IOUtils.setByteArrayMaxOverride(-1);
  308. }
  309. }
  310. @Test
  311. void testReadFully() throws IOException {
  312. byte[] bytes = new byte[2];
  313. assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 2));
  314. assertArrayEquals(new byte[] {1,2}, bytes);
  315. }
  316. @Test
  317. void testReadFullyEOF() throws IOException {
  318. byte[] bytes = new byte[2];
  319. assertEquals(2, IOUtils.readFully(new NullInputStream(2), bytes, 0, 4));
  320. assertArrayEquals(new byte[] {0,0}, bytes);
  321. }
  322. @Test
  323. void testReadFullyEOFZero() throws IOException {
  324. byte[] bytes = new byte[2];
  325. assertEquals(-1, IOUtils.readFully(new NullInputStream(0), bytes, 0, 4));
  326. assertArrayEquals(new byte[] {0,0}, bytes);
  327. }
  328. @Test
  329. void testReadFullySimple() throws IOException {
  330. byte[] bytes = new byte[2];
  331. assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes));
  332. assertArrayEquals(new byte[] {1,2}, bytes);
  333. }
  334. @Test
  335. void testReadFullyOffset() throws IOException {
  336. byte[] bytes = new byte[3];
  337. assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 1, 2));
  338. assertArrayEquals(new byte[] {0, 1,2}, bytes);
  339. }
  340. @Test
  341. void testReadFullyAtLength() throws IOException {
  342. byte[] bytes = new byte[3];
  343. assertEquals(3, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 3));
  344. assertArrayEquals(new byte[] {1,2, 3}, bytes);
  345. }
  346. @Test
  347. void testReadFullyChannel() throws IOException {
  348. ByteBuffer bytes = ByteBuffer.allocate(2);
  349. assertEquals(2, IOUtils.readFully(new SimpleByteChannel(new byte[]{1, 2, 3}), bytes));
  350. assertArrayEquals(new byte[] {1,2}, bytes.array());
  351. assertEquals(2, bytes.position());
  352. }
  353. @Test
  354. void testReadFullyChannelEOF() throws IOException {
  355. ByteBuffer bytes = ByteBuffer.allocate(2);
  356. assertEquals(-1, IOUtils.readFully(new EOFByteChannel(false), bytes));
  357. assertArrayEquals(new byte[] {0,0}, bytes.array());
  358. assertEquals(0, bytes.position());
  359. }
  360. @Test
  361. void testReadFullyChannelEOFException() {
  362. ByteBuffer bytes = ByteBuffer.allocate(2);
  363. assertThrows(IOException.class,
  364. () -> IOUtils.readFully(new EOFByteChannel(true), bytes));
  365. }
  366. @Test
  367. void testReadFullyChannelSimple() throws IOException {
  368. ByteBuffer bytes = ByteBuffer.allocate(2);
  369. assertEquals(2, IOUtils.readFully(new SimpleByteChannel(new byte[] {1, 2, 3}), bytes));
  370. assertArrayEquals(new byte[] {1,2}, bytes.array());
  371. assertEquals(2, bytes.position());
  372. }
  373. @Test
  374. public void testChecksum() {
  375. assertEquals(0L, IOUtils.calculateChecksum(new byte[0]));
  376. assertEquals(3057449933L, IOUtils.calculateChecksum(new byte[] { 1, 2, 3, 4}));
  377. }
  378. @Test
  379. public void testChecksumStream() throws IOException {
  380. assertEquals(0L, IOUtils.calculateChecksum(new NullInputStream(0)));
  381. assertEquals(0L, IOUtils.calculateChecksum(new NullInputStream(1)));
  382. assertEquals(3057449933L, IOUtils.calculateChecksum(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4})));
  383. assertThrows(EOFException.class,
  384. () -> IOUtils.calculateChecksum(new NullInputStream(1, true)));
  385. }
  386. /**
  387. * This returns 0 for the first call to skip and then reads
  388. * as requested. This tests that the fallback to read() works.
  389. */
  390. private static class WonkyInputStream extends InputStream {
  391. int skipCalled;
  392. int readCalled;
  393. @Override
  394. public int read() {
  395. readCalled++;
  396. return 0;
  397. }
  398. @Override
  399. public int read(byte[] arr, int offset, int len) {
  400. readCalled++;
  401. return len;
  402. }
  403. @Override
  404. public long skip(long len) {
  405. skipCalled++;
  406. if (skipCalled == 1) {
  407. return 0;
  408. } else if (skipCalled > 100) {
  409. return len;
  410. } else {
  411. return 100;
  412. }
  413. }
  414. @Override
  415. public int available() {
  416. return 100000;
  417. }
  418. }
  419. private static class EOFByteChannel implements ReadableByteChannel {
  420. private final boolean throwException;
  421. public EOFByteChannel(boolean throwException) {
  422. this.throwException = throwException;
  423. }
  424. @Override
  425. public int read(ByteBuffer dst) throws IOException {
  426. if (throwException) {
  427. throw new IOException("EOF");
  428. }
  429. return -1;
  430. }
  431. @Override
  432. public boolean isOpen() {
  433. return false;
  434. }
  435. @Override
  436. public void close() throws IOException {
  437. }
  438. }
  439. private static class SimpleByteChannel extends InputStream implements ReadableByteChannel {
  440. private final byte[] bytes;
  441. public SimpleByteChannel(byte[] bytes) {
  442. this.bytes = bytes;
  443. }
  444. @Override
  445. public int read() throws IOException {
  446. throw new UnsupportedOperationException();
  447. }
  448. @Override
  449. public int read(ByteBuffer dst) throws IOException {
  450. int toRead = Math.min(bytes.length, dst.capacity());
  451. dst.put(bytes, 0, toRead);
  452. return toRead;
  453. }
  454. @Override
  455. public boolean isOpen() {
  456. return false;
  457. }
  458. }
  459. public static class NullInputStream extends InputStream {
  460. private final int bytes;
  461. private final boolean exception;
  462. private int position;
  463. public NullInputStream(int bytes) {
  464. this(bytes, false);
  465. }
  466. public NullInputStream(int bytes, boolean exception) {
  467. this.bytes = bytes;
  468. this.exception = exception;
  469. }
  470. @Override
  471. public int read() throws IOException {
  472. if (position >= bytes) {
  473. return handleReturn();
  474. }
  475. position++;
  476. return 0;
  477. }
  478. private int handleReturn() throws EOFException {
  479. if (exception) {
  480. throw new EOFException();
  481. } else {
  482. return -1;
  483. }
  484. }
  485. @Override
  486. public int read(byte[] b, int off, int len) throws IOException {
  487. int toRead = Math.min(b.length, len);
  488. if (toRead > (bytes - position)) {
  489. return handleReturn();
  490. }
  491. toRead = Math.min(toRead, (bytes - position));
  492. position += toRead;
  493. return toRead;
  494. }
  495. }
  496. }