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.input;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertThrows;
22  import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.time.Duration;
28  import java.time.temporal.ChronoUnit;
29  import java.util.concurrent.atomic.AtomicBoolean;
30  
31  import org.apache.commons.io.IOUtils;
32  import org.apache.commons.io.input.ThrottledInputStream.Builder;
33  import org.apache.commons.io.test.CustomIOException;
34  import org.junit.jupiter.api.Test;
35  
36  /**
37   * Tests {@link ThrottledInputStream}.
38   */
39  class ThrottledInputStreamTest extends ProxyInputStreamTest<ThrottledInputStream> {
40  
41      @Override
42      @SuppressWarnings({ "resource" })
43      protected ThrottledInputStream createFixture() throws IOException {
44          return ThrottledInputStream.builder().setInputStream(createOriginInputStream()).get();
45      }
46  
47      @Test
48      void testAfterReadConsumer() throws Exception {
49          final AtomicBoolean boolRef = new AtomicBoolean();
50          // @formatter:off
51          try (InputStream bounded = ThrottledInputStream.builder()
52                  .setCharSequence("Hi")
53                  .setAfterRead(i -> boolRef.set(true))
54                  .get()) {
55              IOUtils.consume(bounded);
56          }
57          // @formatter:on
58          assertTrue(boolRef.get());
59          // Throwing
60          final String message = "test exception message";
61          // @formatter:off
62          try (InputStream inputStream = ThrottledInputStream.builder()
63                  .setCharSequence("Hi")
64                  .setAfterRead(i -> {
65                      throw new CustomIOException(message);
66                  })
67                  .get()) {
68              assertEquals(message, assertThrowsExactly(CustomIOException.class, () -> IOUtils.consume(inputStream)).getMessage());
69          }
70          // @formatter:on
71      }
72  
73      @Test
74      void testBuilder() throws IOException {
75          final Builder builder = ThrottledInputStream.builder();
76          assertThrows(IllegalArgumentException.class, () -> builder.setMaxBytesPerSecond(-1));
77          assertThrows(IllegalArgumentException.class, () -> builder.setMaxBytesPerSecond(0));
78          assertThrows(IllegalArgumentException.class, () -> builder.setMaxBytes(1, Duration.ZERO.minusMillis(1)));
79          assertThrows(IllegalArgumentException.class, () -> builder.setMaxBytes(1, Duration.ZERO));
80          assertThrows(NullPointerException.class, () -> builder.setMaxBytes(1, (Duration) null));
81          assertThrows(NullPointerException.class, () -> builder.setMaxBytes(1, (ChronoUnit) null));
82          //
83          // 2 bytes per second
84          builder.setMaxBytesPerSecond(2);
85          assertEquals(2.0, builder.getMaxBytesPerSecond());
86          // @formatter:off
87          try (ThrottledInputStream inputStream = builder
88                  .setInputStream(createOriginInputStream())
89                  .get()) {
90              assertEquals(2.0, builder.getMaxBytesPerSecond());
91              assertEquals(2.0, inputStream.getMaxBytesPerSecond());
92          }
93          try (ThrottledInputStream inputStream = builder
94                  .setInputStream(createOriginInputStream())
95                  .setMaxBytes(2, ChronoUnit.SECONDS)
96                  .get()) {
97              assertEquals(2.0, builder.getMaxBytesPerSecond());
98              assertEquals(2.0, inputStream.getMaxBytesPerSecond());
99          }
100         // @formatter:on
101         Duration maxBytesPer = Duration.ofSeconds(1);
102         // @formatter:off
103         try (ThrottledInputStream inputStream = builder
104                 .setInputStream(createOriginInputStream())
105                 .setMaxBytes(2, maxBytesPer)
106                 .get()) {
107             assertEquals(2.0, builder.getMaxBytesPerSecond());
108             assertEquals(2.0, inputStream.getMaxBytesPerSecond());
109         }
110         //
111         // 1 bytes per 1/2 second (30_000 millis)
112         // @formatter:on
113         maxBytesPer = maxBytesPer.dividedBy(2);
114         // @formatter:off
115         try (ThrottledInputStream inputStream = builder
116                 .setInputStream(createOriginInputStream())
117                 .setMaxBytes(1, maxBytesPer)
118                 .get()) {
119             assertEquals(0.5, inputStream.getMaxBytesPerSecond());
120         }
121         // 1 byte/millis
122         try (ThrottledInputStream inputStream = builder
123                 .setInputStream(createOriginInputStream())
124                 .setMaxBytes(1, ChronoUnit.MILLIS)
125                 .get()) {
126             assertEquals(0.001, inputStream.getMaxBytesPerSecond());
127         }
128         // @formatter:on
129         // 1 byte per 10_0011 millis.
130         maxBytesPer = Duration.ofSeconds(20).plusMillis(11);
131         // @formatter:off
132         try (ThrottledInputStream inputStream = builder
133                 .setInputStream(createOriginInputStream())
134                 .setMaxBytes(1, maxBytesPer)
135                 .get()) {
136             assertEquals(20.011, inputStream.getMaxBytesPerSecond());
137         }
138         // @formatter:on
139         // Javadoc example
140         // @formatter:off
141         try (ThrottledInputStream inputStream = builder
142                 .setInputStream(createOriginInputStream())
143                 .setMaxBytes(100_000, ChronoUnit.SECONDS)
144                 .get()) {
145             assertEquals(100_000.0, inputStream.getMaxBytesPerSecond());
146         }
147         // @formatter:on
148     }
149 
150     @Test
151     void testCalSleepTimeMs() {
152         // case 0: initial - no read, no sleep
153         assertEquals(0, ThrottledInputStream.toSleepMillis(0, 1_000, 10_000));
154         // case 1: no threshold
155         assertEquals(0, ThrottledInputStream.toSleepMillis(Long.MAX_VALUE, 1_000, 0));
156         assertEquals(0, ThrottledInputStream.toSleepMillis(Long.MAX_VALUE, 1_000, -1));
157         // case 2: too fast
158         assertEquals(1500, ThrottledInputStream.toSleepMillis(5, 1_000, 2));
159         assertEquals(500, ThrottledInputStream.toSleepMillis(5, 2_000, 2));
160         assertEquals(6500, ThrottledInputStream.toSleepMillis(15, 1_000, 2));
161         assertEquals(4000, ThrottledInputStream.toSleepMillis(5, 1_000, 1));
162         assertEquals(9000, ThrottledInputStream.toSleepMillis(5, 1_000, 0.5));
163         assertEquals(99000, ThrottledInputStream.toSleepMillis(5, 1_000, 0.05));
164         // case 3: too slow, no sleep needed
165         assertEquals(0, ThrottledInputStream.toSleepMillis(1, 1_000, 2));
166         assertEquals(0, ThrottledInputStream.toSleepMillis(2, 2_000, 2));
167         assertEquals(0, ThrottledInputStream.toSleepMillis(1, 1_000, 2));
168         assertEquals(0, ThrottledInputStream.toSleepMillis(1, 1_000, 2.0));
169         assertEquals(0, ThrottledInputStream.toSleepMillis(1, 1_000, 1));
170         assertEquals(0, ThrottledInputStream.toSleepMillis(1, 1_000, 1.0));
171     }
172 
173     @Test
174     void testCloseHandleIOException() throws IOException {
175         ProxyInputStreamTest.testCloseHandleIOException(ThrottledInputStream.builder());
176     }
177 
178     @Override
179     protected void testEos(final ThrottledInputStream inputStream) {
180         assertEquals(3, inputStream.getByteCount());
181     }
182 
183     @Test
184     void testGet() throws IOException {
185         try (ThrottledInputStream inputStream = createFixture()) {
186             inputStream.read();
187             assertEquals(Duration.ZERO, inputStream.getTotalSleepDuration());
188         }
189     }
190 
191 }