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.output;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.io.IOException;
28  import java.util.concurrent.atomic.AtomicBoolean;
29  
30  import org.junit.jupiter.api.BeforeEach;
31  import org.junit.jupiter.api.Test;
32  
33  /**
34   * Tests {@link FlushShieldOutputStream}.
35   */
36  class FlushShieldOutputStreamTest {
37  
38      /** Tracks whether the underlying stream's close() was called. */
39      private AtomicBoolean closed;
40  
41      /** Tracks whether the underlying stream's flush() was called. */
42      private AtomicBoolean flushed;
43  
44      /** The stream under test. */
45      private FlushShieldOutputStream shielded;
46  
47      /** The underlying byte-array-backed stream used as the delegate. */
48      private ByteArrayOutputStream target;
49  
50      @BeforeEach
51      void setUp() {
52          flushed = new AtomicBoolean();
53          closed = new AtomicBoolean();
54          target = new ByteArrayOutputStream() {
55  
56              @Override
57              public void close() throws IOException {
58                  closed.set(true);
59                  super.close();
60              }
61  
62              @Override
63              public void flush() throws IOException {
64                  flushed.set(true);
65                  super.flush();
66              }
67          };
68          shielded = new FlushShieldOutputStream(target);
69      }
70  
71      @Test
72      void testBuilderGet() throws IOException {
73          assertNotNull(FlushShieldOutputStream.builder().setOutputStream(target).get());
74      }
75  
76      @Test
77      void testBuilderWithOutputStream() throws IOException {
78          try (FlushShieldOutputStream built = FlushShieldOutputStream.builder().setOutputStream(target).get()) {
79              assertNotNull(built);
80              assertInstanceOf(FlushShieldOutputStream.class, built);
81              // flush must be shielded for builder-created instances too
82              built.flush();
83              assertFalse(flushed.get(), "flush() via builder instance must NOT reach the underlying stream.");
84              // writes must still work
85              built.write('B');
86              assertEquals(1, target.size());
87              assertEquals('B', target.toByteArray()[0]);
88          }
89      }
90  
91      @Test
92      void testCloseReachesUnderlying() throws IOException {
93          assertFalse(closed.get(), "close should not have been called yet.");
94          shielded.close();
95          assertTrue(closed.get(), "close() must reach the underlying stream.");
96      }
97  
98      @Test
99      void testFlushCanBeCalledMultipleTimes() throws IOException {
100         shielded.flush();
101         shielded.flush();
102         shielded.flush();
103         assertFalse(flushed.get(), "repeated flush() calls must NOT reach the underlying stream.");
104     }
105 
106     @Test
107     void testFlushDoesNotDelegateToUnderlying() throws IOException {
108         assertFalse(flushed.get(), "flush should not have been called yet.");
109         shielded.flush();
110         assertFalse(flushed.get(), "flush() must NOT reach the underlying stream.");
111     }
112 
113     @Test
114     void testWriteByteArray() throws IOException {
115         final byte[] data = { 'H', 'i' };
116         shielded.write(data);
117         assertEquals(2, target.size());
118         assertArrayEquals(data, target.toByteArray());
119     }
120 
121     @Test
122     void testWriteByteArrayWithOffset() throws IOException {
123         final byte[] data = { 'X', 'Y', 'Z' };
124         shielded.write(data, 1, 2);
125         assertEquals(2, target.size());
126         assertArrayEquals(new byte[] { 'Y', 'Z' }, target.toByteArray());
127     }
128 
129     @Test
130     void testWriteInt() throws IOException {
131         shielded.write('A');
132         assertEquals(1, target.size());
133         assertEquals('A', target.toByteArray()[0]);
134     }
135 }