| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.lang3; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertThrows; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.junit.jupiter.api.Test; |
| |
| /** |
| * Tests {@link AutoCloseables}. |
| */ |
| class AutoCloseablesTest extends AbstractLangTest { |
| |
| /** An AutoCloseable that always throws on close(). */ |
| private static class ThrowingCloseable implements Closeable { |
| |
| private final IOException exception; |
| |
| ThrowingCloseable(final IOException exception) { |
| this.exception = exception; |
| } |
| |
| @Override |
| public void close() throws IOException { |
| throw exception; |
| } |
| } |
| |
| /** An AutoCloseable that tracks whether close() was called. */ |
| private static class TrackingCloseable implements Closeable { |
| |
| private final AtomicBoolean closed = new AtomicBoolean(); |
| |
| @Override |
| public void close() { |
| closed.set(true); |
| } |
| |
| boolean isClosed() { |
| return closed.get(); |
| } |
| } |
| |
| @Test |
| void testClose_closesResource() throws Exception { |
| final TrackingCloseable tc = new TrackingCloseable(); |
| AutoCloseables.close(tc); |
| assertTrue(tc.isClosed(), "Expected resource to be closed"); |
| } |
| |
| @Test |
| void testClose_null_doesNotThrow() throws Exception { |
| AutoCloseables.close((AutoCloseable) null); |
| } |
| |
| @Test |
| void testClose_propagatesException() { |
| final IOException cause = new IOException("boom"); |
| final ThrowingCloseable tc = new ThrowingCloseable(cause); |
| final Exception thrown = assertThrows(Exception.class, () -> AutoCloseables.close(tc)); |
| assertEquals(cause, thrown); |
| } |
| |
| @Test |
| void testClose_withConsumer_closesResource() throws Exception { |
| final TrackingCloseable tc = new TrackingCloseable(); |
| AutoCloseables.close(tc, null); |
| assertTrue(tc.isClosed(), "Expected resource to be closed"); |
| } |
| |
| @Test |
| void testClose_withConsumer_exceptionDeliveredToConsumer() throws Exception { |
| final IOException cause = new IOException("boom"); |
| final ThrowingCloseable tc = new ThrowingCloseable(cause); |
| final AtomicReference<Exception> received = new AtomicReference<>(); |
| AutoCloseables.close(tc, received::set); |
| assertEquals(cause, received.get(), "Consumer should have received the exception"); |
| } |
| |
| @Test |
| void testClose_withConsumer_null_doesNotThrow() throws Exception { |
| AutoCloseables.close(null, null); |
| } |
| |
| @Test |
| void testCloseQuietly_closesResource() { |
| final TrackingCloseable tc = new TrackingCloseable(); |
| AutoCloseables.closeQuietly(tc); |
| assertTrue(tc.isClosed(), "Expected resource to be closed"); |
| } |
| |
| @Test |
| void testCloseQuietly_iterable_closesAllResources() { |
| final TrackingCloseable tc1 = new TrackingCloseable(); |
| final TrackingCloseable tc2 = new TrackingCloseable(); |
| AutoCloseables.closeQuietly(Arrays.asList(tc1, tc2)); |
| assertTrue(tc1.isClosed(), "Expected first resource to be closed"); |
| assertTrue(tc2.isClosed(), "Expected second resource to be closed"); |
| } |
| |
| @Test |
| void testCloseQuietly_iterable_null_doesNotThrow() { |
| AutoCloseables.closeQuietly((Iterable<AutoCloseable>) null); |
| } |
| |
| @Test |
| void testCloseQuietly_iterable_swallowsExceptions() { |
| final ThrowingCloseable tc1 = new ThrowingCloseable(new IOException("first")); |
| final ThrowingCloseable tc2 = new ThrowingCloseable(new IOException("second")); |
| AutoCloseables.closeQuietly(Arrays.asList(tc1, tc2)); |
| } |
| |
| @Test |
| void testCloseQuietly_null_doesNotThrow() { |
| AutoCloseables.closeQuietly((AutoCloseable) null); |
| } |
| |
| @Test |
| void testCloseQuietly_swallowsException() { |
| final ThrowingCloseable tc = new ThrowingCloseable(new IOException("ignored")); |
| AutoCloseables.closeQuietly(tc); |
| } |
| |
| @Test |
| void testCloseQuietly_withConsumer_exceptionDeliveredToConsumer() { |
| final IOException cause = new IOException("boom"); |
| final ThrowingCloseable tc = new ThrowingCloseable(cause); |
| final AtomicReference<Exception> received = new AtomicReference<>(); |
| AutoCloseables.closeQuietly(tc, received::set); |
| assertEquals(cause, received.get(), "Consumer should have received the exception"); |
| } |
| |
| @Test |
| void testCloseQuietly_withConsumer_null_doesNotThrow() { |
| AutoCloseables.closeQuietly(null, (java.util.function.Consumer<Exception>) null); |
| } |
| |
| @Test |
| void testCloseQuietly_withNullConsumer_swallowsException() { |
| final ThrowingCloseable tc = new ThrowingCloseable(new IOException("ignored")); |
| AutoCloseables.closeQuietly(tc, (java.util.function.Consumer<Exception>) null); |
| } |
| |
| @Test |
| void testCloseQuietlySuppress_addsSuppressedException() { |
| final IOException closeException = new IOException("close failure"); |
| final ThrowingCloseable tc = new ThrowingCloseable(closeException); |
| final RuntimeException primary = new RuntimeException("primary"); |
| final RuntimeException result = AutoCloseables.closeQuietlySuppress(tc, primary); |
| assertEquals(primary, result); |
| assertNotNull(result.getSuppressed()); |
| assertEquals(1, result.getSuppressed().length); |
| assertEquals(closeException, result.getSuppressed()[0]); |
| } |
| |
| @Test |
| void testCloseQuietlySuppress_noException_returnsThrowable() throws Exception { |
| final TrackingCloseable tc = new TrackingCloseable(); |
| final RuntimeException primary = new RuntimeException("primary"); |
| final RuntimeException result = AutoCloseables.closeQuietlySuppress(tc, primary); |
| assertEquals(primary, result); |
| assertTrue(tc.isClosed()); |
| assertEquals(0, result.getSuppressed().length, "No suppressed exceptions expected"); |
| } |
| |
| @Test |
| void testCloseQuietlySuppress_null_returnsThrowable() { |
| final RuntimeException primary = new RuntimeException("primary"); |
| final RuntimeException result = AutoCloseables.closeQuietlySuppress(null, primary); |
| assertEquals(primary, result, "Should return the given throwable unchanged"); |
| assertEquals(0, result.getSuppressed().length, "No suppressed exceptions expected"); |
| } |
| } |