From e9fd35a303a5f5f41726c3216c8acd7598a3a494 Mon Sep 17 00:00:00 2001 From: Jim Hahn Date: Wed, 7 Feb 2018 17:13:32 -0500 Subject: Add ExtractAppender to facilite junit tests Added ExtractAppender to provide a way for junit tests to capture data sent to a logger. Changed logback.version to 1.2.3 and moved the property to the top-level pom. Updated license date in top-level pom. Refactored ExceptionsText, adding ErrorsTester and ThrowablesTester classes to reduce sonar issues. Change-Id: Ief7d08972bf4e7037b59c2afe4b77b252f2ad60a Issue-ID: POLICY-582 Signed-off-by: Jim Hahn --- utils-test/pom.xml | 5 + .../common/utils/test/ConstructionError.java | 58 +++ .../policy/common/utils/test/ErrorsTester.java | 46 ++ .../policy/common/utils/test/ExceptionsTester.java | 490 ++------------------ .../policy/common/utils/test/ThrowablesTester.java | 515 +++++++++++++++++++++ .../utils/test/log/logback/ExtractAppender.java | 191 ++++++++ .../common/utils/test/ConstructionErrorTest.java | 37 ++ .../policy/common/utils/test/ErrorsTesterTest.java | 77 +++ .../common/utils/test/ExceptionsTesterTest.java | 110 +---- .../common/utils/test/ThrowablesTesterTest.java | 216 +++++++++ .../test/log/logback/ExtractAppenderTest.java | 484 +++++++++++++++++++ utils-test/src/test/resources/logback-test.xml | 37 ++ 12 files changed, 1720 insertions(+), 546 deletions(-) create mode 100644 utils-test/src/main/java/org/onap/policy/common/utils/test/ConstructionError.java create mode 100644 utils-test/src/main/java/org/onap/policy/common/utils/test/ErrorsTester.java create mode 100644 utils-test/src/main/java/org/onap/policy/common/utils/test/ThrowablesTester.java create mode 100644 utils-test/src/main/java/org/onap/policy/common/utils/test/log/logback/ExtractAppender.java create mode 100644 utils-test/src/test/java/org/onap/policy/common/utils/test/ConstructionErrorTest.java create mode 100644 utils-test/src/test/java/org/onap/policy/common/utils/test/ErrorsTesterTest.java create mode 100644 utils-test/src/test/java/org/onap/policy/common/utils/test/ThrowablesTesterTest.java create mode 100644 utils-test/src/test/java/org/onap/policy/common/utils/test/log/logback/ExtractAppenderTest.java create mode 100644 utils-test/src/test/resources/logback-test.xml (limited to 'utils-test') diff --git a/utils-test/pom.xml b/utils-test/pom.xml index f9b314f6..31a27367 100644 --- a/utils-test/pom.xml +++ b/utils-test/pom.xml @@ -38,6 +38,11 @@ junit 4.11 + + ch.qos.logback + logback-classic + ${logback.version} + diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/test/ConstructionError.java b/utils-test/src/main/java/org/onap/policy/common/utils/test/ConstructionError.java new file mode 100644 index 00000000..336dc602 --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/test/ConstructionError.java @@ -0,0 +1,58 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Audit + * ================================================================================ + * 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; + +/** + * An error that occurred while trying to construct an object for a junit test. + */ +public class ConstructionError extends AssertionError { + private static final long serialVersionUID = 1L; + + /** + * + */ + public ConstructionError() { + super(); + } + + /** + * @param message + */ + public ConstructionError(String message) { + super(message); + } + + /** + * @param cause + */ + public ConstructionError(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public ConstructionError(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/test/ErrorsTester.java b/utils-test/src/main/java/org/onap/policy/common/utils/test/ErrorsTester.java new file mode 100644 index 00000000..12b48442 --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/test/ErrorsTester.java @@ -0,0 +1,46 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Monitor + * ================================================================================ + * 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; + +/** + * Used to test various Error subclasses. Uses reflection to identify the + * constructors that the subclass supports. + */ +public class ErrorsTester extends ThrowablesTester { + + /** + * Runs tests, on an Error subclass, for all of the standard constructors. + * If the Error 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 Error subclass cannot be constructed + * @throws AssertionError + * if the constructed objects fail to pass various tests + */ + public int testError(Class claz) { + return testThrowable(claz); + } +} diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/test/ExceptionsTester.java b/utils-test/src/main/java/org/onap/policy/common/utils/test/ExceptionsTester.java index 8705c48c..156fca89 100644 --- a/utils-test/src/main/java/org/onap/policy/common/utils/test/ExceptionsTester.java +++ b/utils-test/src/main/java/org/onap/policy/common/utils/test/ExceptionsTester.java @@ -22,7 +22,6 @@ 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; @@ -31,7 +30,7 @@ import java.lang.reflect.Constructor; * Used to test various Exception subclasses. Uses reflection to identify the * constructors that the subclass supports. */ -public class ExceptionsTester { +public class ExceptionsTester extends ThrowablesTester { /** * Runs tests, on an Exception subclass, for all of the standard @@ -40,144 +39,43 @@ public class ExceptionsTester { * * @param claz * subclass to be tested - * @return the number of constructors that the test found/tested - * @throws Exception - * if the Exception cannot be constructed + * @return the number of constructors that were found/tested + * @throws ConstructionError + * if the Exception subclass cannot be constructed + * @throws AssertionError + * if the constructed objects fail to pass various tests */ - public int test(Class claz) throws Exception { - int ncons = 0; + public int test(Class claz) { + int ncons = testException(claz); - ncons += testDefault(claz); - ncons += testString(claz); - ncons += testThrowable(claz); - ncons += testException(claz); - ncons += testStringThrowable(claz); - ncons += testStringException(claz); - ncons += testStringThrowableBooleanBoolean(claz); - ncons += testStringExceptionBooleanBoolean(claz); - assertTrue(ncons > 0); return ncons; } /** - * Tests exceptions created via the default constructor. Verifies that: - *
    - *
  • toString() returns a non-null value
  • - *
  • getMessage() returns null
  • - *
  • getCause() returns null
  • - *
- * - * If the Exception 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 Exception - * if the Exception cannot be constructed - */ - public int testDefault(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test - return 0; - } - - T ex = cons.newInstance(); - - assertNotNull(ex.toString()); - assertNull(ex.getMessage()); - assertNull(ex.getCause()); - - return 1; - } - - /** - * Tests exceptions 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 Exception 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 Exception - * if the Exception cannot be constructed - */ - public int testString(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(String.class); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test - return 0; - } - - T ex = cons.newInstance("hello"); - - assertNotNull(ex.toString()); - assertEquals("hello", ex.getMessage()); - assertNull(ex.getCause()); - - return 1; - } - - /** - * Tests exceptions 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 Exception subclass does not support this type of constructor, then - * this method simply returns. + * Runs tests, on an Exception subclass, for all of the standard + * constructors. If the Exception 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 {@code 1}, if the subclass supports this type of constructor, - * {@code 0} otherwise - * @throws Exception - * if the Exception cannot be constructed + * @return the number of constructors that were found/tested + * @throws ConstructionError + * if the Exception subclass cannot be constructed + * @throws AssertionError + * if the constructed objects fail to pass various tests */ - public int testThrowable(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(Throwable.class); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test - return 0; - } - - Throwable cause = new Throwable("expected exception"); - T ex = cons.newInstance(cause); + public int testException(Class claz) { + int ncons = 0; - assertEquals(ex.getMessage(), ex.getMessage()); - assertNotNull(ex.toString()); - assertEquals(cause, ex.getCause()); + ncons += testThrowable(claz); + ncons += testException_Exception(claz); + ncons += testException_StringException(claz); + ncons += testException_StringExceptionBooleanBoolean(claz); - return 1; + return ncons; } /** @@ -197,22 +95,19 @@ public class ExceptionsTester { * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise - * @throws Exception - * if the Exception cannot be constructed + * @throws ConstructionError + * if the Exception subclass cannot be constructed + * @throws AssertionError + * if the constructed objects fail to pass various tests */ - public int testException(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(Exception.class); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test + public int testException_Exception(Class claz) { + Constructor cons = getConstructor(claz, "exception", Exception.class); + if (cons == null) { return 0; } - Exception cause = new Exception("expected exception"); - T ex = cons.newInstance(cause); + Exception cause = new Exception(EXPECTED_EXCEPTION_MSG); + T ex = newInstance(cons, cause); assertNotNull(ex.toString()); assertEquals(ex.getMessage(), ex.getMessage()); @@ -221,48 +116,6 @@ public class ExceptionsTester { return 1; } - /** - * Tests exceptions 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 Exception 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 Exception - * if the Exception cannot be constructed - */ - public int testStringThrowable(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(String.class, Throwable.class); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test - return 0; - } - - Throwable cause = new Throwable("expected exception"); - T ex = cons.newInstance("world", cause); - - assertNotNull(ex.toString()); - assertEquals("world", ex.getMessage()); - assertEquals(cause, ex.getCause()); - - return 1; - } - /** * Tests exceptions created via the constructor that takes a String and an * Exception. Verifies that: @@ -281,22 +134,17 @@ public class ExceptionsTester { * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise - * @throws Exception - * if the Exception cannot be constructed + * @throws ConstructionError + * if the Exception subclass cannot be constructed */ - public int testStringException(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(String.class, Exception.class); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test + public int testException_StringException(Class claz) { + Constructor cons = getConstructor(claz, "string-exception", String.class, Exception.class); + if (cons == null) { return 0; } - Exception cause = new Exception("expected exception"); - T ex = cons.newInstance("world", cause); + Exception cause = new Exception(EXPECTED_EXCEPTION_MSG); + T ex = newInstance(cons, "world", cause); assertNotNull(ex.toString()); assertEquals("world", ex.getMessage()); @@ -305,52 +153,6 @@ public class ExceptionsTester { return 1; } - /** - * Tests exceptions 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 Exception 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 Exception - * if the Exception cannot be constructed - */ - public int testStringThrowableBooleanBoolean(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(String.class, Throwable.class, Boolean.TYPE, Boolean.TYPE); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test - return 0; - } - - // test each combination of "message" and "cause" - testMessageCauseCombos(cons); - - // test each combination of the boolean flags - testSuppressStack(cons); - testSuppressNoStack(cons); - testNoSuppressStack(cons); - testNoSuppressNoStack(cons); - - return 1; - } - /** * Tests exceptions created via the constructor that takes a String, an * Exception, and two booleans. Verifies that: @@ -371,218 +173,22 @@ public class ExceptionsTester { * subclass to be tested * @return {@code 1}, if the subclass supports this type of constructor, * {@code 0} otherwise - * @throws Exception - * if the Exception cannot be constructed + * @throws ConstructionError + * if the Exception subclass cannot be constructed */ - public int testStringExceptionBooleanBoolean(Class claz) throws Exception { - Constructor cons; - - try { - cons = claz.getConstructor(String.class, Exception.class, Boolean.TYPE, Boolean.TYPE); - - } catch (NoSuchMethodException | SecurityException e) { - // this constructor is not defined so nothing to test + public int testException_StringExceptionBooleanBoolean(Class claz) { + Constructor cons = getConstructor(claz, "string-exception-flags", String.class, Exception.class, + Boolean.TYPE, Boolean.TYPE); + if (cons == null) { return 0; } // test each combination of "message" and "cause" - testMessageCauseCombos(cons); + testThrowable_MessageCauseCombos(cons); // test each combination of the boolean flags - testFlagCombos(cons); + testThrowable_FlagCombos(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 Exception - * if the Exception cannot be constructed - */ - private void testMessageCauseCombos(Constructor cons) throws Exception { - T ex; - Exception cause = new Exception("expected throwable"); - - ex = cons.newInstance(null, null, true, true); - assertNotNull(ex.toString()); - assertNull(ex.getMessage()); - assertNull(ex.getCause()); - - ex = cons.newInstance("abc", null, true, true); - assertNotNull(ex.toString()); - assertEquals("abc", ex.getMessage()); - assertNull(ex.getCause()); - - ex = cons.newInstance(null, cause, true, true); - assertNotNull(ex.toString()); - assertNull(ex.getMessage()); - assertEquals(cause, ex.getCause()); - - ex = cons.newInstance("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 Exception - * if the Exception cannot be constructed - */ - public void testFlagCombos(Constructor cons) throws Exception { - testSuppressStack(cons); - testSuppressNoStack(cons); - testNoSuppressStack(cons); - testNoSuppressNoStack(cons); - } - - /** - * Tests exceptions 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 exception's class constructor - * @throws Exception - * if the Exception cannot be constructed - */ - public void testSuppressStack(Constructor cons) throws Exception { - Exception cause = new Exception("expected exception"); - Throwable supr = new Throwable("expected suppressed exception"); - T ex = cons.newInstance("yes,yes", cause, true, true); - - ex.addSuppressed(supr); - - assertNotNull(ex.toString()); - assertEquals("yes,yes", ex.getMessage()); - assertEquals(cause, ex.getCause()); - - assertEquals(1, ex.getSuppressed().length); - assertEquals(supr, ex.getSuppressed()[0]); - - assertTrue(ex.getStackTrace().length > 0); - } - - /** - * Tests exceptions 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 exception's class constructor - * @throws Exception - * if the Exception cannot be constructed - */ - public void testSuppressNoStack(Constructor cons) throws Exception { - Exception cause = new Exception("expected exception"); - Throwable supr = new Throwable("expected suppressed exception"); - T ex = cons.newInstance("yes,no", cause, true, false); - - ex.addSuppressed(supr); - - assertNotNull(ex.toString()); - assertEquals("yes,no", ex.getMessage()); - assertEquals(cause, ex.getCause()); - - assertEquals(1, ex.getSuppressed().length); - assertEquals(supr, ex.getSuppressed()[0]); - - assertEquals(0, ex.getStackTrace().length); - } - - /** - * Tests exceptions 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 exception's class constructor - * @throws Exception - * if the Exception cannot be constructed - */ - public void testNoSuppressStack(Constructor cons) throws Exception { - Exception cause = new Exception("expected exception"); - Throwable supr = new Throwable("expected suppressed exception"); - T ex = cons.newInstance("no,yes", cause, false, true); - - ex.addSuppressed(supr); - - assertNotNull(ex.toString()); - assertEquals("no,yes", ex.getMessage()); - assertEquals(cause, ex.getCause()); - - assertEquals(0, ex.getSuppressed().length); - - assertTrue(ex.getStackTrace().length > 0); - } - - /** - * Tests exceptions 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 exception's class constructor - * @throws Exception - * if the Exception cannot be constructed - */ - public void testNoSuppressNoStack(Constructor cons) throws Exception { - Exception cause = new Exception("expected exception"); - Throwable supr = new Throwable("expected suppressed exception"); - T ex = cons.newInstance("no,no", cause, false, false); - - ex.addSuppressed(supr); - - assertNotNull(ex.toString()); - assertEquals("no,no", ex.getMessage()); - assertEquals(cause, ex.getCause()); - - assertEquals(0, ex.getSuppressed().length); - assertEquals(0, ex.getStackTrace().length); - } } diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/test/ThrowablesTester.java b/utils-test/src/main/java/org/onap/policy/common/utils/test/ThrowablesTester.java new file mode 100644 index 00000000..bd8db4fc --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/test/ThrowablesTester.java @@ -0,0 +1,515 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Monitor + * ================================================================================ + * 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: + *
      + *
    • toString() returns a non-null value
    • + *
    • getMessage() returns null
    • + *
    • 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_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 due to no " + testType + " constructor for: " + claz); + 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); + } + + } +} diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/test/log/logback/ExtractAppender.java b/utils-test/src/main/java/org/onap/policy/common/utils/test/log/logback/ExtractAppender.java new file mode 100644 index 00000000..f012464c --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/test/log/logback/ExtractAppender.java @@ -0,0 +1,191 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Audit + * ================================================================================ + * 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.log.logback; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +/** + * This is an appender that is intended for use by JUnit tests that wish to + * capture logged messages. The appender takes an optional list of regular + * expressions that are used to identify and extract data of interest. + *

    + * If no patterns are provided, then every logged message is recorded. However, + * if patterns are provided, then only messages that match one of the patterns + * are recorded. In addition, if the pattern contains a capture group that is + * non-null, only the captured group is recorded. Otherwise, the entire portion + * of the message that matches the pattern is recorded. + *

    + * All operations are thread-safe. + */ +public class ExtractAppender extends AppenderBase { + + /** + * Extracted text is placed here. + */ + private final Queue extracted; + + /** + * Regular expressions/Patterns to be used to extract text. Uses a + * LinkedHashMap so that order is preserved. + */ + private final LinkedHashMap patterns; + + /** + * Records every message that is logged. + */ + public ExtractAppender() { + this(new LinkedList<>()); + } + + /** + * Records portions of messages that match one of the regular expressions. + * + * @param regex + * regular expression (i.e., {@link Pattern}) to match + */ + public ExtractAppender(String... regex) { + this(new LinkedList<>(), regex); + } + + /** + * Rather than allocating an internal queue to store matched messages, + * messages are recorded in the specified target queue using the + * {@link Queue#offer(Object)} method. Note: whenever the queue is used, it + * will be synchronized to prevent simultaneous accesses. + * + * @param target + * @param regex + * regular expression (i.e., {@link Pattern}) to match + */ + public ExtractAppender(Queue target, String... regex) { + extracted = target; + patterns = new LinkedHashMap<>(regex.length); + + for (String re : regex) { + patterns.put(re, Pattern.compile(re)); + } + } + + /* + * (non-Javadoc) + * + * @see ch.qos.logback.core.AppenderBase#append(Object) + */ + @Override + protected void append(ILoggingEvent event) { + + String msg = event.getMessage(); + + synchronized (patterns) { + if (patterns.isEmpty()) { + addExtraction(msg); + return; + } + + for (Pattern p : patterns.values()) { + Matcher m = p.matcher(msg); + + if (m.find()) { + addGroupMatch(m); + break; + } + } + } + } + + /** + * Adds the first match group to {@link #extracted}. + * + * @param mat + * the matcher containing the groups + * @return {@code true} if a group was found, {@code false} otherwise + */ + private void addGroupMatch(Matcher mat) { + int ngroups = mat.groupCount(); + + for (int x = 1; x <= ngroups; ++x) { + String txt = mat.group(x); + + if (txt != null) { + addExtraction(txt); + return; + } + } + + addExtraction(mat.group()); + } + + /** + * Adds an item to {@link #extracted}, in a thread-safe manner. It uses the + * queue's offer() method so that the queue can discard the item if + * it so chooses, without generating an exception. + * + * @param txt + * text to be added + */ + private void addExtraction(String txt) { + synchronized (extracted) { + extracted.offer(txt); + } + } + + /** + * Gets the text that has been extracted. + * + * @return a copy of the text that has been extracted + */ + public List getExtracted() { + synchronized (extracted) { + return new ArrayList<>(extracted); + } + } + + /** + * Clears the list of extracted text. + */ + public void clearExtractions() { + synchronized (extracted) { + extracted.clear(); + } + } + + /** + * Adds a pattern to be matched by this appender. + * + * @param regex + * regular expression (i.e., {@link Pattern}) to match + */ + public void setPattern(String regex) { + synchronized (patterns) { + patterns.put(regex, Pattern.compile(regex)); + } + } + +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/test/ConstructionErrorTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/test/ConstructionErrorTest.java new file mode 100644 index 00000000..4a6cd90e --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/test/ConstructionErrorTest.java @@ -0,0 +1,37 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Audit + * ================================================================================ + * 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.*; + +import org.junit.Test; + +/** + * + */ +public class ConstructionErrorTest extends ErrorsTester { + + @Test + public void test() throws Exception { + assertEquals(4, testError(ConstructionError.class)); + } + +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/test/ErrorsTesterTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/test/ErrorsTesterTest.java new file mode 100644 index 00000000..ae684d52 --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/test/ErrorsTesterTest.java @@ -0,0 +1,77 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Monitor + * ================================================================================ + * 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 org.junit.Test; + +public class ErrorsTesterTest { + + @Test + public void test() { + assertEquals(2, new ErrorsTester().testError(SimpleError.class)); + assertEquals(5, new ErrorsTester().testError(StaticError.class)); + } + + /** + * Used to test a simple success case. + */ + public static class SimpleError extends Error { + private static final long serialVersionUID = 1L; + + public SimpleError() { + super(); + } + + public SimpleError(String message) { + super(message); + } + } + + /** + * Used to test the exhaustive success case. + */ + public static class StaticError extends Error { + private static final long serialVersionUID = 1L; + + public StaticError() { + super(); + } + + public StaticError(String message) { + super(message); + } + + public StaticError(Throwable cause) { + super(cause); + } + + public StaticError(String message, Throwable cause) { + super(message, cause); + } + + public StaticError(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } + +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/test/ExceptionsTesterTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/test/ExceptionsTesterTest.java index c6af77a2..3848acd0 100644 --- a/utils-test/src/test/java/org/onap/policy/common/utils/test/ExceptionsTesterTest.java +++ b/utils-test/src/test/java/org/onap/policy/common/utils/test/ExceptionsTesterTest.java @@ -27,124 +27,26 @@ import org.junit.Test; public class ExceptionsTesterTest { @Test - public void test() throws Exception { + public void test() { assertEquals(2, new ExceptionsTester().test(SimpleException.class)); assertEquals(8, new ExceptionsTester().test(StaticException.class)); } @Test(expected = AssertionError.class) - public void testIgnoreMessageException() throws Exception { - new ExceptionsTester().test(IgnoreMessageException.class); - } - - @Test(expected = AssertionError.class) - public void testIgnoreCauseException() throws Exception { - new ExceptionsTester().test(IgnoreCauseException.class); - } - - @Test(expected = AssertionError.class) - public void testNonStaticException() throws Exception { + public void testNoConstructorsException() { new ExceptionsTester().test(NoConstructorsException.class); } - @Test(expected = AssertionError.class) - public void testAlwaysSuppressException() throws Exception { - new ExceptionsTester().test(AlwaysSuppressException.class); - } - - @Test(expected = AssertionError.class) - public void testNeverSuppressException() throws Exception { - new ExceptionsTester().test(NeverSuppressException.class); - } - - @Test(expected = AssertionError.class) - public void testAlwaysWritableException() throws Exception { - new ExceptionsTester().test(AlwaysWritableException.class); - } - - @Test(expected = AssertionError.class) - public void testNeverWritableException() throws Exception { - new ExceptionsTester().test(NeverWritableException.class); - } - /** - * Used to test a failure case - message text is ignored. - */ - public static class IgnoreMessageException extends Exception { - private static final long serialVersionUID = 1L; - - public IgnoreMessageException(String message) { - super("bogus"); - } - } - - /** - * Used to test a failure case - cause is ignored. - */ - public static class IgnoreCauseException extends Exception { - private static final long serialVersionUID = 1L; - - public IgnoreCauseException(Throwable cause) { - super(new Exception("another cause")); - } - } - - /** - * Used to test a failure case - this has no standard constructions. + * Used to test a failure case - this has no standard constructors. The only + * constructor it has takes an "int", thus it is not one of the standard + * constructors. */ public static class NoConstructorsException extends Exception { private static final long serialVersionUID = 1L; public NoConstructorsException(int value) { - super(String.valueOf(value)); - } - } - - /** - * Used to test a failure case - always suppresses. - */ - public static class AlwaysSuppressException extends Exception { - private static final long serialVersionUID = 1L; - - public AlwaysSuppressException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, true, writableStackTrace); - } - } - - /** - * Used to test a failure case - never suppresses. - */ - public static class NeverSuppressException extends Exception { - private static final long serialVersionUID = 1L; - - public NeverSuppressException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, false, writableStackTrace); - } - } - - /** - * Used to test a failure case - always allows stack writes. - */ - public static class AlwaysWritableException extends Exception { - private static final long serialVersionUID = 1L; - - public AlwaysWritableException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, true); - } - } - - /** - * Used to test a failure case - never allows stack writes. - */ - public static class NeverWritableException extends Exception { - private static final long serialVersionUID = 1L; - - public NeverWritableException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, false); + super(); } } diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/test/ThrowablesTesterTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/test/ThrowablesTesterTest.java new file mode 100644 index 00000000..cb96e98f --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/test/ThrowablesTesterTest.java @@ -0,0 +1,216 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Monitor + * ================================================================================ + * 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 org.junit.Test; + +public class ThrowablesTesterTest { + + @Test + public void test() { + assertEquals(2, new ThrowablesTester().testThrowable(SimpleThrowable.class)); + assertEquals(5, new ThrowablesTester().testThrowable(StaticThrowable.class)); + } + + @Test + public void testNoConstructorsThrowable() { + // this will not throw an error, but it should return 0, as there are + // no matching constructors + assertEquals(0, new ThrowablesTester().testThrowable(NoConstructorsThrowable.class)); + } + + @Test(expected = AssertionError.class) + public void testIgnoreMessageThrowable() { + new ThrowablesTester().testThrowable(IgnoreMessageThrowable.class); + } + + @Test(expected = AssertionError.class) + public void testIgnoreCauseThrowable() { + new ThrowablesTester().testThrowable(IgnoreCauseThrowable.class); + } + + @Test(expected = AssertionError.class) + public void testAlwaysSuppressThrowable() { + new ThrowablesTester().testThrowable(AlwaysSuppressThrowable.class); + } + + @Test(expected = AssertionError.class) + public void testNeverSuppressThrowable() { + new ThrowablesTester().testThrowable(NeverSuppressThrowable.class); + } + + @Test(expected = AssertionError.class) + public void testAlwaysWritableThrowable() { + new ThrowablesTester().testThrowable(AlwaysWritableThrowable.class); + } + + @Test(expected = AssertionError.class) + public void testNeverWritableThrowable() { + new ThrowablesTester().testThrowable(NeverWritableThrowable.class); + } + + @Test(expected = ConstructionError.class) + public void testThrowInstantiationException() { + new ThrowablesTester().testThrowable(ThrowInstantiationException.class); + } + + /** + * Used to test a failure case - message text is ignored. + */ + public static class IgnoreMessageThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public IgnoreMessageThrowable(String message) { + super("bogus"); + } + } + + /** + * Used to test a failure case - cause is ignored. + */ + public static class IgnoreCauseThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public IgnoreCauseThrowable(Throwable cause) { + super(new Throwable("another cause")); + } + } + + /** + * Used to test a failure case - this has no standard constructors. The only + * constructor it has takes an "int", thus it is not one of the standard + * constructors. + */ + public static class NoConstructorsThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public NoConstructorsThrowable(int value) { + super(); + } + } + + /** + * Used to test a failure case - always suppresses. + */ + public static class AlwaysSuppressThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public AlwaysSuppressThrowable(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, true, writableStackTrace); + } + } + + /** + * Used to test a failure case - never suppresses. + */ + public static class NeverSuppressThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public NeverSuppressThrowable(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, false, writableStackTrace); + } + } + + /** + * Used to test a failure case - always allows stack writes. + */ + public static class AlwaysWritableThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public AlwaysWritableThrowable(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, true); + } + } + + /** + * Used to test a failure case - never allows stack writes. + */ + public static class NeverWritableThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public NeverWritableThrowable(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, false); + } + } + + /** + * Used to test a failure case - throws InstantiationException when + * constructed. + */ + public static class ThrowInstantiationException extends Throwable { + private static final long serialVersionUID = 1L; + + public ThrowInstantiationException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) throws InstantiationException { + + throw new InstantiationException(ThrowablesTester.EXPECTED_EXCEPTION_MSG); + } + } + + /** + * Used to test a simple success case. + */ + public static class SimpleThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public SimpleThrowable() { + super(); + } + + public SimpleThrowable(String message) { + super(message); + } + } + + /** + * Used to test the exhaustive success case. + */ + public static class StaticThrowable extends Throwable { + private static final long serialVersionUID = 1L; + + public StaticThrowable() { + super(); + } + + public StaticThrowable(String message) { + super(message); + } + + public StaticThrowable(Throwable cause) { + super(cause); + } + + public StaticThrowable(String message, Throwable cause) { + super(message, cause); + } + + public StaticThrowable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } + +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/test/log/logback/ExtractAppenderTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/test/log/logback/ExtractAppenderTest.java new file mode 100644 index 00000000..a9a69252 --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/test/log/logback/ExtractAppenderTest.java @@ -0,0 +1,484 @@ +/* + * ============LICENSE_START======================================================= + * Integrity Audit + * ================================================================================ + * 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.log.logback; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; + +public class ExtractAppenderTest { + + /** + * Milliseconds to wait for a thread to terminate. + */ + private static final long THREAD_WAIT_MS = 5000l; + + private static Logger logger; + + private List threads; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + logger = (Logger) LoggerFactory.getLogger(ExtractAppenderTest.class); + logger.setLevel(Level.INFO); + } + + @Before + public void setUp() throws Exception { + threads = new LinkedList<>(); + } + + @After + public void tearDown() throws Exception { + logger.detachAndStopAllAppenders(); + + for (Thread p : threads) { + p.interrupt(); + p.join(THREAD_WAIT_MS); + } + } + + @Test + public void testExtractAppender() { + AtomicInteger count = new AtomicInteger(0); + + ExtractAppender p = new ExtractAppender() { + @Override + protected void append(ILoggingEvent event) { + count.incrementAndGet(); + super.append(event); + } + }; + + addAppender(p); + + logger.info("hello"); + logger.info("world"); + + // "append" should always be called + assertEquals(2, count.get()); + + // appender with no patterns - everything should match + assertEquals(strList("hello", "world"), p.getExtracted()); + + // add a pattern and verify match + p.setPattern("abc[0-9]"); + logger.info("hello abc1"); + + // this should not match + logger.info("hello def2"); + + assertEquals(4, count.get()); + assertEquals(strList("hello", "world", "abc1"), p.getExtracted()); + } + + @Test + public void testExtractAppenderStringArray() { + AtomicInteger count = new AtomicInteger(0); + + ExtractAppender p = new ExtractAppender("abc[0-9]", "def[0-9]") { + @Override + protected void append(ILoggingEvent event) { + count.incrementAndGet(); + super.append(event); + } + }; + + addAppender(p); + + logger.info("hello abc1 world"); + logger.info("world ghi2 world"); // no match + logger.info("world def3 world"); + logger.info("hello abc4"); + logger.info("abc5 world"); + logger.info("hello def6"); + logger.info("ghi7 world"); // no match + logger.info("def8 world"); + + // "append" should always be called + assertEquals(8, count.get()); + + assertEquals(strList("abc1", "def3", "abc4", "abc5", "def6", "def8"), p.getExtracted()); + + p.setPattern("ghi[0-9]"); + logger.info("hello abc9"); + logger.info("hello ghi9"); + + // this should not match + logger.info("hello xyz"); + + assertEquals(11, count.get()); + assertEquals(strList("abc1", "def3", "abc4", "abc5", "def6", "def8", "abc9", "ghi9"), p.getExtracted()); + } + + @Test + public void testExtractAppenderQueueStringArray() { + // no. of matches allowed in the list + int nallowed = 3; + + AtomicInteger count = new AtomicInteger(0); + + LinkedList queue = new LinkedList() { + private static final long serialVersionUID = 1L; + + @Override + public boolean offer(String e) { + if(count.incrementAndGet() <= nallowed) { + return super.offer(e); + + } else { + return false; + } + } + }; + + ExtractAppender p = new ExtractAppender(queue, "abc[0-9]"); + addAppender(p); + + // these shouldn't match + for(int x = 0; x < 10; ++x) { + logger.info("xyz"); + } + + int nmatches = 10; + + LinkedList expected = new LinkedList<>(); + + for(int x = 0; x < nmatches; ++x) { + String msg = "abc" + x; + logger.info(msg + " world"); + + if(x < nallowed) { + expected.add(msg); + } + } + + // "offer" should always be called for a match + assertEquals(nmatches, count.get()); + + assertEquals(expected, p.getExtracted()); + } + + @Test + public void testAppendILoggingEvent_NoPatterns() { + ExtractAppender p = makeAppender(); + + logger.info("hello"); + logger.info("world"); + + assertEquals(strList("hello", "world"), p.getExtracted()); + } + + @Test + public void testAppendILoggingEvent_MatchFirstPattern() { + ExtractAppender p = makeAppender("abc[0-9]", "def[0-9]"); + + logger.info("hello abc1"); + logger.info("world xyz2"); + + assertEquals(strList("abc1"), p.getExtracted()); + } + + @Test + public void testAppendILoggingEvent_MatchLastPattern() { + ExtractAppender p = makeAppender("abc[0-9]", "def[0-9]"); + + logger.info("hello def1"); + logger.info("world xyz2"); + + assertEquals(strList("def1"), p.getExtracted()); + } + + @Test + public void testAppendILoggingEvent_Group1() { + ExtractAppender p = makeAppender("hello (abc)|(xyz)", "def[0-9]"); + + logger.info("hello abc, world!"); + logger.info("world abc"); + + assertEquals(strList("abc"), p.getExtracted()); + } + + @Test + public void testAppendILoggingEvent_Group3() { + ExtractAppender p = makeAppender("hello (abc)|(pdq)|(xyz)", "def[0-9]"); + + logger.info("say hello xyz, world!"); + logger.info("world abc"); + + assertEquals(strList("xyz"), p.getExtracted()); + } + + @Test + public void testAppendILoggingEvent_NoGroup() { + ExtractAppender p = makeAppender("hello abc"); + + logger.info("say hello abc, world!"); + logger.info("world abc"); + + assertEquals(strList("hello abc"), p.getExtracted()); + } + + @Test + public void testGetExtracted() { + ExtractAppender p = makeAppender("abc[1-9]"); + + logger.info("hello abc1 world"); + logger.info("world ghi2 world"); // no match + logger.info("hello abc3"); + + List oldlst = p.getExtracted(); + assertEquals(strList("abc1", "abc3"), oldlst); + assertEquals(oldlst, p.getExtracted()); + + logger.info("abc9"); + assertEquals(strList("abc1", "abc3", "abc9"), p.getExtracted()); + } + + @Test + public void testClearExtractions() { + ExtractAppender p = makeAppender("abc[1-9]"); + + logger.info("hello abc1 world"); + logger.info("world ghi2 world"); + logger.info("hello abc3"); + + assertEquals(strList("abc1", "abc3"), p.getExtracted()); + + p.clearExtractions(); + + // list should be empty now + assertEquals(strList(), p.getExtracted()); + + logger.info("hello abc4 world"); + logger.info("world ghi5 world"); + logger.info("hello abc6"); + + // list should only contain the new items + assertEquals(strList("abc4", "abc6"), p.getExtracted()); + } + + @Test + public void testSetPattern() { + ExtractAppender p = makeAppender("abc[1-9]"); + + logger.info("hello abc1 world"); + logger.info("world ghi2 world"); // no match + logger.info("hello abc3"); + + assertEquals(strList("abc1", "abc3"), p.getExtracted()); + + p.setPattern("ghi[0-9]"); + + logger.info("world ghi4 world"); // this should match now + logger.info("hello abc5"); // this should still match + logger.info("hello xyz5"); // no match + + assertEquals(strList("abc1", "abc3", "ghi4", "abc5"), p.getExtracted()); + } + + /** + * Launches threads doing everything in parallel to ensure nothing crashes. + * + * @throws Exception + */ + @Test + public void test_MultiThreaded() throws Exception { + // when to stop + long tend = System.currentTimeMillis() + 250; + + // maximum number of items allowed in the extraction list + int maxItems = 10; + + // this will be set if one of the threads generates an error + AtomicBoolean err = new AtomicBoolean(false); + + // extracted messages go here - this is a finite-length queue since + // we don't know how many messages may actually be logged + LinkedList queue = new LinkedList() { + private static final long serialVersionUID = 1L; + + @Override + public boolean offer(String e) { + if (size() < maxItems) { + return super.offer(e); + } else { + return false; + } + } + }; + + ExtractAppender app = new ExtractAppender(queue, "abc[1-9]"); + addAppender(app); + + // create some threads to add another pattern + addThread(tend, err, xtxt -> { + app.setPattern("def[0-9]"); + }); + + // create some threads to log "abc" messages + addThread(tend, err, xtxt -> { + logger.info("hello abc" + xtxt + "world!"); + }); + + // create some threads to log "def" messages + addThread(tend, err, xtxt -> { + logger.info("hello def" + xtxt + "world!"); + }); + + // create some threads to get extractions + addThread(tend, err, xtxt -> { + app.getExtracted(); + }); + + // create some threads to clear extractions + addThread(tend, err, xtxt -> { + app.clearExtractions(); + + // don't want to clear the list too frequently + // so sleep a bit in between + try { + Thread.sleep(10 + Integer.valueOf(xtxt)); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw e; + } + }); + + /* + * Finally ready to start. + */ + + // start all of the threads + for (Thread t : threads) { + t.setDaemon(true); + t.start(); + } + + // wait for each thread to stop + for (Thread t : threads) { + t.join(THREAD_WAIT_MS); + assertFalse(t.isAlive()); + } + + // ensure none of the threads threw an exception + assertFalse(err.get()); + } + + /** + * Adds multiple threads to perform some function repeatedly until the given + * time is reached. + * + * @param tend + * time, in milliseconds, when the test should terminate + * @param haderr + * this will be set to {@code true} if the function throws an + * exception other than an InterruptedException + * @param func + * function to be repeatedly invoked + */ + private void addThread(long tend, AtomicBoolean haderr, VoidFunction func) { + // number of threads of each type to create + int neach = 3; + + for (int x = 0; x < neach; ++x) { + String xtxt = String.valueOf(x); + + threads.add(new Thread() { + @Override + public void run() { + try { + while (System.currentTimeMillis() < tend) { + func.apply(xtxt); + } + + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + + } catch (Exception ex) { + haderr.set(true); + } + } + }); + + } + } + + /** + * Makes an appender that recognizes the given set of strings. + * + * @param strings + * regular expressions to be matched + * @return a new appender + */ + private ExtractAppender makeAppender(String... strings) { + ExtractAppender p = new ExtractAppender(strings); + + addAppender(p); + + return p; + } + + /** + * Adds an appender to the logger. + * + * @param app + * appender to be added + */ + private void addAppender(ExtractAppender app) { + app.setContext(logger.getLoggerContext()); + app.start(); + + logger.addAppender(app); + } + + /** + * Converts an array of strings into a list of strings. + * + * @param strings + * array of strings + * @return a list of the strings + */ + private List strList(String... strings) { + return Arrays.asList(strings); + } + + @FunctionalInterface + public interface VoidFunction { + public void apply(String text) throws InterruptedException; + } +} diff --git a/utils-test/src/test/resources/logback-test.xml b/utils-test/src/test/resources/logback-test.xml new file mode 100644 index 00000000..20869a43 --- /dev/null +++ b/utils-test/src/test/resources/logback-test.xml @@ -0,0 +1,37 @@ + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M\(%line\) - %msg%n + + + + + + + + + -- cgit 1.2.3-korg