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  package org.apache.commons.io.input;
18  
19  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertNotEquals;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.io.ByteArrayInputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.nio.file.Files;
30  import java.nio.file.Paths;
31  import java.security.MessageDigest;
32  import java.security.NoSuchAlgorithmException;
33  import java.util.Random;
34  import java.util.concurrent.atomic.AtomicBoolean;
35  
36  import org.apache.commons.codec.digest.DigestUtils;
37  import org.apache.commons.codec.digest.MessageDigestAlgorithms;
38  import org.apache.commons.io.IOExceptionList;
39  import org.apache.commons.io.IOUtils;
40  import org.apache.commons.io.test.CustomIOException;
41  import org.junit.jupiter.api.Test;
42  
43  /**
44   * Tests {@link MessageDigestInputStream}.
45   */
46  class MessageDigestInputStreamTest {
47  
48      static byte[] generateRandomByteStream(final int size) {
49          final byte[] buffer = new byte[size];
50          final Random rnd = new Random();
51          rnd.nextBytes(buffer);
52          return buffer;
53      }
54  
55      private InputStream createInputStream() throws IOException, NoSuchAlgorithmException {
56          return createInputStream(new ByteArrayInputStream(MessageDigestInputStreamTest.generateRandomByteStream(256)));
57      }
58  
59      private InputStream createInputStream(final InputStream origin) throws IOException, NoSuchAlgorithmException {
60          // @formatter:off
61          return MessageDigestInputStream.builder()
62                  .setMessageDigest(MessageDigest.getInstance(MessageDigestAlgorithms.SHA_512))
63                  .setInputStream(origin)
64                  .get();
65          // @formatter:on
66      }
67  
68      @Test
69      void testAfterReadConsumer() throws Exception {
70          final AtomicBoolean boolRef = new AtomicBoolean();
71          // @formatter:off
72          try (InputStream bounded = MessageDigestInputStream.builder()
73                  .setMessageDigest(MessageDigest.getInstance(MessageDigestAlgorithms.SHA_512))
74                  .setCharSequence("Hi")
75                  .setAfterRead(i -> boolRef.set(true))
76                  .get()) {
77              IOUtils.consume(bounded);
78          }
79          // @formatter:on
80          assertTrue(boolRef.get());
81          // Throwing
82          final String message = "test exception message";
83          // @formatter:off
84          try (InputStream bounded = MessageDigestInputStream.builder()
85                  .setMessageDigest(MessageDigest.getInstance(MessageDigestAlgorithms.SHA_512))
86                  .setCharSequence("Hi")
87                  .setAfterRead(i -> {
88                      throw new CustomIOException(message);
89                  })
90                  .get()) {
91              assertTrue(assertThrowsExactly(IOExceptionList.class, () -> IOUtils.consume(bounded)).getMessage().contains(message));
92          }
93          // @formatter:on
94      }
95  
96      @SuppressWarnings("resource")
97      @Test
98      void testAvailableAfterClose() throws Exception {
99          final InputStream shadow;
100         try (InputStream in = createInputStream()) {
101             assertTrue(in.available() > 0);
102             shadow = in;
103         }
104         assertEquals(0, shadow.available());
105     }
106 
107     @Test
108     void testAvailableAfterOpen() throws Exception {
109         try (InputStream in = createInputStream()) {
110             assertTrue(in.available() > 0);
111             assertNotEquals(IOUtils.EOF, in.read());
112             assertTrue(in.available() > 0);
113         }
114     }
115 
116     @Test
117     void testNoDefault() throws Exception {
118         // No default by design, call MUST set a message digest
119         // Fail-fast, no need to try to process any input origin
120         assertThrows(NullPointerException.class, () -> MessageDigestInputStream.builder().get());
121         assertThrows(NullPointerException.class, () -> MessageDigestInputStream.builder().setInputStream(new ByteArrayInputStream(new byte[] { 1 })).get());
122     }
123 
124     @Test
125     void testNormalUse() throws Exception {
126         for (int i = 256; i < 8192; i *= 2) {
127             final byte[] buffer = generateRandomByteStream(i);
128             final byte[] expect = DigestUtils.sha512(buffer);
129             try (MessageDigestInputStream messageDigestInputStream = MessageDigestInputStream.builder().setMessageDigest(MessageDigestAlgorithms.SHA_512)
130                     .setInputStream(new ByteArrayInputStream(buffer)).get()) {
131                 messageDigestInputStream.consume();
132                 assertArrayEquals(expect, messageDigestInputStream.getMessageDigest().digest());
133             }
134             try (MessageDigestInputStream messageDigestInputStream = MessageDigestInputStream.builder().setByteArray(buffer)
135                     .setMessageDigest(DigestUtils.getSha512Digest()).get()) {
136                 messageDigestInputStream.consume();
137                 assertArrayEquals(expect, messageDigestInputStream.getMessageDigest().digest());
138             }
139         }
140     }
141 
142     @Test
143     void testReadAfterClose_ByteArrayInputStream() throws Exception {
144         try (InputStream in = createInputStream()) {
145             in.close();
146             assertNotEquals(IOUtils.EOF, in.read());
147         }
148     }
149 
150     @SuppressWarnings("resource")
151     @Test
152     void testReadAfterClose_ChannelInputStream() throws Exception {
153         try (InputStream in = createInputStream(Files.newInputStream(Paths.get("src/test/resources/org/apache/commons/io/abitmorethan16k.txt")))) {
154             in.close();
155             // ChannelInputStream throws when closed
156             assertThrows(IOException.class, in::read);
157         }
158     }
159 
160 }