AbstractConcurrentInitializerCloseAndExceptionsTest.java
/*
* 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.concurrent;
import static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import java.sql.SQLException;
import org.apache.commons.lang3.function.FailableConsumer;
import org.apache.commons.lang3.function.FailableSupplier;
import org.junit.jupiter.api.Test;
/**
* An abstract base class for tests of exceptions thrown during initialize and close methods
* on concrete {@code ConcurrentInitializer} implementations.
*
* This class provides some basic tests for initializer implementations. Derived
* class have to create a {@link ConcurrentInitializer} object on which the
* tests are executed.
*
* @param <T> Domain type.
*/
public abstract class AbstractConcurrentInitializerCloseAndExceptionsTest<T> extends AbstractConcurrentInitializerTest<T> {
protected static final class CloseableObject {
boolean closed;
public void close() {
closed = true;
}
public boolean isClosed() {
return closed;
}
}
protected enum ExceptionToThrow {
IOException,
SQLException,
NullPointerException
}
// The use of enums rather than accepting an Exception as the input means we can have
// multiple exception types on the method signature.
protected static CloseableObject methodThatThrowsException(final ExceptionToThrow input) throws IOException, SQLException, ConcurrentException {
switch (input) {
case IOException:
throw new IOException();
case SQLException:
throw new SQLException();
case NullPointerException:
throw new NullPointerException();
default:
fail();
return new CloseableObject();
}
}
protected abstract ConcurrentInitializer<CloseableObject> createInitializerThatThrowsException(
FailableSupplier<CloseableObject, ? extends Exception> supplier, FailableConsumer<CloseableObject, ? extends Exception> closer);
/**
* This method tests that if AbstractConcurrentInitializer.close catches a
* ConcurrentException it will rethrow it wrapped in a ConcurrentException
*/
@SuppressWarnings("rawtypes")
@Test
void testCloserThrowsCheckedException() throws ConcurrentException {
final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
CloseableObject::new,
CloseableObject -> methodThatThrowsException(ExceptionToThrow.IOException));
try {
initializer.get();
((AbstractConcurrentInitializer) initializer).close();
fail();
} catch (final Exception e) {
assertInstanceOf(ConcurrentException.class, e);
assertInstanceOf(IOException.class, e.getCause());
}
}
/**
* This method tests that if AbstractConcurrentInitializer.close catches a
* RuntimeException it will throw it without wrapping it in a ConcurrentException
*/
@SuppressWarnings("rawtypes")
@Test
void testCloserThrowsRuntimeException() throws ConcurrentException {
final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
CloseableObject::new,
CloseableObject -> methodThatThrowsException(ExceptionToThrow.NullPointerException));
initializer.get();
assertNullPointerException(() -> {
((AbstractConcurrentInitializer) initializer).close();
});
}
/**
* This method tests that if AbstractConcurrentInitializer.initialize catches a checked
* exception it will rethrow it wrapped in a ConcurrentException
*/
@SuppressWarnings("unchecked") //for NOP
@Test
void testSupplierThrowsCheckedException() {
final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
() -> methodThatThrowsException(ExceptionToThrow.IOException),
FailableConsumer.NOP);
assertThrows(ConcurrentException.class, () -> initializer.get());
}
/**
* This method tests that if a AbstractConcurrentInitializer.initialize method catches a
* ConcurrentException it will rethrow it without wrapping it.
*/
@Test
void testSupplierThrowsConcurrentException() {
final ConcurrentException concurrentException = new ConcurrentException();
@SuppressWarnings("unchecked")
final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(() -> {
if ("test".equals("test")) {
throw concurrentException;
}
return new CloseableObject();
}, FailableConsumer.NOP);
try {
initializer.get();
fail();
} catch (final ConcurrentException e) {
assertEquals(concurrentException, e);
}
}
/**
* This method tests that if AbstractConcurrentInitializer.initialize catches a runtime exception
* it will not be wrapped in a ConcurrentException
*/
@SuppressWarnings("unchecked")
@Test
void testSupplierThrowsRuntimeException() {
final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
() -> methodThatThrowsException(ExceptionToThrow.NullPointerException),
FailableConsumer.NOP);
assertNullPointerException(() -> initializer.get());
}
/**
* This method tests that if AbstractConcurrentInitializer.close actually closes the wrapped object
*/
@SuppressWarnings("rawtypes")
@Test
void testWorkingCloser() throws Exception {
final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
CloseableObject::new,
CloseableObject::close);
final CloseableObject closeableObject = initializer.get();
assertFalse(closeableObject.isClosed());
((AbstractConcurrentInitializer) initializer).close();
assertTrue(closeableObject.isClosed());
}
}