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.lang3;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.Closeable;
26  import java.io.IOException;
27  import java.util.Arrays;
28  import java.util.concurrent.atomic.AtomicBoolean;
29  import java.util.concurrent.atomic.AtomicReference;
30  
31  import org.junit.jupiter.api.Test;
32  
33  /**
34   * Tests {@link AutoCloseables}.
35   */
36  class AutoCloseablesTest extends AbstractLangTest {
37  
38      /** An AutoCloseable that always throws on close(). */
39      private static class ThrowingCloseable implements Closeable {
40  
41          private final IOException exception;
42  
43          ThrowingCloseable(final IOException exception) {
44              this.exception = exception;
45          }
46  
47          @Override
48          public void close() throws IOException {
49              throw exception;
50          }
51      }
52  
53      /** An AutoCloseable that tracks whether close() was called. */
54      private static class TrackingCloseable implements Closeable {
55  
56          private final AtomicBoolean closed = new AtomicBoolean();
57  
58          @Override
59          public void close() {
60              closed.set(true);
61          }
62  
63          boolean isClosed() {
64              return closed.get();
65          }
66      }
67  
68      @Test
69      void testClose_closesResource() throws Exception {
70          final TrackingCloseable tc = new TrackingCloseable();
71          AutoCloseables.close(tc);
72          assertTrue(tc.isClosed(), "Expected resource to be closed");
73      }
74  
75      @Test
76      void testClose_null_doesNotThrow() throws Exception {
77          AutoCloseables.close((AutoCloseable) null);
78      }
79  
80      @Test
81      void testClose_propagatesException() {
82          final IOException cause = new IOException("boom");
83          final ThrowingCloseable tc = new ThrowingCloseable(cause);
84          final Exception thrown = assertThrows(Exception.class, () -> AutoCloseables.close(tc));
85          assertEquals(cause, thrown);
86      }
87  
88      @Test
89      void testClose_withConsumer_closesResource() throws Exception {
90          final TrackingCloseable tc = new TrackingCloseable();
91          AutoCloseables.close(tc, null);
92          assertTrue(tc.isClosed(), "Expected resource to be closed");
93      }
94  
95      @Test
96      void testClose_withConsumer_exceptionDeliveredToConsumer() throws Exception {
97          final IOException cause = new IOException("boom");
98          final ThrowingCloseable tc = new ThrowingCloseable(cause);
99          final AtomicReference<Exception> received = new AtomicReference<>();
100         AutoCloseables.close(tc, received::set);
101         assertEquals(cause, received.get(), "Consumer should have received the exception");
102     }
103 
104     @Test
105     void testClose_withConsumer_null_doesNotThrow() throws Exception {
106         AutoCloseables.close(null, null);
107     }
108 
109     @Test
110     void testCloseQuietly_closesResource() {
111         final TrackingCloseable tc = new TrackingCloseable();
112         AutoCloseables.closeQuietly(tc);
113         assertTrue(tc.isClosed(), "Expected resource to be closed");
114     }
115 
116     @Test
117     void testCloseQuietly_iterable_closesAllResources() {
118         final TrackingCloseable tc1 = new TrackingCloseable();
119         final TrackingCloseable tc2 = new TrackingCloseable();
120         AutoCloseables.closeQuietly(Arrays.asList(tc1, tc2));
121         assertTrue(tc1.isClosed(), "Expected first resource to be closed");
122         assertTrue(tc2.isClosed(), "Expected second resource to be closed");
123     }
124 
125     @Test
126     void testCloseQuietly_iterable_null_doesNotThrow() {
127         AutoCloseables.closeQuietly((Iterable<AutoCloseable>) null);
128     }
129 
130     @Test
131     void testCloseQuietly_iterable_swallowsExceptions() {
132         final ThrowingCloseable tc1 = new ThrowingCloseable(new IOException("first"));
133         final ThrowingCloseable tc2 = new ThrowingCloseable(new IOException("second"));
134         AutoCloseables.closeQuietly(Arrays.asList(tc1, tc2));
135     }
136 
137     @Test
138     void testCloseQuietly_null_doesNotThrow() {
139         AutoCloseables.closeQuietly((AutoCloseable) null);
140     }
141 
142     @Test
143     void testCloseQuietly_swallowsException() {
144         final ThrowingCloseable tc = new ThrowingCloseable(new IOException("ignored"));
145         AutoCloseables.closeQuietly(tc);
146     }
147 
148     @Test
149     void testCloseQuietly_withConsumer_exceptionDeliveredToConsumer() {
150         final IOException cause = new IOException("boom");
151         final ThrowingCloseable tc = new ThrowingCloseable(cause);
152         final AtomicReference<Exception> received = new AtomicReference<>();
153         AutoCloseables.closeQuietly(tc, received::set);
154         assertEquals(cause, received.get(), "Consumer should have received the exception");
155     }
156 
157     @Test
158     void testCloseQuietly_withConsumer_null_doesNotThrow() {
159         AutoCloseables.closeQuietly(null, (java.util.function.Consumer<Exception>) null);
160     }
161 
162     @Test
163     void testCloseQuietly_withNullConsumer_swallowsException() {
164         final ThrowingCloseable tc = new ThrowingCloseable(new IOException("ignored"));
165         AutoCloseables.closeQuietly(tc, (java.util.function.Consumer<Exception>) null);
166     }
167 
168     @Test
169     void testCloseQuietlySuppress_addsSuppressedException() {
170         final IOException closeException = new IOException("close failure");
171         final ThrowingCloseable tc = new ThrowingCloseable(closeException);
172         final RuntimeException primary = new RuntimeException("primary");
173         final RuntimeException result = AutoCloseables.closeQuietlySuppress(tc, primary);
174         assertEquals(primary, result);
175         assertNotNull(result.getSuppressed());
176         assertEquals(1, result.getSuppressed().length);
177         assertEquals(closeException, result.getSuppressed()[0]);
178     }
179 
180     @Test
181     void testCloseQuietlySuppress_noException_returnsThrowable() throws Exception {
182         final TrackingCloseable tc = new TrackingCloseable();
183         final RuntimeException primary = new RuntimeException("primary");
184         final RuntimeException result = AutoCloseables.closeQuietlySuppress(tc, primary);
185         assertEquals(primary, result);
186         assertTrue(tc.isClosed());
187         assertEquals(0, result.getSuppressed().length, "No suppressed exceptions expected");
188     }
189 
190     @Test
191     void testCloseQuietlySuppress_null_returnsThrowable() {
192         final RuntimeException primary = new RuntimeException("primary");
193         final RuntimeException result = AutoCloseables.closeQuietlySuppress(null, primary);
194         assertEquals(primary, result, "Should return the given throwable unchanged");
195         assertEquals(0, result.getSuppressed().length, "No suppressed exceptions expected");
196     }
197 }