/* * ============LICENSE_START======================================================= * Common Utils-Test * ================================================================================ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed 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 * * http://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. * ============LICENSE_END========================================================= */ package org.onap.policy.common.utils.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Used to test various Throwable subclasses. Uses reflection to identify the * constructors that the subclass supports. */ public class ThrowablesTester { private static Logger logger = LoggerFactory.getLogger(ThrowablesTester.class); public static final String EXPECTED_EXCEPTION_MSG = "expected exception"; private static final String EXPECTED_SUPPRESSED_EXCEPTION_MSG = "expected suppressed exception"; /** * Passed as a "cause" to constructors. */ public static final Exception CAUSE = new Exception(EXPECTED_EXCEPTION_MSG); /** * Passed to new objects via the addSuppressed() method.. */ public static final Throwable SUPPRESSED = new Throwable(EXPECTED_SUPPRESSED_EXCEPTION_MSG); /** * Runs tests, on an Throwable subclass, for all of the standard * constructors. If the Throwable subclass does not support a given type of * constructor, then it skips that test. Does not throw an exception * if no standard constructors are found. * * @param claz * subclass to be tested * @return the number of constructors that were found/tested * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public int testThrowable(Class claz) { int ncons = 0; ncons += testThrowable_Default(claz); ncons += testThrowable_StringConstuctor(claz); ncons += testThrowable_Throwable(claz); ncons += testThrowable_StringThrowable(claz); ncons += testThrowable_StringThrowableBooleanBoolean(claz); return ncons; } /** * Tests Throwable objects created via the default constructor. Verifies * that: * * * If the Throwable subclass does not support this type of constructor, then * this method simply returns. * * @param claz * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public int testThrowable_Default(Class claz) { Constructor cons = getConstructor(claz, "default"); if (cons == null) { return 0; } T ex = newInstance(cons); assertNotNull(ex.toString()); assertNull(ex.getMessage()); assertNull(ex.getCause()); return 1; } /** * Tests Throwable objects created via the constructor that takes just a * String. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the original message passed to the * constructor
  • *
  • getCause() returns null
  • *
* * If the Throwable subclass does not support this type of constructor, then * this method simply returns. * * @param claz * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public int testThrowable_StringConstuctor(Class claz) { Constructor cons = getConstructor(claz, "string", String.class); if (cons == null) { return 0; } T ex = newInstance(cons, "hello"); assertNotNull(ex.toString()); assertEquals("hello", ex.getMessage()); assertNull(ex.getCause()); return 1; } /** * Tests Throwable objects created via the constructor that takes just a * Throwable. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the cause's message
  • *
  • getCause() returns the original cause passed to the * constructor
  • *
* * If the Throwable subclass does not support this type of constructor, then * this method simply returns. * * @param claz * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public int testThrowable_Throwable(Class claz) { Constructor cons = getConstructor(claz, "throwable", Throwable.class); if (cons == null) { return 0; } T ex = newInstance(cons, CAUSE); assertEquals(ex.getMessage(), ex.getMessage()); assertNotNull(ex.toString()); assertEquals(CAUSE, ex.getCause()); return 1; } /** * Tests Throwable objects created via the constructor that takes a String * and a Throwable. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the original message passed to the * constructor
  • *
  • getCause() returns the original cause passed to the * constructor
  • *
* * If the Throwable subclass does not support this type of constructor, then * this method simply returns. * * @param claz * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public int testThrowable_StringThrowable(Class claz) { Constructor cons = getConstructor(claz, "string-throwable", String.class, Throwable.class); if (cons == null) { return 0; } T ex = newInstance(cons, "world", CAUSE); assertNotNull(ex.toString()); assertEquals("world", ex.getMessage()); assertEquals(CAUSE, ex.getCause()); return 1; } /** * Tests Throwable objects created via the constructor that takes a String, * a Throwable, and two booleans. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the original message passed to the * constructor
  • *
  • getCause() returns the original cause passed to the * constructor
  • *
  • suppressed exceptions can be added, if enabled
  • *
  • the stack trace can be added, if enabled
  • *
* * If the Throwable subclass does not support this type of constructor, then * this method simply returns. * * @param claz * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public int testThrowable_StringThrowableBooleanBoolean(Class claz) { Constructor cons = getConstructor(claz, "string-throwable-flags", String.class, Throwable.class, Boolean.TYPE, Boolean.TYPE); if (cons == null) { return 0; } // test each combination of "message" and "cause" testThrowable_MessageCauseCombos(cons); // test each combination of the boolean flags testThrowable_SuppressStack(cons); testThrowable_SuppressNoStack(cons); testThrowable_NoSuppressStack(cons); testThrowable_NoSuppressNoStack(cons); return 1; } /** * Tests each combination of values for the "message" and the "cause" when * using the constructor that takes a String, a Throwable/Exception, and two * booleans. Verifies that expected values are returned by toString()/i>, * getMessage(), and getCause(). * * * @param cons * constructor to be invoked * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public void testThrowable_MessageCauseCombos(Constructor cons) { T ex; ex = newInstance(cons, null, null, true, true); assertNotNull(ex.toString()); assertNull(ex.getMessage()); assertNull(ex.getCause()); ex = newInstance(cons, "abc", null, true, true); assertNotNull(ex.toString()); assertEquals("abc", ex.getMessage()); assertNull(ex.getCause()); ex = newInstance(cons, null, CAUSE, true, true); assertNotNull(ex.toString()); assertNull(ex.getMessage()); assertEquals(CAUSE, ex.getCause()); ex = newInstance(cons, "xyz", CAUSE, true, true); assertNotNull(ex.toString()); assertEquals("xyz", ex.getMessage()); assertEquals(CAUSE, ex.getCause()); } /** * Tests each combination of values for the "message" and the "cause" when * using the constructor that takes a String, a Throwable/Exception, and two * booleans. Verifies that expected values are returned by toString()/i>, * getMessage(), and getCause(). * * * @param cons * constructor to be invoked * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public void testThrowable_FlagCombos(Constructor cons) { testThrowable_SuppressStack(cons); testThrowable_SuppressNoStack(cons); testThrowable_NoSuppressStack(cons); testThrowable_NoSuppressNoStack(cons); } /** * Tests Throwable objects constructed with {@code enableSuppression=true} * and {@code writableStackTrace=true}. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the original message passed to the * constructor
  • *
  • getCause() returns the original cause passed to the * constructor
  • *
  • suppressed exceptions are added
  • *
  • the stack trace is added
  • *
* * @param cons * the throwable's class constructor * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public void testThrowable_SuppressStack(Constructor cons) { T ex = newInstance(cons, "yes,yes", CAUSE, true, true); ex.addSuppressed(SUPPRESSED); assertNotNull(ex.toString()); assertEquals("yes,yes", ex.getMessage()); assertEquals(CAUSE, ex.getCause()); assertEquals(1, ex.getSuppressed().length); assertEquals(SUPPRESSED, ex.getSuppressed()[0]); assertTrue(ex.getStackTrace().length > 0); } /** * Tests Throwable objects constructed with {@code enableSuppression=true} * and {@code writableStackTrace=false}. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the original message passed to the * constructor
  • *
  • getCause() returns the original cause passed to the * constructor
  • *
  • suppressed exceptions are added
  • *
  • the stack trace is not added
  • *
* * @param cons * the throwable's class constructor * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public void testThrowable_SuppressNoStack(Constructor cons) { T ex = newInstance(cons, "yes,no", CAUSE, true, false); ex.addSuppressed(SUPPRESSED); assertNotNull(ex.toString()); assertEquals("yes,no", ex.getMessage()); assertEquals(CAUSE, ex.getCause()); assertEquals(1, ex.getSuppressed().length); assertEquals(SUPPRESSED, ex.getSuppressed()[0]); assertEquals(0, ex.getStackTrace().length); } /** * Tests Throwable objects constructed with {@code enableSuppression=false} * and {@code writableStackTrace=true}. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the original message passed to the * constructor
  • *
  • getCause() returns the original cause passed to the * constructor
  • *
  • suppressed exceptions are not added
  • *
  • the stack trace is added
  • *
* * @param cons * the throwable's class constructor * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public void testThrowable_NoSuppressStack(Constructor cons) { T ex = newInstance(cons, "no,yes", CAUSE, false, true); ex.addSuppressed(SUPPRESSED); assertNotNull(ex.toString()); assertEquals("no,yes", ex.getMessage()); assertEquals(CAUSE, ex.getCause()); assertEquals(0, ex.getSuppressed().length); assertTrue(ex.getStackTrace().length > 0); } /** * Tests Throwable objects constructed with {@code enableSuppression=false} * and {@code writableStackTrace=false}. Verifies that: *
    *
  • toString() returns a non-null value
  • *
  • getMessage() returns the original message passed to the * constructor
  • *
  • getCause() returns the original cause passed to the * constructor
  • *
  • suppressed exceptions are not added
  • *
  • the stack trace is not added
  • * * @param cons * the throwable's class constructor * @throws ConstructionError * if the Throwable subclass cannot be constructed * @throws AssertionError * if the constructed objects fail to pass various tests */ public void testThrowable_NoSuppressNoStack(Constructor cons) { T ex = newInstance(cons, "no,no", CAUSE, false, false); ex.addSuppressed(SUPPRESSED); assertNotNull(ex.toString()); assertEquals("no,no", ex.getMessage()); assertEquals(CAUSE, ex.getCause()); assertEquals(0, ex.getSuppressed().length); assertEquals(0, ex.getStackTrace().length); } /** * Attempts to get a constructor for objects of a given type. * * @param claz * class of objects whose constructor is to be gotten * @param testType * type of test being run * @param argTypes * argument types to be passed to the constructor * @return the desired constructor, or {@code null} if the desired * constructor is not available */ protected Constructor getConstructor(Class claz, String testType, Class... argTypes) { try { return claz.getConstructor(argTypes); } catch (NoSuchMethodException | SecurityException e) { // this constructor is not defined so nothing to test logger.debug("skipped test, no constructor for: " + claz + " due to: " + e); return null; } } /** * Creates a new instance of an Throwable subclass. * * @param cons * subclass constructor * @param args * arguments to be passed to the constructor * @return a new instance of the Throwable subclass * @throws ConstructionError * if the Throwable subclass cannot be constructed */ protected T newInstance(Constructor cons, Object... args) { try { return cons.newInstance(args); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new ConstructionError(e); } } }