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    *      http://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.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNotEquals;
21  import static org.junit.jupiter.api.Assertions.assertNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.ByteArrayInputStream;
26  import java.io.IOException;
27  
28  import org.apache.commons.io.IOUtils;
29  import org.apache.commons.io.input.ObservableInputStream.Observer;
30  import org.apache.commons.io.output.NullOutputStream;
31  import org.junit.jupiter.api.Test;
32  
33  /**
34   * Tests {@link ObservableInputStream}.
35   */
36  public class ObservableInputStreamTest {
37  
38      private static final class DataViewObserver extends MethodCountObserver {
39          private byte[] buffer;
40          private int lastValue = -1;
41          private int length = -1;
42          private int offset = -1;
43  
44          @Override
45          public void data(final byte[] buffer, final int offset, final int length) throws IOException {
46              this.buffer = buffer;
47              this.offset = offset;
48              this.length = length;
49          }
50  
51          @Override
52          public void data(final int value) throws IOException {
53              super.data(value);
54              lastValue = value;
55          }
56      }
57  
58      private static final class LengthObserver extends Observer {
59          private long total;
60  
61          @Override
62          public void data(final byte[] buffer, final int offset, final int length) throws IOException {
63              this.total += length;
64          }
65  
66          @Override
67          public void data(final int value) throws IOException {
68              total++;
69          }
70  
71          public long getTotal() {
72              return total;
73          }
74      }
75  
76      private static class MethodCountObserver extends Observer {
77          private long closedCount;
78          private long dataBufferCount;
79          private long dataCount;
80          private long errorCount;
81          private long finishedCount;
82  
83          @Override
84          public void closed() throws IOException {
85              closedCount++;
86          }
87  
88          @Override
89          public void data(final byte[] buffer, final int offset, final int length) throws IOException {
90              dataBufferCount++;
91          }
92  
93          @Override
94          public void data(final int value) throws IOException {
95              dataCount++;
96          }
97  
98          @Override
99          public void error(final IOException exception) throws IOException {
100             errorCount++;
101         }
102 
103         @Override
104         public void finished() throws IOException {
105             finishedCount++;
106         }
107 
108         public long getClosedCount() {
109             return closedCount;
110         }
111 
112         public long getDataBufferCount() {
113             return dataBufferCount;
114         }
115 
116         public long getDataCount() {
117             return dataCount;
118         }
119 
120         public long getErrorCount() {
121             return errorCount;
122         }
123 
124         public long getFinishedCount() {
125             return finishedCount;
126         }
127 
128     }
129 
130     @Test
131     public void testBrokenInputStreamRead() throws IOException {
132         try (ObservableInputStream ois = new ObservableInputStream(BrokenInputStream.INSTANCE)) {
133             assertThrows(IOException.class, ois::read);
134         }
135     }
136 
137     @Test
138     public void testBrokenInputStreamReadBuffer() throws IOException {
139         try (ObservableInputStream ois = new ObservableInputStream(BrokenInputStream.INSTANCE)) {
140             assertThrows(IOException.class, () -> ois.read(new byte[1]));
141         }
142     }
143 
144     @Test
145     public void testBrokenInputStreamReadSubBuffer() throws IOException {
146         try (ObservableInputStream ois = new ObservableInputStream(BrokenInputStream.INSTANCE)) {
147             assertThrows(IOException.class, () -> ois.read(new byte[2], 0, 1));
148         }
149     }
150 
151     /**
152      * Tests that {@link Observer#data(int)} is called.
153      */
154     @Test
155     public void testDataByteCalled_add() throws Exception {
156         final byte[] buffer = MessageDigestInputStreamTest.generateRandomByteStream(IOUtils.DEFAULT_BUFFER_SIZE);
157         final DataViewObserver lko = new DataViewObserver();
158         try (ObservableInputStream ois = new ObservableInputStream(new ByteArrayInputStream(buffer))) {
159             assertEquals(-1, lko.lastValue);
160             ois.read();
161             assertEquals(-1, lko.lastValue);
162             assertEquals(0, lko.getFinishedCount());
163             assertEquals(0, lko.getClosedCount());
164             ois.add(lko);
165             for (int i = 1; i < buffer.length; i++) {
166                 final int result = ois.read();
167                 assertEquals((byte) result, buffer[i]);
168                 assertEquals(result, lko.lastValue);
169                 assertEquals(0, lko.getFinishedCount());
170                 assertEquals(0, lko.getClosedCount());
171             }
172             final int result = ois.read();
173             assertEquals(-1, result);
174             assertEquals(1, lko.getFinishedCount());
175             assertEquals(0, lko.getClosedCount());
176             ois.close();
177             assertEquals(1, lko.getFinishedCount());
178             assertEquals(1, lko.getClosedCount());
179         }
180     }
181 
182     /**
183      * Tests that {@link Observer#data(int)} is called.
184      */
185     @Test
186     public void testDataByteCalled_ctor() throws Exception {
187         final byte[] buffer = MessageDigestInputStreamTest.generateRandomByteStream(IOUtils.DEFAULT_BUFFER_SIZE);
188         final DataViewObserver lko = new DataViewObserver();
189         try (ObservableInputStream ois = new ObservableInputStream(new ByteArrayInputStream(buffer), lko)) {
190             assertEquals(-1, lko.lastValue);
191             ois.read();
192             assertNotEquals(-1, lko.lastValue);
193             assertEquals(0, lko.getFinishedCount());
194             assertEquals(0, lko.getClosedCount());
195             for (int i = 1; i < buffer.length; i++) {
196                 final int result = ois.read();
197                 assertEquals((byte) result, buffer[i]);
198                 assertEquals(result, lko.lastValue);
199                 assertEquals(0, lko.getFinishedCount());
200                 assertEquals(0, lko.getClosedCount());
201             }
202             final int result = ois.read();
203             assertEquals(-1, result);
204             assertEquals(1, lko.getFinishedCount());
205             assertEquals(0, lko.getClosedCount());
206             ois.close();
207             assertEquals(1, lko.getFinishedCount());
208             assertEquals(1, lko.getClosedCount());
209         }
210     }
211 
212     /**
213      * Tests that {@link Observer#data(byte[],int,int)} is called.
214      */
215     @Test
216     public void testDataBytesCalled() throws Exception {
217         final byte[] buffer = MessageDigestInputStreamTest.generateRandomByteStream(IOUtils.DEFAULT_BUFFER_SIZE);
218         try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
219                 final ObservableInputStream ois = new ObservableInputStream(bais)) {
220             final DataViewObserver observer = new DataViewObserver();
221             final byte[] readBuffer = new byte[23];
222             assertNull(observer.buffer);
223             ois.read(readBuffer);
224             assertNull(observer.buffer);
225             ois.add(observer);
226             for (;;) {
227                 if (bais.available() >= 2048) {
228                     final int result = ois.read(readBuffer);
229                     if (result == -1) {
230                         ois.close();
231                         break;
232                     }
233                     assertEquals(readBuffer, observer.buffer);
234                     assertEquals(0, observer.offset);
235                     assertEquals(readBuffer.length, observer.length);
236                 } else {
237                     final int res = Math.min(11, bais.available());
238                     final int result = ois.read(readBuffer, 1, 11);
239                     if (result == -1) {
240                         ois.close();
241                         break;
242                     }
243                     assertEquals(readBuffer, observer.buffer);
244                     assertEquals(1, observer.offset);
245                     assertEquals(res, observer.length);
246                 }
247             }
248         }
249     }
250 
251     @Test
252     public void testGetObservers0() throws IOException {
253         try (ObservableInputStream ois = new ObservableInputStream(NullInputStream.INSTANCE)) {
254             assertTrue(ois.getObservers().isEmpty());
255         }
256     }
257 
258     @Test
259     public void testGetObservers1() throws IOException {
260         final DataViewObserver observer0 = new DataViewObserver();
261         try (ObservableInputStream ois = new ObservableInputStream(NullInputStream.INSTANCE, observer0)) {
262             assertEquals(observer0, ois.getObservers().get(0));
263         }
264     }
265 
266     @Test
267     public void testGetObserversOrder() throws IOException {
268         final DataViewObserver observer0 = new DataViewObserver();
269         final DataViewObserver observer1 = new DataViewObserver();
270         try (ObservableInputStream ois = new ObservableInputStream(NullInputStream.INSTANCE, observer0, observer1)) {
271             assertEquals(observer0, ois.getObservers().get(0));
272             assertEquals(observer1, ois.getObservers().get(1));
273         }
274     }
275 
276     private void testNotificationCallbacks(final int bufferSize) throws IOException {
277         final byte[] buffer = IOUtils.byteArray();
278         final LengthObserver lengthObserver = new LengthObserver();
279         final MethodCountObserver methodCountObserver = new MethodCountObserver();
280         try (ObservableInputStream ois = new ObservableInputStream(new ByteArrayInputStream(buffer),
281             lengthObserver, methodCountObserver)) {
282             assertEquals(IOUtils.DEFAULT_BUFFER_SIZE,
283                 IOUtils.copy(ois, NullOutputStream.INSTANCE, bufferSize));
284         }
285         assertEquals(IOUtils.DEFAULT_BUFFER_SIZE, lengthObserver.getTotal());
286         assertEquals(1, methodCountObserver.getClosedCount());
287         assertEquals(1, methodCountObserver.getFinishedCount());
288         assertEquals(0, methodCountObserver.getErrorCount());
289         assertEquals(0, methodCountObserver.getDataCount());
290         assertEquals(buffer.length / bufferSize, methodCountObserver.getDataBufferCount());
291     }
292 
293     @Test
294     public void testNotificationCallbacksBufferSize1() throws Exception {
295         testNotificationCallbacks(1);
296     }
297 
298     @Test
299     public void testNotificationCallbacksBufferSize2() throws Exception {
300         testNotificationCallbacks(2);
301     }
302 
303     @Test
304     public void testNotificationCallbacksBufferSizeDefault() throws Exception {
305         testNotificationCallbacks(IOUtils.DEFAULT_BUFFER_SIZE);
306     }
307 }