From 7e15972d2d0ad6538841747145b0b0595b4bc403 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Sun, 14 Oct 2018 16:39:04 -0400 Subject: [PATCH 01/91] additional unit tests for LngValidator --- .../andhow/valid/LngValidatorTest.java | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java index b52e18ad..3703b6b1 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java @@ -9,6 +9,8 @@ */ public class LngValidatorTest { + private static final String ALWAYS_VALID_MESSAGE = "THIS VALIDATION IS ALWAYS VALID"; + @Test public void testGreaterThanIsSpecificationValid() { LngValidator.GreaterThan instance = new LngValidator.GreaterThan(5); @@ -18,13 +20,20 @@ public void testGreaterThanIsSpecificationValid() { assertTrue(instance.isSpecificationValid()); } + @Test + public void testGreatherThanGetInvalidSpecificationMessage() { + LngValidator.GreaterThan instance = new LngValidator.GreaterThan(5); + assertEquals(ALWAYS_VALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } + @Test public void testGreaterThanIsValid() { LngValidator.GreaterThan instance = new LngValidator.GreaterThan(5); assertFalse(instance.isValid(4L)); assertFalse(instance.isValid(5L)); + assertFalse(instance.isValid(null)); assertTrue(instance.isValid(6L)); - + instance = new LngValidator.GreaterThan(0); assertFalse(instance.isValid(-1L)); assertFalse(instance.isValid(0L)); @@ -35,6 +44,13 @@ public void testGreaterThanIsValid() { assertFalse(instance.isValid(-99L)); assertTrue(instance.isValid(-98L)); } + + @Test + public void testGreaterThanGetTheValueMustDescription() { + final long ref = 5; + LngValidator.GreaterThan instance = new LngValidator.GreaterThan(ref); + assertEquals("be greater than " + ref, instance.getTheValueMustDescription()); + } @Test public void testGreaterThanOrEqualToIsSpecificationValid() { @@ -45,25 +61,37 @@ public void testGreaterThanOrEqualToIsSpecificationValid() { assertTrue(instance.isSpecificationValid()); } + @Test + public void testGreaterThanOrEqualToGetInvalidSpecificationMessage() { + LngValidator.GreaterThanOrEqualTo instance = new LngValidator.GreaterThanOrEqualTo(5); + assertEquals(ALWAYS_VALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } + @Test public void testGreaterThanOrEqualToIsValid() { LngValidator.GreaterThanOrEqualTo instance = new LngValidator.GreaterThanOrEqualTo(5); assertFalse(instance.isValid(4L)); + assertFalse(instance.isValid(null)); assertTrue(instance.isValid(5L)); assertTrue(instance.isValid(6L)); - + instance = new LngValidator.GreaterThanOrEqualTo(0); assertFalse(instance.isValid(-1L)); assertTrue(instance.isValid(0L)); assertTrue(instance.isValid(1L)); - - + instance = new LngValidator.GreaterThanOrEqualTo(-99); assertFalse(instance.isValid(-100L)); assertTrue(instance.isValid(-99L)); assertTrue(instance.isValid(-98L)); } - + + @Test + public void testGreaterThanOrEqualToGetTheValueMustDescription() { + final long ref = 5; + LngValidator.GreaterThanOrEqualTo instance = new LngValidator.GreaterThanOrEqualTo(ref); + assertEquals("be greater than or equal to " + ref, instance.getTheValueMustDescription()); + } @Test public void testLessThanIsSpecificationValid() { @@ -74,12 +102,19 @@ public void testLessThanIsSpecificationValid() { assertTrue(instance.isSpecificationValid()); } + @Test + public void testLessThanGetInvalidSpecificationMessage() { + LngValidator.LessThan instance = new LngValidator.LessThan(5); + assertEquals(ALWAYS_VALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } + @Test public void testLessThanIsValid() { LngValidator.LessThan instance = new LngValidator.LessThan(5); assertTrue(instance.isValid(4L)); assertFalse(instance.isValid(5L)); assertFalse(instance.isValid(6L)); + assertFalse(instance.isValid(null)); instance = new LngValidator.LessThan(0); assertTrue(instance.isValid(-1L)); @@ -91,6 +126,13 @@ public void testLessThanIsValid() { assertFalse(instance.isValid(-99L)); assertFalse(instance.isValid(-98L)); } + + @Test + public void testLessThanGetTheValueMustDescription() { + final long ref = 5; + LngValidator.LessThan instance = new LngValidator.LessThan(ref); + assertEquals("be less than " + ref, instance.getTheValueMustDescription()); + } @Test public void testLessThanOrEqualsToIsSpecificationValid() { @@ -100,6 +142,12 @@ public void testLessThanOrEqualsToIsSpecificationValid() { instance = new LngValidator.LessThanOrEqualTo(-999999999); assertTrue(instance.isSpecificationValid()); } + + @Test + public void testLessThanOrEqualToGetInvalidSpecificationMessage() { + LngValidator.LessThanOrEqualTo instance = new LngValidator.LessThanOrEqualTo(5); + assertEquals(ALWAYS_VALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } @Test public void testLessThanOrEqualToIsValid() { @@ -107,6 +155,7 @@ public void testLessThanOrEqualToIsValid() { assertTrue(instance.isValid(4L)); assertTrue(instance.isValid(5L)); assertFalse(instance.isValid(6L)); + assertFalse(instance.isValid(null)); instance = new LngValidator.LessThanOrEqualTo(0); assertTrue(instance.isValid(-1L)); @@ -117,9 +166,12 @@ public void testLessThanOrEqualToIsValid() { assertTrue(instance.isValid(-100L)); assertTrue(instance.isValid(-99L)); assertFalse(instance.isValid(-98L)); - } - - + @Test + public void testLessThanOrEqualToGetTheValueMustDescription() { + final long ref = 5; + LngValidator.LessThanOrEqualTo instance = new LngValidator.LessThanOrEqualTo(ref); + assertEquals("be less than or equal to " + ref, instance.getTheValueMustDescription()); + } } From 1bc3a776c4e455c3cd01d9b4013abb5b54e01f1a Mon Sep 17 00:00:00 2001 From: Shou Xian Date: Sun, 14 Oct 2018 19:55:46 +0800 Subject: [PATCH 02/91] Small changes to TextUtil logic (findFirst and LastInstanceOf) and increasing class coverage of unit test --- .../org/yarnandtail/andhow/util/TextUtil.java | 4 +- .../yarnandtail/andhow/util/TextUtilTest.java | 72 ++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java b/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java index 93ab1d24..058ba6dd 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java @@ -334,7 +334,7 @@ public static List wrap(String in, int length) { public static int findFirstInstanceOf(String toBeSearched, int searchFrom, String... toBeFound) { int result = -1; - if (toBeSearched != null || toBeFound.length > 0) { + if (toBeSearched != null && toBeFound.length > 0) { for (String s : toBeFound) { int i = toBeSearched.indexOf(s, searchFrom); if (i > -1 && (i < result || result == -1)) { @@ -356,7 +356,7 @@ public static int findFirstInstanceOf(String toBeSearched, int searchFrom, Strin public static int findLastInstanceOf(String toBeSearched, int searchFrom, String... toBeFound) { int result = -1; - if (toBeSearched != null || toBeFound.length > 0) { + if (toBeSearched != null && toBeFound.length > 0) { for (String s : toBeFound) { int i = toBeSearched.lastIndexOf(s, searchFrom); if (i > result) { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java index 740efb96..ad05e0d5 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java @@ -70,6 +70,12 @@ public void testPatternsWithCurlyBraceLiteralsPrintln() { TextUtil.println(ps, "abc\\{}xyz", "XXX"); assertEquals("abc\\{}xyz", getStreamContent()); } + + @Test + public void testPatternWithBreak() { + TextUtil.println(ps, 5, "//", "abc{}def {}xyz", "XXX", "YYY"); + assertEquals("//abcXXXdef// YYYxyz", getStreamContent()); + } @Test public void testRepeat() { @@ -189,7 +195,28 @@ public void testWrapWithCompromiseBreakLocations() { assertEquals("i", result.get(1)); } - + + @Test + public void testWrapWithEmptyString() { + List result; + + result = TextUtil.wrap("", 10); + assertEquals(0, result.size()); + } + + @Test + public void testWrapWithNewlineChar() { + List result; + + result = TextUtil.wrap("\nabc", 10); + assertEquals(1, result.size()); + assertEquals("abc", result.get(0)); + + result = TextUtil.wrap("abc \ndefg hijklmnopqrstuvwxyzabcdefgh", 10); + assertEquals(2, result.size()); + assertEquals("abc", result.get(0)); + assertEquals("defg hijklmnopqrstuvwxyzabcdefgh", result.get(1)); + } @Test public void testWrapWithPrefix() { @@ -216,12 +243,35 @@ public void testWrapWithPrefix() { assertEquals("# xxxqrs tuv", result.get(2)); } + + @Test + public void testWrapNullPrefix() { + List result; + + result = TextUtil.wrap("abcdefghij klmnopqrstuvwxyz", 10, null, "xxx"); + assertEquals(2, result.size()); + assertEquals("abcdefghij", result.get(0)); + assertEquals("xxxklmnopqrstuvwxyz", result.get(1)); + } + + @Test + public void testWrapNullLineIndent() { + List result; + + result = TextUtil.wrap("abcdefghij klmnopqrstuvwxyz", 10, "# ", null); + assertEquals(2, result.size()); + assertEquals("# abcdefghij", result.get(0)); + assertEquals("# klmnopqrstuvwxyz", result.get(1)); + } @Test public void testEscapeXml() { assertEquals("<some text>", TextUtil.escapeXml("")); assertEquals("<some&text"", TextUtil.escapeXml(" Date: Mon, 15 Oct 2018 23:04:08 +0800 Subject: [PATCH 03/91] Extra docs to clarify functionality and additional unit tests to achieve better coverage --- .../org/yarnandtail/andhow/util/TextUtil.java | 10 +++- .../yarnandtail/andhow/util/TextUtilTest.java | 47 +++++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java b/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java index 058ba6dd..03e46850 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/util/TextUtil.java @@ -324,7 +324,9 @@ public static List wrap(String in, int length) { /** * Find the first occurrence of one of an array of possible strings in another string, - * starting at the specified position. + * starting at the specified position, looking to the right. If there are more than one + * string arguments to be searched from, it returns the index of the argument which is + * of the lower index * * @param toBeSearched * @param searchFrom Same symantics as String.indexOf(String, int) @@ -347,7 +349,11 @@ public static int findFirstInstanceOf(String toBeSearched, int searchFrom, Strin } /** - * + * Find the first occurrence of one of an array of possible strings in another string, + * starting at the specified position and seeking to the left. If there are more than + * one string arguments to be searched from, it returns the index of the argument which is + * of the higher index + * * @param toBeSearched * @param searchFrom Start looking from this position and to the left * @param toBeFound diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java index ad05e0d5..b5191c1a 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java @@ -15,7 +15,8 @@ public class TextUtilTest { ByteArrayOutputStream baos; PrintStream ps; - + private static String FIND_INSTANCE_STRING_BROWNFOX = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog"; + @Before public void setup() { baos = new ByteArrayOutputStream(); @@ -50,6 +51,7 @@ public void testFormat() { //Some more edge cases assertEquals("abc[[NULL]]xyz", TextUtil.format("abc{}xyz", (String)null)); assertEquals("abc[[NULL]]xyz", TextUtil.format("abc{}xyz", null, null, null)); + assertEquals("abcXXXdef[[NULL]]xyz", TextUtil.format("abc{}def{}xyz", "XXX", null, null)); } @Test(expected = ArrayIndexOutOfBoundsException.class) @@ -285,13 +287,25 @@ public void testNullToEmpty() { } @Test - public void testFindFirstInstanceOfNullToBeSearched() { - assertEquals(-1, TextUtil.findFirstInstanceOf(null, 10, " ", "\t", "-")); + public void testFindFirstInstanceOf() { + assertEquals(31, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 3, "the", "lazy")); + assertEquals(40, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 6, "dog")); + assertEquals(20, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, -1, "jumps", "over")); + assertEquals(0, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 0, "The", "fox")); + assertEquals(65, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 30, "jumps", "over")); + assertEquals(49, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 30, "brown", "quick")); } @Test - public void testFindLastInstanceOfNullToBeSearched() { - assertEquals(-1, TextUtil.findLastInstanceOf(null, 10, " ", "\t", "-")); + public void testFindFirstInstanceOfNotFound() { + assertEquals(-1, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 80, "jumps", "over")); + assertEquals(-1, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 44, "dog.")); + assertEquals(-1, TextUtil.findFirstInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 0, "excuse")); + } + + @Test + public void testFindFirstInstanceOfNullToBeSearched() { + assertEquals(-1, TextUtil.findFirstInstanceOf(null, 10, " ", "\t", "-")); } @Test @@ -299,6 +313,29 @@ public void testFindFirstInstanceOfWithEmptyToBeFound() { assertEquals(-1, TextUtil.findFirstInstanceOf("abcd", 10)); } + @Test + public void testFindLastInstanceOf() { + assertEquals(35, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 50, "the", "lazy")); + assertEquals(85, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, FIND_INSTANCE_STRING_BROWNFOX.length(), "dog")); + assertEquals(16, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 43, "The", "fox")); + assertEquals(26, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 40, "jumps", "over")); + assertEquals(10, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 40, "brown", "quick")); + } + + @Test + public void testFindLastInstanceOfNotFound() { + assertEquals(-1, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, -1, "jumps", "over")); + assertEquals(-1, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 15, "lazy")); + assertEquals(-1, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, 0, "quick")); + assertEquals(-1, TextUtil.findLastInstanceOf(FIND_INSTANCE_STRING_BROWNFOX, FIND_INSTANCE_STRING_BROWNFOX.length(), "notfound")); + } + + + @Test + public void testFindLastInstanceOfNullToBeSearched() { + assertEquals(-1, TextUtil.findLastInstanceOf(null, 10, " ", "\t", "-")); + } + @Test public void testFindLastInstanceOfWithEmptyToBeFound() { assertEquals(-1, TextUtil.findLastInstanceOf("abcd", 10)); From 38d8e0226f3db9a571a86982b92d37f092e2a36a Mon Sep 17 00:00:00 2001 From: jr7square Date: Sun, 14 Oct 2018 23:04:07 -0400 Subject: [PATCH 04/91] Unit tests for AppFatalException class --- .../andhow/api/AppFatalExceptionTest.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java new file mode 100644 index 00000000..f9b9b8d8 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java @@ -0,0 +1,65 @@ +package org.yarnandtail.andhow.api; + +import static org.junit.Assert.*; +import org.junit.Test; + +public class AppFatalExceptionTest { + + @Test + public void testConstructorFromString() { + AppFatalException instance = new AppFatalException("test"); + assertEquals(instance.getMessage(), "test"); + assertNotNull(instance.getProblems()); + } + + @Test + public void testConstructorFromStringAndProblemList() { + ProblemList problems = new ProblemList(); + AppFatalException instance = new AppFatalException("test", problems); + assertEquals(instance.getMessage(), "test"); + assertNotNull(instance.getProblems()); + + problems = null; + instance = new AppFatalException("test", problems); + assertEquals(instance.getMessage(), "test"); + assertNotNull(instance.getProblems()); + } + + @Test + public void testConstructorFromStringAndProblem() { + Problem problem = new TestProblem(); + AppFatalException instance = new AppFatalException("test", problem); + assertEquals(instance.getMessage(), "test"); + assertNotNull(instance.getProblems()); + assertEquals(instance.getProblems().get(0).getFullMessage(), "TEST MESSAGE"); + + problem = null; + instance = new AppFatalException("test", problem); + assertEquals(instance.getMessage(), "test"); + assertNotNull(instance.getProblems()); + } + + @Test + public void testSampleDirectory() { + AppFatalException instance = new AppFatalException("test"); + instance.setSampleDirectory("test/path"); + } + + class TestProblem implements Problem { + + @Override + public String getFullMessage() { + return "TEST MESSAGE"; + } + + @Override + public String getProblemContext() { + return "TEST PROBLEM CONTEXT"; + } + + @Override + public String getProblemDescription() { + return "TEST PROBLEM DESCRIPTION"; + } + } +} \ No newline at end of file From 4692ce3b5dd38d50309b21a5957a9fe50df76e58 Mon Sep 17 00:00:00 2001 From: jr7square Date: Mon, 15 Oct 2018 20:30:02 -0400 Subject: [PATCH 05/91] Added missing asserting --- .../java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java index f9b9b8d8..b9fc105d 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java @@ -43,6 +43,7 @@ public void testConstructorFromStringAndProblem() { public void testSampleDirectory() { AppFatalException instance = new AppFatalException("test"); instance.setSampleDirectory("test/path"); + assertEquals(instance.getSampleDirectory(), "test/path"); } class TestProblem implements Problem { From 6a2a9cb1addaf19360f779a2ed1350295890671c Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Fri, 12 Oct 2018 21:31:19 -0500 Subject: [PATCH 06/91] Added initial detection of reentrant initiation - WIP --- .../java/org/yarnandtail/andhow/AndHow.java | 34 +++++++++++++++++++ .../andhow/AndHowReentrantTest.java | 34 +++++++++++++++++++ .../org/yarnandtail/andhow/AndHowTest.java | 14 ++++++++ .../yarnandtail/andhow/ReentrantSample1.java | 13 +++++++ 4 files changed, 95 insertions(+) create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/ReentrantSample1.java diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java index 9de90cbd..9134d4dc 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java @@ -41,9 +41,17 @@ public class AndHow implements StaticPropertyConfiguration, ValidatedValues { private static final Object LOCK = new Object(); private volatile AndHowCore core; + + /** Stack trace and time of startup */ + private volatile Initialization initialization; + private AndHow(AndHowConfiguration config) throws AppFatalException { synchronized (LOCK) { + + //to late here - needs to be in the instance methods + //initialization = new Initialization(); + core = new AndHowCore( config.getNamingStrategy(), config.buildLoaders(), @@ -219,5 +227,31 @@ private static void throwFatal(String message, Throwable throwable) { throw afe; } } + + + /** + * Encapsilate when and where AndHow was initialized. + * + * Useful for debugging re-entrant startups or uncontrolled startup conditions. + */ + public static class Initialization { + private StackTraceElement[] stackTrace; + private long timeStamp; + + public Initialization() { + timeStamp = System.currentTimeMillis(); + StackTraceElement[] ste = new Exception().getStackTrace(); + stackTrace = Arrays.copyOfRange(ste, 1, ste.length - 1); + } + + public StackTraceElement[] getStackTrace() { + return stackTrace; + } + + public long getTimeStamp() { + return timeStamp; + } + + } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java new file mode 100644 index 00000000..2bd96b20 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java @@ -0,0 +1,34 @@ +package org.yarnandtail.andhow; + + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.yarnandtail.andhow.api.AppFatalException; +import org.yarnandtail.andhow.internal.RequirementProblem; +import org.yarnandtail.andhow.load.KeyValuePairLoader; +import org.yarnandtail.andhow.property.*; + +/** + * + * @author eeverman + */ +public class AndHowReentrantTest extends AndHowCoreTestBase { + + + @Test + public void testAllValuesAreSet() { + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(ReentrantSample1.class); + + AndHow.instance(config); + + assertEquals("one", ReentrantSample1.STR_1.getValue()); + assertEquals("one", ReentrantSample1.STR_2.getValue()); + + fail("This should have blown up"); + } + + +} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java index b8136006..c183a715 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java @@ -62,6 +62,20 @@ public void setup() throws Exception { } + @Test + public void testInitialization() { + Long startTime = System.currentTimeMillis(); + + AndHow.Initialization init = new AndHow.Initialization(); + + Long endTime = System.currentTimeMillis(); + + assertEquals(this.getClass().getName(), init.getStackTrace()[0].getClassName()); + assertEquals("testInitialization", init.getStackTrace()[0].getMethodName()); + assertTrue(startTime <= init.getTimeStamp()); + assertTrue(endTime >= init.getTimeStamp()); + } + @Test public void testIsInitialize() { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/ReentrantSample1.java b/andhow-core/src/test/java/org/yarnandtail/andhow/ReentrantSample1.java new file mode 100644 index 00000000..3ef3c794 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/ReentrantSample1.java @@ -0,0 +1,13 @@ +package org.yarnandtail.andhow; + +import org.yarnandtail.andhow.property.*; + +/** + * Demonstrates an invalid reference to a Property value at construction time. + * @author ericeverman + */ +public class ReentrantSample1 { + static final StrProp STR_1 = StrProp.builder().defaultValue("one").build(); + static final StrProp STR_2 = StrProp.builder().defaultValue(STR_1.getValue()).build(); +} + From 27d7722acf5e8195e17d7d9d160a597cf7a577ef Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Fri, 12 Oct 2018 22:58:28 -0500 Subject: [PATCH 07/91] WIP --- .../java/org/yarnandtail/andhow/AndHow.java | 26 ++++++++++---- .../andhow/api/AppFatalException.java | 15 ++++++-- .../andhow/internal/ConstructionProblem.java | 35 +++++++++++++++++++ .../andhow/AndHowReentrantTest.java | 19 +++++----- 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java index 9134d4dc..e9c8c9bd 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java @@ -4,6 +4,7 @@ import java.util.*; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.AndHowCore; +import org.yarnandtail.andhow.internal.ConstructionProblem; import org.yarnandtail.andhow.util.AndHowUtil; /** @@ -43,15 +44,12 @@ public class AndHow implements StaticPropertyConfiguration, ValidatedValues { private volatile AndHowCore core; /** Stack trace and time of startup */ - private volatile Initialization initialization; + private static volatile Initialization initialization; private AndHow(AndHowConfiguration config) throws AppFatalException { synchronized (LOCK) { - //to late here - needs to be in the instance methods - //initialization = new Initialization(); - core = new AndHowCore( config.getNamingStrategy(), config.buildLoaders(), @@ -125,8 +123,19 @@ public static AndHow instance(AndHowConfiguration config) throws AppFatalExcepti } else { if (singleInstance == null) { - - singleInstance = new AndHow(config); + + Initialization newInit = new Initialization(); //record when & where of init + + if (initialization == null) { + + initialization = newInit; + singleInstance = new AndHow(config); + + } else { + throw new AppFatalException( + new ConstructionProblem.InitiationLoopException(initialization, newInit)); + } + } else if (singleInstance.core == null) { @@ -134,8 +143,11 @@ public static AndHow instance(AndHowConfiguration config) throws AppFatalExcepti core is deleted to force AndHow to reload. Its really an invalid state (instance and core should be null/non-null together, but its handled here to simplify testing. */ + + initialization = new Initialization(); + try { - + AndHowCore newCore = new AndHowCore( config.getNamingStrategy(), config.buildLoaders(), diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/api/AppFatalException.java b/andhow-core/src/main/java/org/yarnandtail/andhow/api/AppFatalException.java index 9e1eb255..5e64c1d9 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/api/AppFatalException.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/api/AppFatalException.java @@ -24,7 +24,7 @@ public class AppFatalException extends RuntimeException { */ public AppFatalException(String message, Throwable throwable) { super(message, throwable); - this.problems = new ProblemList(); + this.problems = ProblemList.EMPTY_PROBLEM_LIST; } /** @@ -34,7 +34,7 @@ public AppFatalException(String message, Throwable throwable) { */ public AppFatalException(String message) { super(message); - this.problems = new ProblemList(); + this.problems = ProblemList.EMPTY_PROBLEM_LIST; } public AppFatalException(String message, ProblemList problems) { @@ -58,6 +58,17 @@ public AppFatalException(String message, Problem problem) { } } + public AppFatalException(Problem problem) { + super(problem != null?problem.getFullMessage():"Unknown AndHow fatal exception"); + + if (problem != null) { + this.problems = new ProblemList(); + this.problems.add(problem); + } else { + this.problems = ProblemList.EMPTY_PROBLEM_LIST; + } + } + public ProblemList getProblems() { return problems; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/internal/ConstructionProblem.java b/andhow-core/src/main/java/org/yarnandtail/andhow/internal/ConstructionProblem.java index f3ce61f4..b0256988 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/internal/ConstructionProblem.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/internal/ConstructionProblem.java @@ -319,4 +319,39 @@ public String getProblemDescription() { AndHowInit.class.getCanonicalName(), joined); } } + + public static class InitiationLoopException extends ConstructionProblem { + AndHow.Initialization originalInit; + AndHow.Initialization secondInit; + + public InitiationLoopException(AndHow.Initialization originalInit, AndHow.Initialization secondInit) { + this.originalInit = originalInit; + this.secondInit = secondInit; + } + + public AndHow.Initialization getOriginalInit() { + return originalInit; + } + + public AndHow.Initialization getSecondInit() { + return secondInit; + } + + + @Override + public String getProblemDescription() { + + return "AndHow detected a loop during initiation. " + + "Likely causes are: " + System.lineSeparator() + + "- Multiple places in application code where AndHow.instance(AndHowConfiguration) is called" + System.lineSeparator() + + "- static initiation blocks or static variables that are initiated refering to the value of an AndHow Property" + System.lineSeparator() + + "- AndHow Properties that refer to the value of other AndHow properties in their construction" + System.lineSeparator() + + "::The first line in the stack trace following this error referring to your application code is likely causing the initiation loop::"; + } + + @Override + public String getFullMessage() { + return getProblemDescription(); + } + } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java index 2bd96b20..8b06dcbf 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java @@ -3,12 +3,9 @@ import static org.junit.Assert.*; -import org.junit.Before; import org.junit.Test; import org.yarnandtail.andhow.api.AppFatalException; -import org.yarnandtail.andhow.internal.RequirementProblem; -import org.yarnandtail.andhow.load.KeyValuePairLoader; -import org.yarnandtail.andhow.property.*; +import org.yarnandtail.andhow.internal.ConstructionProblem; /** * @@ -22,12 +19,18 @@ public void testAllValuesAreSet() { AndHowConfiguration config = AndHowCoreTestConfig.instance() .group(ReentrantSample1.class); - AndHow.instance(config); + try { + AndHow.instance(config); + fail("This should have blown up"); + } catch (Throwable t) { + assertTrue(t.getCause() instanceof AppFatalException); + AppFatalException afe = (AppFatalException)t.getCause(); + assertEquals(1, afe.getProblems().size()); + assertTrue(afe.getProblems().get(0) instanceof ConstructionProblem.InitiationLoopException); + } + - assertEquals("one", ReentrantSample1.STR_1.getValue()); - assertEquals("one", ReentrantSample1.STR_2.getValue()); - fail("This should have blown up"); } From 2466f9124f1220be85a1ce08b4b656aad1e969d0 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Mon, 15 Oct 2018 22:32:36 -0500 Subject: [PATCH 08/91] * Added a initializing flag to block reentrant initialization * Added a stacktrace of the initiation to help w/ debugging, as well as a method to fetch it * Added testing for it --- .../java/org/yarnandtail/andhow/AndHow.java | 91 ++++++++++++++----- .../andhow/AndHowCoreTestUtil.java | 1 - .../andhow/AndHowReentrantTest.java | 40 +++++++- ...a => AndHowReentrantTest_BadSample_1.java} | 2 +- .../AndHowReentrantTest_BadSample_2.java | 18 ++++ .../AndHowReentrantTest_OkSample_1.java | 17 ++++ .../org/yarnandtail/andhow/AndHowTest.java | 1 + 7 files changed, 143 insertions(+), 27 deletions(-) rename andhow-core/src/test/java/org/yarnandtail/andhow/{ReentrantSample1.java => AndHowReentrantTest_BadSample_1.java} (88%) create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_BadSample_2.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_OkSample_1.java diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java index e9c8c9bd..ed1390cb 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHow.java @@ -2,6 +2,7 @@ import java.lang.reflect.Field; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.AndHowCore; import org.yarnandtail.andhow.internal.ConstructionProblem; @@ -46,6 +47,11 @@ public class AndHow implements StaticPropertyConfiguration, ValidatedValues { /** Stack trace and time of startup */ private static volatile Initialization initialization; + /** + * True only during the instance(AndHowConfiguration) method to detect + * re-entrant initialization + */ + private static AtomicBoolean initializing = new AtomicBoolean(false); private AndHow(AndHowConfiguration config) throws AppFatalException { synchronized (LOCK) { @@ -124,19 +130,24 @@ public static AndHow instance(AndHowConfiguration config) throws AppFatalExcepti if (singleInstance == null) { - Initialization newInit = new Initialization(); //record when & where of init - - if (initialization == null) { + if (! initializing.get()) { - initialization = newInit; - singleInstance = new AndHow(config); + try { + + initializing.getAndSet(true); //Block re-entrant initialization + initialization = new Initialization(); //Record initialization time & place + singleInstance = new AndHow(config); //Build new instance + + } finally { + initializing.getAndSet(false); //Done w/ init regardless of possible error + } } else { + throw new AppFatalException( - new ConstructionProblem.InitiationLoopException(initialization, newInit)); + new ConstructionProblem.InitiationLoopException(initialization, new Initialization())); } - } else if (singleInstance.core == null) { /* This is a concession for testing. During testing the @@ -144,27 +155,41 @@ public static AndHow instance(AndHowConfiguration config) throws AppFatalExcepti invalid state (instance and core should be null/non-null together, but its handled here to simplify testing. */ - initialization = new Initialization(); - - try { + if (! initializing.get()) { - AndHowCore newCore = new AndHowCore( - config.getNamingStrategy(), - config.buildLoaders(), - config.getRegisteredGroups()); - Field coreField = AndHow.class.getDeclaredField("core"); - coreField.setAccessible(true); - coreField.set(singleInstance, newCore); + try { + + initializing.getAndSet(true); //Block re-entrant initialization + initialization = new Initialization(); //Record initialization time & place + + AndHowCore newCore = new AndHowCore( + config.getNamingStrategy(), + config.buildLoaders(), + config.getRegisteredGroups()); + Field coreField = AndHow.class.getDeclaredField("core"); + coreField.setAccessible(true); + coreField.set(singleInstance, newCore); - } catch (Exception ex) { - if (ex instanceof AppFatalException) { - throw (AppFatalException) ex; - } else { - throwFatal("", ex); + } catch (Exception ex) { + + if (ex instanceof AppFatalException) { + throw (AppFatalException) ex; + } else { + throwFatal("", ex); + } + } finally { + initializing.getAndSet(false); //Done w/ init regardless of possible error } + + } else { + + throw new AppFatalException( + new ConstructionProblem.InitiationLoopException(initialization, new Initialization())); + } } + return singleInstance; } @@ -180,6 +205,28 @@ invalid state (instance and core should be null/non-null public static boolean isInitialize() { return singleInstance != null && singleInstance.core != null; } + + /** + * Get the stacktrace of where AndHow was initialized. + * + * This can be useful for debugging InitiationLoopException errors or + * errors caused by trying to initialize AndHow when it is already initialized. + * This stacktrace identifies the point in code that caused the initial + * AndHow initialization, prior to the error. The reported exception will + * point to the place where AndHow entered a loop during its construction + * or application code attempted to re-initialize AndHow. + * + * @return A stacktrace if it is available (some JVMs may not provide one) + * or an empty stacktrace array if it is not available or AndHow is not + * yet initialized. + */ + public static StackTraceElement[] getInitializationTrace() { + if (initialization != null) { + return initialization.getStackTrace(); + } else { + return new StackTraceElement[0]; + } + } // //PropertyValues Interface diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestUtil.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestUtil.java index 19332d95..9ff913d6 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestUtil.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestUtil.java @@ -105,7 +105,6 @@ public static AndHow setAndHowInstance(AndHow newInstance) { } - public static void forceRebuild(AndHowConfiguration config) { AndHow ahInstance = getAndHowInstance(); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java index 8b06dcbf..aa2bb33e 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java @@ -2,22 +2,28 @@ import static org.junit.Assert.*; +import org.junit.FixMethodOrder; import org.junit.Test; +import org.junit.runners.MethodSorters; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.internal.ConstructionProblem; /** * + * Note: The order of the tests is important and is sorted by method name. + * The key reason for this is to test that an InitiationLoopException does not + * bleed over from one test to another. * @author eeverman */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class AndHowReentrantTest extends AndHowCoreTestBase { @Test - public void testAllValuesAreSet() { + public void test_1_AndHowReentrantTest_BadSample_1() { AndHowConfiguration config = AndHowCoreTestConfig.instance() - .group(ReentrantSample1.class); + .group(AndHowReentrantTest_BadSample_1.class); try { AndHow.instance(config); @@ -28,10 +34,38 @@ public void testAllValuesAreSet() { assertEquals(1, afe.getProblems().size()); assertTrue(afe.getProblems().get(0) instanceof ConstructionProblem.InitiationLoopException); } - + } + + @Test + public void test_2_AndHowReentrantTest_OkSample_1() { + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(AndHowReentrantTest_OkSample_1.class); + + AndHow.instance(config); + + assertEquals("onetwo", AndHowReentrantTest_OkSample_1.getSomeString()); + assertEquals("one", AndHowReentrantTest_OkSample_1.STR_1.getValue()); + assertEquals("two", AndHowReentrantTest_OkSample_1.STR_2.getValue()); + + } + + @Test + public void test_3_AndHowReentrantTest_BadSample_2() { + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(AndHowReentrantTest_BadSample_2.class); + try { + AndHow.instance(config); + fail("This should have blown up"); + } catch (Throwable t) { + assertTrue(t.getCause() instanceof AppFatalException); + AppFatalException afe = (AppFatalException)t.getCause(); + assertEquals(1, afe.getProblems().size()); + assertTrue(afe.getProblems().get(0) instanceof ConstructionProblem.InitiationLoopException); + } } + } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/ReentrantSample1.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_BadSample_1.java similarity index 88% rename from andhow-core/src/test/java/org/yarnandtail/andhow/ReentrantSample1.java rename to andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_BadSample_1.java index 3ef3c794..8f80a5cd 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/ReentrantSample1.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_BadSample_1.java @@ -6,7 +6,7 @@ * Demonstrates an invalid reference to a Property value at construction time. * @author ericeverman */ -public class ReentrantSample1 { +public class AndHowReentrantTest_BadSample_1 { static final StrProp STR_1 = StrProp.builder().defaultValue("one").build(); static final StrProp STR_2 = StrProp.builder().defaultValue(STR_1.getValue()).build(); } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_BadSample_2.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_BadSample_2.java new file mode 100644 index 00000000..6b3f174c --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_BadSample_2.java @@ -0,0 +1,18 @@ +package org.yarnandtail.andhow; + +import org.yarnandtail.andhow.property.*; + +/** + * Demonstrates an invalid reference to a Property value at construction time. + * @author ericeverman + */ +public class AndHowReentrantTest_BadSample_2 { + static final StrProp STR_1 = StrProp.builder().defaultValue("one").build(); + static final StrProp STR_2 = StrProp.builder().defaultValue("two").build(); + static final String SOME_STRING; + + static { + SOME_STRING = STR_1.getValue() + STR_2.getValue(); //This should be ok + } +} + diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_OkSample_1.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_OkSample_1.java new file mode 100644 index 00000000..2f41854b --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest_OkSample_1.java @@ -0,0 +1,17 @@ +package org.yarnandtail.andhow; + +import org.yarnandtail.andhow.property.*; + +/** + * This should be just fine + * @author ericeverman + */ +public class AndHowReentrantTest_OkSample_1 { + static final StrProp STR_1 = StrProp.builder().defaultValue("one").build(); + static final StrProp STR_2 = StrProp.builder().defaultValue("two").build(); + + static String getSomeString() { + return STR_1.getValue() + STR_2.getValue(); //This should be ok + } +} + diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java index c183a715..09c564bf 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java @@ -98,6 +98,7 @@ public void testCmdLineLoaderUsingClassBaseName() { AndHow.instance(config); + assertTrue(AndHow.getInitializationTrace().length > 0); assertEquals("test", SimpleParams.STR_BOB.getValue()); assertEquals("not_null", SimpleParams.STR_NULL.getValue()); assertEquals(false, SimpleParams.FLAG_TRUE.getValue()); From 02fc9963ce46bfd3d1ea06ec995899b63feb6f3a Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Mon, 15 Oct 2018 23:04:35 -0500 Subject: [PATCH 09/91] Added a test for a new constructor --- .../andhow/api/AppFatalExceptionTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java index b9fc105d..5afef967 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java @@ -38,6 +38,21 @@ public void testConstructorFromStringAndProblem() { assertEquals(instance.getMessage(), "test"); assertNotNull(instance.getProblems()); } + + @Test + public void testConstructorFromProblem() { + Problem problem = new TestProblem(); + AppFatalException instance = new AppFatalException(problem); + assertEquals(instance.getMessage(), problem.getFullMessage()); + assertNotNull(instance.getProblems()); + assertEquals(instance.getProblems().get(0), problem); + + problem = null; + instance = new AppFatalException(problem); + assertEquals(instance.getMessage(), "Unknown AndHow fatal exception"); + assertNotNull(instance.getProblems()); + assertEquals(0, instance.getProblems().size()); + } @Test public void testSampleDirectory() { From 56e8d54fc8760b663b3edad95af886bb0cef2abe Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Tue, 16 Oct 2018 23:34:53 -0500 Subject: [PATCH 10/91] Updated readme file w/ better explanations and added main(String[] args) --- README.md | 90 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 924c0b05..e3445402 100644 --- a/README.md +++ b/README.md @@ -47,30 +47,31 @@ package org.simple; import org.yarnandtail.andhow.property.*; public class GettingStarted { - + //1 - final static IntProp COUNT_DOWN_START = IntProp.builder().mustBeNonNull() - .desc("Start the countdown from this number") - .mustBeGreaterThanOrEqualTo(1).defaultValue(2).build(); - + public final static IntProp COUNT_DOWN_START = IntProp.builder().mustBeNonNull() + .mustBeGreaterThanOrEqualTo(1).defaultValue(3).build(); + private final static StrProp LAUNCH_CMD = StrProp.builder().mustBeNonNull() - .desc("What to say when its time to launch") - .mustMatchRegex(".*Go.*").defaultValue("GoGoGo!").build(); - + .desc("What to say when its time to launch") + .mustMatchRegex(".*Go.*").defaultValue("Go-Go-Go!").build(); + public String launch() { String launch = ""; - + //2 for (int i = COUNT_DOWN_START.getValue(); i >= 1; i--) { launch = launch += i + "..."; } - + return launch + LAUNCH_CMD.getValue(); } - + public static void main(String[] args) { - GettingStarted gs = new GettingStarted(); - System.out.println(gs.launch()); + AndHow.findConfig().setCmdLineArgs(args); //3 Optional + + System.out.println( new GettingStarted().launch() ); + System.out.println( LAUNCH_CMD.getValue().replace("Go", "Gone") ); } } ``` @@ -78,42 +79,65 @@ public class GettingStarted { Properties must be `final static`, but may be `private` or any other scope. `builder` methods simplify adding validation, description, defaults and other metadata. -Properties are strongly typed, so default values and validation are specific to -the type, for instance, `StrProp` has regex validation rules for `String`s -while the `IntProp` has greater-than and less-than rules available. +Properties are strongly typed, so default values and validation are type specific, e.g., +`StrProp` has Regex validation while the `IntProp` has GreaterThan / LessThan rules available. ### Section //2 : Using AndHow Properties AndHow Properties are used just like static final constants with an added -`.getValue()` on the end to fetch the value. -Strong typing means that calling `COUNT_DOWN_START.getValue()` +`.getValue()` tacked on. Strong typing means that calling `COUNT_DOWN_START.getValue()` returns an `Integer` while calling `LAUNCH_CMD.getValue()` returns a `String`. +An AndHow Property (and its value) can be accessed anywhere it is visible. +`COUNT_DOWN_START` is public in a public class, so it could be used anywhere, while +`LAUNCH_CMD` is private. +AndHow Properties are always `static`, so they can be accessed in both static +and instance methods, just like this example shows. + +### Section //3 : Accepting Command Line Arguments +If an application needs command line arguments (CLAs), just pass them to AndHow +at startup as this example shows. Properties are referred to using 'dot notation', e.g.: +``` +java -jar GettingStarted.jar org.simple.GettingStarted.LAUNCH_CMD=GoManGo +``` +If you don't need to accept CLA's, you can leave line `//3` out - +AndHow will initialize and startup without any explicit _init_ method when +the first Property is accessed. + ### How do I actually configure some values? We're getting there. The example has defaults for each property so with no other configuration available, -the main method uses the defaults and prints: `3...2...1...GoGoGo!` -Things are more interesting if the default values are removed from the code above. +the main method uses the defaults and prints: +``` +3...2...1...Go-Go-Go! +Gone-Gone-Gone! +``` +Things are more interesting if the default values are removed from the code above: +```java +public final static IntProp COUNT_DOWN_START = IntProp.builder().mustBeNonNull() + .mustBeGreaterThanOrEqualTo(1).build(); //default removed + +private final static StrProp LAUNCH_CMD = StrProp.builder().mustBeNonNull() + .mustMatchRegex(".*Go.*").build(); //default removed +``` Both properties must be non-null, so removing the defaults causes the validation -rules to be violated at startup. Here is an excerpt of the console output when that happens: +rules to be violated at startup. Here is an excerpt from the console when that happens: ``` ======================================================================== Drat! There were AndHow startup errors. Sample configuration files will be written to: '/some_local_tmp_directory/andhow-samples/' ======================================================================== -REQUIRMENT PROBLEMS - When a required property is not provided -Detailed list of Requirements Problems: -Property org.simple.GettingStarted.COUNT_DOWN_START: This Property must be non-null - It must have a non-null default or be loaded by one of the loaders to a non-null value -Property org.simple.GettingStarted.LAUNCH_CMD: This Property must be non-null - It must have a non-null default or be loaded by one of the loaders to a non-null value +Property org.simple.GettingStarted.COUNT_DOWN_START: This Property must be non-null +Property org.simple.GettingStarted.LAUNCH_CMD: This Property must be non-null ======================================================================== ``` -**Validation happens at startup and happens for all properties in the entire application.** -Properties, even those defined in 3rd party jars, are discovered and values for +**AndHow does validation at startup for all properties in the entire application.** +Properties, _even those defined in 3rd party jars_, are discovered and values for them are loaded and validated. -If that fails (as it did above), AndHow throws a RuntimeException to stop +If validation fails (as it did above), AndHow throws a RuntimeException to stop application startup and uses property metadata to generate specific error messages and (helpfully) sample configuration files. -Here is an excerpt of the Java Properties file created when the example code failed validation: +Here is an excerpt of the Java Properties file created when the code above failed validation: ``` # ###################################################################### # Property Group org.simple.GettingStarted @@ -129,15 +153,15 @@ org.simple.GettingStarted.LAUNCH_CMD = [String] AndHow uses all of the provided metadata to create a detailed and well commented configuration file for your project. Insert some real values into that file and place it on your classpath at -/andhow.properties and it will automatically be discovered and loaded at startup. -By default, AndHow automatically discovers and loads configuration from seven common sources. +`/andhow.properties` and it will automatically be discovered and loaded at startup. +By default, AndHow discovers and loads configuration from seven common sources. The default list of configuration loading, in order, is: 1. Fixed values (explicitly set in code for AndHow to use) 2. String[] arguments from the static void main() method -3. System Properties +3. System Properties _(Like the one auto-generated above)_ 4. Environmental Variables 5. JNDI -6. Java properties file on the filesystem (path specified as an AndHow property) +6. Java properties file on the filesystem (path must be specified) 7. Java properties file on the classpath (defaults to /andhow.properties) Property values are set on a first-win basis, so if a property is set as fixed value, From 50dbdceb7259e6c7bb3d01921c8c5479bb399357 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Tue, 16 Oct 2018 23:35:48 -0500 Subject: [PATCH 11/91] Added forgotten import --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e3445402..7508deb9 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ are available at the [AndHow main site](https://sites.google.com/view/andhow)**_ ```java package org.simple; +import org.yarnandtail.andhow.AndHow; import org.yarnandtail.andhow.property.*; public class GettingStarted { From f64409bb937802ed6d9364defd30ae059fdc2b3f Mon Sep 17 00:00:00 2001 From: Yann IRRILO <15703478+yirrilo@users.noreply.github.com> Date: Sat, 20 Oct 2018 07:07:12 +0200 Subject: [PATCH 12/91] Add some javadoc to IOUtil.java to fix issue andhow#416 --- .../org/yarnandtail/andhow/util/IOUtil.java | 76 ++++++++++++++----- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java b/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java index 04f31bfb..258d6903 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java @@ -4,37 +4,60 @@ import java.nio.charset.Charset; /** - * + * Utility class to handle some IO purposes. * @author ericeverman */ public class IOUtil { + private static final String JAVA_TMP_KEY = "java.io.tmpdir"; - private static final String USER_DIR_KEY = "user.home"; - + + /** + * All utility methods are static. + */ private IOUtil() { //no instances } - + + /** + * Retrieve a String from an UTF-8 resource using the path of that resource. + * @param path Path of the UTF-8 resource. + * @return the String saved into the stream. + * @throws IOException when the resource cannot be loaded. + */ public static String getUTF8ResourceAsString(String path) throws IOException { InputStream in = IOUtil.class.getResourceAsStream(path); - + if (in == null) { throw new IOException("Unable to find the resource '" + path + "'"); } - + return toString(in, Charset.forName("UTF-8")); } - + + /** + * Retrieve a String from a resource using the path of that resource. + * @param path Path of the resource. + * @param encoding Charset used to store the data of the resource. + * @return the String saved into the stream. + * @throws IOException when the resource cannot be loaded. + */ public static String getResourceAsString(String path, Charset encoding) throws IOException { InputStream in = IOUtil.class.getResourceAsStream(path); - + if (in == null) { throw new IOException("Unable to find the resource '" + path + "'"); } - + return toString(in, encoding); } - + + /** + * Build a String from the content on an InputStream using the encoding provided. + * @param input Stream containing the data to extract. + * @param encoding Charset used to read the input. + * @return String representing the content of the input. + * @throws IOException when the resource cannot be read. + */ public static String toString(InputStream input, Charset encoding) throws IOException { StringBuilder builder = new StringBuilder(); @@ -48,43 +71,54 @@ public static String toString(InputStream input, Charset encoding) throws IOExce if (builder.length() > 0) builder.setLength(builder.length() - System.lineSeparator().length()); - + return builder.toString(); } - + + /** + * Expands directory path, replacing known values like java.io.tmpdir + * w/ their values. Paths are assumed to use forward slashes, which are replaced + * on Windows systems to be backslashes. + * + * As the provided path has to be a directory, the returned value will end with + * a file separator character. + * + * @param path directory path to expand if needed. + * @return the expanded directory path. + */ public static String expandDirectoryPath(String path) { path = expandFilePath(path); - + if (! path.endsWith(String.valueOf(File.separatorChar))) { return path + File.separatorChar; } else { return path; } } - + /** * Expands a file or directory path, replacing known values like java.io.tmpdir * w/ their values. Paths are assumed to use forward slashes, which are replaced * on Windows systems to be backslashes. - * + * * The returned path may be either a file path (not ending with a separator) * or directory (ending with a separator). - * - * @param path - * @return + * + * @param path path to expand if needed. + * @return the expanded path. */ public static String expandFilePath(String path) { - + String tmpDir = System.getProperty(JAVA_TMP_KEY); path = path.trim(); - + //rm the trailing separatorChar from the temp directory if there is //possibly more to the path than just the temp directory. Otherwise the //sep char gets doubled up. if (path.length() > JAVA_TMP_KEY.length() && tmpDir.endsWith(System.getProperty("file.separator"))) { tmpDir = tmpDir.substring(0, tmpDir.length() - 1); } - + path = path.replace((CharSequence)JAVA_TMP_KEY, (CharSequence)tmpDir); path = path.replace('/', System.getProperty("file.separator").charAt(0)); return path; From 7e6d38278f0cf8a8adef55be93992fa7972e635d Mon Sep 17 00:00:00 2001 From: Yann IRRILO <15703478+yirrilo@users.noreply.github.com> Date: Sat, 20 Oct 2018 21:14:00 +0200 Subject: [PATCH 13/91] Modification to manage code review comments on the javadoc --- .../java/org/yarnandtail/andhow/util/IOUtil.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java b/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java index 258d6903..e1523db6 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java @@ -19,9 +19,9 @@ private IOUtil() { } /** - * Retrieve a String from an UTF-8 resource using the path of that resource. - * @param path Path of the UTF-8 resource. - * @return the String saved into the stream. + * Retrieve a String from an UTF-8 encoded classpath resource. + * @param path location on the classpath. + * @return a string reprensenting the entire content of the resource. * @throws IOException when the resource cannot be loaded. */ public static String getUTF8ResourceAsString(String path) throws IOException { @@ -35,10 +35,9 @@ public static String getUTF8ResourceAsString(String path) throws IOException { } /** - * Retrieve a String from a resource using the path of that resource. - * @param path Path of the resource. - * @param encoding Charset used to store the data of the resource. - * @return the String saved into the stream. + * Retrieve a String from a classpath resource. + * @param path location on the classpath. + * @return a string reprensenting the entire content of the resource. * @throws IOException when the resource cannot be loaded. */ public static String getResourceAsString(String path, Charset encoding) throws IOException { @@ -67,6 +66,7 @@ public static String toString(InputStream input, Charset encoding) throws IOExce builder.append(line).append(System.lineSeparator()); line = reader.readLine(); } + * @param encoding Charset used to store the data of the resource. } if (builder.length() > 0) From 5a68e4efdb13c8183aac3876f85971547937546d Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Sat, 20 Oct 2018 21:47:06 -0500 Subject: [PATCH 14/91] RM extra javadoc line that ended up in the code. --- .../src/main/java/org/yarnandtail/andhow/util/IOUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java b/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java index e1523db6..d4fe3b9f 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/util/IOUtil.java @@ -66,7 +66,6 @@ public static String toString(InputStream input, Charset encoding) throws IOExce builder.append(line).append(System.lineSeparator()); line = reader.readLine(); } - * @param encoding Charset used to store the data of the resource. } if (builder.length() > 0) From c8ca865e18c5fd124c3b25180f26ef3cb29adfa0 Mon Sep 17 00:00:00 2001 From: Junior Recinos Date: Sun, 21 Oct 2018 21:41:30 -0400 Subject: [PATCH 15/91] Construction problem unit tests (#415) * Unit tests for AppFatalException class * Added missing asserting * update unit tests from upstream * Inital testing * better unit tests for classes that user Group and Property constructors --- andhow-core/pom.xml | 5 +- .../internal/ConstructionProblemTest.java | 193 ++++++++++++++++++ pom.xml | 6 + 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index 74647c66..daeeaf4e 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -27,7 +27,10 @@ commons-io commons-io + + + org.mockito + mockito-all - diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java new file mode 100644 index 00000000..569c5113 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java @@ -0,0 +1,193 @@ +package org.yarnandtail.andhow.internal; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.Test; +import org.yarnandtail.andhow.api.*; +import org.yarnandtail.andhow.AndHowInit; +import java.util.ArrayList; +import java.util.List; +import org.yarnandtail.andhow.AndHow; + + + +public class ConstructionProblemTest { + + @Test + public void testNoUniqueNames() { + GroupProxy refGroup = mock(GroupProxy.class); + Class someUserClass = Object.class; + when(refGroup.getProxiedGroup()).thenReturn(someUserClass); + Property refProperty = mock(Property.class); + GroupProxy badGroup = mock(GroupProxy.class); + when(badGroup.getProxiedGroup()).thenReturn(someUserClass); + Property badProperty = mock(Property.class); + String conflictName = "conflict name test"; + ConstructionProblem.NonUniqueNames instance = new ConstructionProblem. + NonUniqueNames(refGroup, refProperty, badGroup, badProperty, conflictName); + + assertNotNull(instance.getProblemDescription()); + assertEquals(instance.getConflictName(), conflictName); + assertEquals(instance.getRefPropertyCoord().getGroup(), someUserClass); + assertEquals(instance.getBadPropertyCoord().getGroup(), someUserClass); + assertEquals(instance.getRefPropertyCoord().getProperty(), refProperty); + assertEquals(instance.getBadPropertyCoord().getProperty(), badProperty); + } + + @Test + public void testDuplicateProperty() { + Class someUserClass = Object.class; + GroupProxy refGroup = mock(GroupProxy.class); + when(refGroup.getProxiedGroup()).thenReturn(someUserClass); + Property refProperty = mock(Property.class); + GroupProxy badGroup = mock(GroupProxy.class); + when(badGroup.getProxiedGroup()).thenReturn(someUserClass); + Property badProperty = mock(Property.class); + ConstructionProblem.DuplicateProperty instance = new ConstructionProblem. + DuplicateProperty(refGroup, refProperty, badGroup, badProperty); + + assertNotNull(instance.getBadPropertyCoord()); + assertNotNull(instance.getRefPropertyCoord()); + assertNotNull(instance.getProblemDescription()); + assertEquals(instance.getRefPropertyCoord().getGroup(), someUserClass); + assertEquals(instance.getBadPropertyCoord().getGroup(), someUserClass); + assertEquals(instance.getRefPropertyCoord().getProperty(), refProperty); + assertEquals(instance.getBadPropertyCoord().getProperty(), badProperty); + } + + @Test + public void testDuplicateLoader() { + Loader loader = mock(Loader.class); + ConstructionProblem.DuplicateLoader instance = new ConstructionProblem.DuplicateLoader(loader); + + assertNotNull(instance.getLoader()); + assertNotNull(instance.getProblemContext()); + assertNotNull(instance.getProblemDescription()); + } + + @Test + public void testLoaderPropertyIsNull() { + Loader loader = mock(Loader.class); + ConstructionProblem.LoaderPropertyIsNull instance = new ConstructionProblem.LoaderPropertyIsNull(loader); + + assertNotNull(instance.getLoader()); + assertNotNull(instance.getProblemContext()); + assertNotNull(instance.getProblemContext()); + } + + @Test + public void testLoaderPropertyNotRegistered() { + Loader loader = mock(Loader.class); + Property property = mock(Property.class); + ConstructionProblem.LoaderPropertyNotRegistered instance = new ConstructionProblem. + LoaderPropertyNotRegistered(loader, property); + + assertNotNull(instance.getLoader()); + assertNotNull(instance.getProperty()); + assertNotNull(instance.getProblemDescription()); + assertEquals(instance.getProperty(), property); + } + + @Test + public void testSecurityException() { + String group = "test class"; + Exception exception = new Exception("test"); + ConstructionProblem.SecurityException instance = new ConstructionProblem. + SecurityException(exception, group.getClass()); + + assertNotNull(instance.getException()); + assertNotNull(instance.getProblemContext()); + assertNotNull(instance.getProblemDescription()); + } + + @Test + public void testPropertyNotPartOfGroup() { + GroupProxy group = mock(GroupProxy.class); + Class testClass = Object.class; + when(group.getProxiedGroup()).thenReturn(testClass); + Property prop = mock(Property.class); + + ConstructionProblem.PropertyNotPartOfGroup instance = new ConstructionProblem. + PropertyNotPartOfGroup(group, prop); + + assertNotNull(instance.getProblemDescription()); + assertEquals(instance.getBadPropertyCoord().getGroup(), testClass); + assertEquals(instance.getBadPropertyCoord().getProperty(), prop); + } + + @Test + public void testExportException() { + Exception exception = new Exception("test"); + GroupProxy group = mock(GroupProxy.class); + Class testClass = Object.class; + when(group.getProxiedGroup()).thenReturn(testClass); + String message = "test message"; + ConstructionProblem.ExportException instance = new ConstructionProblem. + ExportException(exception, group, message); + + assertEquals(instance.getException().getMessage(), "test"); + assertNotNull(instance.getProblemDescription()); + assertEquals(instance.getBadPropertyCoord().getGroup(), testClass); + } + + @Test + public void testInvalidDefaultValue() { + GroupProxy group = mock(GroupProxy.class); + Class testClass = Object.class; + when(group.getProxiedGroup()).thenReturn(testClass); + Property prop = mock(Property.class); + String invalidMessage = "test invalid message"; + ConstructionProblem.InvalidDefaultValue instance = new ConstructionProblem. + InvalidDefaultValue(group, prop, invalidMessage); + + assertEquals(instance.getInvalidMessage(), invalidMessage); + assertNotNull(instance.getProblemDescription()); + assertEquals(instance.getBadPropertyCoord().getGroup(), testClass); + assertEquals(instance.getBadPropertyCoord().getProperty(), prop); + } + + @Test + public void testInvalidValidationConfiguration() { + GroupProxy group = mock(GroupProxy.class); + Class testClass = Object.class; + when(group.getProxiedGroup()).thenReturn(testClass); + Property property = mock(Property.class); + Validator valid = mock(Validator.class); + + ConstructionProblem.InvalidValidationConfiguration instance = new ConstructionProblem. + InvalidValidationConfiguration(group, property, valid); + + assertEquals(instance.getValidator(), valid); + assertNotNull(instance.getProblemDescription()); + assertEquals(instance.getBadPropertyCoord().getGroup(), testClass); + assertEquals(instance.getBadPropertyCoord().getProperty(), property); + } + + @Test + public void testTooManyAndHowInitInstances() { + AndHowInit item = mock(AndHowInit.class); + List instances = new ArrayList(); + instances.add(item); + + ConstructionProblem.TooManyAndHowInitInstances instance = new ConstructionProblem. + TooManyAndHowInitInstances(instances); + + assertNotNull(instance.getInstanceNames()); + assertNotNull(instance.getProblemDescription()); + } + + @Test + public void testInitiationLoop() { + AndHow.Initialization originalInit = mock(AndHow.Initialization.class); + AndHow.Initialization secondInit = mock(AndHow.Initialization.class); + + ConstructionProblem.InitiationLoopException instance = new ConstructionProblem. + InitiationLoopException(originalInit, secondInit); + + assertNotNull(instance.getOriginalInit()); + assertNotNull(instance.getSecondInit()); + assertNotNull(instance.getProblemDescription()); + assertNotNull(instance.getFullMessage()); + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index b53c81e0..747e25a7 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,12 @@ 0.15 test + + org.mockito + mockito-all + 1.10.19 + test + From 29f0d84598102c62e9640ed5fd716e76446fd21a Mon Sep 17 00:00:00 2001 From: Ariel Rodriguez Date: Tue, 23 Oct 2018 01:47:44 -0300 Subject: [PATCH 16/91] Fixes #437 (#440) --- .travis.yml | 3 +++ appveyor.yml | 3 +++ codecov.yml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5bd06656..22f399b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,9 @@ language: java jdk: - oraclejdk8 - openjdk8 +branches: + except: + - logo_submission script: "mvn cobertura:cobertura" diff --git a/appveyor.yml b/appveyor.yml index 7cad25ae..8c996077 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,6 +5,9 @@ environment: matrix: - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 # Append additional lines defining new values of JAVA_HOME to test multiple versions +branches: + except: + - logo_submission install: # Prepend Java entry, remove Ruby entry (C:\Ruby193\bin;) from PATH - cmd: choco upgrade maven diff --git a/codecov.yml b/codecov.yml index a64b1ef2..7ee3554e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -3,4 +3,4 @@ coverage: range: 0..60 round: down - precision: 2 \ No newline at end of file + precision: 2 From 0966301ae38fea8ee55eb590246dc799a005dbcc Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Fri, 26 Oct 2018 20:37:26 -0500 Subject: [PATCH 17/91] Added logo contest to the Readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7508deb9..224ae049 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# AndHow is looking for a new Logo - [Enter your design before the end of October!](https://github.com/eeverman/andhow/issues/427) +## [>>See the latest logo submissions <<](https://twitter.com/hashtag/andhowconfig_logo) [![Build Status](https://travis-ci.org/eeverman/andhow.svg?branch=master)](https://travis-ci.org/eeverman/andhow) [![codecov](https://codecov.io/gh/eeverman/andhow/branch/master/graph/badge.svg)](https://codecov.io/gh/eeverman/andhow) [![Javadocs](https://www.javadoc.io/badge/org.yarnandtail/andhow.svg)](https://www.javadoc.io/doc/org.yarnandtail/andhow) From febd9f4a9bcc08d3f8c5742ff6645bb577db977a Mon Sep 17 00:00:00 2001 From: Titu BG Date: Sat, 27 Oct 2018 17:05:33 +0530 Subject: [PATCH 18/91] Adding javadoc linting options to ignore warnings - -none: To eliminate all warnings: JDK8+ - additionalparam: <=Maven 2.9 (removed as mvn 3+ used) - additionalJOption: => Maven 3.0 - doclint: maven-javadoc-plugin 3.0.0+ --- andhow/pom.xml | 13 ------------- pom.xml | 37 ++++++++++++++++++------------------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/andhow/pom.xml b/andhow/pom.xml index b789fb48..7693833a 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -56,19 +56,6 @@ org.apache.maven.plugins maven-javadoc-plugin - - - javadoc-jar - package - - jar - - - - true - - - diff --git a/pom.xml b/pom.xml index 747e25a7..517fef51 100644 --- a/pom.xml +++ b/pom.xml @@ -140,10 +140,24 @@ org.apache.maven.plugins maven-javadoc-plugin 3.0.1 - - -Xdoclint:none - true - + true + + + javadoc-jar + package + + jar + + true + + true + -Xdoclint:none + -Xdoclint:-missing + none,-missing + true + + + true @@ -240,21 +254,6 @@ - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-javadocs - - jar - - - - - -Xdoclint:none - - From 84597de0c4ea97713062cb4c6e5500a85ac6c982 Mon Sep 17 00:00:00 2001 From: winsonrich <42903383+winsonrich@users.noreply.github.com> Date: Sat, 20 Oct 2018 14:58:37 +0800 Subject: [PATCH 19/91] Update StringValidator.java modifier is added. --- .../org/yarnandtail/andhow/valid/StringValidator.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java index 79fa04fb..b7685141 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java @@ -14,8 +14,8 @@ public class StringValidator { */ public static class StartsWith implements Validator { - String prefix; - boolean ignoreCase; + private String prefix; + private boolean ignoreCase; public StartsWith(String prefix, boolean ignoreCase) { this.prefix = prefix; @@ -56,8 +56,8 @@ public String getTheValueMustDescription() { */ public static class EndsWith implements Validator { - String sufix; - boolean ignoreCase; + private String sufix; + private boolean ignoreCase; public EndsWith(String sufix, boolean ignoreCase) { this.sufix = sufix; @@ -97,7 +97,7 @@ public String getTheValueMustDescription() { */ public static class Regex implements Validator { - String regex; + private String regex; public Regex(String regex) { this.regex = regex; From fb76e1f88de0be2d7c018666c248d4eb163ecb1d Mon Sep 17 00:00:00 2001 From: winsonrich <42903383+winsonrich@users.noreply.github.com> Date: Sat, 20 Oct 2018 14:56:31 +0800 Subject: [PATCH 20/91] Update LngValidator.java modifier is added. --- .../java/org/yarnandtail/andhow/valid/LngValidator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/LngValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/LngValidator.java index 0f9cda60..3632372d 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/LngValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/LngValidator.java @@ -50,7 +50,7 @@ public String getTheValueMustDescription() { */ public static class GreaterThanOrEqualTo extends LngValidator { - long ref; + private long ref; public GreaterThanOrEqualTo(long ref) { this.ref = ref; @@ -76,7 +76,7 @@ public String getTheValueMustDescription() { */ public static class LessThan extends LngValidator { - long ref; + private long ref; public LessThan(long ref) { this.ref = ref; @@ -102,7 +102,7 @@ public String getTheValueMustDescription() { */ public static class LessThanOrEqualTo extends LngValidator { - long ref; + private long ref; public LessThanOrEqualTo(long ref) { this.ref = ref; From 9c4953a0a883d812042d9b4195282baf14ff9011 Mon Sep 17 00:00:00 2001 From: winsonrich <42903383+winsonrich@users.noreply.github.com> Date: Sat, 20 Oct 2018 14:55:16 +0800 Subject: [PATCH 21/91] Update IntValidator.java modifier is added. --- .../java/org/yarnandtail/andhow/valid/IntValidator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/IntValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/IntValidator.java index 564a5aec..936de764 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/IntValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/IntValidator.java @@ -24,7 +24,7 @@ public String getInvalidSpecificationMessage() { */ public static class GreaterThan extends IntValidator { - int ref; + private int ref; public GreaterThan(int ref) { this.ref = ref; @@ -50,7 +50,7 @@ public String getTheValueMustDescription() { */ public static class GreaterThanOrEqualTo extends IntValidator { - int ref; + private int ref; public GreaterThanOrEqualTo(int ref) { this.ref = ref; @@ -76,7 +76,7 @@ public String getTheValueMustDescription() { */ public static class LessThan extends IntValidator { - int ref; + private int ref; public LessThan(int ref) { this.ref = ref; @@ -102,7 +102,7 @@ public String getTheValueMustDescription() { */ public static class LessThanOrEqualTo extends IntValidator { - int ref; + private int ref; public LessThanOrEqualTo(int ref) { this.ref = ref; From 3a47d5d19fc4c3ea352aca3ddd60efc631fd4ae1 Mon Sep 17 00:00:00 2001 From: winsonrich <42903383+winsonrich@users.noreply.github.com> Date: Sat, 20 Oct 2018 14:53:06 +0800 Subject: [PATCH 22/91] Update DblValidator.java modifier is added --- .../java/org/yarnandtail/andhow/valid/DblValidator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/DblValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/DblValidator.java index 4b037c2d..ad3365df 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/DblValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/DblValidator.java @@ -24,7 +24,7 @@ public String getInvalidSpecificationMessage() { */ public static class GreaterThan extends DblValidator { - double ref; + private double ref; public GreaterThan(double ref) { this.ref = ref; @@ -50,7 +50,7 @@ public String getTheValueMustDescription() { */ public static class GreaterThanOrEqualTo extends DblValidator { - double ref; + private double ref; public GreaterThanOrEqualTo(double ref) { this.ref = ref; @@ -76,7 +76,7 @@ public String getTheValueMustDescription() { */ public static class LessThan extends DblValidator { - double ref; + private double ref; public LessThan(double ref) { this.ref = ref; @@ -102,7 +102,7 @@ public String getTheValueMustDescription() { */ public static class LessThanOrEqualTo extends DblValidator { - double ref; + private double ref; public LessThanOrEqualTo(double ref) { this.ref = ref; From 09bebf700c52de823539eb6e734cbc79cd3b5469 Mon Sep 17 00:00:00 2001 From: Titu BG Date: Sun, 28 Oct 2018 21:27:19 +0530 Subject: [PATCH 23/91] Fixing the last javadoc warning. --- .../main/java/org/yarnandtail/andhow/compile/CompileUnit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java index 0b470a3c..d3f78256 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java @@ -147,7 +147,7 @@ public boolean addProperty(String name, boolean _static, boolean _final) { * If the current state is at the root of the top level class, an empty list * is returned. * - * @see getInnerPathNames() for just the names of the nested inner classes. + * @see #getInnerPathNames() for just the names of the nested inner classes. * @return */ public List getInnerPath() { From 5ed56c1af64a49a40620ec4acc324a22289a39ba Mon Sep 17 00:00:00 2001 From: UfukOzcelik Date: Tue, 30 Oct 2018 18:05:09 +0300 Subject: [PATCH 24/91] Remove duplicate and misnamed clearJNDIBeforeTest method. --- .../java/org/yarnandtail/andhow/AndHowCoreTestBase.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java index 79a40c2e..b5afc4ce 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java @@ -65,13 +65,6 @@ public void storeSysPropsBeforeTest() { beforeTestSystemProps = AndHowCoreTestUtil.clone(System.getProperties()); } - @After - public void clearJNDIBeforeTest() { - if (builder != null) { - builder.clear(); - } - } - @After public void killAndHowStateAfterTest() { AndHowCoreTestUtil.destroyAndHow(); From c5bbffa8ef85b25894548abe0e5411b7722557ba Mon Sep 17 00:00:00 2001 From: Titu BG Date: Thu, 1 Nov 2018 20:36:20 +0530 Subject: [PATCH 25/91] Steps towards removing the static get method - get() calls instance() internally - Added (some) javadocs - Edited a couple of missing javadocs --- .../yarnandtail/andhow/valuetype/BolType.java | 15 ++++++++++--- .../yarnandtail/andhow/valuetype/DblType.java | 15 ++++++++++--- .../andhow/valuetype/FlagType.java | 15 ++++++++++--- .../yarnandtail/andhow/valuetype/IntType.java | 15 ++++++++++--- .../yarnandtail/andhow/valuetype/LngType.java | 15 ++++++++++--- .../andhow/valuetype/LocalDateTimeType.java | 21 +++++++++++++------ .../yarnandtail/andhow/valuetype/StrType.java | 5 ++++- 7 files changed, 79 insertions(+), 22 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BolType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BolType.java index 0915e767..2206b785 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BolType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BolType.java @@ -17,11 +17,20 @@ public class BolType extends BaseValueType { private BolType() { super(Boolean.class); } - + + /** + * @deprecated since 0.4.1. Use {@link #instance()} instead + * + * @return An instance of the {@link #BolType()} + */ + @Deprecated() public static BolType get() { - return instance; + return instance(); } - + + /** + * @return An instance of the {@link #BolType()} + */ public static BolType instance() { return instance; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/DblType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/DblType.java index 94847605..fa1aac6b 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/DblType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/DblType.java @@ -17,11 +17,20 @@ public class DblType extends BaseValueType { private DblType() { super(Double.class); } - + + /** + * @deprecated since 0.4.1. Use {@link #instance()} instead + * + * @return An instance of the {@link #DblType()} + */ + @Deprecated() public static DblType get() { - return instance; + return instance(); } - + + /** + * @return An instance of the {@link #DblType()} + */ public static DblType instance() { return instance; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/FlagType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/FlagType.java index 0285b986..042fecd6 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/FlagType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/FlagType.java @@ -17,11 +17,20 @@ public class FlagType extends BaseValueType { private FlagType() { super(Boolean.class); } - + + /** + * @deprecated since 0.4.1. Use {@link #instance()} instead + * + * @return An instance of the {@link #FlagType()} + */ + @Deprecated() public static FlagType get() { - return instance; + return instance(); } - + + /** + * @return An instance of the {@link #FlagType()} + */ public static FlagType instance() { return instance; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/IntType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/IntType.java index d371b3c6..7ca664b5 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/IntType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/IntType.java @@ -17,11 +17,20 @@ public class IntType extends BaseValueType { private IntType() { super(Integer.class); } - + + /** + * @deprecated since 0.4.1. Use {@link #instance()} instead + * + * @return An instance of the {@link #IntType()} + */ + @Deprecated() public static IntType get() { - return instance; + return instance(); } - + + /** + * @return An instance of the {@link #IntType()} + */ public static IntType instance() { return instance; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LngType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LngType.java index 3bbfd154..b57cf81f 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LngType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LngType.java @@ -17,11 +17,20 @@ public class LngType extends BaseValueType { private LngType() { super(Long.class); } - + + /** + * @deprecated since 0.4.1. Use {@link #instance()} instead + * + * @return An instance of the {@link #LngType()} + */ + @Deprecated() public static LngType get() { - return instance; + return instance(); } - + + /** + * @return An instance of the {@link #LngType()} + */ public static LngType instance() { return instance; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LocalDateTimeType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LocalDateTimeType.java index 120fe847..b6778e7b 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LocalDateTimeType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/LocalDateTimeType.java @@ -23,11 +23,20 @@ public class LocalDateTimeType extends BaseValueType { private LocalDateTimeType() { super(LocalDateTime.class); } - + + /** + * @deprecated since 0.4.1. Use {@link #instance()} instead + * + * @return An instance of the {@link #LocalDateTimeType()} + */ + @Deprecated public static LocalDateTimeType get() { - return instance; + return instance(); } - + + /** + * @return An instance of the {@link #LocalDateTimeType()} + */ public static LocalDateTimeType instance() { return instance; } @@ -45,9 +54,9 @@ public static LocalDateTimeType instance() { *
  • 2011-12-03T00:15:30 - The first hour is hour zero *
  • 2011-12-03T23:00:00 - The last hour is hour 23 * - * @param sourceValue - * @return - * @throws ParsingException + * @param sourceValue The @{@link String} value to be parsed into a @{@link LocalDateTime} + * @return A valid @{@link LocalDateTime} + * @throws ParsingException If the @{@link String} can't be parsed into a @{@link LocalDateTime} */ @Override public LocalDateTime parse(String sourceValue) throws ParsingException { diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/StrType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/StrType.java index 7ed37302..f32a4cb0 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/StrType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/StrType.java @@ -17,7 +17,10 @@ public class StrType extends BaseValueType { private StrType() { super(String.class); } - + + /** + * @return An instance of the {@link #StrType()} + */ public static StrType instance() { return instance; } From ca298de4bd85576050b26e734641d551cde83ffc Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Sun, 21 Oct 2018 21:09:31 -0400 Subject: [PATCH 26/91] add intellij iml files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9d8282e9..58469f48 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ release.properties #IntelliJ Project directory .idea/ +**.iml \ No newline at end of file From ea95de98e23ef956212709e350453cb2cccb4c83 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Mon, 22 Oct 2018 00:16:37 -0400 Subject: [PATCH 27/91] Big Decimal type and tests --- .../andhow/valuetype/BigDecType.java | 43 ++++++++++++++ .../andhow/valuetype/BigDecTypeTest.java | 58 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java new file mode 100644 index 00000000..02c5781d --- /dev/null +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java @@ -0,0 +1,43 @@ +package org.yarnandtail.andhow.valuetype; + +import org.yarnandtail.andhow.api.ParsingException; + +import java.math.BigDecimal; + +/** + * Type representation of Java BigDecimal objects. + * + * This class is threadsafe and uses a singleton pattern to prevent multiple + * instances, since all users can safely use the same instance. + * + * @author chace86 + */ +public class BigDecType extends BaseValueType { + + private static final BigDecType INSTANCE = new BigDecType(); + + private BigDecType() { + super(BigDecimal.class); + } + + public static BigDecType instance() { + return INSTANCE; + } + + @Override + public BigDecimal parse(String sourceValue) throws ParsingException { + if (sourceValue == null) { + return null; + } + try { + return new BigDecimal(sourceValue); + } catch (Exception e) { + throw new ParsingException("Unable to convert to a BigDecimal numeric value", sourceValue, e); + } + } + + @Override + public BigDecimal cast(Object o) throws RuntimeException { + return (BigDecimal)o; + } +} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java new file mode 100644 index 00000000..6c86ecae --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java @@ -0,0 +1,58 @@ +package org.yarnandtail.andhow.valuetype; + + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.yarnandtail.andhow.api.ParsingException; +import java.math.BigDecimal; + +import static org.junit.Assert.*; + +/** + * Unit tests for BigDecType + * + * @author chace86 + */ +public class BigDecTypeTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + private static final String PARSE_ERROR_MSG = "Unable to convert to a BigDecimal numeric value"; + private BigDecType type = BigDecType.instance(); + + @Test + public void testParse() throws ParsingException { + assertNull(type.parse(null)); + assertEquals(BigDecimal.ZERO, type.parse("0")); + assertEquals(BigDecimal.ONE, type.parse("1")); + assertEquals(new BigDecimal("12.34567"), type.parse("12.34567")); + assertEquals(new BigDecimal("-300.724578"), type.parse("-300.724578")); + } + + @Test + public void testParseEmpty() throws ParsingException { + expectParsingException(); + assertFalse(type.isParsable("")); + type.parse(""); + } + + @Test + public void testParseNotANumber() throws ParsingException { + expectParsingException(); + assertFalse(type.isParsable("apple")); + type.parse("apple"); + } + + @Test + public void testCast() { + Object o = new BigDecimal("123.456"); + assertEquals(new BigDecimal("123.456"), o); + assertTrue(type.cast(o) instanceof BigDecimal); + } + + private void expectParsingException() { + thrown.expect(ParsingException.class); + thrown.expectMessage(PARSE_ERROR_MSG); + } +} From 1a477d00ebfbf2bda258f72c4f26ecb70457e8db Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Tue, 23 Oct 2018 23:10:14 -0400 Subject: [PATCH 28/91] BigDecimal validator and tests --- .../andhow/valid/BigDecValidator.java | 106 ++++++++++++ .../andhow/valid/BigDecValidatorTest.java | 159 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java new file mode 100644 index 00000000..f9a9ffa2 --- /dev/null +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java @@ -0,0 +1,106 @@ +package org.yarnandtail.andhow.valid; + +import org.yarnandtail.andhow.api.Validator; + +import java.math.BigDecimal; + +public abstract class BigDecValidator implements Validator { + + @Override + public boolean isSpecificationValid() { + return true; + } + + @Override + public String getInvalidSpecificationMessage() { + return "THIS VALIDATION IS ALWAYS VALID"; + } + + /** + * Validate that a BigDecimal is greather than a specified reference. + */ + public static class GreaterThan extends BigDecValidator { + + BigDecimal ref; + + public GreaterThan(BigDecimal ref) { + this.ref = ref; + } + + @Override + public boolean isValid(BigDecimal value) { + return (value != null) && (value.compareTo(ref) > 0); + } + + @Override + public String getTheValueMustDescription() { + return "be greater than " + ref; + } + } + + /** + * Validate that a BigDecimal is greater than or equal to a specified reference. + */ + public static class GreaterThanOrEqualTo extends BigDecValidator { + + BigDecimal ref; + + public GreaterThanOrEqualTo(BigDecimal ref) { + this.ref = ref; + } + + @Override + public boolean isValid(BigDecimal value) { + return (value != null) && (value.compareTo(ref) >= 0); + } + + @Override + public String getTheValueMustDescription() { + return "be greater than or equal to " + ref; + } + } + + /** + * Validate that a BigDecimal is less than a specified reference. + */ + public static class LessThan extends BigDecValidator { + + BigDecimal ref; + + public LessThan(BigDecimal ref) { + this.ref = ref; + } + + @Override + public boolean isValid(BigDecimal value) { + return (value != null) && (value.compareTo(ref) < 0); + } + + @Override + public String getTheValueMustDescription() { + return "be less than " + ref; + } + } + + /** + * Validate that a BigDecimal is less than or equal to a specified reference. + */ + public static class LessThanOrEqualTo extends BigDecValidator { + + BigDecimal ref; + + public LessThanOrEqualTo(BigDecimal ref) { + this.ref = ref; + } + + @Override + public boolean isValid(BigDecimal value) { + return (value != null) && (value.compareTo(ref) <= 0); + } + + @Override + public String getTheValueMustDescription() { + return "be less than or equal to " + ref; + } + } +} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java new file mode 100644 index 00000000..c56d9917 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java @@ -0,0 +1,159 @@ +package org.yarnandtail.andhow.valid; + +import org.junit.Test; + +import java.math.BigDecimal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class BigDecValidatorTest { + + private static String EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE = "THIS VALIDATION IS ALWAYS VALID"; + private static final BigDecimal FOUR = new BigDecimal("4.5678"); + private static final BigDecimal FIVE = new BigDecimal("5.12345"); + private static final BigDecimal SIX = new BigDecimal("6.452134563456"); + private static final BigDecimal NEGATIVE_ONE_HUNDRED = new BigDecimal("100.34532").negate(); + private static final BigDecimal NEGATIVE_NINETY_NINE = new BigDecimal("99.45623").negate(); + private static final BigDecimal NEGATIVE_NINETY_EIGHT = new BigDecimal("98.1234").negate(); + + @Test + public void testGreaterThanIsSpecificationValid() { + BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); + assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.GreaterThan(new BigDecimal("-12.8765")); + assertTrue(instance.isSpecificationValid()); + } + + @Test + public void testGreaterThanIsValid() { + BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); + assertFalse(instance.isValid(FOUR)); + assertFalse(instance.isValid(FIVE)); + assertTrue(instance.isValid(SIX)); + assertFalse(instance.isValid(null)); + + instance = new BigDecValidator.GreaterThan(BigDecimal.ZERO); + assertFalse(instance.isValid(BigDecimal.ONE.negate())); + assertFalse(instance.isValid(BigDecimal.ZERO)); + assertTrue(instance.isValid(BigDecimal.ONE)); + + instance = new BigDecValidator.GreaterThan(NEGATIVE_NINETY_NINE); + assertFalse(instance.isValid(NEGATIVE_ONE_HUNDRED)); + assertFalse(instance.isValid(NEGATIVE_NINETY_NINE)); + assertTrue(instance.isValid(NEGATIVE_NINETY_EIGHT)); + } + + @Test + public void testGreaterThanOrEqualToIsSpecificationValid() { + BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); + assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.GreaterThanOrEqualTo(new BigDecimal("-999999999")); + assertTrue(instance.isSpecificationValid()); + } + + @Test + public void testGreaterThanOrEqualToIsValid() { + BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); + assertFalse(instance.isValid(FOUR)); + assertTrue(instance.isValid(FIVE)); + assertTrue(instance.isValid(SIX)); + assertFalse(instance.isValid(null)); + + instance = new BigDecValidator.GreaterThanOrEqualTo(BigDecimal.ZERO); + assertFalse(instance.isValid(BigDecimal.ONE.negate())); + assertTrue(instance.isValid(BigDecimal.ZERO)); + assertTrue(instance.isValid(BigDecimal.ONE)); + + + instance = new BigDecValidator.GreaterThanOrEqualTo(NEGATIVE_NINETY_NINE); + assertFalse(instance.isValid(NEGATIVE_ONE_HUNDRED)); + assertTrue(instance.isValid(NEGATIVE_NINETY_NINE)); + assertTrue(instance.isValid(NEGATIVE_NINETY_EIGHT)); + } + + + @Test + public void testLessThanIsSpecificationValid() { + BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); + assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.LessThan(NEGATIVE_NINETY_NINE); + assertTrue(instance.isSpecificationValid()); + } + + @Test + public void testLessThanIsValid() { + BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); + assertTrue(instance.isValid(FOUR)); + assertFalse(instance.isValid(FIVE)); + assertFalse(instance.isValid(SIX)); + assertFalse(instance.isValid(null)); + + instance = new BigDecValidator.LessThan(BigDecimal.ZERO); + assertTrue(instance.isValid(BigDecimal.ONE.negate())); + assertFalse(instance.isValid(BigDecimal.ZERO)); + assertFalse(instance.isValid(BigDecimal.ONE)); + + instance = new BigDecValidator.LessThan(NEGATIVE_NINETY_NINE); + assertTrue(instance.isValid(NEGATIVE_ONE_HUNDRED)); + assertFalse(instance.isValid(NEGATIVE_NINETY_NINE)); + assertFalse(instance.isValid(NEGATIVE_NINETY_EIGHT)); + } + + @Test + public void testLessThanOrEqualsToIsSpecificationValid() { + BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); + assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.LessThanOrEqualTo(NEGATIVE_NINETY_NINE); + assertTrue(instance.isSpecificationValid()); + } + + @Test + public void testLessThanOrEqualToIsValid() { + BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); + assertTrue(instance.isValid(FOUR)); + assertTrue(instance.isValid(FIVE)); + assertFalse(instance.isValid(SIX)); + assertFalse(instance.isValid(null)); + + instance = new BigDecValidator.LessThanOrEqualTo(BigDecimal.ZERO); + assertTrue(instance.isValid(BigDecimal.ONE.negate())); + assertTrue(instance.isValid(BigDecimal.ZERO)); + assertFalse(instance.isValid(BigDecimal.ONE)); + + instance = new BigDecValidator.LessThanOrEqualTo(NEGATIVE_NINETY_NINE); + assertTrue(instance.isValid(NEGATIVE_ONE_HUNDRED)); + assertTrue(instance.isValid(NEGATIVE_NINETY_NINE)); + assertFalse(instance.isValid(NEGATIVE_NINETY_EIGHT)); + + } + + @Test + public void testGreaterThanInvalidSpecificationMessage() { + BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } + + @Test + public void testGreaterThanOrEqualToInvalidSpecificationMessage() { + BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } + + @Test + public void testLessThanInvalidSpecificationMessage() { + BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } + + @Test + public void testLessThanOrEqualToInvalidSpecificationMessage() { + BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); + } +} From 41551973bb4bb42f4c01f2f384eb016775a2b7ca Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Sun, 28 Oct 2018 23:13:15 -0400 Subject: [PATCH 29/91] add BigDecimal property and tests --- .../andhow/property/BigDecProp.java | 66 ++++++++++++ .../andhow/valid/BigDecValidator.java | 10 +- .../andhow/property/BigDecPropTest.java | 100 ++++++++++++++++++ .../BigDecPropTest_badPath.properties | 7 ++ .../BigDecPropTest_default.properties | 5 + .../BigDecPropTest_happyPath.properties | 11 ++ .../BigDecPropTest_missingProps.properties | 0 7 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java create mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_badPath.properties create mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties create mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties create mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_missingProps.properties diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java new file mode 100644 index 00000000..89a331bf --- /dev/null +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java @@ -0,0 +1,66 @@ +package org.yarnandtail.andhow.property; + +import org.yarnandtail.andhow.api.*; +import org.yarnandtail.andhow.valid.BigDecValidator; +import org.yarnandtail.andhow.valuetype.BigDecType; + +import java.math.BigDecimal; +import java.util.List; + +/** + * A Property that refers to a BigDecimal value. + * + * By default this uses the TrimToNullTrimmer, which removes all whitespace from + * the value and ultimately null if the value is all whitespace. The String + * constructor version is used when creating instances of BigDecimal. + * + * @author chace86 + */ +public class BigDecProp extends PropertyBase { + + public BigDecProp(BigDecimal defaultValue, boolean required, String shortDesc, List> validators, + List aliases, PropertyType paramType, ValueType valueType, Trimmer trimmer, + String helpText) { + + super(defaultValue, required, shortDesc, validators, aliases, paramType, valueType, trimmer, helpText); + } + + public static BigDecBuilder builder() { + return new BigDecBuilder(); + } + + public static class BigDecBuilder extends PropertyBuilderBase { + + public BigDecBuilder() { + instance = this; + valueType(BigDecType.instance()); + trimmer(TrimToNullTrimmer.instance()); + } + + @Override + public BigDecProp build() { + return new BigDecProp(_defaultValue, _nonNull, _desc, _validators, + _aliases, PropertyType.SINGLE_NAME_VALUE, _valueType, _trimmer, _helpText); + } + + public BigDecBuilder mustBeGreaterThan(BigDecimal reference) { + validation(new BigDecValidator.GreaterThan(reference)); + return instance; + } + + public BigDecBuilder mustBeGreaterThanOrEqualTo(BigDecimal reference) { + validation(new BigDecValidator.GreaterThanOrEqualTo(reference)); + return instance; + } + + public BigDecBuilder mustBeLessThan(BigDecimal reference) { + validation(new BigDecValidator.LessThan(reference)); + return instance; + } + + public BigDecBuilder mustBeLessThanOrEqualTo(BigDecimal reference) { + validation(new BigDecValidator.LessThanOrEqualTo(reference)); + return instance; + } + } +} diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java index f9a9ffa2..aae344b1 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java @@ -17,11 +17,11 @@ public String getInvalidSpecificationMessage() { } /** - * Validate that a BigDecimal is greather than a specified reference. + * Validate that a BigDecimal is greater than a specified reference. */ public static class GreaterThan extends BigDecValidator { - BigDecimal ref; + private final BigDecimal ref; public GreaterThan(BigDecimal ref) { this.ref = ref; @@ -43,7 +43,7 @@ public String getTheValueMustDescription() { */ public static class GreaterThanOrEqualTo extends BigDecValidator { - BigDecimal ref; + private final BigDecimal ref; public GreaterThanOrEqualTo(BigDecimal ref) { this.ref = ref; @@ -65,7 +65,7 @@ public String getTheValueMustDescription() { */ public static class LessThan extends BigDecValidator { - BigDecimal ref; + private final BigDecimal ref; public LessThan(BigDecimal ref) { this.ref = ref; @@ -87,7 +87,7 @@ public String getTheValueMustDescription() { */ public static class LessThanOrEqualTo extends BigDecValidator { - BigDecimal ref; + private final BigDecimal ref; public LessThanOrEqualTo(BigDecimal ref) { this.ref = ref; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java new file mode 100644 index 00000000..4668b3bd --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java @@ -0,0 +1,100 @@ +package org.yarnandtail.andhow.property; + +import org.junit.Test; +import org.yarnandtail.andhow.api.AppFatalException; +import org.yarnandtail.andhow.api.Problem; +import org.yarnandtail.andhow.api.ProblemList; +import org.yarnandtail.andhow.internal.RequirementProblem.NonNullPropertyProblem; +import org.yarnandtail.andhow.internal.ValueProblem.InvalidValueProblem; + +import static org.junit.Assert.*; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * Tests BigDecProp instances as they would be used in an app. + * + * Focuses on builder functionality, validation, and metadata. + * + * @author chace86 + */ +public class BigDecPropTest extends PropertyTestBase { + private static final BigDecimal VALUE_1 = new BigDecimal("10.789"); + private static final BigDecimal VALUE_2 = new BigDecimal("101.123"); + private static final BigDecimal VALUE_3 = new BigDecimal("100.123456"); + private static final BigDecimal DEFAULT_VALUE = new BigDecimal("456.456"); + private static final String DESCRIPTION = "BigDecimal description"; + + @Test + public void happyPathTest() { + this.buildConfig(this, "_happyPath", BigDecGroup.class); + assertEquals(VALUE_1, BigDecGroup.NOT_NULL.getValue()); + assertEquals(DESCRIPTION, BigDecGroup.NOT_NULL.getDescription()); + assertEquals(VALUE_2, BigDecGroup.GREATER_THAN.getValue()); + assertEquals(VALUE_3, BigDecGroup.GREATER_THAN_OR_EQUAL.getValue()); + assertEquals(VALUE_2.negate(), BigDecGroup.LESS_THAN.getValue()); + assertEquals(VALUE_3, BigDecGroup.LESS_THAN_OR_EQUAL.getValue()); + assertEquals(new BigDecimal("789.789"), BigDecGroup.DEFAULT.getValue()); + assertEquals(null, BigDecGroup.NULL.getValue()); + } + + @Test + public void happyPathTest_DefaultValue() { + this.buildConfig(this, "_default", BigDecGroup.class); + assertEquals(DEFAULT_VALUE, BigDecGroup.DEFAULT.getDefaultValue()); + } + + @Test + public void happyPathTest_Alias() { + this.buildConfig(this, "_default", BigDecGroup.class); + System.out.print(BigDecGroup.GREATER_THAN.getValue()); + assertEquals(VALUE_2, BigDecGroup.GREATER_THAN.getValue()); + assertEquals("alias", BigDecGroup.GREATER_THAN.getRequestedAliases().get(0).getActualName()); + } + + @Test + public void badPathTest_InvalidValues() { + try { + this.buildConfig(this, "_badPath", BigDecGroup.class); + fail("Should throw exception!"); + } catch(AppFatalException e) { + ProblemList problems = e.getProblems(); + List descriptions = new ArrayList<>(); + for (Problem problem : problems) { + assertTrue(problem instanceof InvalidValueProblem); + descriptions.add(problem.getProblemDescription()); + } + assertEquals(4, problems.size()); + assertEquals("The value '" + VALUE_2.negate() + "' must be greater than " + VALUE_3, descriptions.get(0)); + assertEquals("The value '" + VALUE_3.negate() + "' must be greater than or equal to " + VALUE_3, descriptions.get(1)); + assertEquals("The value '" + VALUE_2 + "' must be less than " + VALUE_3, descriptions.get(2)); + assertEquals("The value '" + VALUE_3.add(BigDecimal.ONE) + "' must be less than or equal to " + VALUE_3, descriptions.get(3)); + } + } + + @Test + public void badPathTest_NullProperty() { + try { + this.buildConfig(this, "_missingProps", BigDecGroup.class); + fail("Should throw exception!"); + } catch (AppFatalException e) { + ProblemList problems = e.getProblems(); + assertEquals(1, problems.size()); + Problem problem = problems.get(0); + assertTrue(problem instanceof NonNullPropertyProblem); + assertEquals("This Property must be non-null - It must have a non-null default or be loaded by one of the loaders to a non-null value", problem.getProblemDescription()); + } + } + + public interface BigDecGroup { + BigDecProp NOT_NULL = BigDecProp.builder().mustBeNonNull().desc(DESCRIPTION).build(); + BigDecProp NULL = BigDecProp.builder().build(); + BigDecProp GREATER_THAN = BigDecProp.builder().mustBeGreaterThan(new BigDecimal("100.123456")).aliasInAndOut("alias").build(); + BigDecProp GREATER_THAN_OR_EQUAL = BigDecProp.builder().mustBeGreaterThanOrEqualTo(new BigDecimal("100.123456")).build(); + BigDecProp LESS_THAN = BigDecProp.builder().mustBeLessThan(new BigDecimal("100.123456")).build(); + BigDecProp LESS_THAN_OR_EQUAL = BigDecProp.builder().mustBeLessThanOrEqualTo(new BigDecimal("100.123456")).build(); + BigDecProp DEFAULT = BigDecProp.builder().defaultValue(new BigDecimal("456.456")).build(); + } +} diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_badPath.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_badPath.properties new file mode 100644 index 00000000..7a0d7317 --- /dev/null +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_badPath.properties @@ -0,0 +1,7 @@ +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.NOT_NULL = 10.789 + +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN = -101.123 +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN_OR_EQUAL = -100.123456 + +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN= 101.123 +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN_OR_EQUAL = 101.123456 \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties new file mode 100644 index 00000000..38c323ab --- /dev/null +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties @@ -0,0 +1,5 @@ +# non-null property +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.NOT_NULL = 10.789 + +# test alias +alias = 101.123 \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties new file mode 100644 index 00000000..b962663c --- /dev/null +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties @@ -0,0 +1,11 @@ +# non-null property +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.NOT_NULL = 10.789 + +# greater than, greater than or equal, less than, less than or equal properties +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN = 101.123 +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN_OR_EQUAL = 100.123456 +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN= -101.123 +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN_OR_EQUAL = 100.123456 + +# default property +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.DEFAULT = 789.789 \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_missingProps.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_missingProps.properties new file mode 100644 index 00000000..e69de29b From 1250c24e907dfd3ab6a019040eb0bc2510b0ccb0 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Sun, 28 Oct 2018 23:55:59 -0400 Subject: [PATCH 30/91] replace values with constants --- .../org/yarnandtail/andhow/property/BigDecPropTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java index 4668b3bd..56602b3d 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java @@ -91,10 +91,10 @@ public void badPathTest_NullProperty() { public interface BigDecGroup { BigDecProp NOT_NULL = BigDecProp.builder().mustBeNonNull().desc(DESCRIPTION).build(); BigDecProp NULL = BigDecProp.builder().build(); - BigDecProp GREATER_THAN = BigDecProp.builder().mustBeGreaterThan(new BigDecimal("100.123456")).aliasInAndOut("alias").build(); - BigDecProp GREATER_THAN_OR_EQUAL = BigDecProp.builder().mustBeGreaterThanOrEqualTo(new BigDecimal("100.123456")).build(); - BigDecProp LESS_THAN = BigDecProp.builder().mustBeLessThan(new BigDecimal("100.123456")).build(); - BigDecProp LESS_THAN_OR_EQUAL = BigDecProp.builder().mustBeLessThanOrEqualTo(new BigDecimal("100.123456")).build(); + BigDecProp GREATER_THAN = BigDecProp.builder().mustBeGreaterThan(VALUE_3).aliasInAndOut("alias").build(); + BigDecProp GREATER_THAN_OR_EQUAL = BigDecProp.builder().mustBeGreaterThanOrEqualTo(VALUE_3).build(); + BigDecProp LESS_THAN = BigDecProp.builder().mustBeLessThan(VALUE_3).build(); + BigDecProp LESS_THAN_OR_EQUAL = BigDecProp.builder().mustBeLessThanOrEqualTo(VALUE_3).build(); BigDecProp DEFAULT = BigDecProp.builder().defaultValue(new BigDecimal("456.456")).build(); } } From 38afd72874930a2aa7f106ac3cfd1673b90e701a Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Mon, 29 Oct 2018 19:41:03 -0400 Subject: [PATCH 31/91] add javadocs to property builder methods --- .../yarnandtail/andhow/property/BigDecProp.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java index 89a331bf..039c626d 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java @@ -43,21 +43,37 @@ public BigDecProp build() { _aliases, PropertyType.SINGLE_NAME_VALUE, _valueType, _trimmer, _helpText); } + /** + * The property must be greater than the reference + * @param reference value the property must be greater than + */ public BigDecBuilder mustBeGreaterThan(BigDecimal reference) { validation(new BigDecValidator.GreaterThan(reference)); return instance; } + /** + * The property must be greater than or equal to the reference + * @param reference value the property must be greater than or equal to + */ public BigDecBuilder mustBeGreaterThanOrEqualTo(BigDecimal reference) { validation(new BigDecValidator.GreaterThanOrEqualTo(reference)); return instance; } + /** + * The property must be less than the reference + * @param reference value the property must be less than + */ public BigDecBuilder mustBeLessThan(BigDecimal reference) { validation(new BigDecValidator.LessThan(reference)); return instance; } + /** + * The property must be less than or equal to the reference + * @param reference value the property must be less than or equal to + */ public BigDecBuilder mustBeLessThanOrEqualTo(BigDecimal reference) { validation(new BigDecValidator.LessThanOrEqualTo(reference)); return instance; From b2285fd043b3829cadf5034335039c79e75380cc Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Tue, 30 Oct 2018 22:14:39 -0400 Subject: [PATCH 32/91] add javadocs to BigDec prop, validator, type --- .../andhow/property/BigDecProp.java | 18 ++++++++++++++++++ .../andhow/valid/BigDecValidator.java | 5 +++++ .../andhow/valuetype/BigDecType.java | 3 +++ 3 files changed, 26 insertions(+) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java index 039c626d..04abbef7 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java @@ -18,6 +18,18 @@ */ public class BigDecProp extends PropertyBase { + /** + * Constructor for creating a BigDecProp + * @param defaultValue default value + * @param required make the property required or not + * @param shortDesc short description of the property + * @param validators list of validators for the property + * @param aliases aliases of the property + * @param paramType property type + * @param valueType property value type + * @param trimmer trimmer associated with the property + * @param helpText help text of the property + */ public BigDecProp(BigDecimal defaultValue, boolean required, String shortDesc, List> validators, List aliases, PropertyType paramType, ValueType valueType, Trimmer trimmer, String helpText) { @@ -25,10 +37,16 @@ public BigDecProp(BigDecimal defaultValue, boolean required, String shortDesc, L super(defaultValue, required, shortDesc, validators, aliases, paramType, valueType, trimmer, helpText); } + /** + * Return an instance of BigDecBuilder + */ public static BigDecBuilder builder() { return new BigDecBuilder(); } + /** + * Build a BigDecProp + */ public static class BigDecBuilder extends PropertyBuilderBase { public BigDecBuilder() { diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java index aae344b1..d6bf5dfa 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java @@ -4,6 +4,11 @@ import java.math.BigDecimal; +/** + * Abstract class implementing Validator interface for BigDec. + * Extended by nested static classes. The nested classes implement + * constraints that may be used when building the property. + */ public abstract class BigDecValidator implements Validator { @Override diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java index 02c5781d..02deed03 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java @@ -20,6 +20,9 @@ private BigDecType() { super(BigDecimal.class); } + /** + * Get an instance of BigDecType + */ public static BigDecType instance() { return INSTANCE; } From ca1ed819650f49926ddf7c666bb9ecddd6adcfa8 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Tue, 30 Oct 2018 23:33:03 -0400 Subject: [PATCH 33/91] add jndi and system prop tests to BigDec --- .../andhow/property/BigDecPropTest.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java index 56602b3d..df3e7cdc 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java @@ -1,12 +1,15 @@ package org.yarnandtail.andhow.property; import org.junit.Test; +import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.api.Problem; import org.yarnandtail.andhow.api.ProblemList; import org.yarnandtail.andhow.internal.RequirementProblem.NonNullPropertyProblem; import org.yarnandtail.andhow.internal.ValueProblem.InvalidValueProblem; +import javax.naming.NamingException; + import static org.junit.Assert.*; import java.math.BigDecimal; @@ -25,7 +28,11 @@ public class BigDecPropTest extends PropertyTestBase { private static final BigDecimal VALUE_2 = new BigDecimal("101.123"); private static final BigDecimal VALUE_3 = new BigDecimal("100.123456"); private static final BigDecimal DEFAULT_VALUE = new BigDecimal("456.456"); + private static final BigDecimal JNDI_VALUE = new BigDecimal("777.777"); + private static final BigDecimal SYS_PROP_VALUE = new BigDecimal("-523.789"); private static final String DESCRIPTION = "BigDecimal description"; + private static final String GREATER_THAN_JNDI_PATH = "org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN"; + private static final String LESS_THAN_SYS_PROP_PATH = "org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN"; @Test public void happyPathTest() { @@ -37,7 +44,7 @@ public void happyPathTest() { assertEquals(VALUE_2.negate(), BigDecGroup.LESS_THAN.getValue()); assertEquals(VALUE_3, BigDecGroup.LESS_THAN_OR_EQUAL.getValue()); assertEquals(new BigDecimal("789.789"), BigDecGroup.DEFAULT.getValue()); - assertEquals(null, BigDecGroup.NULL.getValue()); + assertNull(BigDecGroup.NULL.getValue()); } @Test @@ -49,11 +56,26 @@ public void happyPathTest_DefaultValue() { @Test public void happyPathTest_Alias() { this.buildConfig(this, "_default", BigDecGroup.class); - System.out.print(BigDecGroup.GREATER_THAN.getValue()); assertEquals(VALUE_2, BigDecGroup.GREATER_THAN.getValue()); assertEquals("alias", BigDecGroup.GREATER_THAN.getRequestedAliases().get(0).getActualName()); } + @Test + public void happyPathTest_JndiProp() throws NamingException { + SimpleNamingContextBuilder jndi = getJndi(); + jndi.bind("java:" + GREATER_THAN_JNDI_PATH, JNDI_VALUE); + jndi.activate(); + this.buildConfig(this, "_happyPath", BigDecGroup.class); + assertEquals(JNDI_VALUE, BigDecGroup.GREATER_THAN.getValue()); + } + + @Test + public void happyPathTest_SystemProp() { + System.setProperty(LESS_THAN_SYS_PROP_PATH, SYS_PROP_VALUE.toString()); + this.buildConfig(this, "_happyPath", BigDecGroup.class); + assertEquals(SYS_PROP_VALUE, BigDecGroup.LESS_THAN.getValue()); + } + @Test public void badPathTest_InvalidValues() { try { @@ -95,6 +117,6 @@ public interface BigDecGroup { BigDecProp GREATER_THAN_OR_EQUAL = BigDecProp.builder().mustBeGreaterThanOrEqualTo(VALUE_3).build(); BigDecProp LESS_THAN = BigDecProp.builder().mustBeLessThan(VALUE_3).build(); BigDecProp LESS_THAN_OR_EQUAL = BigDecProp.builder().mustBeLessThanOrEqualTo(VALUE_3).build(); - BigDecProp DEFAULT = BigDecProp.builder().defaultValue(new BigDecimal("456.456")).build(); + BigDecProp DEFAULT = BigDecProp.builder().defaultValue(DEFAULT_VALUE).build(); } } From ed489ac1df2dae31e498b95cf4404537b5731bc8 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Tue, 30 Oct 2018 23:37:27 -0400 Subject: [PATCH 34/91] simplify test to assertNotNull --- .../java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java index 6c86ecae..78cb77ee 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java @@ -48,7 +48,7 @@ public void testParseNotANumber() throws ParsingException { public void testCast() { Object o = new BigDecimal("123.456"); assertEquals(new BigDecimal("123.456"), o); - assertTrue(type.cast(o) instanceof BigDecimal); + assertNotNull(type.cast(o)); } private void expectParsingException() { From 5ce45143590135f00d8e2cc83e3038f83f22b863 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Wed, 31 Oct 2018 22:24:50 -0400 Subject: [PATCH 35/91] validator javadocs, abstract common logic with subclasses, rework specification is valid and update tests --- .../andhow/valid/BigDecValidator.java | 50 ++++++++++++------- .../andhow/valid/BigDecValidatorTest.java | 12 +++++ 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java index d6bf5dfa..3adffd08 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java @@ -9,11 +9,21 @@ * Extended by nested static classes. The nested classes implement * constraints that may be used when building the property. */ -public abstract class BigDecValidator implements Validator { +public abstract class BigDecValidator implements Validator { + + final BigDecimal ref; + + /** + * Base constructor of BigDecValidator constraints + * @param ref to be compared to property value + */ + BigDecValidator(BigDecimal ref) { + this.ref = ref; + } @Override public boolean isSpecificationValid() { - return true; + return ref != null; } @Override @@ -25,11 +35,12 @@ public String getInvalidSpecificationMessage() { * Validate that a BigDecimal is greater than a specified reference. */ public static class GreaterThan extends BigDecValidator { - - private final BigDecimal ref; - + /** + * Construct a GreaterThan property constraint + * @param ref to be compared to property value + */ public GreaterThan(BigDecimal ref) { - this.ref = ref; + super(ref); } @Override @@ -47,11 +58,12 @@ public String getTheValueMustDescription() { * Validate that a BigDecimal is greater than or equal to a specified reference. */ public static class GreaterThanOrEqualTo extends BigDecValidator { - - private final BigDecimal ref; - + /** + * Construct a GreaterThanOrEqualTo property constraint + * @param ref to be compared to property value + */ public GreaterThanOrEqualTo(BigDecimal ref) { - this.ref = ref; + super(ref); } @Override @@ -69,11 +81,12 @@ public String getTheValueMustDescription() { * Validate that a BigDecimal is less than a specified reference. */ public static class LessThan extends BigDecValidator { - - private final BigDecimal ref; - + /** + * Construct a LessThan property constraint + * @param ref to be compared to property value + */ public LessThan(BigDecimal ref) { - this.ref = ref; + super(ref); } @Override @@ -91,11 +104,12 @@ public String getTheValueMustDescription() { * Validate that a BigDecimal is less than or equal to a specified reference. */ public static class LessThanOrEqualTo extends BigDecValidator { - - private final BigDecimal ref; - + /** + * Construct a LessThanOrEqualTo property constraint + * @param ref to be compared to property value + */ public LessThanOrEqualTo(BigDecimal ref) { - this.ref = ref; + super(ref); } @Override diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java index c56d9917..c5be6bb1 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java @@ -25,6 +25,9 @@ public void testGreaterThanIsSpecificationValid() { instance = new BigDecValidator.GreaterThan(new BigDecimal("-12.8765")); assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.GreaterThan(null); + assertFalse(instance.isSpecificationValid()); } @Test @@ -53,6 +56,9 @@ public void testGreaterThanOrEqualToIsSpecificationValid() { instance = new BigDecValidator.GreaterThanOrEqualTo(new BigDecimal("-999999999")); assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.GreaterThanOrEqualTo(null); + assertFalse(instance.isSpecificationValid()); } @Test @@ -83,6 +89,9 @@ public void testLessThanIsSpecificationValid() { instance = new BigDecValidator.LessThan(NEGATIVE_NINETY_NINE); assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.LessThan(null); + assertFalse(instance.isSpecificationValid()); } @Test @@ -111,6 +120,9 @@ public void testLessThanOrEqualsToIsSpecificationValid() { instance = new BigDecValidator.LessThanOrEqualTo(NEGATIVE_NINETY_NINE); assertTrue(instance.isSpecificationValid()); + + instance = new BigDecValidator.LessThanOrEqualTo(null); + assertFalse(instance.isSpecificationValid()); } @Test From 7ebe7ee39ef21bbc0eb83436fde317c23752e3e9 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Wed, 31 Oct 2018 22:38:47 -0400 Subject: [PATCH 36/91] expand javadoc on how source is parsed in BigDecType --- .../java/org/yarnandtail/andhow/valuetype/BigDecType.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java index 02deed03..0bec4fe9 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valuetype/BigDecType.java @@ -21,12 +21,18 @@ private BigDecType() { } /** - * Get an instance of BigDecType + * Construct an instance of BigDecType */ public static BigDecType instance() { return INSTANCE; } + + /** + * {@inheritDoc} + * For more information on how the sourceValue is parsed, see + * BigDecimal String constructor. + */ @Override public BigDecimal parse(String sourceValue) throws ParsingException { if (sourceValue == null) { From 80f42dfad9454e549978a29eb3c58488f9e250d6 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Wed, 31 Oct 2018 22:42:52 -0400 Subject: [PATCH 37/91] add return to BigDec prop javadocs --- .../java/org/yarnandtail/andhow/property/BigDecProp.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java index 04abbef7..e80a6f22 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/BigDecProp.java @@ -19,7 +19,7 @@ public class BigDecProp extends PropertyBase { /** - * Constructor for creating a BigDecProp + * Construct an instance of BigDecProp * @param defaultValue default value * @param required make the property required or not * @param shortDesc short description of the property @@ -49,6 +49,9 @@ public static BigDecBuilder builder() { */ public static class BigDecBuilder extends PropertyBuilderBase { + /** + * Construct an instance of BigDecBuilder + */ public BigDecBuilder() { instance = this; valueType(BigDecType.instance()); @@ -64,6 +67,7 @@ public BigDecProp build() { /** * The property must be greater than the reference * @param reference value the property must be greater than + * @return the builder instance */ public BigDecBuilder mustBeGreaterThan(BigDecimal reference) { validation(new BigDecValidator.GreaterThan(reference)); @@ -73,6 +77,7 @@ public BigDecBuilder mustBeGreaterThan(BigDecimal reference) { /** * The property must be greater than or equal to the reference * @param reference value the property must be greater than or equal to + * @return the builder instance */ public BigDecBuilder mustBeGreaterThanOrEqualTo(BigDecimal reference) { validation(new BigDecValidator.GreaterThanOrEqualTo(reference)); @@ -82,6 +87,7 @@ public BigDecBuilder mustBeGreaterThanOrEqualTo(BigDecimal reference) { /** * The property must be less than the reference * @param reference value the property must be less than + * @return the builder instance */ public BigDecBuilder mustBeLessThan(BigDecimal reference) { validation(new BigDecValidator.LessThan(reference)); @@ -91,6 +97,7 @@ public BigDecBuilder mustBeLessThan(BigDecimal reference) { /** * The property must be less than or equal to the reference * @param reference value the property must be less than or equal to + * @return the builder instance */ public BigDecBuilder mustBeLessThanOrEqualTo(BigDecimal reference) { validation(new BigDecValidator.LessThanOrEqualTo(reference)); From 0b050c7e2e60812c858d57bf3e8c3ee08673615f Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Wed, 31 Oct 2018 23:03:08 -0400 Subject: [PATCH 38/91] add SciNotation numbers; rename test constants for readability --- .../andhow/property/BigDecPropTest.java | 33 ++++++++----------- .../BigDecPropTest_default.properties | 2 +- .../BigDecPropTest_happyPath.properties | 4 +-- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java index df3e7cdc..036fb82b 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java @@ -13,8 +13,6 @@ import static org.junit.Assert.*; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; /** * Tests BigDecProp instances as they would be used in an app. @@ -25,8 +23,10 @@ */ public class BigDecPropTest extends PropertyTestBase { private static final BigDecimal VALUE_1 = new BigDecimal("10.789"); - private static final BigDecimal VALUE_2 = new BigDecimal("101.123"); - private static final BigDecimal VALUE_3 = new BigDecimal("100.123456"); + private static final BigDecimal GREATER_THAN_VALUE = new BigDecimal("101.123E+5"); + private static final BigDecimal GREATER_THAN_OR_EQUAL_VALUE = new BigDecimal("100.123456"); + private static final BigDecimal LESS_THAN_VALUE = GREATER_THAN_VALUE.negate(); + private static final BigDecimal LESS_THAN_VALUE_OR_EQUAL_VALUE = GREATER_THAN_OR_EQUAL_VALUE; private static final BigDecimal DEFAULT_VALUE = new BigDecimal("456.456"); private static final BigDecimal JNDI_VALUE = new BigDecimal("777.777"); private static final BigDecimal SYS_PROP_VALUE = new BigDecimal("-523.789"); @@ -39,10 +39,10 @@ public void happyPathTest() { this.buildConfig(this, "_happyPath", BigDecGroup.class); assertEquals(VALUE_1, BigDecGroup.NOT_NULL.getValue()); assertEquals(DESCRIPTION, BigDecGroup.NOT_NULL.getDescription()); - assertEquals(VALUE_2, BigDecGroup.GREATER_THAN.getValue()); - assertEquals(VALUE_3, BigDecGroup.GREATER_THAN_OR_EQUAL.getValue()); - assertEquals(VALUE_2.negate(), BigDecGroup.LESS_THAN.getValue()); - assertEquals(VALUE_3, BigDecGroup.LESS_THAN_OR_EQUAL.getValue()); + assertEquals(GREATER_THAN_VALUE, BigDecGroup.GREATER_THAN.getValue()); + assertEquals(GREATER_THAN_OR_EQUAL_VALUE, BigDecGroup.GREATER_THAN_OR_EQUAL.getValue()); + assertEquals(LESS_THAN_VALUE, BigDecGroup.LESS_THAN.getValue()); + assertEquals(LESS_THAN_VALUE_OR_EQUAL_VALUE, BigDecGroup.LESS_THAN_OR_EQUAL.getValue()); assertEquals(new BigDecimal("789.789"), BigDecGroup.DEFAULT.getValue()); assertNull(BigDecGroup.NULL.getValue()); } @@ -56,7 +56,7 @@ public void happyPathTest_DefaultValue() { @Test public void happyPathTest_Alias() { this.buildConfig(this, "_default", BigDecGroup.class); - assertEquals(VALUE_2, BigDecGroup.GREATER_THAN.getValue()); + assertEquals(GREATER_THAN_VALUE, BigDecGroup.GREATER_THAN.getValue()); assertEquals("alias", BigDecGroup.GREATER_THAN.getRequestedAliases().get(0).getActualName()); } @@ -83,16 +83,10 @@ public void badPathTest_InvalidValues() { fail("Should throw exception!"); } catch(AppFatalException e) { ProblemList problems = e.getProblems(); - List descriptions = new ArrayList<>(); for (Problem problem : problems) { assertTrue(problem instanceof InvalidValueProblem); - descriptions.add(problem.getProblemDescription()); } assertEquals(4, problems.size()); - assertEquals("The value '" + VALUE_2.negate() + "' must be greater than " + VALUE_3, descriptions.get(0)); - assertEquals("The value '" + VALUE_3.negate() + "' must be greater than or equal to " + VALUE_3, descriptions.get(1)); - assertEquals("The value '" + VALUE_2 + "' must be less than " + VALUE_3, descriptions.get(2)); - assertEquals("The value '" + VALUE_3.add(BigDecimal.ONE) + "' must be less than or equal to " + VALUE_3, descriptions.get(3)); } } @@ -106,17 +100,16 @@ public void badPathTest_NullProperty() { assertEquals(1, problems.size()); Problem problem = problems.get(0); assertTrue(problem instanceof NonNullPropertyProblem); - assertEquals("This Property must be non-null - It must have a non-null default or be loaded by one of the loaders to a non-null value", problem.getProblemDescription()); } } public interface BigDecGroup { BigDecProp NOT_NULL = BigDecProp.builder().mustBeNonNull().desc(DESCRIPTION).build(); BigDecProp NULL = BigDecProp.builder().build(); - BigDecProp GREATER_THAN = BigDecProp.builder().mustBeGreaterThan(VALUE_3).aliasInAndOut("alias").build(); - BigDecProp GREATER_THAN_OR_EQUAL = BigDecProp.builder().mustBeGreaterThanOrEqualTo(VALUE_3).build(); - BigDecProp LESS_THAN = BigDecProp.builder().mustBeLessThan(VALUE_3).build(); - BigDecProp LESS_THAN_OR_EQUAL = BigDecProp.builder().mustBeLessThanOrEqualTo(VALUE_3).build(); + BigDecProp GREATER_THAN = BigDecProp.builder().mustBeGreaterThan(GREATER_THAN_OR_EQUAL_VALUE).aliasInAndOut("alias").build(); + BigDecProp GREATER_THAN_OR_EQUAL = BigDecProp.builder().mustBeGreaterThanOrEqualTo(GREATER_THAN_OR_EQUAL_VALUE).build(); + BigDecProp LESS_THAN = BigDecProp.builder().mustBeLessThan(GREATER_THAN_OR_EQUAL_VALUE).build(); + BigDecProp LESS_THAN_OR_EQUAL = BigDecProp.builder().mustBeLessThanOrEqualTo(LESS_THAN_VALUE_OR_EQUAL_VALUE).build(); BigDecProp DEFAULT = BigDecProp.builder().defaultValue(DEFAULT_VALUE).build(); } } diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties index 38c323ab..99b8c968 100644 --- a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_default.properties @@ -2,4 +2,4 @@ org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.NOT_NULL = 10.789 # test alias -alias = 101.123 \ No newline at end of file +alias = 101.123E+5 \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties index b962663c..7b8925a8 100644 --- a/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/property/BigDecPropTest_happyPath.properties @@ -2,9 +2,9 @@ org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.NOT_NULL = 10.789 # greater than, greater than or equal, less than, less than or equal properties -org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN = 101.123 +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN = 101.123E+5 org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.GREATER_THAN_OR_EQUAL = 100.123456 -org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN= -101.123 +org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN= -1.01123E+7 org.yarnandtail.andhow.property.BigDecPropTest.BigDecGroup.LESS_THAN_OR_EQUAL = 100.123456 # default property From 2d3dc843cee4b60f0cc86d35be1090a3493d4892 Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Wed, 31 Oct 2018 23:24:41 -0400 Subject: [PATCH 39/91] add validator must description tests to validator tests --- .../andhow/valid/BigDecValidatorTest.java | 92 ++++++++----------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java index c5be6bb1..1a0449d0 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java @@ -14,12 +14,10 @@ public class BigDecValidatorTest { private static final BigDecimal FOUR = new BigDecimal("4.5678"); private static final BigDecimal FIVE = new BigDecimal("5.12345"); private static final BigDecimal SIX = new BigDecimal("6.452134563456"); - private static final BigDecimal NEGATIVE_ONE_HUNDRED = new BigDecimal("100.34532").negate(); private static final BigDecimal NEGATIVE_NINETY_NINE = new BigDecimal("99.45623").negate(); - private static final BigDecimal NEGATIVE_NINETY_EIGHT = new BigDecimal("98.1234").negate(); @Test - public void testGreaterThanIsSpecificationValid() { + public void testGreaterThan_IsSpecificationValid() { BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); assertTrue(instance.isSpecificationValid()); @@ -31,26 +29,22 @@ public void testGreaterThanIsSpecificationValid() { } @Test - public void testGreaterThanIsValid() { + public void testGreaterThan_GetTheValueMustDescription() { + BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); + assertEquals("be greater than " + FIVE, instance.getTheValueMustDescription()); + } + + @Test + public void testGreaterThan_IsValid() { BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); assertFalse(instance.isValid(FOUR)); assertFalse(instance.isValid(FIVE)); assertTrue(instance.isValid(SIX)); assertFalse(instance.isValid(null)); - - instance = new BigDecValidator.GreaterThan(BigDecimal.ZERO); - assertFalse(instance.isValid(BigDecimal.ONE.negate())); - assertFalse(instance.isValid(BigDecimal.ZERO)); - assertTrue(instance.isValid(BigDecimal.ONE)); - - instance = new BigDecValidator.GreaterThan(NEGATIVE_NINETY_NINE); - assertFalse(instance.isValid(NEGATIVE_ONE_HUNDRED)); - assertFalse(instance.isValid(NEGATIVE_NINETY_NINE)); - assertTrue(instance.isValid(NEGATIVE_NINETY_EIGHT)); } @Test - public void testGreaterThanOrEqualToIsSpecificationValid() { + public void testGreaterThanOrEqualTo_IsSpecificationValid() { BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); assertTrue(instance.isSpecificationValid()); @@ -62,28 +56,23 @@ public void testGreaterThanOrEqualToIsSpecificationValid() { } @Test - public void testGreaterThanOrEqualToIsValid() { + public void testGreaterThanOrEqualTo_GetTheValueMustDescription() { + BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); + assertEquals("be greater than or equal to " + FIVE, instance.getTheValueMustDescription()); + } + + @Test + public void testGreaterThanOrEqualTo_IsValid() { BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); assertFalse(instance.isValid(FOUR)); assertTrue(instance.isValid(FIVE)); assertTrue(instance.isValid(SIX)); assertFalse(instance.isValid(null)); - - instance = new BigDecValidator.GreaterThanOrEqualTo(BigDecimal.ZERO); - assertFalse(instance.isValid(BigDecimal.ONE.negate())); - assertTrue(instance.isValid(BigDecimal.ZERO)); - assertTrue(instance.isValid(BigDecimal.ONE)); - - - instance = new BigDecValidator.GreaterThanOrEqualTo(NEGATIVE_NINETY_NINE); - assertFalse(instance.isValid(NEGATIVE_ONE_HUNDRED)); - assertTrue(instance.isValid(NEGATIVE_NINETY_NINE)); - assertTrue(instance.isValid(NEGATIVE_NINETY_EIGHT)); } @Test - public void testLessThanIsSpecificationValid() { + public void testLessThan_IsSpecificationValid() { BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); assertTrue(instance.isSpecificationValid()); @@ -95,26 +84,22 @@ public void testLessThanIsSpecificationValid() { } @Test - public void testLessThanIsValid() { + public void testLessThan_GetTheValueMustDescription() { + BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); + assertEquals("be less than " + FIVE, instance.getTheValueMustDescription()); + } + + @Test + public void testLessThan_IsValid() { BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); assertTrue(instance.isValid(FOUR)); assertFalse(instance.isValid(FIVE)); assertFalse(instance.isValid(SIX)); assertFalse(instance.isValid(null)); - - instance = new BigDecValidator.LessThan(BigDecimal.ZERO); - assertTrue(instance.isValid(BigDecimal.ONE.negate())); - assertFalse(instance.isValid(BigDecimal.ZERO)); - assertFalse(instance.isValid(BigDecimal.ONE)); - - instance = new BigDecValidator.LessThan(NEGATIVE_NINETY_NINE); - assertTrue(instance.isValid(NEGATIVE_ONE_HUNDRED)); - assertFalse(instance.isValid(NEGATIVE_NINETY_NINE)); - assertFalse(instance.isValid(NEGATIVE_NINETY_EIGHT)); } @Test - public void testLessThanOrEqualsToIsSpecificationValid() { + public void testLessThanOrEqualsTo_IsSpecificationValid() { BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); assertTrue(instance.isSpecificationValid()); @@ -126,45 +111,40 @@ public void testLessThanOrEqualsToIsSpecificationValid() { } @Test - public void testLessThanOrEqualToIsValid() { + public void testLessThanOrEqualTo_GetTheValueMustDescription() { + BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); + assertEquals("be less than or equal to " + FIVE, instance.getTheValueMustDescription()); + } + + @Test + public void testLessThanOrEqualTo_IsValid() { BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); assertTrue(instance.isValid(FOUR)); assertTrue(instance.isValid(FIVE)); assertFalse(instance.isValid(SIX)); assertFalse(instance.isValid(null)); - - instance = new BigDecValidator.LessThanOrEqualTo(BigDecimal.ZERO); - assertTrue(instance.isValid(BigDecimal.ONE.negate())); - assertTrue(instance.isValid(BigDecimal.ZERO)); - assertFalse(instance.isValid(BigDecimal.ONE)); - - instance = new BigDecValidator.LessThanOrEqualTo(NEGATIVE_NINETY_NINE); - assertTrue(instance.isValid(NEGATIVE_ONE_HUNDRED)); - assertTrue(instance.isValid(NEGATIVE_NINETY_NINE)); - assertFalse(instance.isValid(NEGATIVE_NINETY_EIGHT)); - } @Test - public void testGreaterThanInvalidSpecificationMessage() { + public void testGreaterThan_InvalidSpecificationMessage() { BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } @Test - public void testGreaterThanOrEqualToInvalidSpecificationMessage() { + public void testGreaterThanOrEqualTo_InvalidSpecificationMessage() { BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } @Test - public void testLessThanInvalidSpecificationMessage() { + public void testLessThan_InvalidSpecificationMessage() { BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } @Test - public void testLessThanOrEqualToInvalidSpecificationMessage() { + public void testLessThanOrEqualTo_InvalidSpecificationMessage() { BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } From 64c799c147a3681dacdfaf29ce7d28b7821eb22e Mon Sep 17 00:00:00 2001 From: Chace Anderson Date: Sat, 3 Nov 2018 09:14:16 -0400 Subject: [PATCH 40/91] change validator invalid specification message; change validator tests --- .../andhow/valid/BigDecValidator.java | 2 +- .../andhow/valid/BigDecValidatorTest.java | 30 ++++--------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java index 3adffd08..3ca8a6d8 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/BigDecValidator.java @@ -28,7 +28,7 @@ public boolean isSpecificationValid() { @Override public String getInvalidSpecificationMessage() { - return "THIS VALIDATION IS ALWAYS VALID"; + return "The constraint may not be null"; } /** diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java index 1a0449d0..e51a68f1 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java @@ -10,7 +10,7 @@ public class BigDecValidatorTest { - private static String EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE = "THIS VALIDATION IS ALWAYS VALID"; + private static String EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE = "The constraint may not be null"; private static final BigDecimal FOUR = new BigDecimal("4.5678"); private static final BigDecimal FIVE = new BigDecimal("5.12345"); private static final BigDecimal SIX = new BigDecimal("6.452134563456"); @@ -26,6 +26,7 @@ public void testGreaterThan_IsSpecificationValid() { instance = new BigDecValidator.GreaterThan(null); assertFalse(instance.isSpecificationValid()); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } @Test @@ -53,6 +54,7 @@ public void testGreaterThanOrEqualTo_IsSpecificationValid() { instance = new BigDecValidator.GreaterThanOrEqualTo(null); assertFalse(instance.isSpecificationValid()); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } @Test @@ -81,6 +83,7 @@ public void testLessThan_IsSpecificationValid() { instance = new BigDecValidator.LessThan(null); assertFalse(instance.isSpecificationValid()); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } @Test @@ -108,6 +111,7 @@ public void testLessThanOrEqualsTo_IsSpecificationValid() { instance = new BigDecValidator.LessThanOrEqualTo(null); assertFalse(instance.isSpecificationValid()); + assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); } @Test @@ -124,28 +128,4 @@ public void testLessThanOrEqualTo_IsValid() { assertFalse(instance.isValid(SIX)); assertFalse(instance.isValid(null)); } - - @Test - public void testGreaterThan_InvalidSpecificationMessage() { - BigDecValidator.GreaterThan instance = new BigDecValidator.GreaterThan(FIVE); - assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); - } - - @Test - public void testGreaterThanOrEqualTo_InvalidSpecificationMessage() { - BigDecValidator.GreaterThanOrEqualTo instance = new BigDecValidator.GreaterThanOrEqualTo(FIVE); - assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); - } - - @Test - public void testLessThan_InvalidSpecificationMessage() { - BigDecValidator.LessThan instance = new BigDecValidator.LessThan(FIVE); - assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); - } - - @Test - public void testLessThanOrEqualTo_InvalidSpecificationMessage() { - BigDecValidator.LessThanOrEqualTo instance = new BigDecValidator.LessThanOrEqualTo(FIVE); - assertEquals(EXPECTED_DBL_VALIDATOR_INVALID_MESSAGE, instance.getInvalidSpecificationMessage()); - } } From acfb009b31f601fe3ee020f27c5d1ce0ff99f2da Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 18 Oct 2018 22:34:18 -0500 Subject: [PATCH 41/91] WIP: Initial testing --- .../compile/AndHowCompileProcessor.java | 13 +- ...a => AndHowCompileProcessor_InitTest.java} | 58 +---- .../AndHowCompileProcessor_PropertyTest.java | 200 ++++++++++++++++++ .../andhow/compile/BadProps_1.java | 21 ++ .../andhow/compile/HappyPathProps.java | 21 ++ 5 files changed, 253 insertions(+), 60 deletions(-) rename andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/{AndHowCompileProcessorTest.java => AndHowCompileProcessor_InitTest.java} (73%) create mode 100644 andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java create mode 100644 andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java create mode 100644 andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/HappyPathProps.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index 2838edde..a99e3996 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -8,6 +8,7 @@ import java.util.*; import javax.annotation.processing.*; import javax.lang.model.element.*; +import javax.tools.Diagnostic; import javax.tools.FileObject; @@ -147,14 +148,20 @@ public boolean process(Set annotations, RoundEnvironment } if (ret.getErrors().size() > 0) { - LOG.error( + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "AndHow Property definition errors prevented compilation to complete. " + "Each of the following errors must be fixed before compilation is possible."); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "AndHow Property definition errors discovered: " + ret.getErrors().size()); for (String err : ret.getErrors()) { - LOG.error("AndHow Property Error: {0}", err); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "AndHow Property Error: " + err); } - throw new RuntimeException("AndHowCompileProcessor threw a fatal exception - See error log for details."); + //The docs for printMessage(Kind.Error) say that an error + //message should actually throw an error, but that does not + //seem to happen, so the runtime exception is needed + throw new RuntimeException("AndHowCompileProcessor threw a fatal exception - See the error details above."); } } diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java similarity index 73% rename from andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTest.java rename to andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java index 2e64408d..f937282a 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java @@ -17,7 +17,7 @@ * https://gist.github.com/johncarl81/46306590cbdde5a3003f * @author ericeverman */ -public class AndHowCompileProcessorTest { +public class AndHowCompileProcessor_InitTest { // //Names of classes in the resources directory, used for compile testing of @@ -28,62 +28,6 @@ public class AndHowCompileProcessorTest { protected static final String AndHowTestInitAbstract_NAME = "org.yarnandtail.andhow.compile.AndHowTestInitAbstract"; protected static final String AndHowTestInitA_NAME = "org.yarnandtail.andhow.compile.AndHowTestInitA"; protected static final String AndHowTestInitB_NAME = "org.yarnandtail.andhow.compile.AndHowTestInitB"; - - - @Test - public void testCompileAnnotationProcessorOutput() throws Exception { - - final String CLASS_PACKAGE = "org.yarnandtail.andhow.compile"; - final String CLASS_NAME = CLASS_PACKAGE + ".PropertySample"; - final String CLASS_SOURCE_PATH = "/" + CLASS_NAME.replace(".", "/") + ".java"; - final String GEN_CLASS_NAME = CLASS_PACKAGE + ".$PropertySample_AndHowProps"; - - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - final MemoryFileManager manager = new MemoryFileManager(compiler); - TestClassLoader loader = new TestClassLoader(manager); - - List options=new ArrayList(); - //options.add("-verbose"); - - - Set input = new HashSet(); - String classContent = IOUtil.getUTF8ResourceAsString(CLASS_SOURCE_PATH); - input.add(new TestSource(CLASS_NAME, JavaFileObject.Kind.SOURCE, classContent)); - - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); - task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); - task.call(); - - Object genClass = loader.loadClass(GEN_CLASS_NAME).newInstance(); - String genSvsFile = IOUtil.toString(loader.getResourceAsStream("/META-INF/services/org.yarnandtail.andhow.service.PropertyRegistrar"), Charset.forName("UTF-8")); - - assertNotNull(genClass); - - PropertyRegistrar registrar = (PropertyRegistrar)genClass; - - //final String ROOT = "org.yarnandtail.andhow.compile.PropertySample"; - - assertEquals(CLASS_NAME, registrar.getRootCanonicalName()); - List propRegs = registrar.getRegistrationList(); - - assertEquals(CLASS_NAME + ".STRING", propRegs.get(0).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".STRING_PUB", propRegs.get(1).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PC.STRING", propRegs.get(2).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PC.STRING_PUB", propRegs.get(3).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PC.PC_PC.STRING", propRegs.get(4).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PC.PC_PI.STRING", propRegs.get(5).getCanonicalPropertyName()); - - assertEquals(CLASS_NAME + ".PI.STRING", propRegs.get(6).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PI.PI_DC.STRING", propRegs.get(7).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PI.PI_DC.STRING_PUB", propRegs.get(8).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PI.PI_DI.STRING", propRegs.get(9).getCanonicalPropertyName()); - assertEquals(CLASS_NAME + ".PI.PI_DI.STRING_PUB", propRegs.get(10).getCanonicalPropertyName()); - - // - //Test the registration file - assertNotNull(genSvsFile); - assertEquals(GEN_CLASS_NAME, genSvsFile.trim()); - } @Test diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java new file mode 100644 index 00000000..999e032f --- /dev/null +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java @@ -0,0 +1,200 @@ +package org.yarnandtail.andhow.compile; + +import org.yarnandtail.compile.*; +import org.yarnandtail.andhow.service.PropertyRegistrar; +import org.yarnandtail.andhow.service.PropertyRegistration; +import java.io.*; +import java.nio.charset.Charset; +import java.util.*; +import javax.tools.*; +import org.junit.Test; +import org.yarnandtail.andhow.util.IOUtil; + +import static org.junit.Assert.*; +import org.junit.Before; + +/** + * A lot of this code was borrowed from here: + * https://gist.github.com/johncarl81/46306590cbdde5a3003f + * @author ericeverman + */ +public class AndHowCompileProcessor_PropertyTest { + + /** Classpath of the generated service file for AndHow property registration */ + static final String PROPERTY_REGISTRAR_CLASSPATH = + "/META-INF/services/org.yarnandtail.andhow.service.PropertyRegistrar"; + + /** Shortcut to this package */ + static final String pkg = AndHowCompileProcessor_PropertyTest.class.getPackage().getName(); + + JavaCompiler compiler; + MemoryFileManager manager; + TestClassLoader loader; + + @Before + public void setupTest() { + compiler = ToolProvider.getSystemJavaCompiler(); + manager = new MemoryFileManager(compiler); + loader = new TestClassLoader(manager); + } + + /** + * The source path to where to find this file on the classpath + * @param classPackage + * @param simpleClassName + * @return + */ + public String srcPath(String classPackage, String simpleClassName) { + return "/" + classPackage.replace(".", "/") + "/" + simpleClassName + ".java"; + } + + /** + * Build the canonical name of the generated class from the source class. + * + * @param classPackage + * @param simpleClassName + * @return + */ + public String genName(String classPackage, String simpleClassName) { + return classPackage + ".$" + simpleClassName + "_AndHowProps"; + } + + /** + * Full canonical name of the class. + * @param classPackage + * @param simpleClassName + * @return + */ + public String fullName(String classPackage, String simpleClassName) { + return classPackage + "." + simpleClassName; + } + + /** + * Builds a new TestSource object for a Java source file on the classpath + * @param classPackage + * @param simpleClassName + * @return + * @throws Exception + */ + public TestSource buidTestSource(String classPackage, String simpleClassName) throws Exception { + String classContent = IOUtil.getUTF8ResourceAsString(srcPath(pkg, simpleClassName)); + return new TestSource(fullName(pkg, simpleClassName), JavaFileObject.Kind.SOURCE, classContent); + } + + @Test + public void testComplexNestedPropertySampleClass() throws Exception { + + String classSimpleName = "PropertySample"; + + List options=new ArrayList(); + //options.add("-verbose"); + + + Set input = new HashSet(); + input.add(buidTestSource(pkg, classSimpleName)); + + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); + task.call(); + + Object genClass = loader.loadClass(genName(pkg, classSimpleName)).newInstance(); + String genSvsFile = IOUtil.toString( + loader.getResourceAsStream(PROPERTY_REGISTRAR_CLASSPATH), Charset.forName("UTF-8")); + + assertNotNull(genClass); + + PropertyRegistrar registrar = (PropertyRegistrar)genClass; + + //final String ROOT = "org.yarnandtail.andhow.compile.PropertySample"; + + assertEquals(fullName(pkg, classSimpleName), registrar.getRootCanonicalName()); + List propRegs = registrar.getRegistrationList(); + + assertEquals(fullName(pkg, classSimpleName) + ".STRING", propRegs.get(0).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".STRING_PUB", propRegs.get(1).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PC.STRING", propRegs.get(2).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PC.STRING_PUB", propRegs.get(3).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PC.PC_PC.STRING", propRegs.get(4).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PC.PC_PI.STRING", propRegs.get(5).getCanonicalPropertyName()); + + assertEquals(fullName(pkg, classSimpleName) + ".PI.STRING", propRegs.get(6).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PI.PI_DC.STRING", propRegs.get(7).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PI.PI_DC.STRING_PUB", propRegs.get(8).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PI.PI_DI.STRING", propRegs.get(9).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".PI.PI_DI.STRING_PUB", propRegs.get(10).getCanonicalPropertyName()); + + // + //Test the registration file + assertNotNull(genSvsFile); + assertEquals(genName(pkg, classSimpleName), genSvsFile.trim()); + } + + + @Test + public void testSimpleHappyPathClass() throws Exception { + + String classSimpleName = "HappyPathProps"; + + List options=new ArrayList(); + //options.add("-verbose"); + + + Set input = new HashSet(); + input.add(buidTestSource(pkg, classSimpleName)); + + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); + task.call(); + + Object genClass = loader.loadClass(genName(pkg, classSimpleName)).newInstance(); + String genSvsFile = IOUtil.toString( + loader.getResourceAsStream(PROPERTY_REGISTRAR_CLASSPATH), Charset.forName("UTF-8")); + + assertNotNull(genClass); + + PropertyRegistrar registrar = (PropertyRegistrar)genClass; + + //final String ROOT = "org.yarnandtail.andhow.compile.PropertySample"; + + assertEquals(fullName(pkg, classSimpleName), registrar.getRootCanonicalName()); + List propRegs = registrar.getRegistrationList(); + + assertEquals(fullName(pkg, classSimpleName) + ".STR_1", propRegs.get(0).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".STR_2", propRegs.get(1).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".INNER.STR_1", propRegs.get(2).getCanonicalPropertyName()); + assertEquals(fullName(pkg, classSimpleName) + ".INNER.STR_2", propRegs.get(3).getCanonicalPropertyName()); + + // + //Test the registration file + assertNotNull(genSvsFile); + assertEquals(genName(pkg, classSimpleName), genSvsFile.trim()); + } + + + @Test + public void testMissingStaticAndFinalModifiersOnProperties() throws Exception { + + String classSimpleName = "BadProps_1"; + + List options=new ArrayList(); + //options.add("-verbose"); + + + Set input = new HashSet(); + input.add(buidTestSource(pkg, classSimpleName)); + + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); + + try { + task.call(); + fail("This should have thrown an exception"); + } catch (RuntimeException e) { + //add some testing here - would be good to have a custom exceptoin + //so we can get details from it. + } + + } + + +} diff --git a/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java new file mode 100644 index 00000000..84f14075 --- /dev/null +++ b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java @@ -0,0 +1,21 @@ +package org.yarnandtail.andhow.compile; + +import org.yarnandtail.andhow.property.StrProp; + +/** + * A class that should compile just fine w/ AndHow's Annotation compiler + * + * @author ericeverman + */ +public class BadProps_1 { + + private static StrProp STR_1 = StrProp.builder().build(); //---Should be final!!! + public final StrProp STR_2 = StrProp.builder().build(); //---Should be static!!! + + private static interface INNER { + + static final StrProp STR_1 = StrProp.builder().build(); + static final StrProp STR_2 = StrProp.builder().build(); + } + +} diff --git a/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/HappyPathProps.java b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/HappyPathProps.java new file mode 100644 index 00000000..c8c4349a --- /dev/null +++ b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/HappyPathProps.java @@ -0,0 +1,21 @@ +package org.yarnandtail.andhow.compile; + +import org.yarnandtail.andhow.property.StrProp; + +/** + * A class that should compile just fine w/ AndHow's Annotation compiler + * + * @author ericeverman + */ +public class HappyPathProps { + + private static final StrProp STR_1 = StrProp.builder().build(); + public static final StrProp STR_2 = StrProp.builder().build(); + + private static interface INNER { + + static final StrProp STR_1 = StrProp.builder().build(); + static final StrProp STR_2 = StrProp.builder().build(); + } + +} From a66ecf069c6912b3dc4dd4d103c567d4bcf874a2 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Sat, 20 Oct 2018 21:42:10 -0500 Subject: [PATCH 42/91] WIP: new exception and problem type --- .../compile/AndHowCompileException.java | 24 +++++++++++++++++++ .../andhow/compile/CompileProblem.java | 13 ++++++++++ 2 files changed, 37 insertions(+) create mode 100644 andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java create mode 100644 andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java new file mode 100644 index 00000000..479a37b0 --- /dev/null +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java @@ -0,0 +1,24 @@ +package org.yarnandtail.andhow.compile; + +/** + * Exception thrown by the AndHowCompileProcessor for any type of problem + * during compile. + * + * This exception can accumulate a list of Problems so that all problems can + * be found in a single pass and for easier testing (this exception can be + * inspected to determine the exact problem). + * + * @author ericeverman + */ +public class AndHowCompileException extends RuntimeException { + + public AndHowCompileException() { + } + + + @Override + public String getMessage() { + return super.getMessage(); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java new file mode 100644 index 00000000..1a4a554d --- /dev/null +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java @@ -0,0 +1,13 @@ +package org.yarnandtail.andhow.compile; + +/** + * + * @author ericevermanpersonal + */ +public class CompileProblem { + + String groupName; + + String propName; + +} From 429775a1e100bf7b40ac9cea3fe6ec58fe4f881a Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Mon, 22 Oct 2018 22:44:27 -0500 Subject: [PATCH 43/91] Improved reporting of compile time errors * Improved testing --- .../compile/AndHowCompileException.java | 19 ++++- .../compile/AndHowCompileProcessor.java | 39 ++++++---- .../andhow/compile/CompileProblem.java | 72 ++++++++++++++++- .../andhow/compile/CompileUnit.java | 35 +++++---- .../AndHowCompileProcessor_PropertyTest.java | 40 ++++++++-- .../andhow/compile/CompileProblemTest.java | 78 +++++++++++++++++++ .../andhow/compile/BadProps_1.java | 14 +++- .../andhow/compile/BadProps_2.java | 14 ++++ 8 files changed, 266 insertions(+), 45 deletions(-) create mode 100644 andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java create mode 100644 andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_2.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java index 479a37b0..5e93e455 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java @@ -1,5 +1,8 @@ package org.yarnandtail.andhow.compile; +import java.util.Collections; +import java.util.List; + /** * Exception thrown by the AndHowCompileProcessor for any type of problem * during compile. @@ -12,13 +15,25 @@ */ public class AndHowCompileException extends RuntimeException { - public AndHowCompileException() { + final List problems; + + public AndHowCompileException(List problems) { + if (problems != null) { + this.problems = problems; + } else { + this.problems = Collections.emptyList(); + } } @Override public String getMessage() { - return super.getMessage(); //To change body of generated methods, choose Tools | Templates. + return "The AndHowCompileProcessor found a problem during compilation " + + "and threw a fatal exception - See the error details listed above"; + } + + public List getProblems() { + return problems; } } diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index a99e3996..c5ab907e 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -48,6 +48,8 @@ public class AndHowCompileProcessor extends AbstractProcessor { private final List initClasses = new ArrayList(); //List of init classes (should only ever be 1) private final List testInitClasses = new ArrayList(); //List of test init classes (should only ever be 1) + private final List problems = new ArrayList(); //List of problems found. >0== RuntimeException + public AndHowCompileProcessor() { //required by Processor API runDate = new GregorianCalendar(); @@ -99,6 +101,24 @@ public boolean process(Set annotations, RoundEnvironment throw new RuntimeException("Exception while trying to write generated files", e); } + if (problems.size() > 0) { + + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "AndHow Property definition errors prevented compilation to complete. " + + "Each of the following errors must be fixed before compilation is possible."); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + "AndHow Property definition errors discovered: " + problems.size()); + for (CompileProblem err : problems) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, + err.getFullMessage()); + } + + //The docs for printMessage(Kind.Error) say that an error + //message should actually throw an error, but that does not + //seem to happen, so the runtime exception is needed + throw new AndHowCompileException(problems); + } + } else { LOG.trace("Another round of annotation processing. Current root element count: {0}", roundEnv.getRootElements().size()); @@ -146,23 +166,8 @@ public boolean process(Set annotations, RoundEnvironment throw new RuntimeException(ex); } } - - if (ret.getErrors().size() > 0) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "AndHow Property definition errors prevented compilation to complete. " + - "Each of the following errors must be fixed before compilation is possible."); - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "AndHow Property definition errors discovered: " + ret.getErrors().size()); - for (String err : ret.getErrors()) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "AndHow Property Error: " + err); - } - - //The docs for printMessage(Kind.Error) say that an error - //message should actually throw an error, but that does not - //seem to happen, so the runtime exception is needed - throw new RuntimeException("AndHowCompileProcessor threw a fatal exception - See the error details above."); - } + + problems.addAll(ret.getProblems()); } } diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java index 1a4a554d..3d571a4d 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java @@ -1,13 +1,83 @@ package org.yarnandtail.andhow.compile; +import org.yarnandtail.andhow.util.TextUtil; + /** * * @author ericevermanpersonal */ -public class CompileProblem { +public abstract class CompileProblem { String groupName; String propName; + public String getGroupName() { + return groupName; + } + + public String getPropertyName() { + return propName; + } + + @Override + public boolean equals(Object o) { + if (o != null && this.getClass().isInstance(o)) { + CompileProblem cp = (CompileProblem)o; + + return + this.groupName.equals(cp.groupName) && + this.propName.equals(cp.propName); + } else { + return false; + } + } + + public abstract String getFullMessage(); + + static class PropMissingStatic extends CompileProblem { + + public PropMissingStatic(String groupName, String propName) { + this.groupName = groupName; + this.propName = propName; + } + + @Override + public String getFullMessage() { + return TextUtil.format( + "The AndHow Property '{}' in the class '{}' must be declared as static.", + groupName, propName); + } + } + + static class PropMissingFinal extends CompileProblem { + + public PropMissingFinal(String groupName, String propName) { + this.groupName = groupName; + this.propName = propName; + } + + @Override + public String getFullMessage() { + return TextUtil.format( + "The AndHow Property '{}' in the class '{}' must be declared as final.", + groupName, propName); + } + } + + static class PropMissingStaticFinal extends CompileProblem { + + public PropMissingStaticFinal(String groupName, String propName) { + this.groupName = groupName; + this.propName = propName; + } + + @Override + public String getFullMessage() { + return TextUtil.format( + "The AndHow Property '{}' in the class '{}' must be declared as final.", + groupName, propName); + } + } + } diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java index d3f78256..954de4dc 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java @@ -32,7 +32,7 @@ public class CompileUnit { private final String classCanonName; private PropertyRegistrationList registrations; //late init - private List errors; //late init + private List errors; //late init private boolean initClass; //True if an AndHowInit instance (and not AndHowTestInit) private boolean testInitClass; //True if an AndHowTestInit instance @@ -117,7 +117,21 @@ public boolean addProperty(SimpleVariable variableElement) { return true; } else { - addPropertyError(variableElement.getName(), "New AndHow Properties must be assigned to a static final field."); + + if (errors == null) { + errors = new ArrayList(); + } + + String parentName = NameUtil.getJavaName(classCanonName, this.getInnerPathNames()); + + if (variableElement.isStatic()) { + errors.add(new CompileProblem.PropMissingFinal(parentName, variableElement.getName())); + } else if (variableElement.isFinal()) { + errors.add(new CompileProblem.PropMissingStatic(parentName, variableElement.getName())); + } else { + errors.add(new CompileProblem.PropMissingStaticFinal(parentName, variableElement.getName())); + } + return false; } } @@ -193,17 +207,6 @@ public List getInnerPathNames() { return pathNames; } - public void addPropertyError(String propName, String msg) { - - if (errors == null) { - errors = new ArrayList(); - } - - String parentName = NameUtil.getJavaName(classCanonName, this.getInnerPathNames()); - - errors.add("The AndHow Property '" + propName + "' in " + parentName + " is invalid: " + msg); - } - /** * The fully qualified name of the top level class this CompileUnit is for. * @@ -263,16 +266,16 @@ public PropertyRegistrationList getRegistrations() { * * @return */ - public List getErrors() { + public List getProblems() { if (errors != null) { return errors; } else { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } } /** - * Returns true if the getErrors() list would be non-empty. + * Returns true if the getProblems() list would be non-empty. * * @return */ diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java index 999e032f..dfa743ee 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java @@ -9,6 +9,7 @@ import javax.tools.*; import org.junit.Test; import org.yarnandtail.andhow.util.IOUtil; +import static org.yarnandtail.andhow.compile.CompileProblem.*; import static org.junit.Assert.*; import org.junit.Before; @@ -174,14 +175,13 @@ public void testSimpleHappyPathClass() throws Exception { @Test public void testMissingStaticAndFinalModifiersOnProperties() throws Exception { - String classSimpleName = "BadProps_1"; - List options=new ArrayList(); //options.add("-verbose"); Set input = new HashSet(); - input.add(buidTestSource(pkg, classSimpleName)); + input.add(buidTestSource(pkg, "BadProps_1")); + input.add(buidTestSource(pkg, "BadProps_2")); JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); @@ -190,8 +190,38 @@ public void testMissingStaticAndFinalModifiersOnProperties() throws Exception { task.call(); fail("This should have thrown an exception"); } catch (RuntimeException e) { - //add some testing here - would be good to have a custom exceptoin - //so we can get details from it. + + assertTrue(e.getCause() instanceof AndHowCompileException); + + AndHowCompileException ace = (AndHowCompileException) e.getCause(); + + String CLASSNAME = "org.yarnandtail.andhow.compile.BadProps_1"; + String INNER_CLASSNAME = CLASSNAME + "$INNER_CLASS"; + + //Expected CompileProblems + //All problems for both classes should be reported in one exception + CompileProblem prob1 = new PropMissingFinal(CLASSNAME, "STR_1"); + CompileProblem prob2 = new PropMissingStatic(CLASSNAME, "STR_2"); + CompileProblem prob3 = new PropMissingStaticFinal(CLASSNAME, "STR_3"); + CompileProblem prob4 = new PropMissingFinal(INNER_CLASSNAME, "STR_1"); + CompileProblem prob5 = new PropMissingStatic(INNER_CLASSNAME, "STR_2"); + CompileProblem prob6 = new PropMissingStaticFinal(INNER_CLASSNAME, "STR_3"); + + //In 2nd class + CompileProblem prob7 = new PropMissingFinal( + "org.yarnandtail.andhow.compile.BadProps_2", "STR_1"); + + assertEquals(7, ace.getProblems().size()); + assertTrue(ace.getProblems().contains(prob1)); + assertTrue(ace.getProblems().contains(prob2)); + assertTrue(ace.getProblems().contains(prob3)); + assertTrue(ace.getProblems().contains(prob4)); + assertTrue(ace.getProblems().contains(prob5)); + assertTrue(ace.getProblems().contains(prob6)); + assertTrue(ace.getProblems().contains(prob7)); + + } catch (Throwable t) { + fail("This should have thrown an AndHowCompileException"); } } diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java new file mode 100644 index 00000000..b3f0c20c --- /dev/null +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java @@ -0,0 +1,78 @@ +/* + */ +package org.yarnandtail.andhow.compile; + +import org.junit.Test; +import static org.junit.Assert.*; +import static org.yarnandtail.andhow.compile.CompileProblem.*; + +/** + * + * @author ericeverman + */ +public class CompileProblemTest { + + public CompileProblemTest() { + } + + + @Test + public void testBasicPropsOfPropMissingStatic() { + PropMissingStatic prob1 = new PropMissingStatic("g1", "p1"); + PropMissingStatic prob2 = new PropMissingStatic("g1", "p1"); + PropMissingStatic prob3 = new PropMissingStatic("Xg1", "Xp1"); + + assertEquals("g1", prob1.getGroupName()); + assertEquals("p1", prob1.getPropertyName()); + assertTrue(prob1.getFullMessage().contains("g1")); + assertTrue(prob1.getFullMessage().contains("p1")); + + assertEquals(prob1, prob2); + assertNotEquals(prob1, prob3); + assertNotEquals(prob2, prob3); + } + + @Test + public void testBasicPropsOfPropMissingFinal() { + PropMissingFinal prob1 = new PropMissingFinal("g1", "p1"); + PropMissingFinal prob2 = new PropMissingFinal("g1", "p1"); + PropMissingFinal prob3 = new PropMissingFinal("Xg1", "Xp1"); + + assertEquals("g1", prob1.getGroupName()); + assertEquals("p1", prob1.getPropertyName()); + assertTrue(prob1.getFullMessage().contains("g1")); + assertTrue(prob1.getFullMessage().contains("p1")); + + assertEquals(prob1, prob2); + assertNotEquals(prob1, prob3); + assertNotEquals(prob2, prob3); + } + + @Test + public void testBasicPropsOfPropMissingStaticFinal() { + PropMissingStaticFinal prob1 = new PropMissingStaticFinal("g1", "p1"); + PropMissingStaticFinal prob2 = new PropMissingStaticFinal("g1", "p1"); + PropMissingStaticFinal prob3 = new PropMissingStaticFinal("Xg1", "Xp1"); + + assertEquals("g1", prob1.getGroupName()); + assertEquals("p1", prob1.getPropertyName()); + assertTrue(prob1.getFullMessage().contains("g1")); + assertTrue(prob1.getFullMessage().contains("p1")); + + assertEquals(prob1, prob2); + assertNotEquals(prob1, prob3); + assertNotEquals(prob2, prob3); + } + + @Test + public void testEqualsAcrossTypes() { + CompileProblem prob1 = new PropMissingStatic("g1", "p1"); + CompileProblem prob2 = new PropMissingFinal("g1", "p1"); + CompileProblem prob3 = new PropMissingStaticFinal("g1", "p1"); + + assertNotEquals(prob1, prob2); + assertNotEquals(prob1, prob3); + assertNotEquals(prob2, prob3); + } + +} diff --git a/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java index 84f14075..56cc8efa 100644 --- a/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java +++ b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_1.java @@ -3,7 +3,7 @@ import org.yarnandtail.andhow.property.StrProp; /** - * A class that should compile just fine w/ AndHow's Annotation compiler + * A class that should have compile problems * * @author ericeverman */ @@ -11,11 +11,17 @@ public class BadProps_1 { private static StrProp STR_1 = StrProp.builder().build(); //---Should be final!!! public final StrProp STR_2 = StrProp.builder().build(); //---Should be static!!! - + StrProp STR_3 = StrProp.builder().build(); //---Should be static final!!! + private static interface INNER { - - static final StrProp STR_1 = StrProp.builder().build(); + StrProp STR_1 = StrProp.builder().build(); //static final is assumed static final StrProp STR_2 = StrProp.builder().build(); } + private static class INNER_CLASS { + private static StrProp STR_1 = StrProp.builder().build(); //---Should be final!!! + public final StrProp STR_2 = StrProp.builder().build(); //---Should be static!!! + StrProp STR_3 = StrProp.builder().build(); //---Should be static final!!! + } + } diff --git a/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_2.java b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_2.java new file mode 100644 index 00000000..0fe8e6fa --- /dev/null +++ b/andhow-annotation-processor/src/test/resources/org/yarnandtail/andhow/compile/BadProps_2.java @@ -0,0 +1,14 @@ +package org.yarnandtail.andhow.compile; + +import org.yarnandtail.andhow.property.StrProp; + +/** + * A class that should have compile problems + * + * @author ericeverman + */ +public class BadProps_2 { + + private static StrProp STR_1 = StrProp.builder().build(); //---Should be final!!! + +} From 01d2c863a23c03a3e171ab59c6a195d64ea3d9fa Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Tue, 23 Oct 2018 21:52:03 -0500 Subject: [PATCH 44/91] Completely removed the SimpleVariable class - easily factored out --- .../andhow/compile/AndHowElementScanner7.java | 4 +- .../andhow/compile/CompileUnit.java | 39 ++++++------------- .../andhow/compile/SimpleVariable.java | 39 ------------------- .../andhow/compile/CompileUnitTest.java | 12 +++--- .../PropertyRegistrarClassGeneratorTest.java | 12 +++--- 5 files changed, 25 insertions(+), 81 deletions(-) delete mode 100644 andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/SimpleVariable.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowElementScanner7.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowElementScanner7.java index fed4c76e..9fcad8d8 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowElementScanner7.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowElementScanner7.java @@ -78,9 +78,9 @@ public CompileUnit visitVariable(VariableElement e, String p) { ts.scan(trees.getPath(e), marker); if (marker.isNewProperty()) { compileUnit.addProperty( - new SimpleVariable(e.getSimpleName().toString(), + e.getSimpleName().toString(), e.getModifiers().contains(Modifier.STATIC), - e.getModifiers().contains(Modifier.FINAL)) + e.getModifiers().contains(Modifier.FINAL) ); if (LOG.isLoggable(Level.FINE)) { diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java index 954de4dc..6c262709 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUnit.java @@ -101,19 +101,20 @@ public SimpleType popType() { * If modifiers are invalid, an error will be recorded rather than a * Property. * - * @param variableElement A SimpleType representing a variable to which an - * AndHow property is constructed and assigned to. + * @param name The name of the variable the Property is assigned to. + * @param _static Does the variable has the static modifier? + * @param _final Is the variable declared as static? * @return True if the property could be added, false if an error was * recorded instead. */ - public boolean addProperty(SimpleVariable variableElement) { + public boolean addProperty(String name, boolean _static, boolean _final) { - if (variableElement.isStatic() && variableElement.isFinal()) { + if (_static && _final) { if (registrations == null) { registrations = new PropertyRegistrationList(classCanonName); } - registrations.add(variableElement.getName(), getInnerPathNames()); + registrations.add(name, getInnerPathNames()); return true; } else { @@ -124,36 +125,18 @@ public boolean addProperty(SimpleVariable variableElement) { String parentName = NameUtil.getJavaName(classCanonName, this.getInnerPathNames()); - if (variableElement.isStatic()) { - errors.add(new CompileProblem.PropMissingFinal(parentName, variableElement.getName())); - } else if (variableElement.isFinal()) { - errors.add(new CompileProblem.PropMissingStatic(parentName, variableElement.getName())); + if (_static) { + errors.add(new CompileProblem.PropMissingFinal(parentName, name)); + } else if (_final) { + errors.add(new CompileProblem.PropMissingStatic(parentName, name)); } else { - errors.add(new CompileProblem.PropMissingStaticFinal(parentName, variableElement.getName())); + errors.add(new CompileProblem.PropMissingStaticFinal(parentName, name)); } return false; } } - /** - * Register an AndHow Property declaration in the current scope - either - * directly in the the top level class or the recorded path to an inner - * class. - * - * If modifiers are invalid, an error will be recorded rather than a - * Property. - * - * @param name The name of the variable the Property is assigned to. - * @param _static Does the variable has the static modifier? - * @param _final Is the variable declared as static? - * @return True if the property could be added, false if an error was - * recorded instead. - */ - public boolean addProperty(String name, boolean _static, boolean _final) { - return addProperty(new SimpleVariable(name, _static, _final)); - } - /** * Return the state of inner class nesting from the outermost to the * innermost. diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/SimpleVariable.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/SimpleVariable.java deleted file mode 100644 index 204cfe14..00000000 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/SimpleVariable.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.yarnandtail.andhow.compile; - -/** - * Compile time representation of a variable w/ just enough information - * to decide if it is a valid variable for an AndHow Property to be constructed - * and assigned to. - */ -public class SimpleVariable { - - private final String name; - private final boolean _static; - private final boolean _final; - - /** - * - * @param name Name of this variable - * @param _static Is this var marked as static? - * @param _final Is this var marked as final? - */ - public SimpleVariable(String name, boolean _static, boolean _final) { - this.name = name; - this._static = _static; - this._final = _final; - } - - public String getName() { - return name; - } - - public boolean isStatic() { - return _static; - } - - public boolean isFinal() { - return _final; - } - - -} diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java index 2e474f9a..05dfa70a 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java @@ -32,12 +32,12 @@ public void testHappyPath() { CompileUnit cu = new CompileUnit(ROOT_QUAL_NAME); //root prop - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); //0 + cu.addProperty(PROP1_NAME, true, true); //0 { //1st inner class cu.pushType(INNER1_SIMP_NAME, true); - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); //1 + cu.addProperty(PROP1_NAME, true, true); //1 { //2nd inner class @@ -51,8 +51,8 @@ public void testHappyPath() { assertEquals(INNER2_SIMP_NAME, cu.getInnerPathNames().get(1)); // - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); //2 - cu.addProperty(new SimpleVariable(PROP2_NAME, true, true)); //3 + cu.addProperty(PROP1_NAME, true, true); //2 + cu.addProperty(PROP2_NAME, true, true); //3 cu.popType(); //Check we have the right inner path remaining @@ -61,12 +61,12 @@ public void testHappyPath() { } //one more at 1st inner level - cu.addProperty(new SimpleVariable(PROP2_NAME, true, true)); //4 + cu.addProperty(PROP2_NAME, true, true); //4 cu.popType(); } //one more at root level - cu.addProperty(new SimpleVariable(PROP2_NAME, true, true)); //5 + cu.addProperty(PROP2_NAME, true, true); //5 PropertyRegistrationList list = cu.getRegistrations(); diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java index fb8cf10b..bf868ae6 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java @@ -204,7 +204,7 @@ public CompileUnit simpleCompileUnit() { CompileUnit cu = new CompileUnit(ROOT_QUAL_NAME); //root prop - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); + cu.addProperty(PROP1_NAME, true, true); cu.addProperty(PROP2_NAME, true, true); return cu; @@ -215,7 +215,7 @@ public CompileUnit simpleCompileUnit_DefaultPkg() { CompileUnit cu = new CompileUnit(ROOT_SIMPLE_NAME); //root prop - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); + cu.addProperty(PROP1_NAME, true, true); cu.addProperty(PROP2_NAME, true, true); return cu; @@ -229,18 +229,18 @@ public CompileUnit complexCompileUnit() { CompileUnit cu = new CompileUnit(ROOT_QUAL_NAME); //root prop - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); + cu.addProperty(PROP1_NAME, true, true); { //1st inner class cu.pushType(INNER1_SIMP_NAME, true); - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); + cu.addProperty(PROP1_NAME, true, true); { //2nd inner class cu.pushType(INNER2_SIMP_NAME, true); - cu.addProperty(new SimpleVariable(PROP1_NAME, true, true)); - cu.addProperty(new SimpleVariable(PROP2_NAME, true, true)); + cu.addProperty(PROP1_NAME, true, true); + cu.addProperty(PROP2_NAME, true, true); cu.popType(); } From 315642481cfce56558053c29439088acd0335001 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 24 Oct 2018 23:26:30 -0500 Subject: [PATCH 45/91] Removed TooManyInitClassesException and replaced it with a CompileProblem subtype All Problems are reported together now, regardless if there are TooManyInit problems and Property problems at the same time Move several fields to final Updated tests where needed --- .../compile/AndHowCompileException.java | 50 ++++++++++- .../compile/AndHowCompileProcessor.java | 71 +++++++++------- .../andhow/compile/CompileProblem.java | 84 +++++++++++++++++-- .../compile/TooManyInitClassesException.java | 50 ----------- .../AndHowCompileProcessor_InitTest.java | 52 ++++++------ 5 files changed, 188 insertions(+), 119 deletions(-) delete mode 100644 andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/TooManyInitClassesException.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java index 5e93e455..f96a2b28 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java @@ -5,7 +5,7 @@ /** * Exception thrown by the AndHowCompileProcessor for any type of problem - * during compile. + * during the compile. * * This exception can accumulate a list of Problems so that all problems can * be found in a single pass and for easier testing (this exception can be @@ -15,21 +15,63 @@ */ public class AndHowCompileException extends RuntimeException { + String msg = "The AndHowCompileProcessor found a problem during compilation " + + "and threw a fatal exception - See the error details listed nearby."; final List problems; - + final Throwable cause; + + + /** + * Instance when there are one or more AndHow domain 'problems' with the + * code being compiled. + * + * Examples would include Properties that are not static final + * or too many init classes on the classpath. + * + * @param problems A list of problems found during compilation. + */ public AndHowCompileException(List problems) { + + cause = null; + if (problems != null) { this.problems = problems; } else { this.problems = Collections.emptyList(); } } + + /** + * Instance when there is some unexpected, non-AndHow related exception. + * + * Example: Unwritable file system. + * + * @param message AndHow context description + * @param cause Error caught that caused the issue + */ + public AndHowCompileException(String message, Throwable cause) { + msg = message; + this.cause = cause; + this.problems = Collections.emptyList(); + } + + @Override + public synchronized Throwable getCause() { + if (cause != null) { + return cause; + } else { + return super.getCause(); + } + } @Override public String getMessage() { - return "The AndHowCompileProcessor found a problem during compilation " + - "and threw a fatal exception - See the error details listed above"; + if (msg != null) { + return msg; + } else { + return super.getMessage(); + } } public List getProblems() { diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index c5ab907e..edb61548 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -67,42 +67,41 @@ public boolean process(Set annotations, RoundEnvironment if (isLastRound) { LOG.debug("Final round of annotation processing. Total root element count: {0}", roundEnv.getRootElements().size()); - try { - if (initClasses.size() == 1) { - - LOG.info("Found exactly 1 {0} class: {1}", INIT_CLASS_NAME, initClasses.get(0).fullClassName); - writeServiceFile(filer, AndHowInit.class.getCanonicalName(), initClasses); - - } else if (initClasses.size() > 1) { - TooManyInitClassesException err = - new TooManyInitClassesException(INIT_CLASS_NAME, initClasses); + + if (initClasses.size() > 1) { + problems.add(new CompileProblem.TooManyInitClasses( + INIT_CLASS_NAME, initClasses)); + } + + if (testInitClasses.size() > 1) { + problems.add(new CompileProblem.TooManyInitClasses( + TEST_INIT_CLASS_NAME, testInitClasses)); + } + + if (problems.size() == 0) { + try { + if (initClasses.size() == 1) { - err.writeDetails(LOG); - throw err; - } + LOG.info("Found exactly 1 {0} class: {1}", INIT_CLASS_NAME, initClasses.get(0).fullClassName); + writeServiceFile(filer, AndHowInit.class.getCanonicalName(), initClasses); - if (testInitClasses.size() == 1) { + } - LOG.info("Found exactly 1 {0} class: {1}", TEST_INIT_CLASS_NAME, testInitClasses.get(0).fullClassName); - writeServiceFile(filer, TEST_INIT_CLASS_NAME, testInitClasses); + if (testInitClasses.size() == 1) { - } else if (testInitClasses.size() > 1) { - TooManyInitClassesException err = - new TooManyInitClassesException(TEST_INIT_CLASS_NAME, testInitClasses); + LOG.info("Found exactly 1 {0} class: {1}", TEST_INIT_CLASS_NAME, testInitClasses.get(0).fullClassName); + writeServiceFile(filer, TEST_INIT_CLASS_NAME, testInitClasses); - err.writeDetails(LOG); - throw err; - } + } - if (registrars != null && registrars.size() > 0) { - writeServiceFile(filer, PropertyRegistrar.class.getCanonicalName(), registrars); + if (registrars.size() > 0) { + writeServiceFile(filer, PropertyRegistrar.class.getCanonicalName(), registrars); + } + + } catch (IOException e) { + throw new AndHowCompileException("Exception while trying to write generated files", e); } - } catch (IOException e) { - throw new RuntimeException("Exception while trying to write generated files", e); - } - - if (problems.size() > 0) { - + } else { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "AndHow Property definition errors prevented compilation to complete. " + "Each of the following errors must be fixed before compilation is possible."); @@ -119,6 +118,13 @@ public boolean process(Set annotations, RoundEnvironment throw new AndHowCompileException(problems); } + + + if (problems.size() > 0) { + + + } + } else { LOG.trace("Another round of annotation processing. Current root element count: {0}", roundEnv.getRootElements().size()); @@ -215,8 +221,13 @@ protected void writeServiceFile(Filer filer, } /** - * Match up a causal Element w/ the Class name that will be registered in + * Match up a causal Element (Basically the compiler representation of a + * class to be compiled) w/ the Class name that will be registered in * a service registry. + * + * When the AnnotationProcessor writes a new file to the Filer, it wants + * a causal Element to associate with it, apparently this info could be + * used for reporting or something. */ protected static class CauseEffect { String fullClassName; diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java index 3d571a4d..4579f585 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java @@ -1,5 +1,8 @@ package org.yarnandtail.andhow.compile; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import org.yarnandtail.andhow.util.TextUtil; /** @@ -8,9 +11,21 @@ */ public abstract class CompileProblem { - String groupName; + final String groupName; + final String propName; + final boolean propProblem; - String propName; + CompileProblem(String groupName, String propName) { + this.groupName = groupName; + this.propName = propName; + propProblem = true; + } + + CompileProblem() { + groupName = null; + propName = null; + propProblem = false; + } public String getGroupName() { return groupName; @@ -20,6 +35,10 @@ public String getPropertyName() { return propName; } + public boolean isPropertyProblem() { + return propProblem; + } + @Override public boolean equals(Object o) { if (o != null && this.getClass().isInstance(o)) { @@ -27,7 +46,8 @@ public boolean equals(Object o) { return this.groupName.equals(cp.groupName) && - this.propName.equals(cp.propName); + this.propName.equals(cp.propName) && + this.propProblem == cp.propProblem; } else { return false; } @@ -38,8 +58,7 @@ public boolean equals(Object o) { static class PropMissingStatic extends CompileProblem { public PropMissingStatic(String groupName, String propName) { - this.groupName = groupName; - this.propName = propName; + super(groupName, propName); } @Override @@ -53,8 +72,7 @@ public String getFullMessage() { static class PropMissingFinal extends CompileProblem { public PropMissingFinal(String groupName, String propName) { - this.groupName = groupName; - this.propName = propName; + super(groupName, propName); } @Override @@ -68,8 +86,7 @@ public String getFullMessage() { static class PropMissingStaticFinal extends CompileProblem { public PropMissingStaticFinal(String groupName, String propName) { - this.groupName = groupName; - this.propName = propName; + super(groupName, propName); } @Override @@ -80,4 +97,53 @@ public String getFullMessage() { } } + static class TooManyInitClasses extends CompileProblem { + final List _instances = new ArrayList(); + final String _fullInitClassName; + + public TooManyInitClasses( + String fullInitClassName, + List instances) { + + if (instances != null) this._instances.addAll(instances); + this._fullInitClassName = fullInitClassName; + + } + + public List getInstanceNames() { + List names = new ArrayList(); + + for (AndHowCompileProcessor.CauseEffect ce : _instances) { + names.add(ce.fullClassName); + } + + return names; + } + + @Override + public String getFullMessage() { + String impList = _instances.stream() + .map(i -> i.fullClassName).collect(Collectors.joining( ", " )); + + return TextUtil.format("Multiple ({}) implementations of {} were found, " + + "but only one is allowed. Implementations found: {}", + String.valueOf(_instances.size()), + _fullInitClassName, impList); + } + + @Override + public boolean equals(Object o) { + if (o != null && this.getClass().isInstance(o)) { + TooManyInitClasses cp = (TooManyInitClasses)o; + + return + this._instances.containsAll(cp._instances) && + cp._instances.containsAll(this._instances) && + this._fullInitClassName.equals(cp._fullInitClassName); + } else { + return false; + } + } + } + } diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/TooManyInitClassesException.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/TooManyInitClassesException.java deleted file mode 100644 index 5604ed89..00000000 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/TooManyInitClassesException.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.yarnandtail.andhow.compile; - -import java.util.ArrayList; -import java.util.List; -import org.yarnandtail.andhow.compile.AndHowCompileProcessor.CauseEffect; -import org.yarnandtail.andhow.util.AndHowLog; - -/** - * - * @author ericeverman - */ -public class TooManyInitClassesException extends RuntimeException { - protected List _instances = new ArrayList(); - protected String _fullInitClassName; - - public TooManyInitClassesException( - String fullInitClassName, - List instances) { - if (instances != null) this._instances.addAll(instances); - this._fullInitClassName = fullInitClassName; - } - - public List getInstanceNames() { - List names = new ArrayList(); - - for (CauseEffect ce : _instances) { - names.add(ce.fullClassName); - } - - return names; - } - - public void writeDetails(AndHowLog log) { - log.error("Multiple ({0}) {1} implementation classes were found, but only " - + "one is allowed. List follows:", - Integer.valueOf(_instances.size()).toString(), _fullInitClassName); - - for (String name : getInstanceNames()) { - log.error("\t* " + name); - } - } - - @Override - public String getMessage() { - return "Multiple " + _fullInitClassName + " implementations were found - " - + "only one is allowed. See System.err for complete list"; - } - - -} diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java index f937282a..c4991773 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java @@ -4,6 +4,7 @@ import org.yarnandtail.andhow.service.PropertyRegistrar; import org.yarnandtail.andhow.service.PropertyRegistration; import java.io.*; +import static org.yarnandtail.andhow.compile.CompileProblem.*; import java.nio.charset.Charset; import java.util.*; import javax.tools.*; @@ -142,21 +143,20 @@ public void testServiceRegistrationOfAndHowInitWithTooManyProdInstances() throws task.call(); } catch (RuntimeException e) { - TooManyInitClassesException tmi = null; - if (e instanceof TooManyInitClassesException) { - tmi = (TooManyInitClassesException)e; - } else if (e.getCause() != null && e.getCause() instanceof TooManyInitClassesException) { - tmi = (TooManyInitClassesException) e.getCause(); - } + assertNotNull(e.getCause()); + assertTrue(e.getCause() instanceof AndHowCompileException); - if (tmi != null) { - assertEquals(2, tmi.getInstanceNames().size()); - assertTrue(tmi.getInstanceNames().contains(AndHowInitA_NAME)); - assertTrue(tmi.getInstanceNames().contains(AndHowInitB_NAME)); - } else { - fail("Expecting the exception to be TooManyInitClassesException or caused by it."); - } + AndHowCompileException ce = (AndHowCompileException) e.getCause(); + + assertEquals(1, ce.getProblems().size()); + assertTrue(ce.getProblems().get(0) instanceof TooManyInitClasses); + + TooManyInitClasses tmi = (TooManyInitClasses) ce.getProblems().get(0); + + assertEquals(2, tmi.getInstanceNames().size()); + assertTrue(tmi.getInstanceNames().contains(AndHowInitA_NAME)); + assertTrue(tmi.getInstanceNames().contains(AndHowInitB_NAME)); } } @@ -183,21 +183,21 @@ public void testServiceRegistrationOfAndHowInitWithTooManyTestInstances() throws task.call(); } catch (RuntimeException e) { - TooManyInitClassesException tmi = null; - if (e instanceof TooManyInitClassesException) { - tmi = (TooManyInitClassesException)e; - } else if (e.getCause() != null && e.getCause() instanceof TooManyInitClassesException) { - tmi = (TooManyInitClassesException) e.getCause(); - } + assertNotNull(e.getCause()); + assertTrue(e.getCause() instanceof AndHowCompileException); + + AndHowCompileException ce = (AndHowCompileException) e.getCause(); - if (tmi != null) { - assertEquals(2, tmi.getInstanceNames().size()); - assertTrue(tmi.getInstanceNames().contains(AndHowTestInitA_NAME)); - assertTrue(tmi.getInstanceNames().contains(AndHowTestInitB_NAME)); - } else { - fail("Expecting the exception to be TooManyInitClassesException or caused by it."); - } + assertEquals(1, ce.getProblems().size()); + assertTrue(ce.getProblems().get(0) instanceof TooManyInitClasses); + + TooManyInitClasses tmi = (TooManyInitClasses) ce.getProblems().get(0); + + assertEquals(2, tmi.getInstanceNames().size()); + assertTrue(tmi.getInstanceNames().contains(AndHowTestInitA_NAME)); + assertTrue(tmi.getInstanceNames().contains(AndHowTestInitB_NAME)); + } } From 37da16f7d6903403d31a648940679cab2d8c149f Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 25 Oct 2018 09:27:07 -0500 Subject: [PATCH 46/91] Improved test coverage and made some fields private --- andhow-annotation-processor/pom.xml | 4 ++ .../andhow/compile/CompileProblem.java | 8 ++- .../andhow/compile/CompileProblemTest.java | 59 ++++++++++++++++++- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index c7824875..3b1d8966 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -46,6 +46,10 @@ com.google.testing.compile compile-testing + + org.mockito + mockito-all + diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java index 4579f585..09aa9c34 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java @@ -98,8 +98,8 @@ public String getFullMessage() { } static class TooManyInitClasses extends CompileProblem { - final List _instances = new ArrayList(); - final String _fullInitClassName; + private final List _instances = new ArrayList(); + private final String _fullInitClassName; public TooManyInitClasses( String fullInitClassName, @@ -119,6 +119,10 @@ public List getInstanceNames() { return names; } + + public String getInitClassName() { + return _fullInitClassName; + } @Override public String getFullMessage() { diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java index b3f0c20c..f600bb59 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java @@ -2,9 +2,13 @@ */ package org.yarnandtail.andhow.compile; +import java.util.ArrayList; +import javax.lang.model.element.Element; import org.junit.Test; import static org.junit.Assert.*; import static org.yarnandtail.andhow.compile.CompileProblem.*; +import static org.mockito.Mockito.*; +import org.yarnandtail.andhow.compile.AndHowCompileProcessor.CauseEffect; /** * @@ -22,6 +26,7 @@ public void testBasicPropsOfPropMissingStatic() { PropMissingStatic prob2 = new PropMissingStatic("g1", "p1"); PropMissingStatic prob3 = new PropMissingStatic("Xg1", "Xp1"); + assertTrue(prob1.isPropertyProblem()); assertEquals("g1", prob1.getGroupName()); assertEquals("p1", prob1.getPropertyName()); assertTrue(prob1.getFullMessage().contains("g1")); @@ -38,6 +43,7 @@ public void testBasicPropsOfPropMissingFinal() { PropMissingFinal prob2 = new PropMissingFinal("g1", "p1"); PropMissingFinal prob3 = new PropMissingFinal("Xg1", "Xp1"); + assertTrue(prob1.isPropertyProblem()); assertEquals("g1", prob1.getGroupName()); assertEquals("p1", prob1.getPropertyName()); assertTrue(prob1.getFullMessage().contains("g1")); @@ -47,13 +53,14 @@ public void testBasicPropsOfPropMissingFinal() { assertNotEquals(prob1, prob3); assertNotEquals(prob2, prob3); } - + @Test public void testBasicPropsOfPropMissingStaticFinal() { PropMissingStaticFinal prob1 = new PropMissingStaticFinal("g1", "p1"); PropMissingStaticFinal prob2 = new PropMissingStaticFinal("g1", "p1"); PropMissingStaticFinal prob3 = new PropMissingStaticFinal("Xg1", "Xp1"); + assertTrue(prob1.isPropertyProblem()); assertEquals("g1", prob1.getGroupName()); assertEquals("p1", prob1.getPropertyName()); assertTrue(prob1.getFullMessage().contains("g1")); @@ -63,13 +70,63 @@ public void testBasicPropsOfPropMissingStaticFinal() { assertNotEquals(prob1, prob3); assertNotEquals(prob2, prob3); } + + @Test + public void testTooManyInitClasses() { + Element element1 = mock(Element.class); + Element element2 = mock(Element.class); + + CauseEffect ce1 = new CauseEffect("org.MyClass1", element1); + CauseEffect ce2 = new CauseEffect("org.MyClass2", element2); + ArrayList ces1 = new ArrayList(); + ces1.add(ce1); + ces1.add(ce2); + + final String INIT_NAME = "SOME_INTERFACE_NAME"; + + TooManyInitClasses tmi1a = new TooManyInitClasses(INIT_NAME, ces1); + TooManyInitClasses tmi1null = new TooManyInitClasses(INIT_NAME, null); + + assertFalse(tmi1a.isPropertyProblem()); + assertEquals(INIT_NAME, tmi1a.getInitClassName()); + assertTrue(tmi1a.getFullMessage().contains(INIT_NAME)); + assertTrue(tmi1a.getFullMessage().contains(ce1.fullClassName)); + assertTrue(tmi1a.getFullMessage().contains(ce2.fullClassName)); + assertEquals(2, tmi1a.getInstanceNames().size()); + assertTrue(tmi1a.getInstanceNames().contains(ce1.fullClassName)); + assertTrue(tmi1a.getInstanceNames().contains(ce2.fullClassName)); + assertEquals(0, tmi1null.getInstanceNames().size()); + + TooManyInitClasses tmi1b = new TooManyInitClasses(INIT_NAME, ces1); + + ArrayList ces2 = new ArrayList(); + ces2.add(ce1); + TooManyInitClasses tmi2 = new TooManyInitClasses(INIT_NAME, ces2); + + assertEquals(tmi1a, tmi1b); + assertEquals(tmi1b, tmi1a); + assertNotEquals(tmi1a, tmi1null); + assertNotEquals(tmi1a, null); + assertNotEquals(tmi1a, new PropMissingFinal("g1", "p1")); + assertNotEquals(tmi1a, tmi2); + assertNotEquals(tmi2, tmi1a); + } + @Test public void testEqualsAcrossTypes() { + Integer i1 = 1; CompileProblem prob1 = new PropMissingStatic("g1", "p1"); CompileProblem prob2 = new PropMissingFinal("g1", "p1"); CompileProblem prob3 = new PropMissingStaticFinal("g1", "p1"); + //Just checking + assertEquals(prob1, prob1); + assertEquals(prob2, prob2); + assertEquals(prob3, prob3); + + assertNotEquals(prob1, null); + assertNotEquals(prob1, i1); assertNotEquals(prob1, prob2); assertNotEquals(prob1, prob3); assertNotEquals(prob2, prob3); From 4222eabda32e7eba2e5ba02ad98f8be6c9c259d6 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 25 Oct 2018 20:47:05 -0500 Subject: [PATCH 47/91] Improved testability and testing for AndHowCompileException --- .../compile/AndHowCompileException.java | 18 +++---- .../compile/AndHowCompileExceptionTest.java | 54 +++++++++++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java index f96a2b28..7214496d 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java @@ -15,10 +15,13 @@ */ public class AndHowCompileException extends RuntimeException { - String msg = "The AndHowCompileProcessor found a problem during compilation " + - "and threw a fatal exception - See the error details listed nearby."; - final List problems; - final Throwable cause; + public static final String DEFAULT_MSG = "The AndHowCompileProcessor found a problem" + + " during compilation and threw a fatal exception - " + + "See the error details listed nearby."; + + private final String msg; + private final List problems; + private final Throwable cause; /** @@ -33,6 +36,7 @@ public class AndHowCompileException extends RuntimeException { public AndHowCompileException(List problems) { cause = null; + msg = DEFAULT_MSG; if (problems != null) { this.problems = problems; @@ -67,11 +71,7 @@ public synchronized Throwable getCause() { @Override public String getMessage() { - if (msg != null) { - return msg; - } else { - return super.getMessage(); - } + return msg; } public List getProblems() { diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java new file mode 100644 index 00000000..a8b4146e --- /dev/null +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java @@ -0,0 +1,54 @@ +package org.yarnandtail.andhow.compile; + +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Before; + +/** + * + * @author ericeverman + */ +public class AndHowCompileExceptionTest { + + List problems = new ArrayList(); + + @Before + public void setup() { + CompileProblem.PropMissingStaticFinal prob1 = new CompileProblem.PropMissingStaticFinal("g1", "p1"); + CompileProblem.PropMissingStaticFinal prob2 = new CompileProblem.PropMissingStaticFinal("g1", "p1"); + problems.clear(); + problems.add(prob1); + problems.add(prob2); + } + + @Test + public void testAllForProblemConstructor() { + + AndHowCompileException ahce = new AndHowCompileException(problems); + assertNull(ahce.getCause()); + assertEquals(AndHowCompileException.DEFAULT_MSG, ahce.getMessage()); + assertTrue(problems.containsAll(ahce.getProblems())); + assertTrue(ahce.getProblems().containsAll(problems)); + } + + @Test + public void testNullProblemsShouldNotBombr() { + + AndHowCompileException ahce = new AndHowCompileException(null); + assertEquals(0, ahce.getProblems().size()); + } + + @Test + public void testAllForMessageThrowableConstructor() { + + final String MSG = "ABC"; + final Exception E = new Exception(); + + AndHowCompileException ahce = new AndHowCompileException(MSG, E); + assertEquals(E, ahce.getCause()); + assertEquals(MSG, ahce.getMessage()); + assertEquals(0, ahce.getProblems().size()); + } +} From e63e4ed0578c0ddd82868e79b00ced1d08cef4f5 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 25 Oct 2018 20:49:17 -0500 Subject: [PATCH 48/91] Simplified the test --- .../compile/AndHowCompileExceptionTest.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java index a8b4146e..496eda37 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java @@ -12,20 +12,18 @@ */ public class AndHowCompileExceptionTest { - List problems = new ArrayList(); - - @Before - public void setup() { + @Test + public void testAllForProblemConstructor() { + + //Setup + List problems = new ArrayList(); CompileProblem.PropMissingStaticFinal prob1 = new CompileProblem.PropMissingStaticFinal("g1", "p1"); CompileProblem.PropMissingStaticFinal prob2 = new CompileProblem.PropMissingStaticFinal("g1", "p1"); problems.clear(); problems.add(prob1); problems.add(prob2); - } - - @Test - public void testAllForProblemConstructor() { - + + AndHowCompileException ahce = new AndHowCompileException(problems); assertNull(ahce.getCause()); assertEquals(AndHowCompileException.DEFAULT_MSG, ahce.getMessage()); From 24231e68d1380d7c6bf138bd1ccd86d8bf069363 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 25 Oct 2018 22:33:03 -0500 Subject: [PATCH 49/91] * Added and simplified tests * Switched to Messager for logging in the AndHowCompileProcessor w new log methods --- .../compile/AndHowCompileProcessor.java | 90 +++++----- .../AndHowCompileProcessorTestBase.java | 89 ++++++++++ .../AndHowCompileProcessor_InitTest.java | 167 +++++++++++------- .../AndHowCompileProcessor_PropertyTest.java | 88 ++------- 4 files changed, 249 insertions(+), 185 deletions(-) create mode 100644 andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index edb61548..6aec4900 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -1,8 +1,6 @@ package org.yarnandtail.andhow.compile; -import java.util.logging.Level; import org.yarnandtail.andhow.service.PropertyRegistrationList; -import org.yarnandtail.andhow.service.PropertyRegistration; import com.sun.source.util.Trees; import java.io.*; import java.util.*; @@ -15,9 +13,9 @@ import org.yarnandtail.andhow.AndHowInit; import org.yarnandtail.andhow.api.Property; import org.yarnandtail.andhow.service.*; -import org.yarnandtail.andhow.util.AndHowLog; import static javax.tools.StandardLocation.CLASS_OUTPUT; +import org.yarnandtail.andhow.util.TextUtil; /** * @@ -29,7 +27,6 @@ */ @SupportedAnnotationTypes("*") public class AndHowCompileProcessor extends AbstractProcessor { - private static final AndHowLog LOG = AndHowLog.getLogger(AndHowCompileProcessor.class); private static final String INIT_CLASS_NAME = AndHowInit.class.getCanonicalName(); private static final String TEST_INIT_CLASS_NAME = "org.yarnandtail.andhow.AndHowTestInit"; @@ -40,8 +37,6 @@ public class AndHowCompileProcessor extends AbstractProcessor { //Static to insure all generated classes have the same timestamp private static Calendar runDate; - - private Trees trees; private final List registrars = new ArrayList(); @@ -58,14 +53,15 @@ public AndHowCompileProcessor() { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { + Filer filer = processingEnv.getFiler(); + Messager log = this.processingEnv.getMessager(); + boolean isLastRound = roundEnv.processingOver(); - Filer filer = this.processingEnv.getFiler(); - - if (isLastRound) { - LOG.debug("Final round of annotation processing. Total root element count: {0}", roundEnv.getRootElements().size()); + debug(log, "Final round of annotation processing. Total root element count: {}", + roundEnv.getRootElements().size()); if (initClasses.size() > 1) { @@ -78,18 +74,21 @@ public boolean process(Set annotations, RoundEnvironment TEST_INIT_CLASS_NAME, testInitClasses)); } - if (problems.size() == 0) { + if (problems.isEmpty()) { try { if (initClasses.size() == 1) { - LOG.info("Found exactly 1 {0} class: {1}", INIT_CLASS_NAME, initClasses.get(0).fullClassName); + debug(log, "Found exactly 1 {} class: {}", + INIT_CLASS_NAME, initClasses.get(0).fullClassName); + writeServiceFile(filer, AndHowInit.class.getCanonicalName(), initClasses); } if (testInitClasses.size() == 1) { - - LOG.info("Found exactly 1 {0} class: {1}", TEST_INIT_CLASS_NAME, testInitClasses.get(0).fullClassName); + debug(log, "Found exactly 1 {} class: {}", + TEST_INIT_CLASS_NAME, testInitClasses.get(0).fullClassName); + writeServiceFile(filer, TEST_INIT_CLASS_NAME, testInitClasses); } @@ -102,31 +101,21 @@ public boolean process(Set annotations, RoundEnvironment throw new AndHowCompileException("Exception while trying to write generated files", e); } } else { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "AndHow Property definition errors prevented compilation to complete. " + - "Each of the following errors must be fixed before compilation is possible."); - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - "AndHow Property definition errors discovered: " + problems.size()); + error(log, "AndHow Property definition or Init class errors " + + "prevented compilation. Each of the following errors " + + "must be fixed before compilation is possible."); + error(log, "AndHow errors discovered: {}", problems.size()); + for (CompileProblem err : problems) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, - err.getFullMessage()); + error(log, err.getFullMessage()); } - //The docs for printMessage(Kind.Error) say that an error - //message should actually throw an error, but that does not - //seem to happen, so the runtime exception is needed throw new AndHowCompileException(problems); } - - - if (problems.size() > 0) { - - - } - } else { - LOG.trace("Another round of annotation processing. Current root element count: {0}", roundEnv.getRootElements().size()); + debug(log, "Another round of annotation processing. " + + "Current root element count: {}", roundEnv.getRootElements().size()); // @@ -152,23 +141,18 @@ public boolean process(Set annotations, RoundEnvironment if (ret.hasRegistrations()) { - LOG.debug("Found {0} AndHow Properties in class {1} ", ret.getRegistrations().size(), ret.getRootCanonicalName()); + debug(log, "Found {} AndHow Properties in class {} ", + ret.getRegistrations().size(), ret.getRootCanonicalName()); + PropertyRegistrarClassGenerator gen = new PropertyRegistrarClassGenerator(ret, AndHowCompileProcessor.class, runDate); registrars.add(new CauseEffect(gen.buildGeneratedClassFullName(), te)); PropertyRegistrationList regs = ret.getRegistrations(); - if (LOG.isLoggable(Level.FINEST)) { - for (PropertyRegistration p : ret.getRegistrations()) { - LOG.trace("Found AndHow Property ''{0}'' in root class ''{1}'', immediate parent is ''{2}''", - p.getCanonicalPropertyName(), p.getCanonicalRootName(), p.getJavaCanonicalParentName()); - } - } - try { writeClassFile(filer, gen, e); - LOG.trace("Wrote new generated class file " + gen.buildGeneratedClassSimpleName()); + debug(log, "Wrote new generated class file {}", gen.buildGeneratedClassSimpleName()); } catch (Exception ex) { - LOG.error("Unable to write generated classfile '" + gen.buildGeneratedClassFullName() + "'", ex); + error(log, "Unable to write generated classfile '" + gen.buildGeneratedClassFullName() + "'", ex); throw new RuntimeException(ex); } } @@ -220,6 +204,28 @@ protected void writeServiceFile(Filer filer, } + /** + * Logs a debug message using the javac standard Messager system. + * + * @param log The Message instance to use + * @param pattern String pattern with curly variable replacement like this: {} + * @param args Arguments to put into the {}'s, in order. + */ + void debug(Messager log, String pattern, Object... args) { + log.printMessage(Diagnostic.Kind.NOTE, TextUtil.format(pattern, args)); + } + + /** + * Logs an error message using the javac standard Messager system. + * + * @param log The Message instance to use + * @param pattern String pattern with curly variable replacement like this: {} + * @param args Arguments to put into the {}'s, in order. + */ + void error(Messager log, String pattern, Object... args) { + log.printMessage(Diagnostic.Kind.ERROR, TextUtil.format(pattern, args)); + } + /** * Match up a causal Element (Basically the compiler representation of a * class to be compiled) w/ the Class name that will be registered in diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java new file mode 100644 index 00000000..e8ea6394 --- /dev/null +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java @@ -0,0 +1,89 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.yarnandtail.andhow.compile; + +import java.util.HashSet; +import java.util.Set; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.ToolProvider; +import org.junit.Before; +import static org.yarnandtail.andhow.compile.AndHowCompileProcessor_PropertyTest.pkg; +import org.yarnandtail.andhow.util.IOUtil; +import org.yarnandtail.compile.MemoryFileManager; +import org.yarnandtail.compile.TestClassLoader; +import org.yarnandtail.compile.TestSource; + +/** + * + * @author ericevermanpersonal + */ +public class AndHowCompileProcessorTestBase { + + /** Classpath of the generated service file for AndHow property registration */ + static final String REGISTRAR_SVS_PATH = + "/META-INF/services/org.yarnandtail.andhow.service.PropertyRegistrar"; + static final String INIT_SVS_PATH = "/META-INF/services/org.yarnandtail.andhow.AndHowInit"; + static final String TEST_INIT_SVS_PATH = "/META-INF/services/org.yarnandtail.andhow.AndHowTestInit"; + + JavaCompiler compiler; + MemoryFileManager manager; + TestClassLoader loader; + + Set sources; //New set of source files to compile + + @Before + public void setupTest() { + compiler = ToolProvider.getSystemJavaCompiler(); + manager = new MemoryFileManager(compiler); + loader = new TestClassLoader(manager); + sources = new HashSet(); + } + + /** + * The source path to where to find this file on the classpath + * @param classPackage + * @param simpleClassName + * @return + */ + public String srcPath(String classPackage, String simpleClassName) { + return "/" + classPackage.replace(".", "/") + "/" + simpleClassName + ".java"; + } + + /** + * Full canonical name of the class. + * @param classPackage + * @param simpleClassName + * @return + */ + public String fullName(String classPackage, String simpleClassName) { + return classPackage + "." + simpleClassName; + } + + /** + * Builds a new TestSource object for a Java source file on the classpath + * @param classPackage + * @param simpleClassName + * @return + * @throws Exception + */ + public TestSource buildTestSource(String classPackage, String simpleClassName) throws Exception { + String classContent = IOUtil.getUTF8ResourceAsString(srcPath(classPackage, simpleClassName)); + return new TestSource(fullName(classPackage, simpleClassName), JavaFileObject.Kind.SOURCE, classContent); + } + + /** + * Build the canonical name of the generated class from the source class. + * + * @param classPackage + * @param simpleClassName + * @return + */ + public String genName(String classPackage, String simpleClassName) { + return classPackage + ".$" + simpleClassName + "_AndHowProps"; + } + +} diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java index c4991773..6daf8478 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java @@ -1,9 +1,6 @@ package org.yarnandtail.andhow.compile; import org.yarnandtail.compile.*; -import org.yarnandtail.andhow.service.PropertyRegistrar; -import org.yarnandtail.andhow.service.PropertyRegistration; -import java.io.*; import static org.yarnandtail.andhow.compile.CompileProblem.*; import java.nio.charset.Charset; import java.util.*; @@ -18,106 +15,89 @@ * https://gist.github.com/johncarl81/46306590cbdde5a3003f * @author ericeverman */ -public class AndHowCompileProcessor_InitTest { +public class AndHowCompileProcessor_InitTest extends AndHowCompileProcessorTestBase { + static final String pkg = AndHowCompileProcessor_PropertyTest.class.getPackage().getName(); + // //Names of classes in the resources directory, used for compile testing of //initiation self discovery. The 'A' class extends the 'Abstract' one for each. - protected static final String AndHowInitAbstract_NAME = "org.yarnandtail.andhow.compile.AndHowInitAbstract"; - protected static final String AndHowInitA_NAME = "org.yarnandtail.andhow.compile.AndHowInitA"; - protected static final String AndHowInitB_NAME = "org.yarnandtail.andhow.compile.AndHowInitB"; - protected static final String AndHowTestInitAbstract_NAME = "org.yarnandtail.andhow.compile.AndHowTestInitAbstract"; - protected static final String AndHowTestInitA_NAME = "org.yarnandtail.andhow.compile.AndHowTestInitA"; - protected static final String AndHowTestInitB_NAME = "org.yarnandtail.andhow.compile.AndHowTestInitB"; - + protected static final String AndHowInitAbstract_NAME = "AndHowInitAbstract"; + protected static final String AndHowInitA_NAME = "AndHowInitA"; + protected static final String AndHowInitB_NAME = "AndHowInitB"; + protected static final String AndHowTestInitAbstract_NAME = "AndHowTestInitAbstract"; + protected static final String AndHowTestInitA_NAME = "AndHowTestInitA"; + protected static final String AndHowTestInitB_NAME = "AndHowTestInitB"; @Test public void testServiceRegistrationOfOneProdAndOneTestInit() throws Exception { - - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - final MemoryFileManager manager = new MemoryFileManager(compiler); - TestClassLoader loader = new TestClassLoader(manager); List options=new ArrayList(); //options.add("-verbose"); - - Set input = new HashSet(); - input.add(new TestSource(AndHowInitAbstract_NAME)); //abstract should be ignored - input.add(new TestSource(AndHowInitA_NAME)); - input.add(new TestSource(AndHowTestInitAbstract_NAME)); - input.add(new TestSource(AndHowTestInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowInitAbstract_NAME)); //Abstract should be igored + sources.add(buildTestSource(pkg, AndHowInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitAbstract_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitA_NAME)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); - String prodInitSvs = IOUtil.toString(loader.getResourceAsStream("/META-INF/services/org.yarnandtail.andhow.AndHowInit"), Charset.forName("UTF-8")); - String testInitSvs = IOUtil.toString(loader.getResourceAsStream("/META-INF/services/org.yarnandtail.andhow.AndHowTestInit"), Charset.forName("UTF-8")); + String prodInitSvs = IOUtil.toString(loader.getResourceAsStream(INIT_SVS_PATH), Charset.forName("UTF-8")); + String testInitSvs = IOUtil.toString(loader.getResourceAsStream(TEST_INIT_SVS_PATH), Charset.forName("UTF-8")); // //Test the initiation files assertNotNull(prodInitSvs); - assertEquals(AndHowInitA_NAME, prodInitSvs.trim()); + assertEquals(fullName(pkg, AndHowInitA_NAME), prodInitSvs.trim()); assertNotNull(testInitSvs); - assertEquals(AndHowTestInitA_NAME, testInitSvs.trim()); + assertEquals(fullName(pkg, AndHowTestInitA_NAME), testInitSvs.trim()); } @Test public void testServiceRegistrationOfOneProdInit() throws Exception { - - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - final MemoryFileManager manager = new MemoryFileManager(compiler); - TestClassLoader loader = new TestClassLoader(manager); List options=new ArrayList(); //options.add("-verbose"); - - Set input = new HashSet(); - input.add(new TestSource(AndHowInitAbstract_NAME)); - input.add(new TestSource(AndHowInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowInitAbstract_NAME)); + sources.add(buildTestSource(pkg, AndHowInitA_NAME)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); - String prodInitSvs = IOUtil.toString(loader.getResourceAsStream("/META-INF/services/org.yarnandtail.andhow.AndHowInit"), Charset.forName("UTF-8")); + String prodInitSvs = IOUtil.toString(loader.getResourceAsStream(INIT_SVS_PATH), Charset.forName("UTF-8")); // //Test the initiation files assertNotNull(prodInitSvs); - assertEquals(AndHowInitA_NAME, prodInitSvs.trim()); + assertEquals(fullName(pkg, AndHowInitA_NAME), prodInitSvs.trim()); } @Test public void testServiceRegistrationOfOneTestInit() throws Exception { - - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - final MemoryFileManager manager = new MemoryFileManager(compiler); - TestClassLoader loader = new TestClassLoader(manager); List options=new ArrayList(); //options.add("-verbose"); - - Set input = new HashSet(); - input.add(new TestSource(AndHowTestInitAbstract_NAME)); - input.add(new TestSource(AndHowTestInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitAbstract_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitA_NAME)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); - String testInitSvs = IOUtil.toString(loader.getResourceAsStream("/META-INF/services/org.yarnandtail.andhow.AndHowTestInit"), Charset.forName("UTF-8")); + String testInitSvs = IOUtil.toString(loader.getResourceAsStream(TEST_INIT_SVS_PATH), Charset.forName("UTF-8")); // //Test the initiation files assertNotNull(testInitSvs); - assertEquals(AndHowTestInitA_NAME, testInitSvs.trim()); + assertEquals(fullName(pkg, AndHowTestInitA_NAME), testInitSvs.trim()); } @@ -125,20 +105,15 @@ public void testServiceRegistrationOfOneTestInit() throws Exception { public void testServiceRegistrationOfAndHowInitWithTooManyProdInstances() throws Exception { try { - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - final MemoryFileManager manager = new MemoryFileManager(compiler); - TestClassLoader loader = new TestClassLoader(manager); List options=new ArrayList(); //options.add("-verbose"); + sources.add(buildTestSource(pkg, AndHowInitAbstract_NAME)); + sources.add(buildTestSource(pkg, AndHowInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowInitB_NAME)); - Set input = new HashSet(); - input.add(new TestSource(AndHowInitAbstract_NAME)); - input.add(new TestSource(AndHowInitA_NAME)); - input.add(new TestSource(AndHowInitB_NAME)); - - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); @@ -155,8 +130,8 @@ public void testServiceRegistrationOfAndHowInitWithTooManyProdInstances() throws TooManyInitClasses tmi = (TooManyInitClasses) ce.getProblems().get(0); assertEquals(2, tmi.getInstanceNames().size()); - assertTrue(tmi.getInstanceNames().contains(AndHowInitA_NAME)); - assertTrue(tmi.getInstanceNames().contains(AndHowInitB_NAME)); + assertTrue(tmi.getInstanceNames().contains(fullName(pkg, AndHowInitA_NAME))); + assertTrue(tmi.getInstanceNames().contains(fullName(pkg, AndHowInitB_NAME))); } } @@ -172,13 +147,11 @@ public void testServiceRegistrationOfAndHowInitWithTooManyTestInstances() throws List options=new ArrayList(); //options.add("-verbose"); + sources.add(buildTestSource(pkg, AndHowTestInitAbstract_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitB_NAME)); - Set input = new HashSet(); - input.add(new TestSource(AndHowTestInitAbstract_NAME)); - input.add(new TestSource(AndHowTestInitA_NAME)); - input.add(new TestSource(AndHowTestInitB_NAME)); - - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); @@ -195,9 +168,69 @@ public void testServiceRegistrationOfAndHowInitWithTooManyTestInstances() throws TooManyInitClasses tmi = (TooManyInitClasses) ce.getProblems().get(0); assertEquals(2, tmi.getInstanceNames().size()); - assertTrue(tmi.getInstanceNames().contains(AndHowTestInitA_NAME)); - assertTrue(tmi.getInstanceNames().contains(AndHowTestInitB_NAME)); + assertTrue(tmi.getInstanceNames().contains(fullName(pkg, AndHowTestInitA_NAME))); + assertTrue(tmi.getInstanceNames().contains(fullName(pkg, AndHowTestInitB_NAME))); + + } + } + + + @Test + public void testServiceRegistrationOfAndHowInitWithTooManyInstAndBadProperties() throws Exception { + + try { + + List options=new ArrayList(); + //options.add("-verbose"); + + sources.add(buildTestSource(pkg, AndHowInitAbstract_NAME)); + sources.add(buildTestSource(pkg, AndHowInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowInitB_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitAbstract_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitA_NAME)); + sources.add(buildTestSource(pkg, AndHowTestInitB_NAME)); + sources.add(buildTestSource(pkg, "BadProps_1")); + sources.add(buildTestSource(pkg, "BadProps_2")); + + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); + task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); + task.call(); + + } catch (RuntimeException e) { + + assertNotNull(e.getCause()); + assertTrue(e.getCause() instanceof AndHowCompileException); + + AndHowCompileException ace = (AndHowCompileException) e.getCause(); + + String CLASSNAME = "org.yarnandtail.andhow.compile.BadProps_1"; + String INNER_CLASSNAME = CLASSNAME + "$INNER_CLASS"; + + //Expected CompileProblems + //All problems for both classes should be reported in one exception + CompileProblem prob1 = new PropMissingFinal(CLASSNAME, "STR_1"); + CompileProblem prob2 = new PropMissingStatic(CLASSNAME, "STR_2"); + CompileProblem prob3 = new PropMissingStaticFinal(CLASSNAME, "STR_3"); + CompileProblem prob4 = new PropMissingFinal(INNER_CLASSNAME, "STR_1"); + CompileProblem prob5 = new PropMissingStatic(INNER_CLASSNAME, "STR_2"); + CompileProblem prob6 = new PropMissingStaticFinal(INNER_CLASSNAME, "STR_3"); + + //In 2nd class + CompileProblem prob7 = new PropMissingFinal( + "org.yarnandtail.andhow.compile.BadProps_2", "STR_1"); + + assertEquals(9, ace.getProblems().size()); + assertEquals(2, + ace.getProblems().stream().filter(i -> i instanceof TooManyInitClasses).count() + ); + assertTrue(ace.getProblems().contains(prob1)); + assertTrue(ace.getProblems().contains(prob2)); + assertTrue(ace.getProblems().contains(prob3)); + assertTrue(ace.getProblems().contains(prob4)); + assertTrue(ace.getProblems().contains(prob5)); + assertTrue(ace.getProblems().contains(prob6)); + assertTrue(ace.getProblems().contains(prob7)); } } diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java index dfa743ee..92b03689 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java @@ -1,9 +1,7 @@ package org.yarnandtail.andhow.compile; import org.yarnandtail.compile.*; -import org.yarnandtail.andhow.service.PropertyRegistrar; -import org.yarnandtail.andhow.service.PropertyRegistration; -import java.io.*; +import org.yarnandtail.andhow.service.*; import java.nio.charset.Charset; import java.util.*; import javax.tools.*; @@ -19,68 +17,12 @@ * https://gist.github.com/johncarl81/46306590cbdde5a3003f * @author ericeverman */ -public class AndHowCompileProcessor_PropertyTest { - - /** Classpath of the generated service file for AndHow property registration */ - static final String PROPERTY_REGISTRAR_CLASSPATH = - "/META-INF/services/org.yarnandtail.andhow.service.PropertyRegistrar"; +public class AndHowCompileProcessor_PropertyTest extends AndHowCompileProcessorTestBase { /** Shortcut to this package */ static final String pkg = AndHowCompileProcessor_PropertyTest.class.getPackage().getName(); - - JavaCompiler compiler; - MemoryFileManager manager; - TestClassLoader loader; - - @Before - public void setupTest() { - compiler = ToolProvider.getSystemJavaCompiler(); - manager = new MemoryFileManager(compiler); - loader = new TestClassLoader(manager); - } - - /** - * The source path to where to find this file on the classpath - * @param classPackage - * @param simpleClassName - * @return - */ - public String srcPath(String classPackage, String simpleClassName) { - return "/" + classPackage.replace(".", "/") + "/" + simpleClassName + ".java"; - } - - /** - * Build the canonical name of the generated class from the source class. - * - * @param classPackage - * @param simpleClassName - * @return - */ - public String genName(String classPackage, String simpleClassName) { - return classPackage + ".$" + simpleClassName + "_AndHowProps"; - } - /** - * Full canonical name of the class. - * @param classPackage - * @param simpleClassName - * @return - */ - public String fullName(String classPackage, String simpleClassName) { - return classPackage + "." + simpleClassName; - } - - /** - * Builds a new TestSource object for a Java source file on the classpath - * @param classPackage - * @param simpleClassName - * @return - * @throws Exception - */ - public TestSource buidTestSource(String classPackage, String simpleClassName) throws Exception { - String classContent = IOUtil.getUTF8ResourceAsString(srcPath(pkg, simpleClassName)); - return new TestSource(fullName(pkg, simpleClassName), JavaFileObject.Kind.SOURCE, classContent); - } + @Test public void testComplexNestedPropertySampleClass() throws Exception { @@ -90,17 +32,15 @@ public void testComplexNestedPropertySampleClass() throws Exception { List options=new ArrayList(); //options.add("-verbose"); - - Set input = new HashSet(); - input.add(buidTestSource(pkg, classSimpleName)); + sources.add(buildTestSource(pkg, classSimpleName)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); Object genClass = loader.loadClass(genName(pkg, classSimpleName)).newInstance(); String genSvsFile = IOUtil.toString( - loader.getResourceAsStream(PROPERTY_REGISTRAR_CLASSPATH), Charset.forName("UTF-8")); + loader.getResourceAsStream(REGISTRAR_SVS_PATH), Charset.forName("UTF-8")); assertNotNull(genClass); @@ -139,17 +79,15 @@ public void testSimpleHappyPathClass() throws Exception { List options=new ArrayList(); //options.add("-verbose"); - - Set input = new HashSet(); - input.add(buidTestSource(pkg, classSimpleName)); + sources.add(buildTestSource(pkg, classSimpleName)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); Object genClass = loader.loadClass(genName(pkg, classSimpleName)).newInstance(); String genSvsFile = IOUtil.toString( - loader.getResourceAsStream(PROPERTY_REGISTRAR_CLASSPATH), Charset.forName("UTF-8")); + loader.getResourceAsStream(REGISTRAR_SVS_PATH), Charset.forName("UTF-8")); assertNotNull(genClass); @@ -178,12 +116,10 @@ public void testMissingStaticAndFinalModifiersOnProperties() throws Exception { List options=new ArrayList(); //options.add("-verbose"); - - Set input = new HashSet(); - input.add(buidTestSource(pkg, "BadProps_1")); - input.add(buidTestSource(pkg, "BadProps_2")); + sources.add(buildTestSource(pkg, "BadProps_1")); + sources.add(buildTestSource(pkg, "BadProps_2")); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, input); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); try { From c2db61df6e61d9f7a906e076e496a6f40ab5dec6 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 1 Nov 2018 21:47:56 -0500 Subject: [PATCH 50/91] =?UTF-8?q?Implemented=20Chace=E2=80=99s=20suggestio?= =?UTF-8?q?n=20Added=20javadocs=20to=20all=20my=20code=20additions=20and?= =?UTF-8?q?=20more.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compile/AndHowCompileException.java | 21 ++-- .../compile/AndHowCompileProcessor.java | 51 +++++++-- .../andhow/compile/CompileProblem.java | 104 +++++++++++++++++- 3 files changed, 154 insertions(+), 22 deletions(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java index 7214496d..3f0b1c5a 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileException.java @@ -31,18 +31,16 @@ public class AndHowCompileException extends RuntimeException { * Examples would include Properties that are not static final * or too many init classes on the classpath. * - * @param problems A list of problems found during compilation. + * @param problems A list of problems found during compilation. This list + * instance is kept, so no modifications to the list should be made by the + * caller. */ public AndHowCompileException(List problems) { cause = null; msg = DEFAULT_MSG; - if (problems != null) { - this.problems = problems; - } else { - this.problems = Collections.emptyList(); - } + this.problems = (problems != null) ? problems : Collections.emptyList(); } /** @@ -61,11 +59,7 @@ public AndHowCompileException(String message, Throwable cause) { @Override public synchronized Throwable getCause() { - if (cause != null) { - return cause; - } else { - return super.getCause(); - } + return (cause != null) ? cause : super.getCause(); } @@ -74,6 +68,11 @@ public String getMessage() { return msg; } + /** + * The list of AndHow CompileProblems discovered, if any. + * + * @return A non-null, by possibly empty list. Do not modify the returned list. + */ public List getProblems() { return problems; } diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index 6aec4900..4b37fb34 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -18,12 +18,22 @@ import org.yarnandtail.andhow.util.TextUtil; /** - * - * Note: check to ensure that Props are not referenced in static init blocks b/c - * we may need to load the class (and run its init) before andHow init can - * complete, causing a circular init loop. - * - * @author ericeverman + * This is the central AndHow compilation class, an Annotation Processor. + * + * An annotation processor nominally reads annotations as classes are compiled. + * This class is annotated {@code SupportedAnnotationTypes("*")}, allowing it to + * 'see' all classes as they are compiled. This class then delegates to a + * 'scanner' class that does deep inspection on compiled code, looking for + * AndHow Properties. + *
    + * When an AndHow Property is found in a class, a new {@code PropertyRegistrar} + * class is created, which contains the list of Properties in that class. + * There is a one-to-one correspondence between user classes that contain + * AndHow Properties and auto-created {@code PropertyRegistrar} classes. + *
    + * At runtime, AndHow will use the {@code ServiceLoader} to discover all instances + * of {@code PropertyRegistrar} on the classpath, thus finding all AndHow + * Property containing classes. */ @SupportedAnnotationTypes("*") public class AndHowCompileProcessor extends AbstractProcessor { @@ -45,8 +55,11 @@ public class AndHowCompileProcessor extends AbstractProcessor { private final List problems = new ArrayList(); //List of problems found. >0== RuntimeException + /** + * A no-arg constructor is required. + */ public AndHowCompileProcessor() { - //required by Processor API + //used to ensure all metadata files have the same date runDate = new GregorianCalendar(); } @@ -166,11 +179,31 @@ public boolean process(Set annotations, RoundEnvironment } - public void writeClassFile(Filer filer, PropertyRegistrarClassGenerator generator, Element causingElement) throws Exception { + /** + * Writes a new class implementing the {@code PropertyRegistrar} interface. + * + * The new class directly corresponds to a user classes containing AndHow + * Properties and will contain meta data about the properties. + * + * @param filer The javac file system representation for writing files. + * @param generator AndHow class capable of generating source code for this + * {@code PropertyRegistrar} class. + * @param causingElement A javac Element, which generically refers to any + * piece of source code such as a keyword, class name, etc.. When a file + * is written to the filer, a {@code causingElement} is recorded as metadata + * so there is an association between the file and the reason it was written. + * Likely this is normally used to associate source code line numbers with + * generated code. + * + * @throws Exception If unable to write (out of disc space?) + */ + public void writeClassFile(Filer filer, + PropertyRegistrarClassGenerator generator, Element causingElement) throws Exception { String classContent = generator.generateSource(); - FileObject classFile = filer.createSourceFile(generator.buildGeneratedClassFullName(), causingElement); + FileObject classFile = filer.createSourceFile( + generator.buildGeneratedClassFullName(), causingElement); try (Writer writer = classFile.openWriter()) { writer.write(classContent); diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java index 09aa9c34..2ac5d68e 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileProblem.java @@ -6,8 +6,8 @@ import org.yarnandtail.andhow.util.TextUtil; /** - * - * @author ericevermanpersonal + * Implementation represent specific problems with AndHow Property usage or + * initiation, discovered during compilation. */ public abstract class CompileProblem { @@ -15,30 +15,65 @@ public abstract class CompileProblem { final String propName; final boolean propProblem; + /** + * Base constructor for Property related problems. + * + * @param groupName Full name of the class containing the {@code Property}. + * If an inner class, should be the complete name w/ the inner class. + * @param propName The declared name of the {@code Property} w/ the problem. + */ CompileProblem(String groupName, String propName) { this.groupName = groupName; this.propName = propName; propProblem = true; } + /** + * Base constructor for non-Property related problems, such as Initiation + * errors. + */ CompileProblem() { groupName = null; propName = null; propProblem = false; } + /** + * Full name of the class containing the {@code Property}. + * In the case of an inner class, this should be the complete name including + * that inner class. + * + * @return May be null if this is not a property related problem. + */ public String getGroupName() { return groupName; } + /** + * The declared name of the {@code Property} that has the problem. + * + * @return May be null if this is not a property related problem. + */ public String getPropertyName() { return propName; } + /** + * If true, the problem is related to a specific {@code Property}. + * @return + */ public boolean isPropertyProblem() { return propProblem; } + /** + * Logical equals implementation based on equality of fields. + * + * This is only used for comparisons during testing. + * + * @param o Compared to me. + * @return True if logically equal. + */ @Override public boolean equals(Object o) { if (o != null && this.getClass().isInstance(o)) { @@ -53,10 +88,28 @@ public boolean equals(Object o) { } } + /** + * The complete message that will be logged during compilation. + * + * Intended to be user readable w/ enough info for the user to understand + * and find the issue. + * + * @return A String explanation of the problem. + */ public abstract String getFullMessage(); + /** + * An AndHow Property is missing the {@code static} modifier. + */ static class PropMissingStatic extends CompileProblem { + /** + * New instance. + * + * @param groupName Full name of the class containing the {@code Property}. + * If an inner class, should be the complete name w/ the inner class. + * @param propName The declared name of the {@code Property} w/ the problem. + */ public PropMissingStatic(String groupName, String propName) { super(groupName, propName); } @@ -69,8 +122,18 @@ public String getFullMessage() { } } + /** + * An AndHow Property is missing the {@code final} modifier. + */ static class PropMissingFinal extends CompileProblem { + /** + * New instance. + * + * @param groupName Full name of the class containing the {@code Property}. + * If an inner class, should be the complete name w/ the inner class. + * @param propName The declared name of the {@code Property} w/ the problem. + */ public PropMissingFinal(String groupName, String propName) { super(groupName, propName); } @@ -83,8 +146,18 @@ public String getFullMessage() { } } + /** + * An AndHow Property is missing the {@code static} and {@code final} modifiers. + */ static class PropMissingStaticFinal extends CompileProblem { + /** + * New instance. + * + * @param groupName Full name of the class containing the {@code Property}. + * If an inner class, should be the complete name w/ the inner class. + * @param propName The declared name of the {@code Property} w/ the problem. + */ public PropMissingStaticFinal(String groupName, String propName) { super(groupName, propName); } @@ -97,10 +170,19 @@ public String getFullMessage() { } } + /** + * More than a single Init class was found on the classpath. + */ static class TooManyInitClasses extends CompileProblem { private final List _instances = new ArrayList(); private final String _fullInitClassName; + /** + * New instance. + * + * @param fullInitClassName The Init interface name + * @param instances A list of instances that implement the interface. + */ public TooManyInitClasses( String fullInitClassName, List instances) { @@ -110,6 +192,11 @@ public TooManyInitClasses( } + /** + * A list of full class names that implement the InitClassName interface. + * + * @return A List of class names. Never null. + */ public List getInstanceNames() { List names = new ArrayList(); @@ -120,6 +207,11 @@ public List getInstanceNames() { return names; } + /** + * The Init interface name. + * + * @return A class name. + */ public String getInitClassName() { return _fullInitClassName; } @@ -135,6 +227,14 @@ public String getFullMessage() { _fullInitClassName, impList); } + /** + * Logical equals implementation based on equality of fields. + * + * This is only used for comparisons during testing. + * + * @param o Compared to me. + * @return True if logically equal. + */ @Override public boolean equals(Object o) { if (o != null && this.getClass().isInstance(o)) { From cf4e68ea130cc29f7c89307bfab78af8aa66f82d Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 21 Nov 2018 21:45:47 -0600 Subject: [PATCH 51/91] Update codecov.yml Increased the code coverage 'green range' to 100 percent so its easier to see where code needs more testing. --- codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index 7ee3554e..53abf197 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,6 +1,6 @@ ## YAML Template. --- coverage: - range: 0..60 + range: 0..100 round: down precision: 2 From 6ba6a5ddc9c0ee428ac0c12cf8161d132aa67f99 Mon Sep 17 00:00:00 2001 From: rupeshA Date: Mon, 29 Oct 2018 21:49:14 +0530 Subject: [PATCH 52/91] Inital commit for MustMatch validation --- .../yarnandtail/andhow/property/StrProp.java | 39 ++++++++------- .../andhow/valid/StringValidator.java | 50 ++++++++++++++++--- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java index 4b9307e9..8ac6cf98 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java @@ -9,7 +9,7 @@ * A Property that refers to a String value. * * All the basic Java types use a three letter abv. to keep declaration lines - * short, in the form of: [Type]Prop + * short, in the form of: [Type]Prop * * By default, this uses the QuotedSpacePreservingTrimmer, which will keep * whitespace inside double quotes. @@ -17,23 +17,19 @@ * @author eeverman */ public class StrProp extends PropertyBase { - - public StrProp( - String defaultValue, boolean nonNull, String shortDesc, List> validators, - List aliases, PropertyType paramType, ValueType valueType, Trimmer trimmer, - String helpText) { - + + public StrProp(String defaultValue, boolean nonNull, String shortDesc, List> validators, + List aliases, PropertyType paramType, ValueType valueType, Trimmer trimmer, String helpText) { + super(defaultValue, nonNull, shortDesc, validators, aliases, paramType, valueType, trimmer, helpText); } - + public static StrBuilder builder() { return new StrBuilder(); } - - + public static class StrBuilder extends PropertyBuilderBase { - public StrBuilder() { instance = this; valueType(StrType.instance()); @@ -43,35 +39,40 @@ public StrBuilder() { @Override public StrProp build() { - return new StrProp(_defaultValue, _nonNull, _desc, _validators, - _aliases, PropertyType.SINGLE_NAME_VALUE, _valueType, _trimmer, _helpText); + return new StrProp(_defaultValue, _nonNull, _desc, _validators, _aliases, PropertyType.SINGLE_NAME_VALUE, + _valueType, _trimmer, _helpText); } - + public StrBuilder mustMatchRegex(String regex) { this.validation(new StringValidator.Regex(regex)); return this; } - + public StrBuilder mustStartWith(String prefix) { this.validation(new StringValidator.StartsWith(prefix, false)); return this; } - + public StrBuilder mustStartWithIgnoreCase(String prefix) { this.validation(new StringValidator.StartsWith(prefix, true)); return this; } - + public StrBuilder mustEndWith(String sufix) { this.validation(new StringValidator.EndsWith(sufix, false)); return this; } - + public StrBuilder mustEndWithIgnoreCase(String sufix) { this.validation(new StringValidator.EndsWith(sufix, true)); return this; } + public StrBuilder mustMatch(String... values) { + this.validation(new StringValidator.MatchesFrom(values)); + return this; + } + } - + } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java index b7685141..54cacd2d 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java @@ -1,5 +1,7 @@ package org.yarnandtail.andhow.valid; +import java.util.Arrays; + import org.yarnandtail.andhow.api.Validator; /** @@ -8,7 +10,42 @@ * @author ericeverman */ public class StringValidator { - + + /** + * Validate that a string is one from the specified set. + */ + public static class MatchesFrom implements Validator { + + String[] values; + + public MatchesFrom(String... values) { + this.values = values; + } + + @Override + public boolean isSpecificationValid() { + return values != null && values.length != 0; + } + + @Override + public String getInvalidSpecificationMessage() { + return "The MatchesFrom expression cannot be null and should have atleast 1 value"; + } + + @Override + public boolean isValid(String value) { + if (value != null) { + return Arrays.stream(values).anyMatch(value::equals); + } + return false; + } + + @Override + public String getTheValueMustDescription() { + return "is one of'" + Arrays.deepToString(values) + "'"; + } + } + /** * Validate that a string starts with a specific string. */ @@ -43,13 +80,12 @@ public boolean isValid(String value) { } return false; } - + @Override public String getTheValueMustDescription() { return "start with '" + prefix + "'"; } } - /** * Validate that a string ends with a specific string. @@ -85,13 +121,13 @@ public boolean isValid(String value) { } return false; } - + @Override public String getTheValueMustDescription() { return "end with '" + sufix + "'"; } } - + /** * Validate based on a regex string. */ @@ -128,11 +164,11 @@ public boolean isValid(String value) { return false; } } - + @Override public String getTheValueMustDescription() { return "match the regex expression '" + regex + "'"; } - } + } } From be6372650a09bb797636cae2c540253105c6bc1b Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 21 Nov 2018 21:53:30 -0600 Subject: [PATCH 53/91] Update StrProp.java Changing the method name to 'mustEqual', since match implies regex. Similarly, the StringValidator 'MatchesFrom' is being renamed 'Equals'. --- .../main/java/org/yarnandtail/andhow/property/StrProp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java index 8ac6cf98..977de4c9 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/StrProp.java @@ -68,8 +68,8 @@ public StrBuilder mustEndWithIgnoreCase(String sufix) { return this; } - public StrBuilder mustMatch(String... values) { - this.validation(new StringValidator.MatchesFrom(values)); + public StrBuilder mustEqual(String... values) { + this.validation(new StringValidator.Equals(values)); return this; } From 7e4fa9b9e9c1cd3ce5af4f7b772407926e16884d Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 21 Nov 2018 21:55:51 -0600 Subject: [PATCH 54/91] Update StringValidator.java Changed the class name to 'Equals' and updated the invalid message. --- .../java/org/yarnandtail/andhow/valid/StringValidator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java index 54cacd2d..02bf9301 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java @@ -14,7 +14,7 @@ public class StringValidator { /** * Validate that a string is one from the specified set. */ - public static class MatchesFrom implements Validator { + public static class Equals implements Validator { String[] values; @@ -42,7 +42,7 @@ public boolean isValid(String value) { @Override public String getTheValueMustDescription() { - return "is one of'" + Arrays.deepToString(values) + "'"; + return "be equal to one of '" + Arrays.deepToString(values) + "'"; } } From 21c821861689f27b91fcc0bdeb1289f46b6545ce Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 21 Nov 2018 22:00:26 -0600 Subject: [PATCH 55/91] Update StringValidator.java Fixed message text --- .../java/org/yarnandtail/andhow/valid/StringValidator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java index 02bf9301..3cd47681 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java @@ -29,7 +29,7 @@ public boolean isSpecificationValid() { @Override public String getInvalidSpecificationMessage() { - return "The MatchesFrom expression cannot be null and should have atleast 1 value"; + return "The Equals list must contain at least one value"; } @Override @@ -66,7 +66,7 @@ public boolean isSpecificationValid() { @Override public String getInvalidSpecificationMessage() { - return "The StartWith expression cannot be null"; + return "The StartsWith expression cannot be null"; } @Override From d58d7c65ef021ebda289bd7497c289c81719340e Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 21 Nov 2018 22:07:31 -0600 Subject: [PATCH 56/91] Update StringValidator.java Fixed constructor name --- .../main/java/org/yarnandtail/andhow/valid/StringValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java index 3cd47681..16504142 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/valid/StringValidator.java @@ -18,7 +18,7 @@ public static class Equals implements Validator { String[] values; - public MatchesFrom(String... values) { + public Equals(String... values) { this.values = values; } From fa35b8aa80ce2d222099c28cc8d572b3d6b52789 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 5 Dec 2018 12:34:10 -0600 Subject: [PATCH 57/91] Issue469 compiler warnings (#472) * Overrode the getSupportedSourceVersion method to the Annotation processor We don't care about new language constructs because we are only scanning for variable declaration of AndHow Properties. By using 'latestSupported', the version of whatever JDK is compiling will be accepted. * Added better class docs * Added a test to ensure we are not getting compiler warnings * Formatted for consistent tabs --- .../compile/AndHowCompileProcessor.java | 8 +++++++ .../AndHowCompileProcessorTestBase.java | 9 +++----- .../AndHowCompileProcessor_InitTest.java | 21 ++++++++++++++++--- .../AndHowCompileProcessor_PropertyTest.java | 21 ++++++++++++------- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index 4b37fb34..d21ecf96 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -5,6 +5,7 @@ import java.io.*; import java.util.*; import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.tools.Diagnostic; @@ -63,6 +64,13 @@ public AndHowCompileProcessor() { runDate = new GregorianCalendar(); } + @Override + public SourceVersion getSupportedSourceVersion() { + //Only scanning for declaration of AndHow Properties, so should + //be immune to most new language constructs. + return SourceVersion.latestSupported(); + } + @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java index e8ea6394..7257ddc6 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java @@ -1,17 +1,12 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.yarnandtail.andhow.compile; import java.util.HashSet; import java.util.Set; +import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.ToolProvider; import org.junit.Before; -import static org.yarnandtail.andhow.compile.AndHowCompileProcessor_PropertyTest.pkg; import org.yarnandtail.andhow.util.IOUtil; import org.yarnandtail.compile.MemoryFileManager; import org.yarnandtail.compile.TestClassLoader; @@ -31,6 +26,7 @@ public class AndHowCompileProcessorTestBase { JavaCompiler compiler; MemoryFileManager manager; + DiagnosticCollector diagnostics; TestClassLoader loader; Set sources; //New set of source files to compile @@ -39,6 +35,7 @@ public class AndHowCompileProcessorTestBase { public void setupTest() { compiler = ToolProvider.getSystemJavaCompiler(); manager = new MemoryFileManager(compiler); + diagnostics = new DiagnosticCollector(); loader = new TestClassLoader(manager); sources = new HashSet(); } diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java index 6daf8478..abfbbf1f 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java @@ -5,6 +5,7 @@ import java.nio.charset.Charset; import java.util.*; import javax.tools.*; +import javax.tools.Diagnostic.Kind; import org.junit.Test; import org.yarnandtail.andhow.util.IOUtil; @@ -40,13 +41,15 @@ public void testServiceRegistrationOfOneProdAndOneTestInit() throws Exception { sources.add(buildTestSource(pkg, AndHowTestInitAbstract_NAME)); sources.add(buildTestSource(pkg, AndHowTestInitA_NAME)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); String prodInitSvs = IOUtil.toString(loader.getResourceAsStream(INIT_SVS_PATH), Charset.forName("UTF-8")); String testInitSvs = IOUtil.toString(loader.getResourceAsStream(TEST_INIT_SVS_PATH), Charset.forName("UTF-8")); + assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING)).count()); // //Test the initiation files @@ -66,12 +69,15 @@ public void testServiceRegistrationOfOneProdInit() throws Exception { sources.add(buildTestSource(pkg, AndHowInitAbstract_NAME)); sources.add(buildTestSource(pkg, AndHowInitA_NAME)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); String prodInitSvs = IOUtil.toString(loader.getResourceAsStream(INIT_SVS_PATH), Charset.forName("UTF-8")); + assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING)).count()); + // //Test the initiation files assertNotNull(prodInitSvs); @@ -87,12 +93,15 @@ public void testServiceRegistrationOfOneTestInit() throws Exception { sources.add(buildTestSource(pkg, AndHowTestInitAbstract_NAME)); sources.add(buildTestSource(pkg, AndHowTestInitA_NAME)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); String testInitSvs = IOUtil.toString(loader.getResourceAsStream(TEST_INIT_SVS_PATH), Charset.forName("UTF-8")); + assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING)).count()); + // //Test the initiation files @@ -116,6 +125,8 @@ public void testServiceRegistrationOfAndHowInitWithTooManyProdInstances() throws JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); + + fail("Should have thrown an exception"); } catch (RuntimeException e) { @@ -155,6 +166,8 @@ public void testServiceRegistrationOfAndHowInitWithTooManyTestInstances() throws task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); + fail("Should have thrown an exception"); + } catch (RuntimeException e) { assertNotNull(e.getCause()); @@ -195,6 +208,8 @@ public void testServiceRegistrationOfAndHowInitWithTooManyInstAndBadProperties() JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); + + fail("Should have thrown an exception"); } catch (RuntimeException e) { diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java index 92b03689..d1c80e4b 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java @@ -1,16 +1,15 @@ package org.yarnandtail.andhow.compile; -import org.yarnandtail.compile.*; -import org.yarnandtail.andhow.service.*; import java.nio.charset.Charset; import java.util.*; import javax.tools.*; +import static org.junit.Assert.*; import org.junit.Test; +import org.yarnandtail.andhow.compile.CompileProblem.PropMissingFinal; +import org.yarnandtail.andhow.compile.CompileProblem.PropMissingStatic; +import org.yarnandtail.andhow.compile.CompileProblem.PropMissingStaticFinal; +import org.yarnandtail.andhow.service.*; import org.yarnandtail.andhow.util.IOUtil; -import static org.yarnandtail.andhow.compile.CompileProblem.*; - -import static org.junit.Assert.*; -import org.junit.Before; /** * A lot of this code was borrowed from here: @@ -34,7 +33,7 @@ public void testComplexNestedPropertySampleClass() throws Exception { sources.add(buildTestSource(pkg, classSimpleName)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); @@ -42,6 +41,9 @@ public void testComplexNestedPropertySampleClass() throws Exception { String genSvsFile = IOUtil.toString( loader.getResourceAsStream(REGISTRAR_SVS_PATH), Charset.forName("UTF-8")); + assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Diagnostic.Kind.ERROR) || d.getKind().equals(Diagnostic.Kind.WARNING)).count()); + assertNotNull(genClass); PropertyRegistrar registrar = (PropertyRegistrar)genClass; @@ -81,7 +83,7 @@ public void testSimpleHappyPathClass() throws Exception { sources.add(buildTestSource(pkg, classSimpleName)); - JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, sources); + JavaCompiler.CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, sources); task.setProcessors(Collections.singleton(new AndHowCompileProcessor())); task.call(); @@ -89,6 +91,9 @@ public void testSimpleHappyPathClass() throws Exception { String genSvsFile = IOUtil.toString( loader.getResourceAsStream(REGISTRAR_SVS_PATH), Charset.forName("UTF-8")); + assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Diagnostic.Kind.ERROR) || d.getKind().equals(Diagnostic.Kind.WARNING)).count()); + assertNotNull(genClass); PropertyRegistrar registrar = (PropertyRegistrar)genClass; From 6531e664350c5da6982f79a8062adb5ad876800e Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Sun, 9 Dec 2018 20:14:43 -0600 Subject: [PATCH 58/91] Updated per new logo (#474) * Updated per new logo * added width to image * Improved readme formatting added logo to repo * small format fix * hopefully a small fix * workaround for a bug in heading rendering --- README.md | 24 +- logo/AndHow-empty-circle-combination-2.png | Bin 0 -> 54596 bytes logo/AndHow-empty-circle.png | Bin 0 -> 33597 bytes logo/andhow-empty-circle.svg | 260 +++++++++++++++++++++ 4 files changed, 272 insertions(+), 12 deletions(-) create mode 100644 logo/AndHow-empty-circle-combination-2.png create mode 100644 logo/AndHow-empty-circle.png create mode 100644 logo/andhow-empty-circle.svg diff --git a/README.md b/README.md index 224ae049..a53e349d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,20 @@ -# AndHow is looking for a new Logo - [Enter your design before the end of October!](https://github.com/eeverman/andhow/issues/427) -## [>>See the latest logo submissions <<](https://twitter.com/hashtag/andhowconfig_logo) [![Build Status](https://travis-ci.org/eeverman/andhow.svg?branch=master)](https://travis-ci.org/eeverman/andhow) [![codecov](https://codecov.io/gh/eeverman/andhow/branch/master/graph/badge.svg)](https://codecov.io/gh/eeverman/andhow) [![Javadocs](https://www.javadoc.io/badge/org.yarnandtail/andhow.svg)](https://www.javadoc.io/doc/org.yarnandtail/andhow) -![Andhow Visual](andhow.gif) +# Introducing AndHow's new Logo +AndHow's new logo + +Many thanks to everyone who participated in the [AndHow logo contest](https://github.com/eeverman/andhow/issues/427) in October. All six logo entries were solid, but [Carl Schroedl](https://github.com/carlschroedl)'s entry was a clear winner. Carl's logo looks a bit like a swiss army knife or bottle opener, reflecting the utility aspect of AndHow. The logo also combines an ampersand (&) and a question mark into one simple and clever symbol. Well done Carl! + +AndHow animation AndHow! strong.valid.simple.AppConfiguration -====== +============================= AndHow is an easy to use configuration framework with strong typing and detailed validation for web apps, command line or any application environment. _**Learn more at the [AndHow main site](https://sites.google.com/view/andhow)**_ -Key Features --------------- +## Key Features * **Strong Typing** * **Detailed validation** * **Simple to use** @@ -21,15 +23,13 @@ Key Features * **Loads values from multiple sources (JNDI, env vars, prop files, etc)** * **Generates configuration sample file based on application properties** -Questions / Discussion / Contact --------------- +## Questions / Discussion / Contact [Join the discussion](https://sites.google.com/view/andhow/join-discussion) on the [user forum](https://groups.google.com/d/forum/andhowuser) or the *Slack* group (See details on the [Join](https://sites.google.com/view/andhow/join-discussion) page). -Use it via Maven (available on Maven Central) --------------- +## Use it via Maven (available on Maven Central) ```xml org.yarnandtail @@ -39,8 +39,7 @@ Use it via Maven (available on Maven Central) ``` **AndHow can be used in projects with Java 8 and above, however, Java 9 and above have [some restrictions](https://sites.google.com/view/andhow/user-guide/java9)** -Complete Usage Example --------------- +## Complete Usage Example _**More usage examples and documentation are available at the [AndHow main site](https://sites.google.com/view/andhow)**_ ```java @@ -174,3 +173,4 @@ Values passed to the main method take precedence over system properties as so on _**For more examples and documentation, visit the [AndHow main site](https://sites.google.com/view/andhow)**_ _**&?!**_ + diff --git a/logo/AndHow-empty-circle-combination-2.png b/logo/AndHow-empty-circle-combination-2.png new file mode 100644 index 0000000000000000000000000000000000000000..79bf4ec04dbf83e3d3c0c2bc31f4842d86425dd2 GIT binary patch literal 54596 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 F{})!|VW + + + + AndHow! + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + AndHow! + + + + + + + ? + + + + + + + AndHow! + ! + + From 8ff57a7a9c51fd9972542a1f09445c3b45d39289 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Sun, 9 Dec 2018 20:26:47 -0600 Subject: [PATCH 59/91] Logo winner (#475) * Updated per new logo * added width to image * Improved readme formatting added logo to repo * small format fix * hopefully a small fix * workaround for a bug in heading rendering * Fixed logo name * revert how the main gif is embedded hopefully fixing a heading issue --- README.md | 5 ++--- ...on-2.png => AndHow-empty-circle-combination.png} | Bin 2 files changed, 2 insertions(+), 3 deletions(-) rename logo/{AndHow-empty-circle-combination-2.png => AndHow-empty-circle-combination.png} (100%) diff --git a/README.md b/README.md index a53e349d..82eb95aa 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,8 @@ Many thanks to everyone who participated in the [AndHow logo contest](https://github.com/eeverman/andhow/issues/427) in October. All six logo entries were solid, but [Carl Schroedl](https://github.com/carlschroedl)'s entry was a clear winner. Carl's logo looks a bit like a swiss army knife or bottle opener, reflecting the utility aspect of AndHow. The logo also combines an ampersand (&) and a question mark into one simple and clever symbol. Well done Carl! -AndHow animation -AndHow! strong.valid.simple.AppConfiguration -============================= +![Andhow Visual](andhow.gif) +# AndHow! strong.valid.simple.AppConfiguration AndHow is an easy to use configuration framework with strong typing and detailed validation for web apps, command line or any application environment. diff --git a/logo/AndHow-empty-circle-combination-2.png b/logo/AndHow-empty-circle-combination.png similarity index 100% rename from logo/AndHow-empty-circle-combination-2.png rename to logo/AndHow-empty-circle-combination.png From 55f7701d02995a4ca9e10b57ccf1dccea044849d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 04:46:48 +0000 Subject: [PATCH 60/91] Bump junit from 4.12 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 517fef51..3d78eea4 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ junit junit - 4.12 + 4.13.1 test From 3a3bf2fb9a172c1177d68e24227c86090b6cfb78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Apr 2021 23:30:37 -0500 Subject: [PATCH 61/91] Bump commons-io from 2.6 to 2.7 (#480) Bumps commons-io from 2.6 to 2.7. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 517fef51..88d16a22 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ commons-io commons-io - 2.6 + 2.7 test From 61c46499826f87ef77ae13dec95298e8e23a1f1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Apr 2021 23:36:47 -0500 Subject: [PATCH 62/91] Bump junit from 4.12 to 4.13.1 (#479) Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 88d16a22..d4b83f90 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ junit junit - 4.12 + 4.13.1 test From 35c18acfc027a1c8feb9addff45aab8b465e46dc Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 28 Apr 2021 23:53:49 -0500 Subject: [PATCH 63/91] Issue481 travis build fails (#482) * Bump junit from 4.12 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] * Changed the distro for the build - rumored to fix this issue. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Eric Everman --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 22f399b2..5eeabe77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: trusty language: java jdk: - oraclejdk8 From e40a5ea99171545e602912c180c2cef8b8a3d53d Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Thu, 29 Apr 2021 00:06:54 -0500 Subject: [PATCH 64/91] Possible fix (#483) Co-authored-by: Eric Everman --- appveyor.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8c996077..81a4571a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,14 +11,15 @@ branches: install: # Prepend Java entry, remove Ruby entry (C:\Ruby193\bin;) from PATH - cmd: choco upgrade maven + - cmd: C:\ProgramData\chocolatey\bin\RefreshEnv - cmd: SET PATH=%JAVA_HOME%\bin;%PATH:C:\Ruby193\bin;=%; - cmd: SET MAVEN_OPTS=-XX:MaxPermSize=1g -Xmx2g - cmd: SET JAVA_OPTS=-XX:MaxPermSize=1g -Xmx2g # Print debug info - - cmd: C:\ProgramData\chocolatey\bin\mvn.exe --version + - cmd: mvn --version - cmd: java -version build_script: - - C:\ProgramData\chocolatey\bin\mvn.exe clean install + - mvn clean install cache: - C:\maven\ -> appveyor.yml - C:\Users\appveyor\.m2\ -> pom.xml From 4e6721c5ec4e73d58f708cb3397a17987595ee08 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Thu, 20 May 2021 22:53:23 -0500 Subject: [PATCH 65/91] Possible fix (#486) Co-authored-by: Eric Everman From 7fe695fd337404c7a415596e10c15226b637203e Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Thu, 20 May 2021 22:53:48 -0500 Subject: [PATCH 66/91] Changed the distro for the build - rumored to fix this issue. (#487) Co-authored-by: Eric Everman From b43e5c1f5e9006049d6157a0fc245e995a9182c7 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Thu, 20 May 2021 23:17:14 -0500 Subject: [PATCH 67/91] Fix Issue 484 and 485 (#488) Co-authored-by: Eric Everman --- .../compile/AndHowCompileProcessor.java | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index d21ecf96..bfe237fc 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -1,8 +1,10 @@ package org.yarnandtail.andhow.compile; import org.yarnandtail.andhow.service.PropertyRegistrationList; -import com.sun.source.util.Trees; import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; import java.util.*; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; @@ -37,6 +39,7 @@ * Property containing classes. */ @SupportedAnnotationTypes("*") +@SupportedSourceVersion(SourceVersion.RELEASE_8) public class AndHowCompileProcessor extends AbstractProcessor { private static final String INIT_CLASS_NAME = AndHowInit.class.getCanonicalName(); @@ -64,18 +67,14 @@ public AndHowCompileProcessor() { runDate = new GregorianCalendar(); } - @Override - public SourceVersion getSupportedSourceVersion() { - //Only scanning for declaration of AndHow Properties, so should - //be immune to most new language constructs. - return SourceVersion.latestSupported(); - } - @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - - Filer filer = processingEnv.getFiler(); - Messager log = this.processingEnv.getMessager(); + + //Some IDEs (IntelliJ) wrap the PE, so we unwrap it first + ProcessingEnvironment unwrappedProcessingEnv = unwrapProcessingEnv(this.processingEnv); + + Filer filer = unwrappedProcessingEnv.getFiler(); + Messager log = unwrappedProcessingEnv.getMessager(); boolean isLastRound = roundEnv.processingOver(); @@ -147,7 +146,7 @@ public boolean process(Set annotations, RoundEnvironment TypeElement te = (TypeElement) e; AndHowElementScanner7 st = new AndHowElementScanner7( - this.processingEnv, + unwrappedProcessingEnv, Property.class.getCanonicalName(), INIT_CLASS_NAME, TEST_INIT_CLASS_NAME); @@ -286,4 +285,38 @@ public CauseEffect(String fullClassName, Element causeElement) { } } + /** + * Unwrap the ProcessingEnvironment from a (possibly) IntelliJ wrapper. + * This implementation was taken from code posted by Vicky Ronnen in a IntelliJ bug discussion thread: + * https://youtrack.jetbrains.com/issue/IDEA-256707 + * It has no apparent license and was posted on a public discussion thread. + * It is apparently a slimmed down version of similar code in Project Lambok, which + * is itself an open source project. + * + * @param possiblyWrappedProcessingEnv + * @return + */ + public static ProcessingEnvironment unwrapProcessingEnv(ProcessingEnvironment possiblyWrappedProcessingEnv) { + if (Proxy.isProxyClass(possiblyWrappedProcessingEnv.getClass())) { + InvocationHandler invocationHandler = Proxy.getInvocationHandler(possiblyWrappedProcessingEnv); + try { + Field field = invocationHandler.getClass().getDeclaredField("val$delegateTo"); + field.setAccessible(true); + Object o = field.get(invocationHandler); + if (o instanceof ProcessingEnvironment) { + return (ProcessingEnvironment) o; + } else { + possiblyWrappedProcessingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "got " + + o.getClass() + " expected instanceof com.sun.tools.javac.processing.JavacProcessingEnvironment"); + return null; + } + } catch (NoSuchFieldException | IllegalAccessException e) { + possiblyWrappedProcessingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage()); + return null; + } + } else { + return possiblyWrappedProcessingEnv; //It wasn't wrapped + } + } + } From ff49eb6ceb9a338b975b6ca559707881acd914ef Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 26 May 2021 11:45:29 -0500 Subject: [PATCH 68/91] took out unwrap code so it can be added by the author (#489) Co-authored-by: Eric Everman --- .../compile/AndHowCompileProcessor.java | 37 +------------------ 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index bfe237fc..e2a64029 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -70,8 +70,8 @@ public AndHowCompileProcessor() { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - //Some IDEs (IntelliJ) wrap the PE, so we unwrap it first - ProcessingEnvironment unwrappedProcessingEnv = unwrapProcessingEnv(this.processingEnv); + //TODO: Unwrap the IntelliJ ProcessingEnvironment + ProcessingEnvironment unwrappedProcessingEnv = this.processingEnv; Filer filer = unwrappedProcessingEnv.getFiler(); Messager log = unwrappedProcessingEnv.getMessager(); @@ -285,38 +285,5 @@ public CauseEffect(String fullClassName, Element causeElement) { } } - /** - * Unwrap the ProcessingEnvironment from a (possibly) IntelliJ wrapper. - * This implementation was taken from code posted by Vicky Ronnen in a IntelliJ bug discussion thread: - * https://youtrack.jetbrains.com/issue/IDEA-256707 - * It has no apparent license and was posted on a public discussion thread. - * It is apparently a slimmed down version of similar code in Project Lambok, which - * is itself an open source project. - * - * @param possiblyWrappedProcessingEnv - * @return - */ - public static ProcessingEnvironment unwrapProcessingEnv(ProcessingEnvironment possiblyWrappedProcessingEnv) { - if (Proxy.isProxyClass(possiblyWrappedProcessingEnv.getClass())) { - InvocationHandler invocationHandler = Proxy.getInvocationHandler(possiblyWrappedProcessingEnv); - try { - Field field = invocationHandler.getClass().getDeclaredField("val$delegateTo"); - field.setAccessible(true); - Object o = field.get(invocationHandler); - if (o instanceof ProcessingEnvironment) { - return (ProcessingEnvironment) o; - } else { - possiblyWrappedProcessingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "got " + - o.getClass() + " expected instanceof com.sun.tools.javac.processing.JavacProcessingEnvironment"); - return null; - } - } catch (NoSuchFieldException | IllegalAccessException e) { - possiblyWrappedProcessingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage()); - return null; - } - } else { - return possiblyWrappedProcessingEnv; //It wasn't wrapped - } - } } From fc3252ef3f0d7d5d5d20d8548a2395ed6086395b Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Mon, 31 May 2021 22:26:50 -0500 Subject: [PATCH 69/91] AnnotationProcessor now reports it supports all JDK versions (#493) Also update the pom to: * specify 1.8 as the source and target * Specify a Java version as 1.8 to 15 * Specify a Maven version 3.5 up to 4.0 Co-authored-by: Eric Everman --- .../andhow/compile/AndHowCompileProcessor.java | 8 +++++++- pom.xml | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index e2a64029..e8e398c4 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -39,7 +39,6 @@ * Property containing classes. */ @SupportedAnnotationTypes("*") -@SupportedSourceVersion(SourceVersion.RELEASE_8) public class AndHowCompileProcessor extends AbstractProcessor { private static final String INIT_CLASS_NAME = AndHowInit.class.getCanonicalName(); @@ -67,6 +66,13 @@ public AndHowCompileProcessor() { runDate = new GregorianCalendar(); } + @Override + public SourceVersion getSupportedSourceVersion() { + //Only scanning for declaration of AndHow Properties, so should + //be immune to most new language constructs. + return SourceVersion.latestSupported(); + } + @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { diff --git a/pom.xml b/pom.xml index d4b83f90..0180ac85 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,8 @@ UTF-8 + 1.8 + 1.8 @@ -181,10 +183,11 @@ - 3.2.2 + (3.5,4.0) - [1.8.0,1.9) + + [1.8.0,16) From f3a5a93fed780767c50defc878094e37282bcb54 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Tue, 1 Jun 2021 23:28:58 -0500 Subject: [PATCH 70/91] Now using the Generated annotation on generated files (#494) Co-authored-by: Eric Everman --- .../andhow/compile/CompileUtil.java | 70 +++++++++++++++++++ .../PropertyRegistrarClassGenerator.java | 13 ++-- ...opertyRegistrarClassGenerator_Template.txt | 11 +-- .../andhow/compile/CompileUtilTest.java | 47 +++++++++++++ .../PropertyRegistrarClassGeneratorTest.java | 13 +++- 5 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUtil.java create mode 100644 andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUtil.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUtil.java new file mode 100644 index 00000000..9ea3e415 --- /dev/null +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/CompileUtil.java @@ -0,0 +1,70 @@ +package org.yarnandtail.andhow.compile; + +/** + * Utilities for the AndHow AnnotationProcessor. + */ +public class CompileUtil { + + /** + * Determine the correct 'Generated' annotation class name based on the current Java runtime. + * + * This method fetches the version of the current runtime via getMajorJavaVersion() and + * uses that to call getGeneratedAnnotationClassName(int). + * + * @return A the fully qualified class name of the Generated annotation. + */ + public static String getGeneratedAnnotationClassName() { + return getGeneratedAnnotationClassName(getMajorJavaVersion(System.getProperty("java.version"))); + } + + /** + * Determine the correct 'Generated' annotation class name based on the Java major version. + * Java 8 uses the @javax.annotation.Generated annotation to mark a generated class. + * Java 9 and beyond uses @javax.annotation.processing.Generated + * + * Both annotations are SOURCE level retention, so they are only present in the source code and no + * record of them is compiled into the binary. Thus, the determination of which one to use is + * based only on the Java version used to compile, not the -source or -target settings. + * + * @param javaMajorVersion The Java version in integer form. Use '8' for 1.8. + * @return A the fully qualified class name of the Generated annotation. + */ + public static String getGeneratedAnnotationClassName(int javaMajorVersion) { + if (javaMajorVersion < 9) { + return "javax.annotation.Generated"; + } else { + return "javax.annotation.processing.Generated"; + } + } + + /** + * Determine the major version of the Java runtime based on a version string. + * + * All versions are integers, thus version `1.8` returns `8`. + * Java 10 introduces the Runtime.version(), which would remove the need for this method, + * however, at the moment the code is still Java 8 compatable. + * + * @param versionString As returned from SystemProperties.getProperty("java.version") + * @return + */ + public static int getMajorJavaVersion(String versionString) { + String[] versionParts = versionString.split("[\\.\\-_]", 3); + + try { + if ("1".equals(versionParts[0])) { + //Old style 1.x format + return Integer.parseInt(versionParts[1]); + } else { + return Integer.parseInt(versionParts[0]); + } + + } catch (NumberFormatException e) { + throw new RuntimeException( + "AndHow couldn't parse '" + versionString + "' as a 'java.version' string in System.properties. " + + "Is this a non-standard JDK? ", e + ); + } + + + } +} diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator.java index 5e50a83b..8cb598bb 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator.java @@ -24,7 +24,8 @@ public class PropertyRegistrarClassGenerator { * Create a new instance w all info needed to generateSource a PropertyRegistrar file. * * @param compUnit CompileUnit instance w/ all needed class and property info - * @param generatingClass The class (likely an AnnotationProcessor) that will be annotated as the generator + * @param generatingClass The class of our AnnotationProcessor to be listed as the generator in the + * Generated annotation. * @param runDate The Calendar date-time of the run, used for annotation. * Passed in so all generated files can have the same timestamp. */ @@ -42,6 +43,7 @@ public String getTemplatePath() { public String getTemplate() throws Exception { return IOUtil.getUTF8ResourceAsString(getTemplatePath()); } + public String generateSource() throws Exception { @@ -50,11 +52,14 @@ public String generateSource() throws Exception { String source = String.format(template, buildPackageString(), - compUnit.getRootCanonicalName(), compUnit.getRootSimpleName(), + compUnit.getRootCanonicalName(), + compUnit.getRootSimpleName(), buildGeneratedClassSimpleName(), PropertyRegistrar.class.getCanonicalName(), - generatingClass.getCanonicalName(), buildRunDateString(), - buildRegistrationAddsString() + generatingClass.getCanonicalName(), + buildRunDateString(), + buildRegistrationAddsString(), + CompileUtil.getGeneratedAnnotationClassName() ); return source; diff --git a/andhow-annotation-processor/src/main/resources/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator_Template.txt b/andhow-annotation-processor/src/main/resources/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator_Template.txt index 9af12a1c..a5833bd8 100644 --- a/andhow-annotation-processor/src/main/resources/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator_Template.txt +++ b/andhow-annotation-processor/src/main/resources/org/yarnandtail/andhow/compile/PropertyRegistrarClassGenerator_Template.txt @@ -4,12 +4,15 @@ import org.yarnandtail.andhow.service.AbstractPropertyRegistrar; import org.yarnandtail.andhow.service.PropertyRegistrationList; /* -Java9 places 'Generated' in a module that needs to be separate included in a build -or brought in as a dependency. As a result, just using a comment instead. -@javax.annotation.Generated( +AndHow generated class that is discovered via the Service Provider API to +register a proxied class as having AndHow properties. These properties +can then be auto-discovered and assigned values at startup. +See: https://github.com/eeverman/andhow +*/ +@%9$s( value="%6$s", date="%7$s", - comments="Proxy for %2$s registered as a service provider in META-INF/services/%5$s") */ + comments="Proxy for %2$s registered as a service provider in META-INF/services/%5$s") public class %4$s extends AbstractPropertyRegistrar { @Override diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java new file mode 100644 index 00000000..75eeaa63 --- /dev/null +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java @@ -0,0 +1,47 @@ +package org.yarnandtail.andhow.compile; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CompileUtilTest { + + @Test + public void getGeneratedAnnotationClassNameTest() { + assertEquals("javax.annotation.Generated", CompileUtil.getGeneratedAnnotationClassName(8)); + assertEquals("javax.annotation.processing.Generated", CompileUtil.getGeneratedAnnotationClassName(9)); + } + + @Test + public void getMajorJavaVersionHappyTest() { + + assertEquals(8, CompileUtil.getMajorJavaVersion("1.8")); + assertEquals(8, CompileUtil.getMajorJavaVersion("1.8.434-be")); + assertEquals(8, CompileUtil.getMajorJavaVersion("1.8.0_152")); + + assertEquals(9, CompileUtil.getMajorJavaVersion("9")); + assertEquals(9, CompileUtil.getMajorJavaVersion("9.0.1")); + + //Weird stuff still ok + assertEquals(9, CompileUtil.getMajorJavaVersion("9-prerelease")); + assertEquals(9, CompileUtil.getMajorJavaVersion("9_prerelease")); + + assertEquals(11, CompileUtil.getMajorJavaVersion("11")); + assertEquals(11, CompileUtil.getMajorJavaVersion("11.0")); + assertEquals(11, CompileUtil.getMajorJavaVersion("11.1")); + assertEquals(11, CompileUtil.getMajorJavaVersion("11.0.1")); + assertEquals(11, CompileUtil.getMajorJavaVersion("11.1-b53")); + } + + @Test + public void getMajorJavaVersionExceptionTest() { + + try { + CompileUtil.getMajorJavaVersion("WeirdVersion"); + fail("This should have thrown an exception"); + } catch (Exception e) { + //Expected + } + + } +} \ No newline at end of file diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java index bf868ae6..48b19216 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java @@ -143,7 +143,7 @@ public void testBuildRegistrationAddsString_Complex() { assertEquals("list.add(\"" + PROP1_NAME + "\", \"" + INNER1_SIMP_NAME + "\", \"" + INNER2_SIMP_NAME + "\");", eachAdds[3]); assertEquals("list.add(\"" + PROP2_NAME + "\");", eachAdds[4]); //No inner path b/c in inherits from above } - + /** * Basic gross test that the generated source is compilable */ @@ -160,6 +160,17 @@ public void testGenerate_simple() throws Exception { assertThat(compilation).succeeded(); + //Check the source file + + //This method is separately tested, so lets trust it works correctly + int javaVersion = CompileUtil.getMajorJavaVersion(System.getProperty("java.version")); + + if (javaVersion < 9) { + assertTrue(sourceStr.contains("@javax.annotation.Generated(")); + } else { + assertTrue(sourceStr.contains("@javax.annotation.processing.Generated(")); + } + } /** From f12cc45bdb3127a1d202e412e55693072a02b56c Mon Sep 17 00:00:00 2001 From: VickyRonnen Date: Wed, 2 Jun 2021 18:19:40 +0200 Subject: [PATCH 71/91] Added conditional unwrapping of ProcessingEnvironment when running build in IntelliJ Idea 2020.3+ (#496) Co-authored-by: Vicky Ronnen --- .../compile/AndHowCompileProcessor.java | 140 +++++++++++------- 1 file changed, 87 insertions(+), 53 deletions(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index e8e398c4..9ec5bca6 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -9,20 +9,19 @@ import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; -import javax.tools.Diagnostic; - import javax.tools.FileObject; import org.yarnandtail.andhow.AndHowInit; import org.yarnandtail.andhow.api.Property; import org.yarnandtail.andhow.service.*; +import static javax.tools.Diagnostic.*; import static javax.tools.StandardLocation.CLASS_OUTPUT; import org.yarnandtail.andhow.util.TextUtil; /** * This is the central AndHow compilation class, an Annotation Processor. - * + * * An annotation processor nominally reads annotations as classes are compiled. * This class is annotated {@code SupportedAnnotationTypes("*")}, allowing it to * 'see' all classes as they are compiled. This class then delegates to a @@ -40,24 +39,24 @@ */ @SupportedAnnotationTypes("*") public class AndHowCompileProcessor extends AbstractProcessor { - + private static final String INIT_CLASS_NAME = AndHowInit.class.getCanonicalName(); private static final String TEST_INIT_CLASS_NAME = "org.yarnandtail.andhow.AndHowTestInit"; - + private static final String SERVICES_PACKAGE = ""; - + private static final String SERVICE_REGISTRY_META_DIR = "META-INF/services/"; - + //Static to insure all generated classes have the same timestamp private static Calendar runDate; - + private final List registrars = new ArrayList(); - + private final List initClasses = new ArrayList(); //List of init classes (should only ever be 1) private final List testInitClasses = new ArrayList(); //List of test init classes (should only ever be 1) private final List problems = new ArrayList(); //List of problems found. >0== RuntimeException - + /** * A no-arg constructor is required. */ @@ -66,6 +65,12 @@ public AndHowCompileProcessor() { runDate = new GregorianCalendar(); } + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + //Unwrap the IntelliJ ProcessingEnvironment if needed + super.init(unwrap(processingEnv)); + } + @Override public SourceVersion getSupportedSourceVersion() { //Only scanning for declaration of AndHow Properties, so should @@ -75,38 +80,35 @@ public SourceVersion getSupportedSourceVersion() { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - - //TODO: Unwrap the IntelliJ ProcessingEnvironment - ProcessingEnvironment unwrappedProcessingEnv = this.processingEnv; - - Filer filer = unwrappedProcessingEnv.getFiler(); - Messager log = unwrappedProcessingEnv.getMessager(); - + + Filer filer = this.processingEnv.getFiler(); + Messager log = this.processingEnv.getMessager(); + boolean isLastRound = roundEnv.processingOver(); - + if (isLastRound) { debug(log, "Final round of annotation processing. Total root element count: {}", roundEnv.getRootElements().size()); - + if (initClasses.size() > 1) { problems.add(new CompileProblem.TooManyInitClasses( INIT_CLASS_NAME, initClasses)); } - + if (testInitClasses.size() > 1) { problems.add(new CompileProblem.TooManyInitClasses( TEST_INIT_CLASS_NAME, testInitClasses)); } - + if (problems.isEmpty()) { try { if (initClasses.size() == 1) { debug(log, "Found exactly 1 {} class: {}", INIT_CLASS_NAME, initClasses.get(0).fullClassName); - + writeServiceFile(filer, AndHowInit.class.getCanonicalName(), initClasses); } @@ -114,7 +116,7 @@ public boolean process(Set annotations, RoundEnvironment if (testInitClasses.size() == 1) { debug(log, "Found exactly 1 {} class: {}", TEST_INIT_CLASS_NAME, testInitClasses.get(0).fullClassName); - + writeServiceFile(filer, TEST_INIT_CLASS_NAME, testInitClasses); } @@ -122,7 +124,7 @@ public boolean process(Set annotations, RoundEnvironment if (registrars.size() > 0) { writeServiceFile(filer, PropertyRegistrar.class.getCanonicalName(), registrars); } - + } catch (IOException e) { throw new AndHowCompileException("Exception while trying to write generated files", e); } @@ -131,14 +133,14 @@ public boolean process(Set annotations, RoundEnvironment + "prevented compilation. Each of the following errors " + "must be fixed before compilation is possible."); error(log, "AndHow errors discovered: {}", problems.size()); - + for (CompileProblem err : problems) { error(log, err.getFullMessage()); } throw new AndHowCompileException(problems); } - + } else { debug(log, "Another round of annotation processing. " + "Current root element count: {}", roundEnv.getRootElements().size()); @@ -152,7 +154,7 @@ public boolean process(Set annotations, RoundEnvironment TypeElement te = (TypeElement) e; AndHowElementScanner7 st = new AndHowElementScanner7( - unwrappedProcessingEnv, + this.processingEnv, Property.class.getCanonicalName(), INIT_CLASS_NAME, TEST_INIT_CLASS_NAME); @@ -167,9 +169,9 @@ public boolean process(Set annotations, RoundEnvironment if (ret.hasRegistrations()) { - debug(log, "Found {} AndHow Properties in class {} ", + debug(log, "Found {} AndHow Properties in class {} ", ret.getRegistrations().size(), ret.getRootCanonicalName()); - + PropertyRegistrarClassGenerator gen = new PropertyRegistrarClassGenerator(ret, AndHowCompileProcessor.class, runDate); registrars.add(new CauseEffect(gen.buildGeneratedClassFullName(), te)); PropertyRegistrationList regs = ret.getRegistrations(); @@ -182,7 +184,7 @@ public boolean process(Set annotations, RoundEnvironment throw new RuntimeException(ex); } } - + problems.addAll(ret.getProblems()); } @@ -194,10 +196,10 @@ public boolean process(Set annotations, RoundEnvironment /** * Writes a new class implementing the {@code PropertyRegistrar} interface. - * + * * The new class directly corresponds to a user classes containing AndHow * Properties and will contain meta data about the properties. - * + * * @param filer The javac file system representation for writing files. * @param generator AndHow class capable of generating source code for this * {@code PropertyRegistrar} class. @@ -207,11 +209,11 @@ public boolean process(Set annotations, RoundEnvironment * so there is an association between the file and the reason it was written. * Likely this is normally used to associate source code line numbers with * generated code. - * + * * @throws Exception If unable to write (out of disc space?) */ - public void writeClassFile(Filer filer, - PropertyRegistrarClassGenerator generator, Element causingElement) throws Exception { + public void writeClassFile(Filer filer, + PropertyRegistrarClassGenerator generator, Element causingElement) throws Exception { String classContent = generator.generateSource(); @@ -220,20 +222,20 @@ public void writeClassFile(Filer filer, try (Writer writer = classFile.openWriter()) { writer.write(classContent); - } + } } - - protected void writeServiceFile(Filer filer, - String fullyQualifiedServiceInterfaceName, - List implementingClasses) throws IOException { - + + protected void writeServiceFile(Filer filer, + String fullyQualifiedServiceInterfaceName, + List implementingClasses) throws IOException { + //Get a unique causing elements HashSet set = new HashSet(); for (CauseEffect ce : implementingClasses) { set.add(ce.causeElement); } - - + + //The CLASS_OUTPUT location is used instead of SOURCE_OUTPUT because it //seems that non-Java files are not copied over from the SOURCE_OUTPUT //location. @@ -247,36 +249,36 @@ protected void writeServiceFile(Filer filer, writer.write(System.lineSeparator()); } } - + } - + /** * Logs a debug message using the javac standard Messager system. - * + * * @param log The Message instance to use * @param pattern String pattern with curly variable replacement like this: {} * @param args Arguments to put into the {}'s, in order. */ void debug(Messager log, String pattern, Object... args) { - log.printMessage(Diagnostic.Kind.NOTE, TextUtil.format(pattern, args)); + log.printMessage(Kind.NOTE, TextUtil.format(pattern, args)); } - + /** * Logs an error message using the javac standard Messager system. - * + * * @param log The Message instance to use * @param pattern String pattern with curly variable replacement like this: {} * @param args Arguments to put into the {}'s, in order. */ void error(Messager log, String pattern, Object... args) { - log.printMessage(Diagnostic.Kind.ERROR, TextUtil.format(pattern, args)); + log.printMessage(Kind.ERROR, TextUtil.format(pattern, args)); } - + /** * Match up a causal Element (Basically the compiler representation of a * class to be compiled) w/ the Class name that will be registered in * a service registry. - * + * * When the AnnotationProcessor writes a new file to the Filer, it wants * a causal Element to associate with it, apparently this info could be * used for reporting or something. @@ -284,12 +286,44 @@ void error(Messager log, String pattern, Object... args) { protected static class CauseEffect { String fullClassName; Element causeElement; - + public CauseEffect(String fullClassName, Element causeElement) { this.fullClassName = fullClassName; this.causeElement = causeElement; } } + /** + * With the introduction of IntelliJ Idea 2020.3 release the ProcessingEnvironment + * is not of type com.sun.tools.javac.processing.JavacProcessingEnvironment + * but java.lang.reflect.Proxy. + * The com.sun.source.util.Trees.instance() throws an IllegalArgumentException when the proxied processingEnv is passed. + * + * @author Vicky Ronnen (vicky.ronnen@upcmail.nl or v.ronnen@gmail.com) + * @link https://youtrack.jetbrains.com/issue/IDEA-256707 + * @param processingEnv possible proxied + * @return ProcessingEnvironment unwrapped from the proxy if proxied or the original processingEnv + */ + private ProcessingEnvironment unwrap(ProcessingEnvironment processingEnv) { + if (Proxy.isProxyClass(processingEnv.getClass())) { + InvocationHandler invocationHandler = Proxy.getInvocationHandler(processingEnv); + try { + Field field = invocationHandler.getClass().getDeclaredField("val$delegateTo"); + field.setAccessible(true); + Object o = field.get(invocationHandler); + if (o instanceof ProcessingEnvironment) { + return (ProcessingEnvironment) o; + } else { + processingEnv.getMessager().printMessage(Kind.ERROR, "got " + o.getClass() + " expected instanceof com.sun.tools.javac.processing.JavacProcessingEnvironment"); + return null; + } + } catch (NoSuchFieldException | IllegalAccessException e) { + processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage()); + return null; + } + } else { + return processingEnv; + } + } } From f1fb37ad17a3bebd6ca2a390b8b27bd737709546 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 2 Jun 2021 21:49:04 -0500 Subject: [PATCH 72/91] Added sonatype's staging plugin to (hopefully) make releases easier (#498) Also cleaned up some formatting that had gotten weird. Co-authored-by: Eric Everman --- .../andhow/compile/AndHowCompileProcessor.java | 5 ++--- pom.xml | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java index 9ec5bca6..ea18e9a2 100644 --- a/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java +++ b/andhow-annotation-processor/src/main/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor.java @@ -213,7 +213,7 @@ public boolean process(Set annotations, RoundEnvironment * @throws Exception If unable to write (out of disc space?) */ public void writeClassFile(Filer filer, - PropertyRegistrarClassGenerator generator, Element causingElement) throws Exception { + PropertyRegistrarClassGenerator generator, Element causingElement) throws Exception { String classContent = generator.generateSource(); @@ -226,8 +226,7 @@ public void writeClassFile(Filer filer, } protected void writeServiceFile(Filer filer, - String fullyQualifiedServiceInterfaceName, - List implementingClasses) throws IOException { + String fullyQualifiedServiceInterfaceName, List implementingClasses) throws IOException { //Get a unique causing elements HashSet set = new HashSet(); diff --git a/pom.xml b/pom.xml index 0180ac85..dd017328 100644 --- a/pom.xml +++ b/pom.xml @@ -236,6 +236,17 @@ deploy + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + false + + From 51950c3c4217b70ea97c2c997609c75c0cc3ac16 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 21:50:44 -0500 Subject: [PATCH 73/91] [maven-release-plugin] prepare release andhow-0.4.1 --- andhow-annotation-processor/pom.xml | 2 +- andhow-core/pom.xml | 2 +- .../andhow-annotation-processor-test-harness/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-tests/pom.xml | 2 +- .../andhow-default-behavior-dep1/pom.xml | 2 +- .../andhow-default-behavior-dep2/pom.xml | 2 +- .../andhow-default-behavior-test/pom.xml | 2 +- .../andhow-multimodule-dataprocess/pom.xml | 2 +- andhow-testing/andhow-simulated-app-tests/pom.xml | 2 +- andhow-testing/andhow-system-tests/pom.xml | 2 +- andhow-testing/andhow-test-harness/pom.xml | 2 +- andhow/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 3b1d8966..9e3845a7 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow-annotation-processor diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index daeeaf4e..ab6d03d3 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow-core diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index 49b1a85e..330dbc90 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index f7b992e9..57ef7cba 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 7f0b8de4..955e70fc 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-dep1 diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 4d6b8d65..03f2ca74 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-dep2 jar diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 097c0780..07433c87 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index 31ca1b7d..0e93a5f8 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-simulated-app-tests - 0.4.1-SNAPSHOT + 0.4.1 andhow-multimodule-dataprocess pom diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index 8f11b49d..b1bbe168 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml andhow-simulated-app-tests diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 21718d43..6d77ab7f 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index 2944339f..ffe97a4b 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow/pom.xml b/andhow/pom.xml index 7693833a..f13d18ef 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow jar diff --git a/pom.xml b/pom.xml index dd017328..f78677f4 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 pom AndHow Parent Project @@ -56,7 +56,7 @@ scm:git:git@github.com:eeverman/andhow.git scm:git:git@github.com:eeverman/andhow.git https://github.com/eeverman/andhow/tree/master - HEAD + andhow-0.4.1 From 2254506913249f7ab713e7d84d069db0af17821a Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 21:50:50 -0500 Subject: [PATCH 74/91] [maven-release-plugin] prepare for next development iteration --- andhow-annotation-processor/pom.xml | 2 +- andhow-core/pom.xml | 2 +- .../andhow-annotation-processor-test-harness/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-tests/pom.xml | 2 +- .../andhow-default-behavior-dep1/pom.xml | 2 +- .../andhow-default-behavior-dep2/pom.xml | 2 +- .../andhow-default-behavior-test/pom.xml | 2 +- .../andhow-multimodule-dataprocess/pom.xml | 2 +- andhow-testing/andhow-simulated-app-tests/pom.xml | 2 +- andhow-testing/andhow-system-tests/pom.xml | 2 +- andhow-testing/andhow-test-harness/pom.xml | 2 +- andhow/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 9e3845a7..0355ca55 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT andhow-annotation-processor diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index ab6d03d3..3538c3d8 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT andhow-core diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index 330dbc90..cbf3398e 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index 57ef7cba..adf08389 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 955e70fc..0bca988f 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.2-SNAPSHOT andhow-default-behavior-dep1 diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 03f2ca74..68c12735 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.2-SNAPSHOT andhow-default-behavior-dep2 jar diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 07433c87..241742f4 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.2-SNAPSHOT andhow-default-behavior-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index 0e93a5f8..a55e75f9 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-simulated-app-tests - 0.4.1 + 0.4.2-SNAPSHOT andhow-multimodule-dataprocess pom diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index b1bbe168..f3b5e160 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml andhow-simulated-app-tests diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 6d77ab7f..31315a6e 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index ffe97a4b..37dcf5bf 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow/pom.xml b/andhow/pom.xml index f13d18ef..de9eaa8b 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT andhow jar diff --git a/pom.xml b/pom.xml index f78677f4..7328eda4 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT pom AndHow Parent Project @@ -56,7 +56,7 @@ scm:git:git@github.com:eeverman/andhow.git scm:git:git@github.com:eeverman/andhow.git https://github.com/eeverman/andhow/tree/master - andhow-0.4.1 + HEAD From 12e31b1a4698fcad7122acad6a1769145298694c Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 22:54:25 -0500 Subject: [PATCH 75/91] [maven-release-plugin] rollback the release of andhow-0.4.1 --- andhow-annotation-processor/pom.xml | 2 +- andhow-core/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-test-harness/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-tests/pom.xml | 2 +- .../andhow-default-behavior-dep1/pom.xml | 2 +- .../andhow-default-behavior-dep2/pom.xml | 2 +- .../andhow-default-behavior-test/pom.xml | 2 +- .../andhow-multimodule-dataprocess/pom.xml | 2 +- andhow-testing/andhow-simulated-app-tests/pom.xml | 2 +- andhow-testing/andhow-system-tests/pom.xml | 2 +- andhow-testing/andhow-test-harness/pom.xml | 2 +- andhow/pom.xml | 2 +- pom.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 0355ca55..3b1d8966 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT andhow-annotation-processor diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index 3538c3d8..daeeaf4e 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT andhow-core diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index cbf3398e..49b1a85e 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index adf08389..f7b992e9 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 0bca988f..7f0b8de4 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT andhow-default-behavior-dep1 diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 68c12735..4d6b8d65 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT andhow-default-behavior-dep2 jar diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 241742f4..097c0780 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT andhow-default-behavior-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index a55e75f9..31ca1b7d 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-simulated-app-tests - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT andhow-multimodule-dataprocess pom diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index f3b5e160..8f11b49d 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT ../../pom.xml andhow-simulated-app-tests diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 31315a6e..21718d43 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index 37dcf5bf..2944339f 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow/pom.xml b/andhow/pom.xml index de9eaa8b..7693833a 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT andhow jar diff --git a/pom.xml b/pom.xml index 7328eda4..dd017328 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.yarnandtail andhow-parent - 0.4.2-SNAPSHOT + 0.4.1-SNAPSHOT pom AndHow Parent Project From d228c448606e0d981b3e539413bd62f2ab2695b1 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 22:57:26 -0500 Subject: [PATCH 76/91] Minor javadoc fix to allow a release --- .../yarnandtail/andhow/AndHowNonProductionUtil.java | 2 +- pom.xml | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowNonProductionUtil.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowNonProductionUtil.java index 87ed5875..00fe900d 100644 --- a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowNonProductionUtil.java +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowNonProductionUtil.java @@ -59,7 +59,7 @@ public static AndHowCore getAndHowCore() { /** * Sets a new {@code AndHowCore} * - * This inserts an entire new state into AndHow. Inserting a {@null} core + * This inserts an entire new state into AndHow. Inserting a null core * puts AndHow into a reset state that is invalid during production, but * can be useful during testing. In this state, AndHow will allow itself * to be reinitialized, which is not the intended operation during diff --git a/pom.xml b/pom.xml index dd017328..4efb3c3d 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,7 @@ UTF-8 1.8 1.8 + none @@ -141,7 +142,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 + 3.3.0 true @@ -153,9 +154,7 @@ true true - -Xdoclint:none - -Xdoclint:-missing - none,-missing + none true @@ -268,6 +267,10 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + From 943f6d268737031b194121007376a81d852b31b8 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 23:02:31 -0500 Subject: [PATCH 77/91] [maven-release-plugin] prepare release andhow-0.4.1 --- andhow-annotation-processor/pom.xml | 2 +- andhow-core/pom.xml | 2 +- .../andhow-annotation-processor-test-harness/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-tests/pom.xml | 2 +- .../andhow-default-behavior-dep1/pom.xml | 2 +- .../andhow-default-behavior-dep2/pom.xml | 2 +- .../andhow-default-behavior-test/pom.xml | 2 +- .../andhow-multimodule-dataprocess/pom.xml | 2 +- andhow-testing/andhow-simulated-app-tests/pom.xml | 2 +- andhow-testing/andhow-system-tests/pom.xml | 2 +- andhow-testing/andhow-test-harness/pom.xml | 2 +- andhow/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 3b1d8966..9e3845a7 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow-annotation-processor diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index daeeaf4e..ab6d03d3 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow-core diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index 49b1a85e..330dbc90 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index f7b992e9..57ef7cba 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 7f0b8de4..955e70fc 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-dep1 diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 4d6b8d65..03f2ca74 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-dep2 jar diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 097c0780..07433c87 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index 31ca1b7d..0e93a5f8 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-simulated-app-tests - 0.4.1-SNAPSHOT + 0.4.1 andhow-multimodule-dataprocess pom diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index 8f11b49d..b1bbe168 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml andhow-simulated-app-tests diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 21718d43..6d77ab7f 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index 2944339f..ffe97a4b 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow/pom.xml b/andhow/pom.xml index 7693833a..f13d18ef 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow jar diff --git a/pom.xml b/pom.xml index 4efb3c3d..ec668129 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 pom AndHow Parent Project @@ -56,7 +56,7 @@ scm:git:git@github.com:eeverman/andhow.git scm:git:git@github.com:eeverman/andhow.git https://github.com/eeverman/andhow/tree/master - HEAD + andhow-0.4.1 From dedd2bdacb840e34766eac7e58606c9b180a88c0 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 23:06:57 -0500 Subject: [PATCH 78/91] Revert "[maven-release-plugin] prepare release andhow-0.4.1" This reverts commit 943f6d268737031b194121007376a81d852b31b8. --- andhow-annotation-processor/pom.xml | 2 +- andhow-core/pom.xml | 2 +- .../andhow-annotation-processor-test-harness/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-tests/pom.xml | 2 +- .../andhow-default-behavior-dep1/pom.xml | 2 +- .../andhow-default-behavior-dep2/pom.xml | 2 +- .../andhow-default-behavior-test/pom.xml | 2 +- .../andhow-multimodule-dataprocess/pom.xml | 2 +- andhow-testing/andhow-simulated-app-tests/pom.xml | 2 +- andhow-testing/andhow-system-tests/pom.xml | 2 +- andhow-testing/andhow-test-harness/pom.xml | 2 +- andhow/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 9e3845a7..3b1d8966 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT andhow-annotation-processor diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index ab6d03d3..daeeaf4e 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT andhow-core diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index 330dbc90..49b1a85e 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index 57ef7cba..f7b992e9 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 955e70fc..7f0b8de4 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.1-SNAPSHOT andhow-default-behavior-dep1 diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 03f2ca74..4d6b8d65 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.1-SNAPSHOT andhow-default-behavior-dep2 jar diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 07433c87..097c0780 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.1-SNAPSHOT andhow-default-behavior-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index 0e93a5f8..31ca1b7d 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-simulated-app-tests - 0.4.1 + 0.4.1-SNAPSHOT andhow-multimodule-dataprocess pom diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index b1bbe168..8f11b49d 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT ../../pom.xml andhow-simulated-app-tests diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 6d77ab7f..21718d43 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index ffe97a4b..2944339f 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT ../../pom.xml diff --git a/andhow/pom.xml b/andhow/pom.xml index f13d18ef..7693833a 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT andhow jar diff --git a/pom.xml b/pom.xml index ec668129..4efb3c3d 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.yarnandtail andhow-parent - 0.4.1 + 0.4.1-SNAPSHOT pom AndHow Parent Project @@ -56,7 +56,7 @@ scm:git:git@github.com:eeverman/andhow.git scm:git:git@github.com:eeverman/andhow.git https://github.com/eeverman/andhow/tree/master - andhow-0.4.1 + HEAD From 64589c6f582c056ba3f09fb43cc298de2a86a5c2 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 23:08:33 -0500 Subject: [PATCH 79/91] [maven-release-plugin] prepare release andhow-0.4.1 --- andhow-annotation-processor/pom.xml | 2 +- andhow-core/pom.xml | 2 +- .../andhow-annotation-processor-test-harness/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-tests/pom.xml | 2 +- .../andhow-default-behavior-dep1/pom.xml | 2 +- .../andhow-default-behavior-dep2/pom.xml | 2 +- .../andhow-default-behavior-test/pom.xml | 2 +- .../andhow-multimodule-dataprocess/pom.xml | 2 +- andhow-testing/andhow-simulated-app-tests/pom.xml | 2 +- andhow-testing/andhow-system-tests/pom.xml | 2 +- andhow-testing/andhow-test-harness/pom.xml | 2 +- andhow/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 3b1d8966..9e3845a7 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow-annotation-processor diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index daeeaf4e..ab6d03d3 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow-core diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index 49b1a85e..330dbc90 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index f7b992e9..57ef7cba 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 7f0b8de4..955e70fc 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-dep1 diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 4d6b8d65..03f2ca74 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-dep2 jar diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 097c0780..07433c87 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1-SNAPSHOT + 0.4.1 andhow-default-behavior-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index 31ca1b7d..0e93a5f8 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-simulated-app-tests - 0.4.1-SNAPSHOT + 0.4.1 andhow-multimodule-dataprocess pom diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index 8f11b49d..b1bbe168 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml andhow-simulated-app-tests diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 21718d43..6d77ab7f 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index 2944339f..ffe97a4b 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 ../../pom.xml diff --git a/andhow/pom.xml b/andhow/pom.xml index 7693833a..f13d18ef 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 andhow jar diff --git a/pom.xml b/pom.xml index 4efb3c3d..ec668129 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.yarnandtail andhow-parent - 0.4.1-SNAPSHOT + 0.4.1 pom AndHow Parent Project @@ -56,7 +56,7 @@ scm:git:git@github.com:eeverman/andhow.git scm:git:git@github.com:eeverman/andhow.git https://github.com/eeverman/andhow/tree/master - HEAD + andhow-0.4.1 From 81f453e6f41df951e1818ae54a2400213ceb8ca6 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 2 Jun 2021 23:08:40 -0500 Subject: [PATCH 80/91] [maven-release-plugin] prepare for next development iteration --- andhow-annotation-processor/pom.xml | 2 +- andhow-core/pom.xml | 2 +- .../andhow-annotation-processor-test-harness/pom.xml | 2 +- andhow-testing/andhow-annotation-processor-tests/pom.xml | 2 +- .../andhow-default-behavior-dep1/pom.xml | 2 +- .../andhow-default-behavior-dep2/pom.xml | 2 +- .../andhow-default-behavior-test/pom.xml | 2 +- .../andhow-multimodule-dataprocess/pom.xml | 2 +- andhow-testing/andhow-simulated-app-tests/pom.xml | 2 +- andhow-testing/andhow-system-tests/pom.xml | 2 +- andhow-testing/andhow-test-harness/pom.xml | 2 +- andhow/pom.xml | 2 +- pom.xml | 4 ++-- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 9e3845a7..0355ca55 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT andhow-annotation-processor diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index ab6d03d3..3538c3d8 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT andhow-core diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index 330dbc90..cbf3398e 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index 57ef7cba..adf08389 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 955e70fc..0bca988f 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.2-SNAPSHOT andhow-default-behavior-dep1 diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 03f2ca74..68c12735 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.2-SNAPSHOT andhow-default-behavior-dep2 jar diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 07433c87..241742f4 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-multimodule-dataprocess - 0.4.1 + 0.4.2-SNAPSHOT andhow-default-behavior-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index 0e93a5f8..a55e75f9 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-simulated-app-tests - 0.4.1 + 0.4.2-SNAPSHOT andhow-multimodule-dataprocess pom diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index b1bbe168..f3b5e160 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml andhow-simulated-app-tests diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 6d77ab7f..31315a6e 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index ffe97a4b..37dcf5bf 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -3,7 +3,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT ../../pom.xml diff --git a/andhow/pom.xml b/andhow/pom.xml index f13d18ef..de9eaa8b 100644 --- a/andhow/pom.xml +++ b/andhow/pom.xml @@ -4,7 +4,7 @@ org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT andhow jar diff --git a/pom.xml b/pom.xml index ec668129..a2624dbe 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.yarnandtail andhow-parent - 0.4.1 + 0.4.2-SNAPSHOT pom AndHow Parent Project @@ -56,7 +56,7 @@ scm:git:git@github.com:eeverman/andhow.git scm:git:git@github.com:eeverman/andhow.git https://github.com/eeverman/andhow/tree/master - andhow-0.4.1 + HEAD From 786b87ef8c40885fe8b538c2cfcae018f76b99a4 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 3 Jun 2021 00:11:33 -0500 Subject: [PATCH 81/91] Readme update per 0.4.1 release --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 82eb95aa..19510bd3 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ [![Build Status](https://travis-ci.org/eeverman/andhow.svg?branch=master)](https://travis-ci.org/eeverman/andhow) [![codecov](https://codecov.io/gh/eeverman/andhow/branch/master/graph/badge.svg)](https://codecov.io/gh/eeverman/andhow) [![Javadocs](https://www.javadoc.io/badge/org.yarnandtail/andhow.svg)](https://www.javadoc.io/doc/org.yarnandtail/andhow) -# Introducing AndHow's new Logo -AndHow's new logo -Many thanks to everyone who participated in the [AndHow logo contest](https://github.com/eeverman/andhow/issues/427) in October. All six logo entries were solid, but [Carl Schroedl](https://github.com/carlschroedl)'s entry was a clear winner. Carl's logo looks a bit like a swiss army knife or bottle opener, reflecting the utility aspect of AndHow. The logo also combines an ampersand (&) and a question mark into one simple and clever symbol. Well done Carl! + +## New Release: 0.4.1, June 2, 2021 ([notes](https://github.com/eeverman/andhow/releases/tag/andhow-0.4.1)). +AndHow's new logo + +This larger update fixes several issues due to newer JVMs and the IntelliJ IDE. +Special thanks to first time contributor [Vicky Ronnen](https://github.com/VickyRonnen) for fixing Issue [497](https://github.com/eeverman/andhow/issues/497) - +This bug made AndHow unusable for anyone using newer versions of IntelliJ. + ![Andhow Visual](andhow.gif) -# AndHow! strong.valid.simple.AppConfiguration +## AndHow! strong.valid.simple.AppConfiguration AndHow is an easy to use configuration framework with strong typing and detailed validation for web apps, command line or any application environment. @@ -33,7 +38,7 @@ or the *Slack* group (See details on the org.yarnandtail andhow - 0.4.0 + 0.4.1 ``` **AndHow can be used in projects with Java 8 and above, however, Java 9 and above have [some restrictions](https://sites.google.com/view/andhow/user-guide/java9)** From c249517dcb94f27bfabe609413b638d6f78767ef Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 3 Jun 2021 00:17:03 -0500 Subject: [PATCH 82/91] Readme formatting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19510bd3..bb3f9e23 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## New Release: 0.4.1, June 2, 2021 ([notes](https://github.com/eeverman/andhow/releases/tag/andhow-0.4.1)). -AndHow's new logo +AndHow's new logo This larger update fixes several issues due to newer JVMs and the IntelliJ IDE. Special thanks to first time contributor [Vicky Ronnen](https://github.com/VickyRonnen) for fixing Issue [497](https://github.com/eeverman/andhow/issues/497) - From 92c5897465cfd8318adc78cfc7875959ce4cc08f Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 3 Jun 2021 00:21:39 -0500 Subject: [PATCH 83/91] readme formatting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb3f9e23..64f67251 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## New Release: 0.4.1, June 2, 2021 ([notes](https://github.com/eeverman/andhow/releases/tag/andhow-0.4.1)). -AndHow's new logo +AndHow's new logo This larger update fixes several issues due to newer JVMs and the IntelliJ IDE. Special thanks to first time contributor [Vicky Ronnen](https://github.com/VickyRonnen) for fixing Issue [497](https://github.com/eeverman/andhow/issues/497) - From 870a90f2525055dca4d7be43bed21735af63be14 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Tue, 8 Jun 2021 13:22:34 -0500 Subject: [PATCH 84/91] Update README.md Readme updated for compatibility for Java 8 - 15 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64f67251..aa465dd7 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ or the *Slack* group (See details on the 0.4.1 ``` -**AndHow can be used in projects with Java 8 and above, however, Java 9 and above have [some restrictions](https://sites.google.com/view/andhow/user-guide/java9)** +**AndHow can be used in projects with Java 8 - Java 15. Work to support Java 16 is underway and will be a major release. There are [some considerations](https://sites.google.com/view/andhow/user-guide/java9-and-above) for Java 9 and above if your project uses Jigsaw Modules.** ## Complete Usage Example _**More usage examples and documentation From 26cd1d1b9582dc2264efc84508a8b117aaae8a9f Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Tue, 8 Jun 2021 19:35:56 -0500 Subject: [PATCH 85/91] Update README.md small text change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa465dd7..4c43de79 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ or the *Slack* group (See details on the 0.4.1 ``` -**AndHow can be used in projects with Java 8 - Java 15. Work to support Java 16 is underway and will be a major release. There are [some considerations](https://sites.google.com/view/andhow/user-guide/java9-and-above) for Java 9 and above if your project uses Jigsaw Modules.** +**AndHow can be used in projects with Java 8 - Java 15. Work to support Java 16 is underway and will be the next major release. There are [some considerations](https://sites.google.com/view/andhow/user-guide/java9-and-above) for Java 9 and above if your project uses Jigsaw Modules.** ## Complete Usage Example _**More usage examples and documentation From d8aaffe8be589549946470b24351fc6a88a7bb24 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Wed, 16 Jun 2021 23:21:56 -0600 Subject: [PATCH 86/91] Issue501 update junit (#502) * upgrade andhow-core module to use JUnit 5 * rm unneeded inports from several classes (over eager IDE...) * Add pom dependencies to support JUnit 4 & 5 * Updated all of andhow-testing to JUnit 5 except the annotation related stuff. The test harness now has Junit 4 & 5 implementations. * Testing module almost ready - one failing module yet. * Fixed a few tests that were partially converted to J5 and thus failing * Completely rm JUnit 4 except for one base class that is intended for use by app dev's using JUnit 4 * Improved test coverage * Add tests for the getJndi method. Also clarified that jndi is completely cleared after each test and after the test class * Added tests for AndHowJunit4/5TestBase's. Co-authored-by: Eric Everman --- .gitignore | 3 +- andhow-annotation-processor/pom.xml | 4 - .../compile/AndHowCompileExceptionTest.java | 5 +- .../AndHowCompileProcessorTestBase.java | 4 +- .../AndHowCompileProcessor_InitTest.java | 25 ++- .../AndHowCompileProcessor_PropertyTest.java | 18 +- .../andhow/compile/CompileProblemTest.java | 4 +- .../andhow/compile/CompileUnitTest.java | 4 +- .../andhow/compile/CompileUtilTest.java | 4 +- .../PropertyRegistrarClassGeneratorTest.java | 10 +- ...mple_PropertyRegistrationForClassTest.java | 4 +- andhow-core/pom.xml | 9 +- .../andhow/AndHowCoreTestBase.java | 24 +- .../andhow/AndHowReentrantTest.java | 10 +- .../org/yarnandtail/andhow/AndHowTest.java | 8 +- .../andhow/AndHowUsageExampleTest.java | 8 +- .../andhow/AndHow_AliasInTest.java | 4 +- .../andhow/AndHow_AliasOutTest.java | 8 +- .../yarnandtail/andhow/PropertyValueTest.java | 11 +- .../org/yarnandtail/andhow/StdConfigTest.java | 4 +- .../andhow/api/AppFatalExceptionTest.java | 4 +- .../andhow/api/LoaderValuesTest.java | 4 +- .../org/yarnandtail/andhow/api/NameTest.java | 4 +- .../andhow/api/PropertyTypeTest.java | 6 +- .../restclient/SampleRestClientAppTest.java | 8 +- .../internal/ConstructionProblemTest.java | 4 +- ...ticPropertyConfigurationImmutableTest.java | 4 +- ...taticPropertyConfigurationMutableTest.java | 4 +- .../ValueMapWithContextMutableTest.java | 4 +- .../org/yarnandtail/andhow/load/KVPTest.java | 68 +++--- .../andhow/load/KeyValuePairLoaderTest.java | 8 +- .../andhow/load/LoaderExceptionTest.java | 2 +- .../PropFileOnClasspathLoaderAppTest.java | 4 +- .../PropFileOnClasspathLoaderUnitTest.java | 8 +- .../PropFileOnFilesystemLoaderAppTest.java | 12 +- .../PropFileOnFilesystemLoaderUnitTest.java | 12 +- .../andhow/load/std/StdEnvVarLoaderTest.java | 11 +- .../andhow/load/std/StdJndiLoaderTest.java | 4 +- .../andhow/load/std/StdSysPropLoaderTest.java | 12 +- .../name/CaseInsensitiveNamingTest.java | 4 +- .../andhow/property/BigDecPropTest.java | 4 +- .../andhow/property/BolPropTest.java | 4 +- .../property/PropertyBuilderBaseTest.java | 36 +-- .../QuotedSpacePreservingTrimmerTest.java | 4 +- .../andhow/property/StrPropTest.java | 4 +- .../property/TrimToNullTrimmerTest.java | 4 +- .../sample/JndiLoaderSamplePrinterTest.java | 13 +- .../PropFileLoaderSamplePrinterTest.java | 12 +- .../andhow/sample/TextLineTest.java | 4 +- .../service/PropertyRegistrarLoaderTest.java | 6 +- .../service/PropertyRegistrationListTest.java | 5 +- .../service/PropertyRegistrationTest.java | 5 +- .../andhow/util/AndHowLogTest.java | 109 ++++----- .../andhow/util/AndHowUtilTest.java | 5 +- .../yarnandtail/andhow/util/IOUtilTest.java | 4 +- .../yarnandtail/andhow/util/NameUtilTest.java | 7 +- .../andhow/util/StackLocatorTest.java | 5 +- .../yarnandtail/andhow/util/TextUtilTest.java | 15 +- .../andhow/valid/BigDecValidatorTest.java | 8 +- .../andhow/valid/DblValidatorTest.java | 4 +- .../andhow/valid/IntValidatorTest.java | 4 +- .../andhow/valid/LngValidatorTest.java | 4 +- .../valid/LocalDateTimeValidatorTest.java | 44 ++-- .../andhow/valid/StringValidatorTest.java | 4 +- .../andhow/valuetype/BigDecTypeTest.java | 26 +-- .../andhow/valuetype/DblTypeTest.java | 22 +- .../andhow/valuetype/IntTypeTest.java | 47 ++-- .../andhow/valuetype/LngTypeTest.java | 47 ++-- .../valuetype/LocalDateTimeTypeTest.java | 42 ++-- .../andhow/valuetype/StrTypeTest.java | 4 +- .../pom.xml | 16 +- .../compile/MemoryFileManagerTest.java | 12 +- .../andhow-annotation-processor-tests/pom.xml | 13 +- .../service/PropertyRegistrarLoaderTest.java | 5 +- .../nonapp/sample/VisibilitySampleTest.java | 4 +- .../andhow-default-behavior-dep1/pom.xml | 21 +- ...thMapMakerNotUsingAHBaseTestClassTest.java | 4 +- ...EarthMapMakerUsingAHBaseTestClassTest.java | 14 +- .../andhow-default-behavior-dep2/pom.xml | 19 -- ...rsMapMakerNotUsingAHBaseTestClassTest.java | 4 +- .../MarsMapMakerUsingAHBaseTestClassTest.java | 16 +- .../andhow-default-behavior-test/pom.xml | 16 -- .../test/java/com/dep1/EarthMapMakerTest.java | 9 +- .../test/java/com/dep2/MarsMapMakerTest.java | 10 +- .../ExternalServiceConnectorTest.java | 8 +- .../andhow-multimodule-dataprocess/pom.xml | 28 +++ andhow-testing/andhow-system-tests/pom.xml | 13 +- .../org/yarnandtail/andhow/AndHowTest.java | 14 +- andhow-testing/andhow-test-harness/pom.xml | 41 +++- .../andhow/AndHowJunit4TestBase.java | 69 ++++++ .../andhow/AndHowJunit5TestBase.java | 69 ++++++ .../yarnandtail/andhow/AndHowTestBase.java | 129 ++++------- .../andhow/AndHowTestBaseImpl.java | 133 +++++++++++ .../andhow/AndHowJunit4TestBaseTest.java | 66 ++++++ .../andhow/AndHowJunit5TestBaseTest.java | 66 ++++++ .../andhow/AndHowNonProductionUtilTest.java | 12 +- .../andhow/AndHowTestBaseImplTest.java | 211 ++++++++++++++++++ .../andhow/AndHowTestBaseTest.java | 66 ++++++ .../andhow/AndHowTestingTestBase.java | 16 +- .../andhow/NonProductionConfigTest.java | 13 +- .../org/yarnandtail/andhow/SimpleConfig.java | 8 + pom.xml | 62 +++++ 102 files changed, 1430 insertions(+), 617 deletions(-) create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit4TestBase.java create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit5TestBase.java create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBaseImpl.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit4TestBaseTest.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit5TestBaseTest.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseImplTest.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseTest.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/SimpleConfig.java diff --git a/.gitignore b/.gitignore index 58469f48..7834853e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ release.properties #IntelliJ Project directory .idea/ -**.iml \ No newline at end of file +**.iml +.java-version diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index 0355ca55..d5d1bda4 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -38,10 +38,6 @@ - - junit - junit - com.google.testing.compile compile-testing diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java index 496eda37..adca2e95 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileExceptionTest.java @@ -2,9 +2,8 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; -import static org.junit.Assert.*; -import org.junit.Before; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java index 7257ddc6..a006b99b 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java @@ -6,7 +6,7 @@ import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.ToolProvider; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import org.yarnandtail.andhow.util.IOUtil; import org.yarnandtail.compile.MemoryFileManager; import org.yarnandtail.compile.TestClassLoader; @@ -31,7 +31,7 @@ public class AndHowCompileProcessorTestBase { Set sources; //New set of source files to compile - @Before + @BeforeEach public void setupTest() { compiler = ToolProvider.getSystemJavaCompiler(); manager = new MemoryFileManager(compiler); diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java index abfbbf1f..aa806fc4 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_InitTest.java @@ -6,10 +6,10 @@ import java.util.*; import javax.tools.*; import javax.tools.Diagnostic.Kind; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.util.IOUtil; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * A lot of this code was borrowed from here: @@ -48,8 +48,11 @@ public void testServiceRegistrationOfOneProdAndOneTestInit() throws Exception { String prodInitSvs = IOUtil.toString(loader.getResourceAsStream(INIT_SVS_PATH), Charset.forName("UTF-8")); String testInitSvs = IOUtil.toString(loader.getResourceAsStream(TEST_INIT_SVS_PATH), Charset.forName("UTF-8")); - assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( - d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING)).count()); + assertEquals(0, + diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING) + ).count(), + "Should be no warn/errors"); // //Test the initiation files @@ -75,8 +78,11 @@ public void testServiceRegistrationOfOneProdInit() throws Exception { String prodInitSvs = IOUtil.toString(loader.getResourceAsStream(INIT_SVS_PATH), Charset.forName("UTF-8")); - assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( - d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING)).count()); + assertEquals(0, + diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING) + ).count(), + "Should be no warn/errors"); // //Test the initiation files @@ -99,8 +105,11 @@ public void testServiceRegistrationOfOneTestInit() throws Exception { String testInitSvs = IOUtil.toString(loader.getResourceAsStream(TEST_INIT_SVS_PATH), Charset.forName("UTF-8")); - assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( - d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING)).count()); + assertEquals(0, + diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Kind.ERROR) || d.getKind().equals(Kind.WARNING) + ).count(), + "Should be no warn/errors"); // diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java index d1c80e4b..ff1cd61d 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java @@ -3,8 +3,8 @@ import java.nio.charset.Charset; import java.util.*; import javax.tools.*; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.compile.CompileProblem.PropMissingFinal; import org.yarnandtail.andhow.compile.CompileProblem.PropMissingStatic; import org.yarnandtail.andhow.compile.CompileProblem.PropMissingStaticFinal; @@ -41,8 +41,11 @@ public void testComplexNestedPropertySampleClass() throws Exception { String genSvsFile = IOUtil.toString( loader.getResourceAsStream(REGISTRAR_SVS_PATH), Charset.forName("UTF-8")); - assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( - d -> d.getKind().equals(Diagnostic.Kind.ERROR) || d.getKind().equals(Diagnostic.Kind.WARNING)).count()); + assertEquals(0, + diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Diagnostic.Kind.ERROR) || d.getKind().equals(Diagnostic.Kind.WARNING) + ).count(), + "Should be no warn/errors"); assertNotNull(genClass); @@ -91,8 +94,11 @@ public void testSimpleHappyPathClass() throws Exception { String genSvsFile = IOUtil.toString( loader.getResourceAsStream(REGISTRAR_SVS_PATH), Charset.forName("UTF-8")); - assertEquals("Should be no warn/errors", 0, diagnostics.getDiagnostics().stream().filter( - d -> d.getKind().equals(Diagnostic.Kind.ERROR) || d.getKind().equals(Diagnostic.Kind.WARNING)).count()); + assertEquals(0, + diagnostics.getDiagnostics().stream().filter( + d -> d.getKind().equals(Diagnostic.Kind.ERROR) || d.getKind().equals(Diagnostic.Kind.WARNING) + ).count(), + "Should be no warn/errors"); assertNotNull(genClass); diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java index f600bb59..2fa41e18 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileProblemTest.java @@ -4,8 +4,8 @@ import java.util.ArrayList; import javax.lang.model.element.Element; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; import static org.yarnandtail.andhow.compile.CompileProblem.*; import static org.mockito.Mockito.*; import org.yarnandtail.andhow.compile.AndHowCompileProcessor.CauseEffect; diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java index 05dfa70a..cbde599d 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUnitTest.java @@ -1,9 +1,9 @@ package org.yarnandtail.andhow.compile; import org.yarnandtail.andhow.service.PropertyRegistrationList; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java index 75eeaa63..4d0c64bf 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/CompileUtilTest.java @@ -1,8 +1,8 @@ package org.yarnandtail.andhow.compile; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CompileUtilTest { diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java index 48b19216..9f845aa3 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/PropertyRegistrarClassGeneratorTest.java @@ -3,13 +3,13 @@ import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import java.util.*; -import javax.tools.JavaFileObject; -import org.junit.Test; -import org.junit.Before; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * @@ -28,7 +28,7 @@ public class PropertyRegistrarClassGeneratorTest { private GregorianCalendar runDate; - @Before + @BeforeEach public void initEach() { runDate = new GregorianCalendar(); //runDate.setTimeZone(TimeZone.getTimeZone(ZoneId.of("GMT-6"))); diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/__PropertySample_PropertyRegistrationForClassTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/__PropertySample_PropertyRegistrationForClassTest.java index af159b8b..ca8008e8 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/__PropertySample_PropertyRegistrationForClassTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/__PropertySample_PropertyRegistrationForClassTest.java @@ -3,9 +3,9 @@ import org.yarnandtail.andhow.service.PropertyRegistrar; import org.yarnandtail.andhow.service.PropertyRegistration; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index 3538c3d8..3c82329e 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -14,11 +14,14 @@ - - junit - junit + org.junit.jupiter + junit-jupiter-api + + + org.hamcrest + hamcrest org.springframework diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java index b5afc4ce..f99bab10 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowCoreTestBase.java @@ -2,7 +2,11 @@ import java.util.Properties; import javax.naming.NamingException; -import org.junit.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.springframework.mock.jndi.SimpleNamingContextBuilder; /** @@ -45,50 +49,50 @@ public class AndHowCoreTestBase { */ private static SimpleNamingContextBuilder builder; - @BeforeClass + @BeforeAll public static void killAndHowStateBeforeClass() { AndHowCoreTestUtil.destroyAndHow(); } - @BeforeClass + @BeforeAll public static void storeSysPropsBeforeClass() { beforeClassSystemProps = AndHowCoreTestUtil.clone(System.getProperties()); } - @Before + @BeforeEach public void killAndHowStateBeforeTest() { AndHowCoreTestUtil.destroyAndHow(); } - @Before + @BeforeEach public void storeSysPropsBeforeTest() { beforeTestSystemProps = AndHowCoreTestUtil.clone(System.getProperties()); } - @After + @AfterEach public void killAndHowStateAfterTest() { AndHowCoreTestUtil.destroyAndHow(); } - @After + @AfterEach public void clearJNDIAfterTest() { if (builder != null) { builder.clear(); } } - @After + @AfterEach public void restoreSysPropsAfterTest() { System.setProperties(beforeTestSystemProps); } - @AfterClass + @AfterAll public static void killAndHowStateAfterClass() { AndHowCoreTestUtil.destroyAndHow(); } - @AfterClass + @AfterAll public static void restoreSysPropsAfterClass() { System.setProperties(beforeClassSystemProps); } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java index aa2bb33e..6a3163e8 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowReentrantTest.java @@ -1,11 +1,11 @@ package org.yarnandtail.andhow; -import static org.junit.Assert.*; -import org.junit.FixMethodOrder; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.TestMethodOrder; -import org.junit.Test; -import org.junit.runners.MethodSorters; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.MethodOrderer; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.internal.ConstructionProblem; @@ -16,7 +16,7 @@ * bleed over from one test to another. * @author eeverman */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@TestMethodOrder(MethodOrderer.MethodName.class) public class AndHowReentrantTest extends AndHowCoreTestBase { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java index 09c564bf..a5779235 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowTest.java @@ -3,7 +3,9 @@ import java.io.File; import java.time.LocalDateTime; import java.util.*; -import org.junit.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.api.Property; import org.yarnandtail.andhow.internal.*; @@ -12,7 +14,7 @@ import org.yarnandtail.andhow.property.FlagProp; import org.yarnandtail.andhow.property.StrProp; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * This test is a minimal unit teat because this class is not really testable @@ -39,7 +41,7 @@ public static interface RequiredParams { FlagProp FLAG_NULL = FlagProp.builder().mustBeNonNull().build(); } - @Before + @BeforeEach public void setup() throws Exception { configPtGroups.clear(); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowUsageExampleTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowUsageExampleTest.java index d8e123d7..c63d35cd 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowUsageExampleTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHowUsageExampleTest.java @@ -1,10 +1,10 @@ package org.yarnandtail.andhow; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.internal.RequirementProblem; import org.yarnandtail.andhow.load.KeyValuePairLoader; @@ -21,7 +21,7 @@ public class AndHowUsageExampleTest extends AndHowCoreTestBase { String[] cmdLineArgsWFullClassName = new String[0]; - @Before + @BeforeEach public void setup() { cmdLineArgsWFullClassName = new String[] { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasInTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasInTest.java index 19e24a7b..314d8665 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasInTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasInTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.internal.ConstructionProblem; @@ -10,7 +10,7 @@ import org.yarnandtail.andhow.property.IntProp; import org.yarnandtail.andhow.property.StrProp; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasOutTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasOutTest.java index f40eb774..a860581c 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasOutTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/AndHow_AliasOutTest.java @@ -2,18 +2,14 @@ import java.util.List; -import static org.junit.Assert.*; - -import org.junit.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.export.SysPropExporter; -import org.yarnandtail.andhow.util.AndHowUtil; import org.yarnandtail.andhow.internal.ConstructionProblem; -import org.yarnandtail.andhow.internal.NameAndProperty; import org.yarnandtail.andhow.property.IntProp; import org.yarnandtail.andhow.property.StrProp; -import org.yarnandtail.andhow.util.NameUtil; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/PropertyValueTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/PropertyValueTest.java index 6b99d4bb..771214b4 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/PropertyValueTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/PropertyValueTest.java @@ -1,9 +1,10 @@ package org.yarnandtail.andhow; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.api.ParsingException; import org.yarnandtail.andhow.property.StrProp; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.yarnandtail.andhow.SimpleParams.*; /** @@ -140,9 +141,11 @@ public void testGetValue() { assertEquals(SPV5.getValue(), "abc"); } - @Test(expected = RuntimeException.class) + @Test public void testConstructor() { - PropertyValue NULL_PROP = new PropertyValue((StrProp)null, null); + assertThrows(RuntimeException.class, () -> + new PropertyValue((StrProp)null, null) + ); } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java index 4a0e615d..52c3b676 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.StdConfig.StdConfigImpl; import org.yarnandtail.andhow.api.Loader; import org.yarnandtail.andhow.api.StandardLoader; @@ -12,7 +12,7 @@ import org.yarnandtail.andhow.load.std.*; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java index 5afef967..5d872469 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/api/AppFatalExceptionTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.api; -import static org.junit.Assert.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class AppFatalExceptionTest { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/api/LoaderValuesTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/api/LoaderValuesTest.java index bf3be355..0fbf03c7 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/api/LoaderValuesTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/api/LoaderValuesTest.java @@ -1,10 +1,10 @@ package org.yarnandtail.andhow.api; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.load.*; import java.util.*; -import org.junit.*; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import org.yarnandtail.andhow.property.StrProp; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/api/NameTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/api/NameTest.java index 5ed13827..982d50c5 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/api/NameTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/api/NameTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.api; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/api/PropertyTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/api/PropertyTypeTest.java index 39f9233d..a7660a34 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/api/PropertyTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/api/PropertyTypeTest.java @@ -1,9 +1,9 @@ package org.yarnandtail.andhow.api; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class PropertyTypeTest { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java index 445ae097..83665cb4 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java @@ -2,10 +2,10 @@ import java.util.ArrayList; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.AppFatalException; @@ -24,7 +24,7 @@ public class SampleRestClientAppTest extends AndHowCoreTestBase { private static final String GROUP_PATH = "org.yarnandtail.andhow.example.restclient.SampleRestClientGroup"; - @Before + @BeforeEach public void setup() { cmdLineArgs = new String[0]; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java index 569c5113..401d5858 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ConstructionProblemTest.java @@ -1,8 +1,8 @@ package org.yarnandtail.andhow.internal; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.AndHowInit; import java.util.ArrayList; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationImmutableTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationImmutableTest.java index 2a958486..f9ed5d75 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationImmutableTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationImmutableTest.java @@ -4,9 +4,9 @@ import org.yarnandtail.andhow.util.AndHowUtil; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; import org.yarnandtail.andhow.property.StrProp; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationMutableTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationMutableTest.java index cfbf1079..a202a71a 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationMutableTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/StaticPropertyConfigurationMutableTest.java @@ -3,9 +3,9 @@ import org.yarnandtail.andhow.util.AndHowUtil; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.NamingStrategy; import org.yarnandtail.andhow.api.ProblemList; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ValueMapWithContextMutableTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ValueMapWithContextMutableTest.java index 868948d6..871464b3 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ValueMapWithContextMutableTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/internal/ValueMapWithContextMutableTest.java @@ -3,9 +3,9 @@ import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.SimpleParams; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.load.PropFileOnClasspathLoader; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/KVPTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KVPTest.java index 9ddb2542..c128479c 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/KVPTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KVPTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.load; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.ParsingException; /** @@ -98,34 +98,46 @@ public void splitKVPGoodNameAndValuesWithMultiDelims() throws Exception { } - @Test(expected=ParsingException.class) - public void splitKVPBadEmptyFlagName() throws Exception { - KVP.splitKVP("=value", KeyValuePairLoader.KVP_DELIMITER); + @Test + public void splitKVPBadEmptyFlagName() { + assertThrows(ParsingException.class, () -> + KVP.splitKVP("=value", KeyValuePairLoader.KVP_DELIMITER) + ); } - @Test(expected=ParsingException.class) - public void splitKVPBadSpaceOnlyFlagName() throws Exception { - KVP.splitKVP(" =value", KeyValuePairLoader.KVP_DELIMITER); + @Test + public void splitKVPBadSpaceOnlyFlagName() { + assertThrows(ParsingException.class, () -> + KVP.splitKVP(" =value", KeyValuePairLoader.KVP_DELIMITER) + ); } - @Test(expected=ParsingException.class) - public void splitKVPBadAllSpaceAndTabFlagName() throws Exception { - KVP.splitKVP(" \t =value", KeyValuePairLoader.KVP_DELIMITER); + @Test + public void splitKVPBadAllSpaceAndTabFlagName() { + assertThrows(ParsingException.class, () -> + KVP.splitKVP(" \t =value", KeyValuePairLoader.KVP_DELIMITER) + ); } - @Test(expected=ParsingException.class) - public void splitKVPBadAllWhitespaceAndNewLinesFlagName() throws Exception { - KVP.splitKVP(" \t\n\r\f = value", KeyValuePairLoader.KVP_DELIMITER); + @Test + public void splitKVPBadAllWhitespaceAndNewLinesFlagName() { + assertThrows(ParsingException.class, () -> + KVP.splitKVP(" \t\n\r\f = value", KeyValuePairLoader.KVP_DELIMITER) + ); } - @Test(expected=ParsingException.class) - public void splitKVPBadAllBackspaceFlagName() throws Exception { - KVP.splitKVP("\b =value", KeyValuePairLoader.KVP_DELIMITER); + @Test + public void splitKVPBadAllBackspaceFlagName() { + assertThrows(ParsingException.class, () -> + KVP.splitKVP("\b =value", KeyValuePairLoader.KVP_DELIMITER) + ); } - @Test(expected=ParsingException.class) - public void splitKVPBadAllWhitespaceAndBackspaceFlagName() throws Exception { - KVP.splitKVP(" \b =value", KeyValuePairLoader.KVP_DELIMITER); + @Test + public void splitKVPBadAllWhitespaceAndBackspaceFlagName() { + assertThrows(ParsingException.class, () -> + KVP.splitKVP(" \b =value", KeyValuePairLoader.KVP_DELIMITER) + ); } @Test @@ -145,9 +157,11 @@ public void newKVPByNameOnlyGoodName() throws Exception { assertNull(kvp.getValue()); } - @Test(expected=ParsingException.class) - public void newKVPByNameOnlyBadEmptyName() throws Exception { - new KVP(" \t \r\n "); + @Test + public void newKVPByNameOnlyBadEmptyName() { + assertThrows(ParsingException.class, () -> + new KVP(" \t \r\n ") + ); } @Test @@ -163,8 +177,10 @@ public void newKVPByNameAndValueGood() throws Exception { assertNull(kvp.getValue()); } - @Test(expected=ParsingException.class) - public void newKVPByNameAndValueBadEmptyName() throws Exception { - new KVP(" \t \r\n ", "value"); + @Test + public void newKVPByNameAndValueBadEmptyName(){ + assertThrows(ParsingException.class, () -> + new KVP(" \t \r\n ", "value") + ); } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java index d54e2c62..43f86b25 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java @@ -3,10 +3,10 @@ import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.*; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; @@ -39,7 +39,7 @@ public interface SimpleParams { FlagProp FLAG_NULL = FlagProp.builder().build(); } - @Before + @BeforeEach public void init() throws Exception { appValuesBuilder = new ValidatedValuesWithContextMutable(); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/LoaderExceptionTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/LoaderExceptionTest.java index bea247c9..28b2bcd0 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/LoaderExceptionTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/LoaderExceptionTest.java @@ -4,7 +4,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class LoaderExceptionTest { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderAppTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderAppTest.java index 0376bdce..7c888cef 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderAppTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderAppTest.java @@ -2,9 +2,9 @@ import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.api.Problem; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderUnitTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderUnitTest.java index f0b3618b..37aff564 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderUnitTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoaderUnitTest.java @@ -2,10 +2,10 @@ import java.util.*; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.StaticPropertyConfigurationMutable; import org.yarnandtail.andhow.internal.LoaderProblem; @@ -41,7 +41,7 @@ public interface SimpleParams { FlagProp FLAG_NULL = FlagProp.builder().build(); } - @Before + @BeforeEach public void init() throws Exception { appValuesBuilder = new ValidatedValuesWithContextMutable(); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderAppTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderAppTest.java index e50141a5..2269b55f 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderAppTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderAppTest.java @@ -4,12 +4,12 @@ import java.net.URL; import java.util.List; import org.apache.commons.io.FileUtils; -import org.junit.After; +import org.junit.jupiter.api.AfterEach; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.internal.ConstructionProblem.LoaderPropertyNotRegistered; @@ -31,7 +31,7 @@ public static interface TestProps { } - @Before + @BeforeEach public void init() throws Exception { //copy a properties file to a temp location @@ -42,7 +42,7 @@ public void init() throws Exception { } - @After + @AfterEach public void afterTest() { if (tempPropertiesFile != null) { tempPropertiesFile.delete(); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderUnitTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderUnitTest.java index 48b73f80..4e7d23b2 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderUnitTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/PropFileOnFilesystemLoaderUnitTest.java @@ -4,12 +4,12 @@ import java.net.URL; import java.util.ArrayList; import org.apache.commons.io.FileUtils; -import org.junit.After; +import org.junit.jupiter.api.AfterEach; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.StaticPropertyConfigurationMutable; import org.yarnandtail.andhow.internal.LoaderProblem; @@ -46,7 +46,7 @@ public interface SimpleParams { FlagProp FLAG_NULL = FlagProp.builder().build(); } - @Before + @BeforeEach public void init() throws Exception { appValuesBuilder = new ValidatedValuesWithContextMutable(); @@ -73,7 +73,7 @@ public void init() throws Exception { } - @After + @AfterEach public void afterTest() { if (tempPropertiesFile != null) { tempPropertiesFile.delete(); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdEnvVarLoaderTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdEnvVarLoaderTest.java index 4efd694d..a16bc2e2 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdEnvVarLoaderTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdEnvVarLoaderTest.java @@ -1,14 +1,11 @@ package org.yarnandtail.andhow.load.std; -import org.yarnandtail.andhow.load.std.StdEnvVarLoader; -import java.util.Collections; import java.util.HashMap; -import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.util.AndHowUtil; import org.yarnandtail.andhow.internal.StaticPropertyConfigurationMutable; @@ -39,7 +36,7 @@ public interface SimpleParams { FlagProp FLAG_NULL = FlagProp.builder().build(); } - @Before + @BeforeEach public void init() throws Exception { appValuesBuilder = new ValidatedValuesWithContextMutable(); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdJndiLoaderTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdJndiLoaderTest.java index aa1d0a13..0c0c47e0 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdJndiLoaderTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdJndiLoaderTest.java @@ -4,9 +4,9 @@ import java.time.LocalDateTime; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.*; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdSysPropLoaderTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdSysPropLoaderTest.java index 769c1c2b..b770b812 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdSysPropLoaderTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/std/StdSysPropLoaderTest.java @@ -1,12 +1,12 @@ package org.yarnandtail.andhow.load.std; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.yarnandtail.andhow.util.AndHowUtil; -import org.junit.After; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.*; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; @@ -35,7 +35,7 @@ public interface SimpleParams { FlagProp FLAG_NULL = FlagProp.builder().build(); } - @Before + @BeforeEach public void init() throws Exception { appValuesBuilder = new ValidatedValuesWithContextMutable(); @@ -55,7 +55,7 @@ public void init() throws Exception { } - @After + @AfterEach public void post() throws Exception { clearSysProps(); } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/name/CaseInsensitiveNamingTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/name/CaseInsensitiveNamingTest.java index e3e64305..564bbf7a 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/name/CaseInsensitiveNamingTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/name/CaseInsensitiveNamingTest.java @@ -1,8 +1,8 @@ package org.yarnandtail.andhow.name; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.PropertyNaming; import org.yarnandtail.andhow.property.StrProp; import org.yarnandtail.andhow.api.GroupProxy; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java index 036fb82b..06221501 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BigDecPropTest.java @@ -1,6 +1,6 @@ package org.yarnandtail.andhow.property; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.api.Problem; @@ -10,7 +10,7 @@ import javax.naming.NamingException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import java.math.BigDecimal; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BolPropTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BolPropTest.java index 1d3b5e25..ac7251ba 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/BolPropTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/BolPropTest.java @@ -1,8 +1,8 @@ package org.yarnandtail.andhow.property; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.RequirementProblem; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/PropertyBuilderBaseTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/PropertyBuilderBaseTest.java index 4260b94b..9d93bfc7 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/PropertyBuilderBaseTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/PropertyBuilderBaseTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.property; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.api.Name; import org.yarnandtail.andhow.valuetype.FlagType; @@ -52,32 +52,40 @@ public void testAliasesHappyPath() { assertTrue(kathy.isIn() && kathy.isOut()); } - @Test(expected=AppFatalException.class) + @Test public void testAliasesSlashCharactersNotAllowed() { TestBuilder builder = new TestBuilder(); - - builder.aliasInAndOut("a/path/looking/one"); + + assertThrows(AppFatalException.class, () -> + builder.aliasInAndOut("a/path/looking/one") + ); } - @Test(expected=AppFatalException.class) + @Test public void testAliasesQuoteCharactersNotAllowed() { TestBuilder builder = new TestBuilder(); - - builder.aliasInAndOut("\"NoQuotes\""); + + assertThrows(AppFatalException.class, () -> + builder.aliasInAndOut("\"NoQuotes\"") + ); } - @Test(expected=AppFatalException.class) + @Test public void testAliasesSpaceCharactersNotAllowed() { TestBuilder builder = new TestBuilder(); - - builder.aliasInAndOut(" NoSpaces"); + + assertThrows(AppFatalException.class, () -> + builder.aliasInAndOut(" NoSpaces") + ); } - @Test(expected=AppFatalException.class) + @Test public void testAliasesNullNotAllowed() { TestBuilder builder = new TestBuilder(); - - builder.aliasInAndOut(null); + + assertThrows(AppFatalException.class, () -> + builder.aliasInAndOut(null) + ); } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/QuotedSpacePreservingTrimmerTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/QuotedSpacePreservingTrimmerTest.java index fbef90f3..c9cbc342 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/QuotedSpacePreservingTrimmerTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/QuotedSpacePreservingTrimmerTest.java @@ -2,8 +2,8 @@ */ package org.yarnandtail.andhow.property; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/StrPropTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/StrPropTest.java index 30d95c71..ceacf2d6 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/StrPropTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/StrPropTest.java @@ -1,8 +1,8 @@ package org.yarnandtail.andhow.property; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.internal.ValueProblem; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/property/TrimToNullTrimmerTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/property/TrimToNullTrimmerTest.java index b79b1614..c3938d0a 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/property/TrimToNullTrimmerTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/property/TrimToNullTrimmerTest.java @@ -1,8 +1,8 @@ package org.yarnandtail.andhow.property; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests TrimToNullTrimmer instances as they would be used in an app. diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/sample/JndiLoaderSamplePrinterTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/sample/JndiLoaderSamplePrinterTest.java index 83bbc5ce..bdeb8311 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/sample/JndiLoaderSamplePrinterTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/sample/JndiLoaderSamplePrinterTest.java @@ -3,8 +3,9 @@ package org.yarnandtail.andhow.sample; import java.io.UnsupportedEncodingException; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; import org.yarnandtail.andhow.api.GroupProxyMutable; import org.yarnandtail.andhow.internal.NameAndProperty; import org.yarnandtail.andhow.internal.StaticPropertyConfigurationMutable; @@ -13,8 +14,6 @@ import org.yarnandtail.andhow.property.StrProp; import org.yarnandtail.andhow.util.TextUtil; -import static org.junit.Assert.*; - /** * * @author ericeverman @@ -31,7 +30,7 @@ public static interface Config { .mustBeNonNull().aliasIn("mp2").aliasInAndOut("mp2_alias2").aliasOut("mp2_out").build(); } - @Before + @BeforeEach public void setup() { config = new StaticPropertyConfigurationMutable(new CaseInsensitiveNaming()); @@ -42,8 +41,8 @@ public void setup() { groupProxy1.addProperty(new NameAndProperty("MY_PROP1", Config.MY_PROP1)); groupProxy1.addProperty(new NameAndProperty("MY_PROP2", Config.MY_PROP2)); - assertNull("Error adding property", config.addProperty(groupProxy1, Config.MY_PROP1)); - assertNull("Error adding property", config.addProperty(groupProxy1, Config.MY_PROP2)); + assertNull(config.addProperty(groupProxy1, Config.MY_PROP1)); + assertNull(config.addProperty(groupProxy1, Config.MY_PROP2)); } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/sample/PropFileLoaderSamplePrinterTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/sample/PropFileLoaderSamplePrinterTest.java index 75a4ded4..0e8f4e54 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/sample/PropFileLoaderSamplePrinterTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/sample/PropFileLoaderSamplePrinterTest.java @@ -3,8 +3,8 @@ package org.yarnandtail.andhow.sample; import java.io.UnsupportedEncodingException; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.GroupProxyMutable; import org.yarnandtail.andhow.internal.NameAndProperty; import org.yarnandtail.andhow.internal.StaticPropertyConfigurationMutable; @@ -13,7 +13,7 @@ import org.yarnandtail.andhow.property.StrProp; import org.yarnandtail.andhow.util.TextUtil; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * @@ -31,7 +31,7 @@ public static interface Config { .mustBeNonNull().aliasIn("mp2").aliasInAndOut("mp2_alias2").aliasOut("mp2_out").build(); } - @Before + @BeforeEach public void setup() { config = new StaticPropertyConfigurationMutable(new CaseInsensitiveNaming()); @@ -42,8 +42,8 @@ public void setup() { groupProxy1.addProperty(new NameAndProperty("MY_PROP1", Config.MY_PROP1)); groupProxy1.addProperty(new NameAndProperty("MY_PROP2", Config.MY_PROP2)); - assertNull("Error adding property", config.addProperty(groupProxy1, Config.MY_PROP1)); - assertNull("Error adding property", config.addProperty(groupProxy1, Config.MY_PROP2)); + assertNull(config.addProperty(groupProxy1, Config.MY_PROP1)); + assertNull(config.addProperty(groupProxy1, Config.MY_PROP2)); } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/sample/TextLineTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/sample/TextLineTest.java index cb8dece2..e34826a7 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/sample/TextLineTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/sample/TextLineTest.java @@ -2,10 +2,10 @@ */ package org.yarnandtail.andhow.sample; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.util.TextUtil; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java index 78cf0277..67d9fd08 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java @@ -2,12 +2,12 @@ import java.util.ArrayList; import java.util.List; -import org.yarnandtail.andhow.service.PropertyRegistrationList; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.GroupProxy; import org.yarnandtail.andhow.property.*; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationListTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationListTest.java index 01cf495b..2eb18d44 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationListTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationListTest.java @@ -1,9 +1,8 @@ package org.yarnandtail.andhow.service; -import org.yarnandtail.andhow.service.PropertyRegistrationList; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationTest.java index b29b3381..3437c709 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrationTest.java @@ -2,11 +2,10 @@ */ package org.yarnandtail.andhow.service; -import org.yarnandtail.andhow.service.PropertyRegistration; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowLogTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowLogTest.java index 88de69aa..99c0402f 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowLogTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowLogTest.java @@ -5,9 +5,12 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.logging.Level; -import org.junit.*; -import static org.junit.Assert.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; /** * @@ -25,7 +28,7 @@ public class AndHowLogTest { public AndHowLogTest() { } - @Before + @BeforeEach public void setUp() { AndHowLogHandler handler = (AndHowLogHandler) log.getHandlers()[0]; @@ -41,7 +44,7 @@ public void setUp() { handler.setNonErrStream(testNonErrPrintStream); } - @After + @AfterEach public void tearDown() { AndHowLogHandler handler = (AndHowLogHandler) log.getHandlers()[0]; @@ -71,13 +74,13 @@ public void testTrace_String() { String sampleMsg = "This is a fact you don't care about"; log.trace(sampleMsg); - assertEquals("trace is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "trace is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(Level.FINEST); log.trace(sampleMsg); assertTrue(testNonErrByteArray.toString().contains(sampleMsg)); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); // Complete reset tearDown(); @@ -89,8 +92,8 @@ public void testTrace_String() { log.setLevel(null); log.trace(sampleMsg); - assertEquals("trace is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "trace is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); System.setProperty(AndHowLogTest.class.getCanonicalName() + ".level", Level.FINEST.getName()); log.setLevel(null); @@ -104,13 +107,13 @@ public void testTrace_String() { @Test public void testTrace_String_ObjectArr() { log.trace("Some debug on line {0}, message ''{1}''", "42", "Bee Boo"); - assertEquals("trace is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "trace is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(Level.FINEST); log.trace("Some debug on line {0}, message ''{1}''", "42", "Bee Boo"); assertTrue(testNonErrByteArray.toString().contains("Some debug on line 42, message 'Bee Boo'")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); // Complete reset tearDown(); @@ -122,8 +125,8 @@ public void testTrace_String_ObjectArr() { log.setLevel(null); log.trace("Some debug on line {0}, message ''{1}''", "42", "Bee Boo"); - assertEquals("trace is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "trace is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); System.setProperty(AndHowLogTest.class.getCanonicalName() + ".level", Level.FINEST.getName()); log.setLevel(null); @@ -138,13 +141,13 @@ public void testTrace_String_ObjectArr() { public void testTrace_String_Throwable() { Exception ex = new Exception("ALERT!"); log.trace("It is nothing serious", ex); - assertEquals("trace is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "trace is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(Level.FINEST); log.trace("It is nothing serious", ex); assertTrue(testNonErrByteArray.toString().contains("It is nothing serious")); assertTrue(testNonErrByteArray.toString().contains("ALERT!")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); } /** @@ -153,13 +156,13 @@ public void testTrace_String_Throwable() { @Test public void testDebug_String() { log.debug("Hello!!!!"); - assertEquals("debug is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "debug is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(Level.FINE); log.debug("Hello!!!!"); assertTrue(testNonErrByteArray.toString().contains("Hello!!!!")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); // Complete reset tearDown(); @@ -172,13 +175,13 @@ public void testDebug_String() { @Test public void testDebug_String_ObjectArr() { log.debug("Some debug on line {0}, message ''{1}''", "42", "Bee Boo"); - assertEquals("debug is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "debug is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(Level.FINE); log.debug("Some debug on line {0}, message ''{1}''", "42", "Bee Boo"); assertTrue(testNonErrByteArray.toString().contains("Some debug on line 42, message 'Bee Boo'")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); // Complete reset tearDown(); setUp(); @@ -191,14 +194,14 @@ public void testDebug_String_ObjectArr() { public void testDebug_String_Throwable() { Exception ex = new Exception("ALERT!"); log.debug("It is nothing serious", ex); - assertEquals("debug is off by default", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "debug is off by default"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(Level.FINE); log.debug("It is nothing serious", ex); assertTrue(testNonErrByteArray.toString().contains("It is nothing serious")); assertTrue(testNonErrByteArray.toString().contains("ALERT!")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); // Complete reset tearDown(); setUp(); @@ -211,13 +214,13 @@ public void testDebug_String_Throwable() { public void testInfo_String() { log.setLevel(Level.OFF); log.info("Hello!!!!"); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.info("Hello!!!!"); assertTrue(testNonErrByteArray.toString().contains("Hello!!!!")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); } /** @@ -227,13 +230,13 @@ public void testInfo_String() { public void testInfo_String_ObjectArr() { log.setLevel(Level.OFF); log.info("Some info on line {0}, message ''{1}''", "42", "Bee Boo"); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.info("Some info on line {0}, message ''{1}''", "42", "Bee Boo"); assertTrue(testNonErrByteArray.toString().contains("Some info on line 42, message 'Bee Boo'")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); } /** @@ -244,14 +247,14 @@ public void testInfo_String_Throwable() { Exception ex = new Exception("ALERT!"); log.setLevel(Level.OFF); log.info("It is nothing serious", ex); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.info("It is nothing serious", ex); assertTrue(testNonErrByteArray.toString().contains("It is nothing serious")); assertTrue(testNonErrByteArray.toString().contains("ALERT!")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); } /** @@ -261,13 +264,13 @@ public void testInfo_String_Throwable() { public void testWarn_String() { log.setLevel(Level.OFF); log.warn("Hello!!!!"); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.warn("Hello!!!!"); assertTrue(testNonErrByteArray.toString().contains("Hello!!!!")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); } @@ -278,13 +281,13 @@ public void testWarn_String() { public void testWarn_String_ObjectArr() { log.setLevel(Level.OFF); log.warn("Some info on line {0}, message ''{1}''", "42", "Bee Boo"); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.warn("Some info on line {0}, message ''{1}''", "42", "Bee Boo"); assertTrue(testNonErrByteArray.toString().contains("Some info on line 42, message 'Bee Boo'")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); } @@ -297,15 +300,15 @@ public void testWarn_String_Throwable() { log.setLevel(Level.OFF); log.warn("It cane be serious", ex); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.warn("It cane be serious", ex); assertTrue(testNonErrByteArray.toString().contains("It cane be serious")); assertTrue(testNonErrByteArray.toString().contains("ALERT!")); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); } @@ -316,13 +319,13 @@ public void testWarn_String_Throwable() { public void testError_String() { log.setLevel(Level.OFF); log.error("Oh-No!!!!"); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.error("Oh-No!!!!"); assertTrue(testErrByteArray.toString().contains("Oh-No!!!!")); - assertEquals("nonErr stream should be empty", 0, testNonErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "nonErr stream should be empty"); } /** @@ -332,13 +335,13 @@ public void testError_String() { public void testError_String_ObjectArr() { log.setLevel(Level.OFF); log.error("Big err on line {0}, message ''{1}''", "42", "Bee Boo"); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.error("Big err on line {0}, message ''{1}''", "42", "Bee Boo"); assertTrue(testErrByteArray.toString().contains("Big err on line 42, message 'Bee Boo'")); - assertEquals("nonErr stream should be empty", 0, testNonErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "nonErr stream should be empty"); } /** @@ -349,14 +352,14 @@ public void testError_String_Throwable() { log.setLevel(Level.OFF); Exception ex = new Exception("ALERT!"); log.error("Big Error!", ex); - assertEquals("Logging is off now", 0, testNonErrByteArray.toString().length()); - assertEquals("err stream should be empty", 0, testErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "Logging is off now"); + assertEquals(0, testErrByteArray.toString().length(), "err stream should be empty"); log.setLevel(null); log.error("Big Error!", ex); assertTrue(testErrByteArray.toString().contains("Big Error!")); assertTrue(testErrByteArray.toString().contains("ALERT!")); - assertEquals("nonErr stream should be empty", 0, testNonErrByteArray.toString().length()); + assertEquals(0, testNonErrByteArray.toString().length(), "nonErr stream should be empty"); } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowUtilTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowUtilTest.java index 2debf492..85ba7ba8 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowUtilTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/AndHowUtilTest.java @@ -2,13 +2,12 @@ */ package org.yarnandtail.andhow.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.lang.reflect.Method; -import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/IOUtilTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/IOUtilTest.java index 7db8f7aa..0fe529df 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/IOUtilTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/IOUtilTest.java @@ -2,8 +2,8 @@ */ package org.yarnandtail.andhow.util; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/NameUtilTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/NameUtilTest.java index 13e85890..fa48ed68 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/NameUtilTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/NameUtilTest.java @@ -1,12 +1,11 @@ package org.yarnandtail.andhow.util; -import org.yarnandtail.andhow.util.NameUtil; import java.util.Arrays; import java.util.List; -import org.junit.Before; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; /** * Naming support for Properties. diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/StackLocatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/StackLocatorTest.java index f7c496ab..bf80849d 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/StackLocatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/StackLocatorTest.java @@ -1,8 +1,7 @@ package org.yarnandtail.andhow.util; -import org.junit.Before; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * This is testing production code from Log4J, so this is really just characterization. diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java index b5191c1a..5623760b 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/util/TextUtilTest.java @@ -4,9 +4,10 @@ import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.util.List; -import static org.junit.Assert.*; -import org.junit.Before; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.api.ParsingException; /** * @@ -17,7 +18,7 @@ public class TextUtilTest { PrintStream ps; private static String FIND_INSTANCE_STRING_BROWNFOX = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog"; - @Before + @BeforeEach public void setup() { baos = new ByteArrayOutputStream(); ps = new PrintStream(baos) { @@ -54,9 +55,11 @@ public void testFormat() { assertEquals("abcXXXdef[[NULL]]xyz", TextUtil.format("abc{}def{}xyz", "XXX", null, null)); } - @Test(expected = ArrayIndexOutOfBoundsException.class) + @Test public void testFormatException() { - assertEquals("abcXXXxyz", TextUtil.format("abc{}xyz{}", "XXX")); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> + assertEquals("abcXXXxyz", TextUtil.format("abc{}xyz{}", "XXX")) + ); } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java index e51a68f1..bf2867ad 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/BigDecValidatorTest.java @@ -1,12 +1,12 @@ package org.yarnandtail.andhow.valid; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.math.BigDecimal; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BigDecValidatorTest { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/DblValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/DblValidatorTest.java index 330645d2..009c8f7f 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/DblValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/DblValidatorTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.valid; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/IntValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/IntValidatorTest.java index 4d2f3611..8ca7b510 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/IntValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/IntValidatorTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.valid; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java index 3703b6b1..81e53dc2 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LngValidatorTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.valid; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LocalDateTimeValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LocalDateTimeValidatorTest.java index 3762b24c..c8fdc765 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LocalDateTimeValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/LocalDateTimeValidatorTest.java @@ -2,8 +2,8 @@ import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * @@ -20,12 +20,12 @@ public void testBefore() { LocalDateTimeValidator.Before instance = new LocalDateTimeValidator.Before(DEC_03_2007_AT_5PM); assertTrue(instance.isSpecificationValid()); - assertTrue("One nano second before", instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS))); - assertTrue("One day second before", instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.DAYS))); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS)), "One nano second before"); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.DAYS)), "One day second before"); - assertFalse("Same date should not be valid", instance.isValid(DEC_03_2007_AT_5PM)); - assertFalse("One nano after should not be valid", instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS))); - assertFalse("Null should not be valid", instance.isValid(null)); + assertFalse(instance.isValid(DEC_03_2007_AT_5PM), "Same date should not be valid"); + assertFalse(instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS)), "One nano after should not be valid"); + assertFalse(instance.isValid(null), "Null should not be valid"); assertEquals(INVALID_SPECIFICATION_MESSAGE, instance.getInvalidSpecificationMessage()); @@ -37,12 +37,12 @@ public void testSameTimeOrBefore() { LocalDateTimeValidator.SameTimeOrBefore instance = new LocalDateTimeValidator.SameTimeOrBefore(DEC_03_2007_AT_5PM); assertTrue(instance.isSpecificationValid()); - assertTrue("One nano second before", instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS))); - assertTrue("One day second before", instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.DAYS))); - assertTrue("Same date should be valid", instance.isValid(DEC_03_2007_AT_5PM)); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS)), "One nano second before"); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.DAYS)), "One day second before"); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM), "Same date should be valid"); - assertFalse("One nano after should not be valid", instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS))); - assertFalse("Null should not be valid", instance.isValid(null)); + assertFalse(instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS)), "One nano after should not be valid"); + assertFalse(instance.isValid(null), "Null should not be valid"); assertEquals(INVALID_SPECIFICATION_MESSAGE, instance.getInvalidSpecificationMessage()); @@ -54,12 +54,12 @@ public void testAfter() { LocalDateTimeValidator.After instance = new LocalDateTimeValidator.After(DEC_03_2007_AT_5PM); assertTrue(instance.isSpecificationValid()); - assertTrue("One nano second after", instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS))); - assertTrue("One day second before", instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.DAYS))); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS)), "One nano second after"); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.DAYS)), "One day second before"); - assertFalse("Same date should not be valid", instance.isValid(DEC_03_2007_AT_5PM)); - assertFalse("One nano before should not be valid", instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS))); - assertFalse("Null should not be valid", instance.isValid(null)); + assertFalse(instance.isValid(DEC_03_2007_AT_5PM), "Same date should not be valid"); + assertFalse(instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS)), "One nano before should not be valid"); + assertFalse(instance.isValid(null), "Null should not be valid"); assertEquals(INVALID_SPECIFICATION_MESSAGE, instance.getInvalidSpecificationMessage()); @@ -71,12 +71,12 @@ public void testSameTimeOrAfter() { LocalDateTimeValidator.SameTimeOrAfter instance = new LocalDateTimeValidator.SameTimeOrAfter(DEC_03_2007_AT_5PM); assertTrue(instance.isSpecificationValid()); - assertTrue("One nano second after", instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS))); - assertTrue("One day second after", instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.DAYS))); - assertTrue("Same date should be valid", instance.isValid(DEC_03_2007_AT_5PM)); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.NANOS)), "One nano second after"); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM.plus(1, ChronoUnit.DAYS)), "One day second after"); + assertTrue(instance.isValid(DEC_03_2007_AT_5PM), "Same date should be valid"); - assertFalse("One nano before should not be valid", instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS))); - assertFalse("Null should not be valid", instance.isValid(null)); + assertFalse(instance.isValid(DEC_03_2007_AT_5PM.minus(1, ChronoUnit.NANOS)), "One nano before should not be valid"); + assertFalse(instance.isValid(null), "Null should not be valid"); assertEquals(INVALID_SPECIFICATION_MESSAGE, instance.getInvalidSpecificationMessage()); diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/StringValidatorTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/StringValidatorTest.java index 205ec87b..469cf492 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valid/StringValidatorTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valid/StringValidatorTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.valid; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java index 78cb77ee..ba606794 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/BigDecTypeTest.java @@ -1,13 +1,11 @@ package org.yarnandtail.andhow.valuetype; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.ParsingException; import java.math.BigDecimal; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for BigDecType @@ -16,8 +14,6 @@ */ public class BigDecTypeTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); private static final String PARSE_ERROR_MSG = "Unable to convert to a BigDecimal numeric value"; private BigDecType type = BigDecType.instance(); @@ -32,16 +28,22 @@ public void testParse() throws ParsingException { @Test public void testParseEmpty() throws ParsingException { - expectParsingException(); assertFalse(type.isParsable("")); - type.parse(""); + + Exception e = assertThrows(ParsingException.class, () -> + type.parse("") + ); + assertEquals(PARSE_ERROR_MSG, e.getMessage()); } @Test public void testParseNotANumber() throws ParsingException { - expectParsingException(); assertFalse(type.isParsable("apple")); - type.parse("apple"); + + Exception e = assertThrows(ParsingException.class, () -> + type.parse("apple") + ); + assertEquals(PARSE_ERROR_MSG, e.getMessage()); } @Test @@ -51,8 +53,4 @@ public void testCast() { assertNotNull(type.cast(o)); } - private void expectParsingException() { - thrown.expect(ParsingException.class); - thrown.expectMessage(PARSE_ERROR_MSG); - } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java index af2d5c84..494e0b91 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.valuetype; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.ParsingException; /** @@ -38,18 +38,24 @@ public void testWeirdDoubleSpecialValues() throws ParsingException { } - @Test(expected=ParsingException.class) - public void testParseNotANumber() throws ParsingException { + @Test + public void testParseNotANumber() { DblType type = DblType.instance(); assertFalse(type.isParsable("apple")); - type.parse("apple"); + + assertThrows(ParsingException.class, () -> + type.parse("apple") + ); } - @Test(expected=ParsingException.class) - public void testParseEmpty() throws ParsingException { + @Test + public void testParseEmpty() { DblType type = DblType.instance(); assertFalse(type.isParsable("")); - type.parse(""); + + assertThrows(ParsingException.class, () -> + type.parse("") + ); } @Test diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/IntTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/IntTypeTest.java index bd4e1725..79dbe9ac 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/IntTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/IntTypeTest.java @@ -2,8 +2,8 @@ */ package org.yarnandtail.andhow.valuetype; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.ParsingException; /** @@ -24,39 +24,54 @@ public void testParseHappyPath() throws ParsingException { assertNull(type.parse(null)); } - @Test(expected=ParsingException.class) - public void testParseDecimalNumber() throws ParsingException { + @Test + public void testParseDecimalNumber() { IntType type = IntType.instance(); assertFalse(type.isParsable("1234.1234")); - type.parse("1234.1234"); + + assertThrows(ParsingException.class, () -> + type.parse("1234.1234") + ); } - @Test(expected=ParsingException.class) - public void testParseNotANumber() throws ParsingException { + @Test + public void testParseNotANumber() { IntType type = IntType.instance(); assertFalse(type.isParsable("apple")); - type.parse("apple"); + + assertThrows(ParsingException.class, () -> + type.parse("apple") + ); } - @Test(expected=ParsingException.class) + @Test public void testParseEmpty() throws ParsingException { IntType type = IntType.instance(); assertFalse(type.isParsable("")); - type.parse(""); + + assertThrows(ParsingException.class, () -> + type.parse("") + ); } - @Test(expected=ParsingException.class) - public void testParseTooBig() throws ParsingException { + @Test + public void testParseTooBig() { IntType type = IntType.instance(); assertFalse(type.isParsable("9999999999999999999999999999999999999999")); - type.parse("9999999999999999999999999999999999999999"); + + assertThrows(ParsingException.class, () -> + type.parse("9999999999999999999999999999999999999999") + ); } - @Test(expected=ParsingException.class) - public void testParseTooSmall() throws ParsingException { + @Test + public void testParseTooSmall() { IntType type = IntType.instance(); assertFalse(type.isParsable("-9999999999999999999999999999999999999999")); - type.parse("-9999999999999999999999999999999999999999"); + + assertThrows(ParsingException.class, () -> + type.parse("-9999999999999999999999999999999999999999") + ); } @Test diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java index eeb111ac..2a17b876 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java @@ -1,7 +1,7 @@ package org.yarnandtail.andhow.valuetype; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.ParsingException; /** @@ -9,8 +9,6 @@ * @author ericeverman */ public class LngTypeTest { - - @Test public void testParseHappyPath() throws ParsingException { @@ -22,39 +20,54 @@ public void testParseHappyPath() throws ParsingException { assertNull(type.parse(null)); } - @Test(expected=ParsingException.class) - public void testParseDecimalNumber() throws ParsingException { + @Test + public void testParseDecimalNumber() { LngType type = LngType.instance(); assertFalse(type.isParsable("1234.1234")); - type.parse("1234.1234"); + + assertThrows(ParsingException.class, () -> + type.parse("1234.1234") + ); } - @Test(expected=ParsingException.class) + @Test public void testParseNotANumber() throws ParsingException { LngType type = LngType.instance(); assertFalse(type.isParsable("apple")); - type.parse("apple"); + + assertThrows(ParsingException.class, () -> + type.parse("apple") + ); } - @Test(expected=ParsingException.class) + @Test public void testParseEmpty() throws ParsingException { LngType type = LngType.instance(); assertFalse(type.isParsable("")); - type.parse(""); + + assertThrows(ParsingException.class, () -> + type.parse("") + ); } - @Test(expected=ParsingException.class) - public void testParseTooBig() throws ParsingException { + @Test + public void testParseTooBig() { LngType type = LngType.instance(); assertFalse(type.isParsable("9999999999999999999999999999999999999999")); - type.parse("9999999999999999999999999999999999999999"); + + assertThrows(ParsingException.class, () -> + type.parse("9999999999999999999999999999999999999999") + ); } - @Test(expected=ParsingException.class) - public void testParseTooSmall() throws ParsingException { + @Test + public void testParseTooSmall() { LngType type = LngType.instance(); assertFalse(type.isParsable("-9999999999999999999999999999999999999999")); - type.parse("-9999999999999999999999999999999999999999"); + + assertThrows(ParsingException.class, () -> + type.parse("-9999999999999999999999999999999999999999") + ); } @Test diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LocalDateTimeTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LocalDateTimeTypeTest.java index 6346917c..e122f022 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LocalDateTimeTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LocalDateTimeTypeTest.java @@ -3,8 +3,8 @@ package org.yarnandtail.andhow.valuetype; import java.time.LocalDateTime; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.ParsingException; /** @@ -12,8 +12,6 @@ * @author ericeverman */ public class LocalDateTimeTypeTest { - - @Test public void testParseHappyPath() throws ParsingException { @@ -32,32 +30,44 @@ public void testParseHappyPath() throws ParsingException { assertNull(type.parse(null)); } - @Test(expected=ParsingException.class) - public void testParseTooManyDecimalPlaces() throws ParsingException { + @Test + public void testParseTooManyDecimalPlaces() { LocalDateTimeType type = LocalDateTimeType.instance(); assertFalse(type.isParsable("2007-12-03T10:15:30.1234567891")); - type.parse("2007-12-03T10:15:30.1234567891"); + + assertThrows(ParsingException.class, () -> + type.parse("2007-12-03T10:15:30.1234567891") + ); } - @Test(expected=ParsingException.class) - public void testParseEmpty() throws ParsingException { + @Test + public void testParseEmpty() { LocalDateTimeType type = LocalDateTimeType.instance(); assertFalse(type.isParsable("")); - type.parse(""); + + assertThrows(ParsingException.class, () -> + type.parse("") + ); } - @Test(expected=ParsingException.class) - public void testParseIncorrect24Hour() throws ParsingException { + @Test + public void testParseIncorrect24Hour() { LocalDateTimeType type = LocalDateTimeType.instance(); assertFalse(type.isParsable("2007-12-03T24:15:30.123456789")); - type.parse("2007-12-03T24:15:30.123456789"); + + assertThrows(ParsingException.class, () -> + type.parse("2007-12-03T24:15:30.123456789") + ); } - @Test(expected=ParsingException.class) - public void testParseMissingZeroPadding() throws ParsingException { + @Test + public void testParseMissingZeroPadding() { LocalDateTimeType type = LocalDateTimeType.instance(); assertFalse(type.isParsable("2007-12-03T10:15:3")); - type.parse("2007-12-03T10:15:3"); + + assertThrows(ParsingException.class, () -> + type.parse("2007-12-03T10:15:3") + ); } @Test diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/StrTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/StrTypeTest.java index c6918b10..0ffd2155 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/StrTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/StrTypeTest.java @@ -2,8 +2,8 @@ */ package org.yarnandtail.andhow.valuetype; -import static org.junit.Assert.*; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.api.ParsingException; /** diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index cbf3398e..afdb1111 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -15,15 +15,23 @@ and creating and testing files in memory. - - junit - junit - ${project.groupId} andhow-core ${project.version} provided + + org.junit.jupiter + junit-jupiter-api + 5.7.2 + test + + + org.hamcrest + hamcrest + 2.2 + test + diff --git a/andhow-testing/andhow-annotation-processor-test-harness/src/test/java/org/yarnandtail/compile/MemoryFileManagerTest.java b/andhow-testing/andhow-annotation-processor-test-harness/src/test/java/org/yarnandtail/compile/MemoryFileManagerTest.java index 5de01947..b78a0408 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/src/test/java/org/yarnandtail/compile/MemoryFileManagerTest.java +++ b/andhow-testing/andhow-annotation-processor-test-harness/src/test/java/org/yarnandtail/compile/MemoryFileManagerTest.java @@ -5,13 +5,11 @@ import java.io.IOException; import java.io.Writer; import javax.tools.*; -import javax.tools.JavaFileManager.Location; -import org.yarnandtail.compile.MemoryFileManager; -import org.yarnandtail.compile.TestClassLoader; -import org.junit.Before; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; /** * @@ -26,7 +24,7 @@ public class MemoryFileManagerTest { public MemoryFileManagerTest() { } - @Before + @BeforeEach public void setUp() { compiler = ToolProvider.getSystemJavaCompiler(); manager = new MemoryFileManager(compiler); diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index adf08389..0d7556e0 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -37,10 +37,17 @@ - junit - junit + org.junit.jupiter + junit-jupiter-api + 5.7.2 + test + + + org.hamcrest + hamcrest + 2.2 + test - diff --git a/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java b/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java index a97661dd..1a3a92c4 100644 --- a/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java +++ b/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/andhow/service/PropertyRegistrarLoaderTest.java @@ -1,12 +1,13 @@ package org.yarnandtail.andhow.service; import java.util.List; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.internal.NameAndProperty; import org.yarnandtail.classvistests.sample.NonStaticInnerClassSample; import org.yarnandtail.classvistests.sample.PropertySample; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import org.yarnandtail.andhow.api.GroupProxy; diff --git a/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/classvistests/nonapp/sample/VisibilitySampleTest.java b/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/classvistests/nonapp/sample/VisibilitySampleTest.java index 3297ee54..533b3f50 100644 --- a/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/classvistests/nonapp/sample/VisibilitySampleTest.java +++ b/andhow-testing/andhow-annotation-processor-tests/src/test/java/org/yarnandtail/classvistests/nonapp/sample/VisibilitySampleTest.java @@ -2,9 +2,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * These are some characterization tests of how visibility of non-public diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml index 0bca988f..90eedebb 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/pom.xml @@ -14,25 +14,6 @@ Sample to be used as a compiled jar by the andhow-default-behavior-test to test property behavior in external compiled libraries. - - - - - org.yarnandtail - andhow - ${project.version} - - - - org.yarnandtail - andhow-test-harness - ${project.version} - test - - - junit - junit - - + diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerNotUsingAHBaseTestClassTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerNotUsingAHBaseTestClassTest.java index f9c3ad71..e8ea8766 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerNotUsingAHBaseTestClassTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerNotUsingAHBaseTestClassTest.java @@ -1,7 +1,7 @@ package com.dep1; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerUsingAHBaseTestClassTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerUsingAHBaseTestClassTest.java index 7eb866b1..38770de4 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerUsingAHBaseTestClassTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep1/src/test/java/com/dep1/EarthMapMakerUsingAHBaseTestClassTest.java @@ -1,12 +1,12 @@ package com.dep1; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runners.MethodSorters; -import org.yarnandtail.andhow.AndHowTestBase; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.MethodOrderer; +import static org.junit.jupiter.api.Assertions.*; +import org.yarnandtail.andhow.AndHowJunit5TestBase; import org.yarnandtail.andhow.NonProductionConfig; -import static org.junit.Assert.*; /** * Note that these test methods are specified to be executed in Alph sort order @@ -14,8 +14,8 @@ * * @author ericeverman */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class EarthMapMakerUsingAHBaseTestClassTest extends AndHowTestBase { +@TestMethodOrder(MethodOrderer.MethodName.class) +public class EarthMapMakerUsingAHBaseTestClassTest extends AndHowJunit5TestBase { public EarthMapMakerUsingAHBaseTestClassTest() { } diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml index 68c12735..ed4b2157 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/pom.xml @@ -12,24 +12,5 @@ andhow-default-behavior-test to test property behavior in external compiled libraries. - - - - org.yarnandtail - andhow - ${project.version} - - - - org.yarnandtail - andhow-test-harness - ${project.version} - test - - - junit - junit - - \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerNotUsingAHBaseTestClassTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerNotUsingAHBaseTestClassTest.java index 051d3f9c..026ec253 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerNotUsingAHBaseTestClassTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerNotUsingAHBaseTestClassTest.java @@ -1,7 +1,7 @@ package com.dep2; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerUsingAHBaseTestClassTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerUsingAHBaseTestClassTest.java index fcca940f..8cc8a8ec 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerUsingAHBaseTestClassTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-dep2/src/test/java/com/dep2/MarsMapMakerUsingAHBaseTestClassTest.java @@ -1,12 +1,12 @@ package com.dep2; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.runners.MethodSorters; -import org.yarnandtail.andhow.AndHowTestBase; -import org.yarnandtail.andhow.NonProductionConfig; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.MethodOrderer; +import static org.junit.jupiter.api.Assertions.*; -import static org.junit.Assert.*; +import org.yarnandtail.andhow.AndHowJunit5TestBase; +import org.yarnandtail.andhow.NonProductionConfig; /** * Note that these test methods are specified to be executed in Alph sort order @@ -14,8 +14,8 @@ * * @author ericeverman */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class MarsMapMakerUsingAHBaseTestClassTest extends AndHowTestBase { +@TestMethodOrder(MethodOrderer.MethodName.class) +public class MarsMapMakerUsingAHBaseTestClassTest extends AndHowJunit5TestBase { public MarsMapMakerUsingAHBaseTestClassTest() { } diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml index 241742f4..14712a46 100755 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/pom.xml @@ -24,22 +24,6 @@ andhow-default-behavior-dep2 ${project.version} - - - org.yarnandtail - andhow - ${project.version} - - - ${project.groupId} - andhow-test-harness - ${project.version} - test - - - junit - junit - org.springframework spring-test diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java index d2bd0282..a0cde875 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java @@ -1,22 +1,19 @@ package com.dep1; import org.dataprocess.ExternalServiceConnector; -import org.junit.Test; import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.internal.LoaderProblem; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * * @author ericeverman */ -public class EarthMapMakerTest extends AndHowTestBase { +public class EarthMapMakerTest extends AndHowJunit5TestBase { @Test public void testConfigFromPropertiesFileOnly() { diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java index 00a46c60..b3ac61f0 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java @@ -1,23 +1,19 @@ package com.dep2; -import com.dep2.*; import org.dataprocess.ExternalServiceConnector; -import org.junit.Test; import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.internal.LoaderProblem; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * * @author ericeverman */ -public class MarsMapMakerTest extends AndHowTestBase { +public class MarsMapMakerTest extends AndHowJunit5TestBase { @Test public void testConfigFromPropertiesFileOnly() { diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java index 9715aff7..60b0da14 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java @@ -1,15 +1,15 @@ package org.dataprocess; -import org.junit.Test; -import org.yarnandtail.andhow.AndHowTestBase; +import org.yarnandtail.andhow.AndHowJunit5TestBase; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; /** * * @author ericeverman */ -public class ExternalServiceConnectorTest extends AndHowTestBase { +public class ExternalServiceConnectorTest extends AndHowJunit5TestBase { public ExternalServiceConnectorTest() { } diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index a55e75f9..8cb36594 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -14,4 +14,32 @@ andhow-default-behavior-dep1 andhow-default-behavior-dep2 + + + + + org.yarnandtail + andhow + ${project.version} + + + org.yarnandtail + andhow-test-harness + ${project.version} + test + + + + org.junit.platform + junit-platform-launcher + + + org.junit.jupiter + junit-jupiter-api + + + org.hamcrest + hamcrest + + \ No newline at end of file diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 31315a6e..65a185c1 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -20,7 +20,7 @@ ${project.groupId} andhow ${project.version} - provided + test ${project.groupId} @@ -28,11 +28,14 @@ ${project.version} test - + - junit - junit - test + org.junit.jupiter + junit-jupiter-api + + + org.hamcrest + hamcrest commons-io diff --git a/andhow-testing/andhow-system-tests/src/test/java/org/yarnandtail/andhow/AndHowTest.java b/andhow-testing/andhow-system-tests/src/test/java/org/yarnandtail/andhow/AndHowTest.java index d50e097f..bc57e865 100644 --- a/andhow-testing/andhow-system-tests/src/test/java/org/yarnandtail/andhow/AndHowTest.java +++ b/andhow-testing/andhow-system-tests/src/test/java/org/yarnandtail/andhow/AndHowTest.java @@ -1,11 +1,13 @@ package org.yarnandtail.andhow; import java.lang.reflect.Field; -import org.junit.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.*; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; import org.yarnandtail.andhow.property.StrProp; -import static org.junit.Assert.*; /** * @@ -24,12 +26,12 @@ public class AndHowTest { private AndHow originalAndHowInstance; - @Before + @BeforeEach public void clearAndHow() { originalAndHowInstance = setAndHowInstance(null); } - @After + @AfterEach public void restoreAndHow() { setAndHowInstance(originalAndHowInstance); } @@ -45,8 +47,8 @@ public void testFindConfig() { AndHowConfiguration config1 = AndHow.findConfig(); AndHowConfiguration config2 = AndHow.findConfig(); - assertNotEquals("Should return a new instance each time", config1, config2); - assertFalse("findConfig should not force initialization", AndHow.isInitialize()); + assertNotEquals(config1, config2, "Should return a new instance each time"); + assertFalse(AndHow.isInitialize(), "findConfig should not force initialization"); } /** diff --git a/andhow-testing/andhow-test-harness/pom.xml b/andhow-testing/andhow-test-harness/pom.xml index 37dcf5bf..b921d495 100644 --- a/andhow-testing/andhow-test-harness/pom.xml +++ b/andhow-testing/andhow-test-harness/pom.xml @@ -19,18 +19,49 @@ ${project.groupId} andhow-core ${project.version} - provided + compile + true - + + + + org.springframework + spring-test + compile + + + junit junit - provided + compile + true - org.springframework - spring-test + org.junit.jupiter + junit-jupiter-api + compile + true + + + org.junit.jupiter + junit-jupiter-engine + compile + true + + + org.junit.vintage + junit-vintage-engine compile + true diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit4TestBase.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit4TestBase.java new file mode 100644 index 00000000..9b8d711c --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit4TestBase.java @@ -0,0 +1,69 @@ +package org.yarnandtail.andhow; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + + +/** + * Optional class that can be used by app developers as a base class for JUnit 4 tests. + * + * This class resets AndHow and System.properties to their prior + * state after each test run, as well as after all tests have run. This, used with + * NonProductionConfig.instance().forceBuild() + * and setting SystemProperties at the start of a test, can be used to set a + * specific AndHow configuration for a test or suite of tests. + * + * + * Here is a exampleHere is a example that shows how this can be used. + * + * @author eeverman + */ +public class AndHowJunit4TestBase extends AndHowTestBaseImpl { + + /** + * Stores the AndHow Core (its state) and System Properties prior to a test class. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. + */ + @BeforeClass + public static void andHowSnapshotBeforeTestClass() { + AndHowTestBaseImpl.andHowSnapshotBeforeTestClass(); + } + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to this class' test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + */ + @AfterClass + public static void resetAndHowSnapshotAfterTestClass() { + AndHowTestBaseImpl.resetAndHowSnapshotAfterTestClass(); + } + + /** + * Stores the AndHow Core (its state) and System Properties prior to a test. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. + */ + @Before + public void andHowSnapshotBeforeSingleTest() { + super.andHowSnapshotBeforeSingleTest(); + } + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to a test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + */ + @After + public void resetAndHowSnapshotAfterSingleTest() { + super.resetAndHowSnapshotAfterSingleTest(); + } + +} diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit5TestBase.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit5TestBase.java new file mode 100644 index 00000000..59d316f7 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowJunit5TestBase.java @@ -0,0 +1,69 @@ +package org.yarnandtail.andhow; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; + + +/** + * Optional class that can be used by app developers as a base class for JUnit 4 tests. + * + * This class resets AndHow and System.properties to their prior + * state after each test run, as well as after all tests have run. This, used with + * NonProductionConfig.instance().forceBuild() + * and setting SystemProperties at the start of a test, can be used to set a + * specific AndHow configuration for a test or suite of tests. + * + * + * Here is a exampleHere is a example that shows how this can be used. + * + * @author eeverman + */ +public class AndHowJunit5TestBase extends AndHowTestBaseImpl { + + /** + * Stores the AndHow Core (its state) and System Properties prior to a test class. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. + */ + @BeforeAll + public static void andHowSnapshotBeforeTestClass() { + AndHowTestBaseImpl.andHowSnapshotBeforeTestClass(); + } + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to this class' test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + */ + @AfterAll + public static void resetAndHowSnapshotAfterTestClass() { + AndHowTestBaseImpl.resetAndHowSnapshotAfterTestClass(); + } + + /** + * Stores the AndHow Core (its state) and System Properties prior to a test. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. + */ + @BeforeEach + public void andHowSnapshotBeforeSingleTest() { + super.andHowSnapshotBeforeSingleTest(); + } + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to a test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + */ + @AfterEach + public void resetAndHowSnapshotAfterSingleTest() { + super.resetAndHowSnapshotAfterSingleTest(); + } + +} diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBase.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBase.java index 4e9640eb..b4cc0e76 100644 --- a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBase.java +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBase.java @@ -1,106 +1,67 @@ package org.yarnandtail.andhow; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.naming.NamingException; import org.junit.*; -import org.springframework.mock.jndi.SimpleNamingContextBuilder; -import org.yarnandtail.andhow.internal.AndHowCore; + /** - * All tests using AppConfig must extend this class so they have access to the - * one and only AppConfig.Reloader, which is a single backdoor to cause the - * AppConfig to reload. + * Optional class that can be used by app developers as a base class for JUnit 4 tests. + * + * This class resets AndHow and System.properties to their prior + * state after each test run, as well as after all tests have run. This, used with + * NonProductionConfig.instance().forceBuild() + * and setting SystemProperties at the start of a test, can be used to set a + * specific AndHow configuration for a test or suite of tests. + * + * + * Here is a exampleHere is a example that shows how this can be used. * * @author eeverman + * @depricated Use AndHowJunit4TestBase instead, or upgrade to AndHowJunit5TestBase. */ -public class AndHowTestBase { - - private static AndHowCore beforeClassCore; - - private AndHowCore beforeTestCore; - - private static Level beforeClassLogLevel; - - /** - * System properties before the tests and subclass @BeforeClass initializer - * are run. Properties prior to the initialization of this test class are - * stored and then reinstated after the test class is complete. - */ - private static Properties beforeClassSystemProps; - - /** - * System properties before an individual test is run and the subclass @Before - * initializers are run. Properties prior to a test are - * stored and then reinstated after the test is complete. If a Test classes - * uses a @BeforeClass initializer that sets system properties, this will - * reinstate to that state. - */ - private Properties beforeTestSystemProps; - - /** - * Builder for a temporary JNDI context - */ - private static SimpleNamingContextBuilder builder; - - - +public class AndHowTestBase extends AndHowTestBaseImpl { + /** - * Simple consistent way to get an empty JNDI context. - * - * bind() each variable, then call build(). - * - * @return - * @throws NamingException + * Stores the AndHow Core (its state) and System Properties prior to a test class. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. */ - public SimpleNamingContextBuilder getJndi() throws NamingException { - if (builder == null) { - builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder(); - } - return builder; - } - @BeforeClass - public static void andHowSnapshotBeforeTestClass() throws Exception { - //The SimpleNamingContextBuilder uses Commons Logging, which defaults to - //using Java logging. It spews a bunch of stuff the console during tests, - //so this turns that off. - - beforeClassLogLevel = Logger.getGlobal().getLevel(); //store log level before class - Logger.getGlobal().setLevel(Level.SEVERE); - Logger.getLogger(SimpleNamingContextBuilder.class.getCanonicalName()).setLevel(Level.SEVERE); - - - beforeClassCore = AndHowNonProductionUtil.getAndHowCore(); - beforeClassSystemProps = AndHowNonProductionUtil.clone(System.getProperties()); + public static void andHowSnapshotBeforeTestClass() { + AndHowTestBaseImpl.andHowSnapshotBeforeTestClass(); } - + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to this class' test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + */ @AfterClass public static void resetAndHowSnapshotAfterTestClass() { - System.setProperties(beforeClassSystemProps); - AndHowNonProductionUtil.setAndHowCore(beforeClassCore); - - //Reset to the log level prior to the test class - Logger.getGlobal().setLevel(beforeClassLogLevel); - Logger.getLogger(SimpleNamingContextBuilder.class.getCanonicalName()).setLevel(beforeClassLogLevel); + AndHowTestBaseImpl.resetAndHowSnapshotAfterTestClass(); } - + + /** + * Stores the AndHow Core (its state) and System Properties prior to a test. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. + */ @Before public void andHowSnapshotBeforeSingleTest() { - beforeTestSystemProps = AndHowNonProductionUtil.clone(System.getProperties()); - beforeTestCore = AndHowNonProductionUtil.getAndHowCore(); + super.andHowSnapshotBeforeSingleTest(); } - + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to a test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + */ @After public void resetAndHowSnapshotAfterSingleTest() { - System.setProperties(beforeTestSystemProps); - AndHowNonProductionUtil.setAndHowCore(beforeTestCore); - - if (builder != null) { - builder.clear(); - } + super.resetAndHowSnapshotAfterSingleTest(); } - - + } diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBaseImpl.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBaseImpl.java new file mode 100644 index 00000000..42ba03e1 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/AndHowTestBaseImpl.java @@ -0,0 +1,133 @@ +package org.yarnandtail.andhow; + +import org.springframework.mock.jndi.SimpleNamingContextBuilder; +import org.yarnandtail.andhow.internal.AndHowCore; + +import javax.naming.NamingException; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Base class for AndHowJunit4/5TestBase classes. + * See those classes for documentation. + */ +public class AndHowTestBaseImpl { + private static AndHowCore beforeClassCore; + private static Level beforeClassLogLevel; + /** + * System properties before the tests and subclass @BeforeClass initializer + * are run. Properties prior to the initialization of this test class are + * stored and then reinstated after the test class is complete. + */ + private static Properties beforeClassSystemProps; + /** + * Builder for a temporary JNDI context + */ + private static SimpleNamingContextBuilder builder; + private AndHowCore beforeTestCore; + /** + * System properties before an individual test is run and the subclass @Before + * initializers are run. Properties prior to a test are + * stored and then reinstated after the test is complete. If a Test classes + * uses a @BeforeClass initializer that sets system properties, this will + * reinstate to that state. + */ + private Properties beforeTestSystemProps; + + /** + * Stores the AndHow Core (its state) and System Properties prior to a test class. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. + */ + public static void andHowSnapshotBeforeTestClass() { + //The SimpleNamingContextBuilder uses Commons Logging, which defaults to + //using Java logging. It spews a bunch of stuff the console during tests, + //so this turns that off. + + beforeClassLogLevel = Logger.getGlobal().getLevel(); //store log level before class + Logger.getGlobal().setLevel(Level.SEVERE); + Logger.getLogger(SimpleNamingContextBuilder.class.getCanonicalName()).setLevel(Level.SEVERE); + + + beforeClassCore = AndHowNonProductionUtil.getAndHowCore(); + beforeClassSystemProps = AndHowNonProductionUtil.clone(System.getProperties()); + } + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to this class' test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + * Any JNDI bindings set via getJndi() are cleared. + */ + public static void resetAndHowSnapshotAfterTestClass() { + System.setProperties(beforeClassSystemProps); + AndHowNonProductionUtil.setAndHowCore(beforeClassCore); + + //Reset to the log level prior to the test class + Logger.getGlobal().setLevel(beforeClassLogLevel); + Logger.getLogger(SimpleNamingContextBuilder.class.getCanonicalName()).setLevel(beforeClassLogLevel); + + if (builder != null) { + builder.clear(); + builder.deactivate(); + } + } + + /** + * Simple consistent way to get an empty JNDI context. + *

    + * Call {@code SimpleNamingContextBuilder.bind()} for each variable to add + * to the context, then {@code SimpleNamingContextBuilder.activate()} to + * make the context active. To fetch values from the context, + * use: + *

    {@code
    +	 * InitialContext ctx = new InitialContext();
    +	 * ctx.lookup("java:comp/some/name");
    +	 * }
    + * The context is deactivated and cleared after each test and after the + * test class completes. + * + * @deprecated This will be removed in the next major release to avoid + * having JNDI dependencies in a user visible class. Most user will not + * need to test their apps with JNDI. + * @return A JNDI context for setting properties via JNDI. + * @throws NamingException If JNDI cannot be initiated. + */ + public SimpleNamingContextBuilder getJndi() throws NamingException { + if (builder == null) { + builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder(); + } + return builder; + } + + /** + * Stores the AndHow Core (its state) and System Properties prior to a test. + * It also sets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to SEVERE. If JNDI is used for a test, it's startup + * is verbose to System.out, so this turns it off. + */ + public void andHowSnapshotBeforeSingleTest() { + beforeTestSystemProps = AndHowNonProductionUtil.clone(System.getProperties()); + beforeTestCore = AndHowNonProductionUtil.getAndHowCore(); + } + + /** + * Restores the AndHow Core (its state) and System Properties + * that were previously stored prior to a test run. + * It also resets the logging level for SimpleNamingContextBuilder (a JNDI + * related class) to what ever it was prior to the run. + * Any JNDI bindings set via getJndi() are cleared. + */ + public void resetAndHowSnapshotAfterSingleTest() { + System.setProperties(beforeTestSystemProps); + AndHowNonProductionUtil.setAndHowCore(beforeTestCore); + + if (builder != null) { + builder.clear(); + builder.deactivate(); + } + } +} diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit4TestBaseTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit4TestBaseTest.java new file mode 100644 index 00000000..40f771a0 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit4TestBaseTest.java @@ -0,0 +1,66 @@ +package org.yarnandtail.andhow; + +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The implementation is all in the base class, so here tests are minimal + * to make sure the base class is being called. + */ +class AndHowJunit4TestBaseTest { + + static final String BOB = "bob"; + + @Test + void andHowSnapshotBeforeAndAfterTestClass() { + + Properties originalProperties = System.getProperties(); + + try { + + //We shouldn't have this property before we start + assertNull(System.getProperty(BOB)); + + //Take the snapshot + AndHowJunit4TestBase.andHowSnapshotBeforeTestClass(); + + //Sys props - completely mess them up - reset should reset them... + System.setProperties(new Properties()); //zap all properties + System.setProperty(BOB, BOB); + + + //Are the sysProps messed up just like we did above? + assertEquals(BOB, System.getProperty(BOB)); + assertEquals(1, System.getProperties().size()); + + // + //reset + AndHowJunit4TestBase.resetAndHowSnapshotAfterTestClass(); + + + //Verify we have the exact same set of SysProps after the reset + assertEquals(originalProperties.size(), System.getProperties().size()); + assertTrue(originalProperties.entrySet().containsAll(System.getProperties().entrySet())); + + } finally { + System.setProperties(originalProperties); + } + + } + + + @Test + void andHowSnapshotBeforeAndAfterSingleTest() { + + AndHowJunit4TestBase testBase = new AndHowJunit4TestBase(); + AndHowTestBaseImplTest implTest = new AndHowTestBaseImplTest(); + + implTest.doAndHowSnapshotBeforeAndAfterSingleTest(testBase); + + } + + +} \ No newline at end of file diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit5TestBaseTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit5TestBaseTest.java new file mode 100644 index 00000000..87ed17df --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowJunit5TestBaseTest.java @@ -0,0 +1,66 @@ +package org.yarnandtail.andhow; + +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The implementation is all in the base class, so here tests are minimal + * to make sure the base class is being called. + */ +class AndHowJunit5TestBaseTest { + + static final String BOB = "bob"; + + @Test + void andHowSnapshotBeforeAndAfterTestClass() { + + Properties originalProperties = System.getProperties(); + + try { + + //We shouldn't have this property before we start + assertNull(System.getProperty(BOB)); + + //Take the snapshot + AndHowJunit5TestBase.andHowSnapshotBeforeTestClass(); + + //Sys props - completely mess them up - reset should reset them... + System.setProperties(new Properties()); //zap all properties + System.setProperty(BOB, BOB); + + + //Are the sysProps messed up just like we did above? + assertEquals(BOB, System.getProperty(BOB)); + assertEquals(1, System.getProperties().size()); + + // + //reset + AndHowJunit5TestBase.resetAndHowSnapshotAfterTestClass(); + + + //Verify we have the exact same set of SysProps after the reset + assertEquals(originalProperties.size(), System.getProperties().size()); + assertTrue(originalProperties.entrySet().containsAll(System.getProperties().entrySet())); + + } finally { + System.setProperties(originalProperties); + } + + } + + + @Test + void andHowSnapshotBeforeAndAfterSingleTest() { + + AndHowJunit5TestBase testBase = new AndHowJunit5TestBase(); + AndHowTestBaseImplTest implTest = new AndHowTestBaseImplTest(); + + implTest.doAndHowSnapshotBeforeAndAfterSingleTest(testBase); + + } + + +} \ No newline at end of file diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowNonProductionUtilTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowNonProductionUtilTest.java index 4a48bc58..3b2c43ff 100644 --- a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowNonProductionUtilTest.java +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowNonProductionUtilTest.java @@ -3,10 +3,10 @@ package org.yarnandtail.andhow; import java.util.Properties; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.internal.AndHowCore; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * @@ -62,7 +62,7 @@ public void testSetAndHowCore() { AndHowNonProductionUtil.setAndHowCore(ahCore1); //swap cores //The key assertion - assertTrue("Should have inserted a new core", ahCore1 == AndHowNonProductionUtil.getAndHowCore()); + assertTrue(ahCore1 == AndHowNonProductionUtil.getAndHowCore(), "Should have inserted a new core"); } /** @@ -75,12 +75,12 @@ public void testForceRebuild() { AndHowConfiguration config = AndHow.findConfig(); AndHowNonProductionUtil.forceRebuild(config); //Should be OK even when a new build AndHowCore ahCore1 = AndHowNonProductionUtil.getAndHowCore(); - assertNotNull("Util should create a new instance even if no current instance", ahCore1); + assertNotNull(ahCore1, "Util should create a new instance even if no current instance"); AndHowNonProductionUtil.forceRebuild(config); //Now an actual rebuild AndHowCore ahCore2 = AndHowNonProductionUtil.getAndHowCore(); assertNotNull(ahCore2); - assertFalse("The core instances should be different instances", ahCore1 == ahCore2); + assertFalse(ahCore1 == ahCore2, "The core instances should be different instances"); } /** @@ -106,7 +106,7 @@ public void testClone() { @Test public void testDestroyAndHowCore() { - assertNull("AndHow should be null at test start", AndHowNonProductionUtil.getAndHowInstance()); + assertNull(AndHowNonProductionUtil.getAndHowInstance(), "AndHow should be null at test start"); AndHow.instance(); //force creation AndHowNonProductionUtil.destroyAndHowCore(); assertNotNull(AndHowNonProductionUtil.getAndHowInstance()); diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseImplTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseImplTest.java new file mode 100644 index 00000000..421fbb3c --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseImplTest.java @@ -0,0 +1,211 @@ +package org.yarnandtail.andhow; + +import org.junit.jupiter.api.Test; +import org.springframework.mock.jndi.SimpleNamingContextBuilder; +import org.yarnandtail.andhow.internal.AndHowCore; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.*; + +class AndHowTestBaseImplTest { + + static final String BOB_NAME = SimpleConfig.class.getCanonicalName() + ".BOB"; + static final String BOB_VALUE = "BobsYourUncle"; + + @Test + void andHowSnapshotBeforeAndAfterTestClass() throws NamingException { + + AndHowTestBaseImpl testBase = new AndHowTestBaseImpl(); + + AndHowCore beforeTheTestCore = AndHowNonProductionUtil.getAndHowCore(); + Properties originalProperties = System.getProperties(); + Level beforeClassLogLevel = Logger.getGlobal().getLevel(); //store log level before class + + try { + + //We shouldn't have this property before we start + assertNull(System.getProperty(BOB_NAME)); + + //Take the snapshot + AndHowTestBaseImpl.andHowSnapshotBeforeTestClass(); + + //Sys props - completely mess them up - reset should reset them... + System.setProperties(new Properties()); //zap all properties + System.setProperty(BOB_NAME, BOB_VALUE); + + //Now build w/ the new SystemProperties + NonProductionConfig.instance().group(SimpleConfig.class).forceBuild(); + + + //Are the sysProps messed up just like we did above? + assertEquals(BOB_VALUE, System.getProperty(BOB_NAME)); + assertEquals(1, System.getProperties().size()); + + //Did the AndHow Property get set? + assertEquals(BOB_VALUE, SimpleConfig.BOB.getValue()); + + //Change the global log level + if (beforeClassLogLevel == null || !beforeClassLogLevel.equals(Level.FINEST)) { + Logger.getGlobal().setLevel(Level.FINEST); + } else { + Logger.getGlobal().setLevel(Level.FINER); + } + + //Is the AndHowCore different than what it was originally? + assertFalse(beforeTheTestCore == AndHowNonProductionUtil.getAndHowCore()); + + // + //reset + AndHowTestBaseImpl.resetAndHowSnapshotAfterTestClass(); + + + //Verify we have the exact same set of SysProps after the reset + assertEquals(originalProperties.size(), System.getProperties().size()); + assertTrue(originalProperties.entrySet().containsAll(System.getProperties().entrySet())); + + //Verify the core is back to the original + assertTrue(beforeTheTestCore == AndHowNonProductionUtil.getAndHowCore()); + + //Did the log level get reset? + assertEquals(beforeClassLogLevel, Logger.getGlobal().getLevel()); + + } finally { + AndHowNonProductionUtil.setAndHowCore(beforeTheTestCore); + System.setProperties(originalProperties); + } + + } + + @Test + void andHowSnapshotBeforeAndAfterSingleTest() { + AndHowTestBaseImpl testBase = new AndHowTestBaseImpl(); + doAndHowSnapshotBeforeAndAfterSingleTest(testBase); + } + + void doAndHowSnapshotBeforeAndAfterSingleTest(AndHowTestBaseImpl testBase) { + + AndHowCore beforeTheTestCore = AndHowNonProductionUtil.getAndHowCore(); + Properties originalProperties = System.getProperties(); + + try { + + //We shouldn't have this property before we start + assertNull(System.getProperty(BOB_NAME)); + + //Take the snapshot + testBase.andHowSnapshotBeforeSingleTest(); + + //Sys props - completely mess them up - reset should reset them... + System.setProperties(new Properties()); //zap all properties + System.setProperty(BOB_NAME, BOB_VALUE); + + //Now build w/ the new SystemProperties + NonProductionConfig.instance().group(SimpleConfig.class).forceBuild(); + + + //Are the sysProps messed up just like we did above? + assertEquals(BOB_VALUE, System.getProperty(BOB_NAME)); + assertEquals(1, System.getProperties().size()); + + //Did the AndHow Property get set? + assertEquals(BOB_VALUE, SimpleConfig.BOB.getValue()); + + //Is the AndHowCore different than what it was originally? + assertFalse(beforeTheTestCore == AndHowNonProductionUtil.getAndHowCore()); + + // + //reset + testBase.resetAndHowSnapshotAfterSingleTest(); + + + //Verify we have the exact same set of SysProps after the reset + assertEquals(originalProperties.size(), System.getProperties().size()); + assertTrue(originalProperties.entrySet().containsAll(System.getProperties().entrySet())); + + //Verify the core is back to the original + assertTrue(beforeTheTestCore == AndHowNonProductionUtil.getAndHowCore()); + + } finally { + AndHowNonProductionUtil.setAndHowCore(beforeTheTestCore); + System.setProperties(originalProperties); + } + + } + + /** + * Simulating the progression of a test where jndi properties are + * set at the start of a single test. + * @throws NamingException + */ + @Test + public void getJndiTest() throws NamingException { + + AndHowTestBaseImpl testBase = new AndHowTestBaseImpl(); + + + try { + + //Start of test class and before a test + AndHowTestBaseImpl.andHowSnapshotBeforeTestClass(); + testBase.andHowSnapshotBeforeSingleTest(); + + //Now we are setting up for a single test w/ a JNDI property + SimpleNamingContextBuilder jndi = testBase.getJndi(); + jndi.bind("java:" + BOB_NAME, BOB_VALUE + "_JNDI"); + jndi.activate(); + + //Can we read the JNDI property? + final InitialContext ctx = new InitialContext(); // Jndi Context should have set value + assertEquals(BOB_VALUE + "_JNDI", ctx.lookup("java:" + BOB_NAME)); + + // + //Now build AndHow - should see JNDI property + NonProductionConfig.instance().group(SimpleConfig.class).forceBuild(); + + //Did the AndHow Property get set? + assertEquals(BOB_VALUE + "_JNDI", SimpleConfig.BOB.getValue()); + + //End of one test and the start of another + testBase.resetAndHowSnapshotAfterSingleTest(); + testBase.andHowSnapshotBeforeSingleTest(); + + assertThrows(NamingException.class, () -> + ctx.lookup("java:" + BOB_NAME) + ); + + // + //Now build AndHow again - 'BOB' should be empty- should see JNDI property + NonProductionConfig.instance().group(SimpleConfig.class).forceBuild(); + + assertNull(SimpleConfig.BOB.getValue()); + + //End of one test and the start of another + testBase.resetAndHowSnapshotAfterSingleTest(); + testBase.andHowSnapshotBeforeSingleTest(); + + //Activate JNDI again - it should still be empty + testBase.getJndi().activate(); + + //should still be empty on the original context + assertThrows(NamingException.class, () -> + ctx.lookup("java:" + BOB_NAME) + ); + + //... and on a new context + final InitialContext ctx2 = new InitialContext(); + assertThrows(NamingException.class, () -> + ctx2.lookup("java:" + BOB_NAME) + ); + + } finally { + testBase.resetAndHowSnapshotAfterSingleTest(); + AndHowTestBaseImpl.resetAndHowSnapshotAfterTestClass(); + } + } + +} \ No newline at end of file diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseTest.java new file mode 100644 index 00000000..8c96fc49 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestBaseTest.java @@ -0,0 +1,66 @@ +package org.yarnandtail.andhow; + +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The implementation is all in the base class, so here tests are minimal + * to make sure the base class is being called. + */ +class AndHowTestBaseTest { + + static final String BOB = "bob"; + + @Test + void andHowSnapshotBeforeAndAfterTestClass() { + + Properties originalProperties = System.getProperties(); + + try { + + //We shouldn't have this property before we start + assertNull(System.getProperty(BOB)); + + //Take the snapshot + AndHowTestBase.andHowSnapshotBeforeTestClass(); + + //Sys props - completely mess them up - reset should reset them... + System.setProperties(new Properties()); //zap all properties + System.setProperty(BOB, BOB); + + + //Are the sysProps messed up just like we did above? + assertEquals(BOB, System.getProperty(BOB)); + assertEquals(1, System.getProperties().size()); + + // + //reset + AndHowTestBase.resetAndHowSnapshotAfterTestClass(); + + + //Verify we have the exact same set of SysProps after the reset + assertEquals(originalProperties.size(), System.getProperties().size()); + assertTrue(originalProperties.entrySet().containsAll(System.getProperties().entrySet())); + + } finally { + System.setProperties(originalProperties); + } + + } + + + @Test + void andHowSnapshotBeforeAndAfterSingleTest() { + + AndHowTestBase testBase = new AndHowTestBase(); + AndHowTestBaseImplTest implTest = new AndHowTestBaseImplTest(); + + implTest.doAndHowSnapshotBeforeAndAfterSingleTest(testBase); + + } + + +} \ No newline at end of file diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestingTestBase.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestingTestBase.java index 13939fca..e26f2cad 100644 --- a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestingTestBase.java +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/AndHowTestingTestBase.java @@ -1,9 +1,11 @@ package org.yarnandtail.andhow; import java.lang.reflect.Field; -import java.util.Properties; -import javax.naming.NamingException; -import org.junit.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.springframework.mock.jndi.SimpleNamingContextBuilder; /** @@ -34,24 +36,24 @@ public class AndHowTestingTestBase { */ private static SimpleNamingContextBuilder builder; - @BeforeClass + @BeforeAll public static void killAndHowStateBeforeClass() { destroyAndHow(); } - @Before + @BeforeEach public void killAndHowStateBeforeTest() { destroyAndHow(); } - @After + @AfterEach public void killAndHowStateAfterTest() { destroyAndHow(); } - @AfterClass + @AfterAll public static void killAndHowStateAfterClass() { destroyAndHow(); } diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/NonProductionConfigTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/NonProductionConfigTest.java index 6e027b77..05568d2e 100644 --- a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/NonProductionConfigTest.java +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/NonProductionConfigTest.java @@ -5,17 +5,16 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.NonProductionConfig.NonProductionConfigImpl; import org.yarnandtail.andhow.api.GroupProxy; import org.yarnandtail.andhow.api.Loader; -import org.yarnandtail.andhow.internal.AndHowCore; import org.yarnandtail.andhow.load.*; import org.yarnandtail.andhow.load.std.StdFixedValueLoader; import org.yarnandtail.andhow.load.std.StdMainStringArgsLoader; import org.yarnandtail.andhow.property.StrProp; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.yarnandtail.andhow.AndHowNonProductionUtil.PERMISSION_MSG; /** @@ -57,11 +56,13 @@ public void testAddCmdLineArg() { assertEquals("NULL", kvps.get(2)); } - @Test(expected = RuntimeException.class) + @Test public void testAddCmdLineArgWithNull() { NonProductionConfigImpl config = NonProductionConfig.instance(); - config.addCmdLineArg(null, "one"); - + + assertThrows(RuntimeException.class, () -> { + config.addCmdLineArg(null, "one"); + }); } @Test diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/SimpleConfig.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/SimpleConfig.java new file mode 100644 index 00000000..54c29572 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/SimpleConfig.java @@ -0,0 +1,8 @@ +package org.yarnandtail.andhow; + +import org.yarnandtail.andhow.property.StrProp; + +public interface SimpleConfig { + StrProp BOB = StrProp.builder().build(); + +} diff --git a/pom.xml b/pom.xml index a2624dbe..22f6a214 100644 --- a/pom.xml +++ b/pom.xml @@ -98,12 +98,50 @@ + + + + org.junit.platform + junit-platform-launcher + 1.7.2 + test + + + org.junit.jupiter + junit-jupiter-api + 5.7.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.7.2 + test + + + org.hamcrest + hamcrest + 2.2 + test + + + junit junit 4.13.1 test + + org.junit.vintage + junit-vintage-engine + 5.7.2 + test + + org.springframework spring-test @@ -117,10 +155,17 @@ test + com.google.testing.compile compile-testing 0.15 test + + + junit + junit + + org.mockito @@ -130,6 +175,18 @@ + + + + + org.junit.platform + junit-platform-launcher + + + org.junit.jupiter + junit-jupiter-engine + + @@ -193,6 +250,11 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + org.codehaus.mojo cobertura-maven-plugin From 84143c13d066b9345c45d253485ba3c1270a9c0e Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Thu, 17 Jun 2021 22:47:34 -0500 Subject: [PATCH 87/91] Update README.md update to new travis-ci.com badge location --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c43de79..ec19ce54 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/eeverman/andhow.svg?branch=master)](https://travis-ci.org/eeverman/andhow) +[![Build Status](https://travis-ci.com/eeverman/andhow.svg?branch=master)](https://travis-ci.com/github/eeverman/andhow) [![codecov](https://codecov.io/gh/eeverman/andhow/branch/master/graph/badge.svg)](https://codecov.io/gh/eeverman/andhow) [![Javadocs](https://www.javadoc.io/badge/org.yarnandtail/andhow.svg)](https://www.javadoc.io/doc/org.yarnandtail/andhow) From b33fbb51a9b3beaab0bcdc5d22985bf96e8609d1 Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Fri, 18 Jun 2021 14:21:27 -0600 Subject: [PATCH 88/91] Exclude annotation-processor-test-harness from the codecov report (#504) * Moved the AndHowCompileProcessorTestBase to the test harness module * cleaned up junit related dependency declaration in pom files Co-authored-by: Eric Everman --- andhow-annotation-processor/pom.xml | 4 --- .../AndHowCompileProcessor_PropertyTest.java | 1 + andhow-core/pom.xml | 12 --------- .../pom.xml | 10 ++------ .../AndHowCompileProcessorTestBase.java | 25 ++++++++----------- .../andhow-annotation-processor-tests/pom.xml | 14 ----------- .../andhow-multimodule-dataprocess/pom.xml | 13 ---------- andhow-testing/andhow-system-tests/pom.xml | 9 ------- codecov.yml | 4 ++- pom.xml | 13 +++++++++- 10 files changed, 29 insertions(+), 76 deletions(-) rename {andhow-annotation-processor/src/test/java/org/yarnandtail/andhow => andhow-testing/andhow-annotation-processor-test-harness/src/main/java/org/yarnandtail}/compile/AndHowCompileProcessorTestBase.java (77%) diff --git a/andhow-annotation-processor/pom.xml b/andhow-annotation-processor/pom.xml index d5d1bda4..f472f676 100644 --- a/andhow-annotation-processor/pom.xml +++ b/andhow-annotation-processor/pom.xml @@ -42,10 +42,6 @@ com.google.testing.compile compile-testing
    - - org.mockito - mockito-all - diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java index ff1cd61d..79f488d9 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java +++ b/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessor_PropertyTest.java @@ -10,6 +10,7 @@ import org.yarnandtail.andhow.compile.CompileProblem.PropMissingStaticFinal; import org.yarnandtail.andhow.service.*; import org.yarnandtail.andhow.util.IOUtil; +import org.yarnandtail.compile.AndHowCompileProcessorTestBase; /** * A lot of this code was borrowed from here: diff --git a/andhow-core/pom.xml b/andhow-core/pom.xml index 3c82329e..7dd5188b 100644 --- a/andhow-core/pom.xml +++ b/andhow-core/pom.xml @@ -15,14 +15,6 @@ - - org.junit.jupiter - junit-jupiter-api - - - org.hamcrest - hamcrest - org.springframework spring-test @@ -30,10 +22,6 @@ commons-io commons-io - - - org.mockito - mockito-all diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index afdb1111..be5d7112 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -24,14 +24,8 @@ org.junit.jupiter junit-jupiter-api - 5.7.2 - test - - - org.hamcrest - hamcrest - 2.2 - test + compile + true diff --git a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java b/andhow-testing/andhow-annotation-processor-test-harness/src/main/java/org/yarnandtail/compile/AndHowCompileProcessorTestBase.java similarity index 77% rename from andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java rename to andhow-testing/andhow-annotation-processor-test-harness/src/main/java/org/yarnandtail/compile/AndHowCompileProcessorTestBase.java index a006b99b..5c1bd698 100644 --- a/andhow-annotation-processor/src/test/java/org/yarnandtail/andhow/compile/AndHowCompileProcessorTestBase.java +++ b/andhow-testing/andhow-annotation-processor-test-harness/src/main/java/org/yarnandtail/compile/AndHowCompileProcessorTestBase.java @@ -1,4 +1,4 @@ -package org.yarnandtail.andhow.compile; +package org.yarnandtail.compile; import java.util.HashSet; import java.util.Set; @@ -8,9 +8,6 @@ import javax.tools.ToolProvider; import org.junit.jupiter.api.BeforeEach; import org.yarnandtail.andhow.util.IOUtil; -import org.yarnandtail.compile.MemoryFileManager; -import org.yarnandtail.compile.TestClassLoader; -import org.yarnandtail.compile.TestSource; /** * @@ -19,17 +16,17 @@ public class AndHowCompileProcessorTestBase { /** Classpath of the generated service file for AndHow property registration */ - static final String REGISTRAR_SVS_PATH = + protected static final String REGISTRAR_SVS_PATH = "/META-INF/services/org.yarnandtail.andhow.service.PropertyRegistrar"; - static final String INIT_SVS_PATH = "/META-INF/services/org.yarnandtail.andhow.AndHowInit"; - static final String TEST_INIT_SVS_PATH = "/META-INF/services/org.yarnandtail.andhow.AndHowTestInit"; - - JavaCompiler compiler; - MemoryFileManager manager; - DiagnosticCollector diagnostics; - TestClassLoader loader; - - Set sources; //New set of source files to compile + protected static final String INIT_SVS_PATH = "/META-INF/services/org.yarnandtail.andhow.AndHowInit"; + protected static final String TEST_INIT_SVS_PATH = "/META-INF/services/org.yarnandtail.andhow.AndHowTestInit"; + + protected JavaCompiler compiler; + protected MemoryFileManager manager; + protected DiagnosticCollector diagnostics; + protected TestClassLoader loader; + + protected Set sources; //New set of source files to compile @BeforeEach public void setupTest() { diff --git a/andhow-testing/andhow-annotation-processor-tests/pom.xml b/andhow-testing/andhow-annotation-processor-tests/pom.xml index 0d7556e0..94e90608 100644 --- a/andhow-testing/andhow-annotation-processor-tests/pom.xml +++ b/andhow-testing/andhow-annotation-processor-tests/pom.xml @@ -34,20 +34,6 @@ ${project.version} provided - - - - org.junit.jupiter - junit-jupiter-api - 5.7.2 - test - - - org.hamcrest - hamcrest - 2.2 - test - diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml index 8cb36594..a903aeb0 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/pom.xml @@ -28,18 +28,5 @@ ${project.version} test - - - org.junit.platform - junit-platform-launcher - - - org.junit.jupiter - junit-jupiter-api - - - org.hamcrest - hamcrest - \ No newline at end of file diff --git a/andhow-testing/andhow-system-tests/pom.xml b/andhow-testing/andhow-system-tests/pom.xml index 65a185c1..f4740f2a 100644 --- a/andhow-testing/andhow-system-tests/pom.xml +++ b/andhow-testing/andhow-system-tests/pom.xml @@ -28,15 +28,6 @@ ${project.version} test - - - org.junit.jupiter - junit-jupiter-api - - - org.hamcrest - hamcrest - commons-io commons-io diff --git a/codecov.yml b/codecov.yml index 53abf197..e80bbe1b 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,6 +1,8 @@ ## YAML Template. --- coverage: - range: 0..100 + range: 50..100 round: down precision: 2 +ignore: + - "andhow-testing/andhow-annotation-processor-test-harness" diff --git a/pom.xml b/pom.xml index 22f6a214..9c612087 100644 --- a/pom.xml +++ b/pom.xml @@ -141,7 +141,6 @@ 5.7.2 test - org.springframework spring-test @@ -186,6 +185,18 @@ org.junit.jupiter junit-jupiter-engine + + org.junit.jupiter + junit-jupiter-api + + + org.hamcrest + hamcrest + + + org.mockito + mockito-all + From 043176125bf0c85e1e5f73bb7aa1e835fe9043ba Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Fri, 9 Jul 2021 15:27:11 -0600 Subject: [PATCH 89/91] WIP: Issue 282 std config should accept fix value names as strings (#507) * Partial work * Completed code, though not tested * Added some testing * Just a little bit more testing * More testing... * More testing * More testing * Added a base class for testing loaders Tests for FixedValueLoader Added better docs and tests for the LngProp and DblProp * More testing for FixedValueLoader * Lots more tests around fixed values * Also moved tests that relate to the StdConfig into a StdConfigXXX named test * Better notes and examples in the multimodule-dataprocess test/example Co-authored-by: Eric Everman --- .../andhow/AndHowConfiguration.java | 43 ++- .../org/yarnandtail/andhow/BaseConfig.java | 9 +- .../org/yarnandtail/andhow/StdConfig.java | 64 +++- .../api/ValidatedValuesWithContext.java | 12 +- .../yarnandtail/andhow/load/BaseLoader.java | 159 ++++++---- .../andhow/load/FixedValueLoader.java | 83 +++-- .../andhow/load/KeyObjectPair.java | 75 +++++ .../andhow/load/std/StdFixedValueLoader.java | 9 +- .../yarnandtail/andhow/property/DblProp.java | 15 + .../yarnandtail/andhow/property/LngProp.java | 8 + .../andhow/StdConfigGetterAndSetterTest.java | 296 ++++++++++++++++++ .../andhow/StdConfigSimulatedAppTest.java | 267 ++++++++++++++++ .../org/yarnandtail/andhow/StdConfigTest.java | 126 -------- .../restclient/SampleRestClientAppTest.java | 138 -------- .../restclient/SampleRestClientGroup.java | 22 -- .../andhow/load/BaseForLoaderTests.java | 60 ++++ .../andhow/load/FixedValueLoaderTest.java | 161 ++++++++++ .../andhow/load/KeyObjectPairTest.java | 56 ++++ .../andhow/load/KeyValuePairLoaderTest.java | 38 +-- .../andhow/valuetype/DblTypeTest.java | 8 + .../andhow/valuetype/LngTypeTest.java | 8 + ...mulatedAppTest.all.props.speced.properties | 8 + ...dConfigSimulatedAppTest.invalid.properties | 7 + ...tedAppTest.minimum.props.speced.properties | 11 + .../restclient/all.points.speced.properties | 8 - .../example/restclient/invalid.properties | 7 - .../minimum.points.speced.properties | 11 - .../test/java/com/dep1/EarthMapMakerTest.java | 121 ++++--- .../test/java/com/dep2/MarsMapMakerTest.java | 108 +------ .../ExternalServiceConnectorTest.java | 5 +- 30 files changed, 1329 insertions(+), 614 deletions(-) create mode 100644 andhow-core/src/main/java/org/yarnandtail/andhow/load/KeyObjectPair.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigSimulatedAppTest.java delete mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java delete mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java delete mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientGroup.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/load/BaseForLoaderTests.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/load/FixedValueLoaderTest.java create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyObjectPairTest.java create mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties create mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.invalid.properties create mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.minimum.props.speced.properties delete mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/all.points.speced.properties delete mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/invalid.properties delete mode 100644 andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/minimum.points.speced.properties diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java index c8dfa6dc..4ae1ff87 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java @@ -30,6 +30,10 @@ public interface AndHowConfiguration { * prior to any other loader. Since the first loaded value for a Property * 'wins', this effectively fixes the value and makes it non-configurable. * + * Values specified by the two addFixedValue methods will + * through a DuplicatePropertyLoaderProblem if they refer to + * the same Property. + * * @param The type of Property and value * @param property The property to set a value for * @param value The value to set. @@ -38,15 +42,48 @@ public interface AndHowConfiguration { C addFixedValue(Property property, T value); /** - * Removes a PropertyValue from the list of fixed values. + * Removes a Property value set only via addFixedValue(Property, T value) + * from the list of fixed values. * - * It is not an error to attempt to remove a property that is not in the - * current fixed value list. + * It is not an error to attempt to remove a property that is not in this fixed value list. * * @param property A non-null property. * @return */ C removeFixedValue(Property property); + + /** + * Sets a fixed, non-configurable value for a named Property. + * + * Property values set in this way use the FixedValueLoader to load values + * prior to any other loader. Since the first loaded value for a Property + * 'wins', this effectively fixes the value and makes it non-configurable. + * + * Values specified by the two addFixedValue methods will + * through a DuplicatePropertyLoaderProblem if they refer to + * the same Property. + * + * @param name The canonical or alias name of Property, which is trimmed to null. + * @param value The Object value to set, which must match the type of the Property. + * @return + */ + C addFixedValue(String name, Object value); + + /** + * Removes a Property value set only via addFixedValue(String name, Object value) + * from the list of fixed values. + * + * Note that to successfully remove a fixed value from this list, the name must exactly + * match the name used to set the property via addFixedValue(String, Object). Since + * Properties can have aliases, you must know the exact name to set the property. + * + * It is not an error to attempt to remove a property that is not in this fixed value list, + * or to attempt to remove a property value that does not exist - these are just no-ops. + * + * @param propertyNameOrAlias The name or alias of a property. + * @return + */ + C removeFixedValue(String propertyNameOrAlias); void build(); } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java b/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java index 227fbf81..e00681e4 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java @@ -3,6 +3,7 @@ import java.lang.reflect.*; import java.util.*; import org.yarnandtail.andhow.api.*; +import org.yarnandtail.andhow.load.KeyObjectPair; import org.yarnandtail.andhow.load.std.*; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; import org.yarnandtail.andhow.property.StrProp; @@ -32,9 +33,14 @@ public abstract class BaseConfig> implements AndHowConfi protected Map, List> insertBefore = new HashMap(); protected Map, List> insertAfter = new HashMap(); - //A list of hardcoded values used by the StdFixedValueLoader + //A list of hardcoded values used by the StdFixedValueLoader. + //Provided w/ live Property references protected final List _fixedVals = new ArrayList(); + //A list of hardcoded values used by the StdFixedValueLoader. + //Provided as key name (string) and value (object) + protected final List _fixedKeyObjectPairVals = new ArrayList(); + //A list of command line arguments protected final List _cmdLineArgs = new ArrayList(); @@ -71,6 +77,7 @@ public NamingStrategy getNamingStrategy() { protected StdFixedValueLoader buildStdFixedValueLoader() { StdFixedValueLoader loader = new StdFixedValueLoader(); loader.setPropertyValues(_fixedVals); + loader.setKeyObjectPairValues(_fixedKeyObjectPairVals); return loader; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java b/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java index b5c4a10c..e2ae1d3a 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java @@ -2,6 +2,7 @@ import java.util.*; import org.yarnandtail.andhow.api.*; +import org.yarnandtail.andhow.load.KeyObjectPair; import org.yarnandtail.andhow.property.StrProp; import org.yarnandtail.andhow.util.TextUtil; @@ -34,6 +35,7 @@ public S addFixedValue(Property property, T value) { throw new IllegalArgumentException("The property cannot be null"); } + //simple check for duplicates doesn't consider KOP values for (PropertyValue pv : _fixedVals) { if (property.equals(pv.getProperty())) { throw new IllegalArgumentException("A fixed value for this property has been assigned twice."); @@ -48,20 +50,36 @@ public S addFixedValue(Property property, T value) { @Override public S removeFixedValue(Property property) { + _fixedVals.removeIf(f -> f.getProperty().equals(property)); + return (S) this; + } - if (property == null) { - throw new IllegalArgumentException("The property cannot be null"); - } + @Override + public S addFixedValue(final String propertyNameOrAlias, final Object value) { - Iterator it = _fixedVals.iterator(); - while (it.hasNext()) { - PropertyValue pv = it.next(); - if (property.equals(pv.getProperty())) { - it.remove(); - break; + try { + KeyObjectPair kop = new KeyObjectPair(propertyNameOrAlias, value); + + //Simple check for duplicates + if (_fixedKeyObjectPairVals.stream().map(k -> k.getName()).anyMatch(n -> n.equals(kop.getName()))) { + throw new IllegalArgumentException( + "A fixed value for the Property '" + kop.getName() + "' has been assigned twice."); } + + _fixedKeyObjectPairVals.add(kop); + + return (S) this; + + } catch (ParsingException e) { + throw new IllegalArgumentException(e); } + } + + @Override + public S removeFixedValue(final String propertyNameOrAlias) { + final String cleanName = TextUtil.trimToNull(propertyNameOrAlias); + _fixedKeyObjectPairVals.removeIf(k -> k.getName().equals(cleanName)); return (S) this; } @@ -220,13 +238,33 @@ public S setSystemProperties(Properties properties) { } /** - * Allows the System environment to be overridden. + * Sets the System environment vars that AndHow will use to load Property values + * from for the {@Code StdEnvVarLoader} loader. + * + * If this method is not called or is called with a null Map, the actual env vars + * from {@Code System.getenv()} will be used. Calling this method with an empty + * Map will effectively prevent AndHow from receiving configuration from env vars. * - * @param envProperties + * This does not actually change actual environment variables or what is + * returned from {@Code System.getenv()}. It only replaces what AndHow will see for env vars. + * + * Note: There is no reason to use this method: Use one of the {@Code addFixedValue()} + * methods instead. Those methods are more clear, don't have to parse values, and + * (unlike this method) are not deprecated. + * + * @deprecated This method will be removed in a future release - it has no meaningful + * usage. Use the addFixedValue() methods instead. + * @param newEnvProperties * @return */ - public S setEnvironmentProperties(Map envProperties) { - this.envProperties = envProperties; + public S setEnvironmentProperties(Map newEnvProperties) { + + if (newEnvProperties != null) { + this.envProperties = new HashMap<>(); + this.envProperties.putAll(newEnvProperties); + } else { + this.envProperties = null; + } return (S) this; } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/api/ValidatedValuesWithContext.java b/andhow-core/src/main/java/org/yarnandtail/andhow/api/ValidatedValuesWithContext.java index a51b6afc..855e80bc 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/api/ValidatedValuesWithContext.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/api/ValidatedValuesWithContext.java @@ -3,18 +3,18 @@ import java.util.List; /** - * Extention of ValueMap that adds contextual information to the ValueMap. + * Extention of ValidatedValues that adds contextual information. * - * ValueMap has all the needed info to provide values for Proerties during runtime. + * ValidatedValues has all the needed info to provide values for Properties during runtime. * This class provides more metadata, such as where a value was loaded from, if * there are Problems encountered during value loading and which values were loaded * by which Loader, etc.. * - * During startup, a mutable version of ValueMapWithContext if incrementally loaded + * During startup, a mutable version of ValueMapWithContext is incrementally loaded * with values and reported issues. After loading is complete, values are copied - * to immutable versions of ValueMap and ValueMapWithContext. ValueMap is used - * to fetch values as needed, ValueMapWithContext provides metadata on values if - * needed. + * to an immutable ValidatedValues and ValidatedValuesWithContext. + * ValidatedValues is used to fetch values as needed, ValidatedValuesWithContext + * provides metadata on values if needed. * * @author eeverman */ diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/load/BaseLoader.java b/andhow-core/src/main/java/org/yarnandtail/andhow/load/BaseLoader.java index 4cf3f92f..e27ed15f 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/load/BaseLoader.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/load/BaseLoader.java @@ -31,7 +31,7 @@ public List getInstanceConfig() { public SamplePrinter getConfigSamplePrinter() { return null; //Each implementation needs to provide its own. } - + /** * Util method to load a String to a property by name. * @@ -45,47 +45,62 @@ public SamplePrinter getConfigSamplePrinter() { */ protected void attemptToAdd(StaticPropertyConfigurationInternal appConfigDef, List values, ProblemList loaderProblems, String key, String strValue) { - - key = TextUtil.trimToNull(key); - - if (key != null) { - - String effKey = appConfigDef.getNamingStrategy().toEffectiveName(key); - - Property prop = appConfigDef.getProperty(effKey); - if (prop != null) { - - ValidatedValue pv = null; - - try { - pv = createValue(appConfigDef, prop, strValue); - } catch (ParsingException e) { - loaderProblems.add(new LoaderProblem.StringConversionLoaderProblem( - this, appConfigDef.getGroupForProperty(prop).getProxiedGroup(), prop, e.getProblemText())); - } - - if (pv != null) { - ValidatedValue dup = findDuplicateProperty(pv, values); - - if (dup == null) { - values.add(pv); - } else { - loaderProblems.add(new DuplicatePropertyLoaderProblem( - this, appConfigDef.getGroupForProperty(prop).getProxiedGroup(), prop)); - } - } - - } else if (this instanceof ReadLoader) { - ReadLoader rl = (ReadLoader)this; - if (rl.isUnknownPropertyAProblem()) { - loaderProblems.add(new UnknownPropertyLoaderProblem(this, key)); - } + Property prop = mapNametoProperty(appConfigDef, key); + + if (prop != null) { + + ValidatedValue validatedValue = null; + + try { + validatedValue = createValue(appConfigDef, prop, strValue); + } catch (ParsingException e) { + loaderProblems.add(new LoaderProblem.StringConversionLoaderProblem( + this, appConfigDef.getGroupForProperty(prop).getProxiedGroup(), prop, e.getProblemText())); } + attemptToAddIfNotDuplicate(appConfigDef, values, loaderProblems, validatedValue); + + } else if (this instanceof ReadLoader) { + ReadLoader rl = (ReadLoader)this; + if (rl.isUnknownPropertyAProblem()) { + loaderProblems.add(new UnknownPropertyLoaderProblem(this, key)); + } } + + } + + /** + * Util method to load an Object value to a named Property. + * + * Intended for code-based loaders where Property names are specified with Object based keys. + * This would happen with hardcoded / in-code loaders, likely during testing, where property + * values can be specified as real objects, but the Properties themselves may not all be + * visible, so names are used instead of Property references. + * + * @param appConfigDef Used to look up the property name for find the actual property + * @param values List of PropertyValues to add to, which should be only the value of this loader. + * @param loaderProblems A list of Problems to add to if there is a loader related problem + * @param key The property name + * @param value The property value as an Object, already of the expected type for the Property. + */ + protected void attemptToAdd(StaticPropertyConfigurationInternal appConfigDef, List values, + ProblemList loaderProblems, String key, Object value) { + + Property prop = mapNametoProperty(appConfigDef, key); + + if (prop != null) { + + attemptToAdd(appConfigDef, values, loaderProblems, prop, value); + + } else if (this instanceof ReadLoader) { + ReadLoader rl = (ReadLoader)this; + if (rl.isUnknownPropertyAProblem()) { + loaderProblems.add(new UnknownPropertyLoaderProblem(this, key)); + } + } + } - /** * Util method to attempt to load an object of an unknown type to a property. @@ -98,23 +113,24 @@ protected void attemptToAdd(StaticPropertyConfigurationInternal appConfigDef, Li * @param values List of PropertyValues to add to, which should be only the value of this loader. * @param loaderProblems A list of LoaderProblems to add to if there is a loader related problem * @param prop The Property to load to - * @param value The Object to be loaded to this property + * @param value The Object to be loaded to this property. If a String and that does + * not match the Property type, parsing is attempted to convert it. */ protected void attemptToAdd(StaticPropertyConfigurationInternal appConfigDef, List values, ProblemList loaderProblems, Property prop, Object value) { if (prop != null) { - ValidatedValue pv = null; + ValidatedValue validatedValue = null; if (value.getClass().equals(prop.getValueType().getDestinationType())) { - pv = new ValidatedValue(prop, value); + validatedValue = new ValidatedValue(prop, value); } else if (value instanceof String) { try { - pv = createValue(appConfigDef, prop, value.toString()); + validatedValue = createValue(appConfigDef, prop, value.toString()); } catch (ParsingException e) { loaderProblems.add(new LoaderProblem.StringConversionLoaderProblem( this, appConfigDef.getGroupForProperty(prop).getProxiedGroup(), prop, e.getProblemText())); @@ -124,21 +140,38 @@ protected void attemptToAdd(StaticPropertyConfigurationInternal appConfigDef, Li loaderProblems.add( new ObjectConversionValueProblem(this, appConfigDef.getGroupForProperty(prop).getProxiedGroup(), prop, value)); } - - if (pv != null) { - - ValidatedValue dup = findDuplicateProperty(pv, values); - - if (dup == null) { - values.add(pv); - } else { - loaderProblems.add(new DuplicatePropertyLoaderProblem( - this, appConfigDef.getGroupForProperty(prop).getProxiedGroup(), prop)); - } - } + + attemptToAddIfNotDuplicate(appConfigDef, values, loaderProblems, validatedValue); } } + + /** + * Adds the ValidatedValue to the VV list if it is not a duplicate. + * This is the actual 'load' moment of the Loader: Adding it to this list means + * that the value has been loaded. + * + * Loader subclasses should use the other attemptToAdd methods - this one is invoked by those. + * + * @param appConfigDef Used to look up the property name for find the actual property + * @param values List of PropertyValues to add to, which should be only the value of this loader. + * @param loaderProblems A list of Problems to add to if there is a loader related problem + * @param validatedValue The validated value to load, which may be null which is a no-op. + */ + protected void attemptToAddIfNotDuplicate(StaticPropertyConfigurationInternal appConfigDef, List values, + ProblemList loaderProblems, ValidatedValue validatedValue) { + + if (validatedValue != null) { + ValidatedValue dup = findDuplicateProperty(validatedValue, values); + + if (dup == null) { + values.add(validatedValue); + } else { + loaderProblems.add(new DuplicatePropertyLoaderProblem( + this, appConfigDef.getGroupForProperty(validatedValue.getProperty()).getProxiedGroup(), validatedValue.getProperty())); + } + } + } protected ValidatedValue findDuplicateProperty(ValidatedValue current, List values) { for (ValidatedValue ref : values) { @@ -173,6 +206,26 @@ protected ValidatedValue createValue(StaticPropertyConfigurationInternal app return new ValidatedValue(prop, value); } + + /** + * Maps the passed Property name or alias to a Property or null if it cannot be found. + * @param appConfigDef The AppConfig + * @param name The name, which will be trimmed and converted to an effective name. + * @return The Property or null if it cannot be found. + */ + protected Property mapNametoProperty(StaticPropertyConfigurationInternal appConfigDef, String name) { + + name = TextUtil.trimToNull(name); + + if (name != null) { + + Property prop = appConfigDef.getProperty(name); + return prop; + + } else { + return null; + } + } @Override public void releaseResources() { diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/load/FixedValueLoader.java b/andhow-core/src/main/java/org/yarnandtail/andhow/load/FixedValueLoader.java index 6f940089..d9162bc5 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/load/FixedValueLoader.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/load/FixedValueLoader.java @@ -7,12 +7,14 @@ /** * A utility loader that is used internally to put fixed values into the effective - * list of values. + * list of configured values. * * This loader does not trim incoming values for String type properties - they are * assumed to already be in final form. * This loader considers it a problem to be passed unrecognized properties - * and will throw a RuntimeException if that happens. + * and will throw a RuntimeException if that happens, though this can be set + * false to allow fixed values set via name to be ignored if the name is not + * recognized. * * @author eeverman */ @@ -20,39 +22,75 @@ public class FixedValueLoader extends BaseLoader implements ReadLoader { protected boolean unknownPropertyAProblem = true; - protected List values = new ArrayList(); + protected final List values = new ArrayList(); + + protected final List keyObjectPairValues = new ArrayList(); public FixedValueLoader() { } - + + /** + * Set property values as PropertyValues, which require live + * references to each Property. + * Values set in this way are additive to properties set via + * setKopValues and duplicate properties between the + * two will nominally be considered duplicates. + * + * @param values + */ public void setPropertyValues(List values) { if (values != null) { this.values.addAll(values); } } - + + /** + * @deprecated This method duplicates functionality and will + * be removed. The logic of this method is slightly wrong as + * well (compared to the List version): As this method is currently + * implemente, an empty array will NOT erase all values. The + * Other List based methods would erase all values in that case. + * @param values + */ public void setPropertyValues(PropertyValue... values) { if (values != null && values.length > 0) { this.values.addAll(Arrays.asList(values)); } } - + + /** + * Set property values as KeyObjectPair's. + * Values set in this way are additive to properties set via + * setPropertyValues and duplicate properties between the + * two will nominally be considered duplicates. + * + * @param values + */ + public void setKeyObjectPairValues(List values) { + if (values != null) { + keyObjectPairValues.addAll(values); + } + } + @Override public LoaderValues load(StaticPropertyConfigurationInternal appConfigDef, ValidatedValuesWithContext existingValues) { - - if (values != null && values.size() > 0) { - List vvs = new ArrayList(values.size()); - - for (int i = 0; i < values.size(); i++) { - ValidatedValue vv = new ValidatedValue(values.get(i).getProperty(), values.get(i).getValue()); - vvs.add(vv); - } - - return new LoaderValues(this, vvs, ProblemList.EMPTY_PROBLEM_LIST); - - } else { - return new LoaderValues(this, Collections.emptyList(), ProblemList.EMPTY_PROBLEM_LIST); - } + + List vvs = new ArrayList(values.size()); + ProblemList problems = new ProblemList(); + + //Add all the PropertyValue's. The Property and value references are 'live', + //so lots of potential errors are not possible, however, the value type may + //not match the Property, so use 'attemptToAdd' to verify. + values.stream().forEach( + v -> this.attemptToAdd(appConfigDef, vvs, problems, v.getProperty(), v.getValue()) + ); + + //Add all the KeyObjectPairs + keyObjectPairValues.stream().forEach( + kop -> this.attemptToAdd(appConfigDef, vvs, problems, kop.getName(), kop.getValue()) + ); + + return new LoaderValues(this, vvs, problems); } @@ -63,7 +101,7 @@ public boolean isTrimmingRequiredForStringValues() { @Override public String getSpecificLoadDescription() { - return "a list of fixed values passed in by the construction code (not dynamically loaded)"; + return "a list of fixed values passed in during startup (not dynamically loaded)"; } @Override @@ -88,7 +126,8 @@ public boolean isUnknownPropertyAProblem() { @Override public void releaseResources() { - values = null; + values.clear(); + keyObjectPairValues.clear(); } } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/load/KeyObjectPair.java b/andhow-core/src/main/java/org/yarnandtail/andhow/load/KeyObjectPair.java new file mode 100644 index 00000000..815c9a76 --- /dev/null +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/load/KeyObjectPair.java @@ -0,0 +1,75 @@ +package org.yarnandtail.andhow.load; + +import org.yarnandtail.andhow.api.ParsingException; +import org.yarnandtail.andhow.util.TextUtil; + +/** + * Key-Object Pair + * Contains a String name and an Object value. + * Names are always trimmed (leading and trailing spaces removed) because they are + * expected to match up to AndHow Property canonical names or alias, which don't includes spaces. + * After trimming, names must be non-empty and non-null. + */ +public class KeyObjectPair { + + private String name; + private Object value; + + /** + * Construct an instance with a null value. + * @param name A name that will be trimmed to remove all leading and trailing spaces. + * @throws ParsingException If the name is empty after trimming. + */ + public KeyObjectPair(final String name) throws ParsingException { + this(name, null); + } + + /** + * New instance with name and value. + * @param name A name that will be trimmed to remove all leading and trailing spaces. + * @param value The value, which will not be trimmed or modified. + * @throws ParsingException If the name is empty after trimming. + */ + public KeyObjectPair(final String name, final Object value) throws ParsingException { + String cleanName = TextUtil.trimToNull(name); + + if (cleanName == null) { + throw new ParsingException("The key (parameter name) cannot be empty or null", name); + } + + this.name = cleanName; + this.value = value; + } + + /** + * The KeyObjectPair name, which has been trimmed to remove leading and trailing spaces. + * @return + */ + public String getName() { + return name; + } + + /** + * The value exactly as passed in the constructor, which may be null. + * @return The value as set. + */ + public Object getValue() { + return value; + } + + //Hashcode and Equals + //Normally a class like this would override these methods, but AndHow never needs to compare + //equality of this class, or if it does, is only concerned with the name and equality based + //only on name seems 'broken'. + + /** + * String representation: name : "value" + * + * If the value is null, the string [null] is used. + * @return A formatted string version of the instance. + */ + @Override + public String toString() { + return name + " : " + "\"" + (value!=null?value.toString():"[null]") + "\""; + } +} diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdFixedValueLoader.java b/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdFixedValueLoader.java index 6c94ceef..85111360 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdFixedValueLoader.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdFixedValueLoader.java @@ -1,6 +1,6 @@ package org.yarnandtail.andhow.load.std; -import org.yarnandtail.andhow.api.StandardLoader; +import org.yarnandtail.andhow.api.*; import org.yarnandtail.andhow.load.FixedValueLoader; /** @@ -27,7 +27,7 @@ *

    Basic Behaviors

    *
      *
    • Pre-trims String values: No (Individual Properties may still trim values) - *
    • Complains about unrecognized properties: NA - This is not possible + *
    • Complains about unrecognized properties: Yes - Only applies to Properties specified by name *
    • Default behavior: None - This loader is only active if values are directly set as shown below *
    *

    Loader Details and Configuration

    @@ -43,7 +43,10 @@ * * {@literal @}Override public AndHowConfiguration getConfiguration() { * return StdConfig.instance() - * .addFixedValue(MY_PROP, "some value"); //MY_PROP is some visible property + * .addFixedValue(MY_PROP, 23L); //MY_PROP is some visible property of type Long. + * .addFixedValue("A_PROPERTY_NAME", "abc") //A name or alias of a Property works as well + * //In both cases, the value (23L or "abc") must be of the same type as the Property or + * //an error will be thrown. * } * } * diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/DblProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/DblProp.java index f42a210d..f786cbad 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/property/DblProp.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/DblProp.java @@ -13,6 +13,21 @@ * * By default this uses the TrimToNullTrimmer, which removes all whitespace from * the value and ultimately null if the value is all whitespace. + * + * If a DblProp is configured as a string, such as from a properties file, on + * command line, environment variable, etc., the value MAY include a trailing + * 'D' of 'F' (lower case is ok too), as is done with Java literals. This is + * different than the behavior of LngProp, but is a result of how Java parses + * these values. + * + * E.g., here are several correct ways to spec double value in a properties file: + * + * name.of.my.double.property.MY_PROPERTY_1 = 90.00 + * name.of.my.double.property.MY_PROPERTY_2 = 80.00D + * name.of.my.double.property.MY_PROPERTY_3 = 70.00F + * name.of.my.double.property.MY_PROPERTY_4 = 60.00d + * name.of.my.double.property.MY_PROPERTY_5 = 4 + * * * @author eeverman */ diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/property/LngProp.java b/andhow-core/src/main/java/org/yarnandtail/andhow/property/LngProp.java index 089d4386..150e87d9 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/property/LngProp.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/property/LngProp.java @@ -13,6 +13,14 @@ * * By default this uses the TrimToNullTrimmer, which removes all whitespace from * the value and ultimately null if the value is all whitespace. + * + * If a LngProp is configured as a string, such as from a properties file, on + * command line, environment variable, etc., the value does NOT include a trailing + * 'L', as is done with Java literals. E.g., this is the correct way to spec + * a long value in a properties file: + * + * name.of.my.long.property.MY_PROPERTY = 90 + * * * @author eeverman */ diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java new file mode 100644 index 00000000..8ac06833 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java @@ -0,0 +1,296 @@ +/* + */ +package org.yarnandtail.andhow; + +import java.util.*; + +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.StdConfig.StdConfigImpl; +import org.yarnandtail.andhow.api.Loader; +import org.yarnandtail.andhow.api.Property; +import org.yarnandtail.andhow.api.StandardLoader; +import org.yarnandtail.andhow.load.*; +import org.yarnandtail.andhow.load.std.*; +import org.yarnandtail.andhow.name.CaseInsensitiveNaming; +import org.yarnandtail.andhow.property.DblProp; +import org.yarnandtail.andhow.property.LngProp; +import org.yarnandtail.andhow.property.StrProp; + +import static org.junit.jupiter.api.Assertions.*; +import static org.hamcrest.Matchers.*; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * + * @author ericeverman + */ +public class StdConfigGetterAndSetterTest { + + public StdConfigGetterAndSetterTest() { + } + + /** + * Test of getNamingStrategy method, of class BaseConfig. + */ + @Test + public void testGetNamingStrategy() { + StdConfigImpl std = StdConfig.instance(); + assertTrue(std.getNamingStrategy() instanceof CaseInsensitiveNaming); + } + + /** + * Test of buildLoaders method, of class BaseConfig. + */ + @Test + public void testBuildLoadersWithoutAnyChanges() { + StdConfigImpl std = StdConfig.instance(); + List loaders = std.buildLoaders(); + assertEquals(7, loaders.size()); + assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); + assertEquals(StdMainStringArgsLoader.class, loaders.get(1).getClass()); + assertEquals(StdSysPropLoader.class, loaders.get(2).getClass()); + assertEquals(StdEnvVarLoader.class, loaders.get(3).getClass()); + assertEquals(StdJndiLoader.class, loaders.get(4).getClass()); + assertEquals(StdPropFileOnFilesystemLoader.class, loaders.get(5).getClass()); + assertEquals(StdPropFileOnClasspathLoader.class, loaders.get(6).getClass()); + } + + @Test + public void testBuildLoadersWithCustomListOfStandardLoaders() { + StdConfigImpl std = StdConfig.instance(); + + List> newLoaders = new ArrayList(); + newLoaders.add(StdFixedValueLoader.class); + newLoaders.add(StdJndiLoader.class); + + std.setStandardLoaders(newLoaders); + List loaders = std.buildLoaders(); + assertEquals(2, loaders.size()); + assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); + assertEquals(StdJndiLoader.class, loaders.get(1).getClass()); + + std.setStandardLoaders(BaseConfig.getDefaultLoaderList()); + loaders = std.buildLoaders(); + assertEquals(7, loaders.size()); + assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); + assertEquals(StdPropFileOnClasspathLoader.class, loaders.get(6).getClass()); + + std.setStandardLoaders(StdSysPropLoader.class, StdPropFileOnFilesystemLoader.class); + loaders = std.buildLoaders(); + assertEquals(2, loaders.size()); + assertEquals(StdSysPropLoader.class, loaders.get(0).getClass()); + assertEquals(StdPropFileOnFilesystemLoader.class, loaders.get(1).getClass()); + } + + @Test + public void testBuildLoadersWithInsertingLoadersBeforeAndAfter() { + StdConfigImpl std = StdConfig.instance(); + + Loader loader1 = new MapLoader(); + Loader loader2 = new KeyValuePairLoader(); + Loader loader3 = new PropFileOnClasspathLoader(); + Loader loader4 = new PropFileOnFilesystemLoader(); + Loader loader5 = new FixedValueLoader(); + Loader loader6 = new MapLoader(); + Loader loader7 = new KeyValuePairLoader(); + Loader loader8 = new PropFileOnClasspathLoader(); + Loader loader9 = new PropFileOnFilesystemLoader(); + + //At the beginning of the list + std.insertLoaderBefore(StdFixedValueLoader.class, loader1); + std.insertLoaderBefore(StdFixedValueLoader.class, loader2); + std.insertLoaderAfter(StdFixedValueLoader.class, loader3); + std.insertLoaderAfter(StdFixedValueLoader.class, loader4); + std.insertLoaderBefore(StdMainStringArgsLoader.class, loader5); + + //At the end of the list + std.insertLoaderBefore(StdPropFileOnFilesystemLoader.class, loader6); + std.insertLoaderAfter(StdPropFileOnFilesystemLoader.class, loader7); + std.insertLoaderBefore(StdPropFileOnClasspathLoader.class, loader8); + std.insertLoaderAfter(StdPropFileOnClasspathLoader.class, loader9); + + List loaders = std.buildLoaders(); + assertEquals(16, loaders.size()); + assertEquals(loader1, loaders.get(0)); + assertEquals(loader2, loaders.get(1)); + assertEquals(StdFixedValueLoader.class, loaders.get(2).getClass()); + assertEquals(loader3, loaders.get(3)); + assertEquals(loader4, loaders.get(4)); + assertEquals(loader5, loaders.get(5)); + assertEquals(StdMainStringArgsLoader.class, loaders.get(6).getClass()); + assertEquals(StdSysPropLoader.class, loaders.get(7).getClass()); + assertEquals(StdEnvVarLoader.class, loaders.get(8).getClass()); + assertEquals(StdJndiLoader.class, loaders.get(9).getClass()); + assertEquals(loader6, loaders.get(10)); + assertEquals(StdPropFileOnFilesystemLoader.class, loaders.get(11).getClass()); + assertEquals(loader7, loaders.get(12)); + assertEquals(loader8, loaders.get(13)); + assertEquals(StdPropFileOnClasspathLoader.class, loaders.get(14).getClass()); + assertEquals(loader9, loaders.get(15)); + } + + @Test + public void FixedValuesBasedOnPropertiesTest() { + MyStdConfig config = new MyStdConfig(); + + StrProp MY_STR_1 = StrProp.builder().build(); + LngProp MY_LNG_2 = LngProp.builder().build(); + DblProp MY_DBL_3 = DblProp.builder().build(); + + config.addFixedValue(MY_STR_1, "ABC"); + config.addFixedValue(MY_LNG_2, 23L); + + assertEquals(2, config.getFixedValues().size()); + assertTrue(containsPropertyAndValue(config.getFixedValues(), MY_STR_1, "ABC")); + assertTrue(containsPropertyAndValue(config.getFixedValues(), MY_LNG_2, 23L)); + + //Try to add a duplicate property + assertThrows(IllegalArgumentException.class, () -> { + config.addFixedValue(MY_STR_1, "ZZZ"); + }); + + assertEquals("ABC", + config.getFixedValues().stream().filter(p -> p.getProperty().equals(MY_STR_1)).findFirst().get() + .getValue().toString(), + "The value set for this Property should be unchanged"); + + //Try to add a null property + assertThrows(IllegalArgumentException.class, () -> { + config.addFixedValue((Property)null, "ZZZ"); + }); + + assertEquals(2, config.getFixedValues().size(), "Still should have two values"); + + //Try removing some + config.removeFixedValue(MY_STR_1); + assertEquals(1, config.getFixedValues().size()); + assertTrue(containsPropertyAndValue(config.getFixedValues(), MY_LNG_2, 23L)); + + //it should be OK and a no-op to attempt to remove a non-existing property + //...but how would this ever happen?? + config.removeFixedValue(MY_DBL_3); + assertEquals(1, config.getFixedValues().size()); + + config.removeFixedValue(MY_LNG_2); + assertEquals(0, config.getFixedValues().size()); + } + + + @Test + public void FixedValuesBasedOnNamesTest() { + MyStdConfig config = new MyStdConfig(); + + //These properties don't really exist - no checking is done until loading, when + //the Loader attempts to match up the name w/ a property. For now this just tests + //the logic in StdConfig. + config.addFixedValue("MY_STR_1", "ABC"); + config.addFixedValue("MY_LNG_2", 23L); + + assertEquals(2, config.getFixedKeyObjectPairValues().size()); + assertTrue(containsNameAndValue(config.getFixedKeyObjectPairValues(), "MY_STR_1", "ABC")); + assertTrue(containsNameAndValue(config.getFixedKeyObjectPairValues(), "MY_LNG_2", 23L)); + + //Try to add a duplicate property + assertThrows(IllegalArgumentException.class, () -> { + config.addFixedValue("MY_STR_1", "ZZZ"); + }); + + assertEquals("ABC", + config.getFixedKeyObjectPairValues().stream().filter(k -> k.getName().equals("MY_STR_1")).findFirst().get() + .getValue().toString(), + "The value set for this Property should be unchanged"); + + //Try to add a null property name + assertThrows(IllegalArgumentException.class, () -> { + config.addFixedValue((String)null, "ZZZ"); + }); + + //Try to add an empty (all space) property name + assertThrows(IllegalArgumentException.class, () -> { + config.addFixedValue(" ", "ZZZ"); + }); + + assertEquals(2, config.getFixedKeyObjectPairValues().size(), "Still should have two values"); + + //Try removing some + config.removeFixedValue("MY_STR_1"); + assertEquals(1, config.getFixedKeyObjectPairValues().size()); + assertTrue(containsNameAndValue(config.getFixedKeyObjectPairValues(), "MY_LNG_2", 23L)); + + //it should be OK and a no-op to attempt to remove a non-existing property, or a property not in the list. + config.removeFixedValue("MY_DBL_3"); + assertEquals(1, config.getFixedKeyObjectPairValues().size()); + + config.removeFixedValue("MY_LNG_2"); + assertEquals(0, config.getFixedKeyObjectPairValues().size()); + } + + @Test + public void setCmdLineArgsTest() { + MyStdConfig config = new MyStdConfig(); + + String[] args = new String[] {"abc=123", "xyz='456"}; + config.setCmdLineArgs(args); + assertThat(config.getCmdLineArgs().toArray(), arrayContainingInAnyOrder(args)); + } + + @Test + public void setEnvironmentPropertiesTest() { + MyStdConfig config = new MyStdConfig(); + + Map envVars = new HashMap<>(); + envVars.put("abc", "123"); + envVars.put("xyz", "456"); + + config.setEnvironmentProperties(envVars); + + assertEquals(2, config.getEnvironmentProperties().size()); + assertTrue(envVars.equals(config.getEnvironmentProperties())); + assertFalse(envVars == config.getEnvironmentProperties(), "Should be disconnected object"); + + //Now try setting new values - they should replace the old + Map envVars2 = new HashMap<>(); + envVars2.put("bob", "bob_val"); + config.setEnvironmentProperties(envVars2); + + assertEquals(1, config.getEnvironmentProperties().size()); + assertTrue(envVars2.equals(config.getEnvironmentProperties())); + + //Now set to null + config.setEnvironmentProperties(null); + assertNull(config.getEnvironmentProperties()); + + } + + boolean containsPropertyAndValue(List propertyValues, Property property, T value) { + PropertyValue pv = propertyValues.stream().filter(p -> p.getProperty().equals(property)).findFirst().get(); + return pv != null && pv.getValue().equals(value); + } + + boolean containsNameAndValue(List keyObjectPairs, String name, Object value) { + KeyObjectPair kop = keyObjectPairs.stream().filter(p -> p.getName().equals(name)).findFirst().get(); + return kop != null && kop.getValue().equals(value); + } + + /** + * Custom StdConfig class that has access methods for fields not otherwise accessable. + */ + public static final class MyStdConfig extends StdConfig.StdConfigAbstract { + public List getFixedValues() { + return _fixedVals; + } + + public List getFixedKeyObjectPairValues() { + return _fixedKeyObjectPairVals; + } + + public List getCmdLineArgs() { + return _cmdLineArgs; + } + + public Map getEnvironmentProperties() { + return envProperties; + } + } + +} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigSimulatedAppTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigSimulatedAppTest.java new file mode 100644 index 00000000..e38c12d6 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigSimulatedAppTest.java @@ -0,0 +1,267 @@ +package org.yarnandtail.andhow; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.mock.jndi.SimpleNamingContextBuilder; +import org.yarnandtail.andhow.api.AppFatalException; +import org.yarnandtail.andhow.api.Property; +import org.yarnandtail.andhow.internal.LoaderProblem; +import org.yarnandtail.andhow.internal.ValueProblem; +import org.yarnandtail.andhow.load.KeyValuePairLoader; +import org.yarnandtail.andhow.property.FlagProp; +import org.yarnandtail.andhow.property.IntProp; +import org.yarnandtail.andhow.property.StrProp; +import static org.yarnandtail.andhow.load.KeyValuePairLoader.KVP_DELIMITER; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * + * @author eeverman + */ +public class StdConfigSimulatedAppTest extends AndHowCoreTestBase { + + private static final String GROUP_PATH = "org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup"; + private static final String CLASSPATH_BEGINNING = "/org/yarnandtail/andhow/StdConfigSimulatedAppTest."; + + + @Test + public void testAllValuesAreSetViaCmdLineArgAndPropFile() { + + //Prop name case ignored + String[] cmdLineArgs = new String[] { + GROUP_PATH + ".classpath_prop_file" + KVP_DELIMITER + CLASSPATH_BEGINNING + "all.props.speced.properties" + }; + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .setCmdLineArgs(cmdLineArgs) + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals("/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties", + SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); + assertAllPointsSpecedValues("testAllValuesAreSetViaCmdLineArgAndPropFile"); + } + + @Test + public void testAllValuesAreSetViaFixedPropertyValueAndPropFile() { + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .addFixedValue(SampleRestClientGroup.CLASSPATH_PROP_FILE, + CLASSPATH_BEGINNING + "all.props.speced.properties") //set via FixedValue (PropertyValue) + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals("/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties", + SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); + assertAllPointsSpecedValues("testAllValuesAreSetViaFixedPropertyValueAndPropFile"); + } + + @Test + public void testAllValuesAreSetViaFixedPropertyValueAndPropFileOverridingWithFixedPropertyValue() { + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .addFixedValue(SampleRestClientGroup.CLASSPATH_PROP_FILE, + CLASSPATH_BEGINNING + "all.props.speced.properties") //set via FixedValue (PropertyValue) + .addFixedValue(SampleRestClientGroup.REST_PORT, 99) //Override value in prop file + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals(" Big App ", SampleRestClientGroup.APP_NAME.getValue()); //Just check one prop file val + assertEquals(99, SampleRestClientGroup.REST_PORT.getValue(), "Fixed value should override prop file"); + } + + @Test + public void testAllValuesAreSetViaFixedKeyObjectPairAndPropFile() { + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .addFixedValue(GROUP_PATH + ".classpath_prop_file", /* case ignored */ + CLASSPATH_BEGINNING + "all.props.speced.properties") //set via FixedValue (KeyObjectPair) + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals("/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties", + SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); + assertAllPointsSpecedValues("testAllValuesAreSetViaFixedKeyObjectPairAndPropFile"); + } + + @Test + public void testAllValuesAreSetViaFixedKeyObjectPairAndPropFileOverridingWithFixedKeyObjectPair() { + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .addFixedValue(GROUP_PATH + ".classpath_prop_file", /* case ignored */ + CLASSPATH_BEGINNING + "all.props.speced.properties") //set via FixedValue (KeyObjectPair) + .addFixedValue(GROUP_PATH + ".REST_PORT", 98) //Override value in prop file + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals(" Big App ", SampleRestClientGroup.APP_NAME.getValue()); //Just check one prop file val + assertEquals(98, SampleRestClientGroup.REST_PORT.getValue(), "Fixed value should override prop file"); + } + + @Test + public void testAllValuesAreSetViaEnvVarAndPropFile() { + + Map envvars = new HashMap(); + envvars.put( + GROUP_PATH + ".classpath_prop_file", /* case ignored */ + CLASSPATH_BEGINNING + "all.props.speced.properties" + ); + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .setEnvironmentProperties(envvars) + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals("/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties", + SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); + assertAllPointsSpecedValues("testAllValuesAreSetViaEnvVarAndPropFile"); + } + + @Test + public void testAllValuesAreSetViaSystemPropertiesAndPropFile() { + + Properties props = new Properties(); + props.put( + GROUP_PATH + ".classpath_prop_file", /* case ignored */ + CLASSPATH_BEGINNING + "all.props.speced.properties" + ); + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .setSystemProperties(props) + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals("/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties", + SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); + assertAllPointsSpecedValues("testAllValuesAreSetViaSystemPropertiesAndPropFile"); + } + + /** + * Asserts each value to match the values spec'ed in the StdConfigSimulatedAppTest.all.props.speced.properties file. + */ + void assertAllPointsSpecedValues(String calledFromTestName) { + String failDesc = "assertion failure in the test named '" + calledFromTestName + "'"; + + assertEquals(" Big App ", SampleRestClientGroup.APP_NAME.getValue(), failDesc); + assertEquals("aquarius.usgs.gov", SampleRestClientGroup.REST_HOST.getValue(), failDesc); + assertEquals(new Integer(8080), SampleRestClientGroup.REST_PORT.getValue(), failDesc); + assertEquals("doquery/", SampleRestClientGroup.REST_SERVICE_NAME.getValue(), failDesc); + assertEquals("abc123", SampleRestClientGroup.AUTH_KEY.getValue(), failDesc); + assertEquals(new Integer(4), SampleRestClientGroup.RETRY_COUNT.getValue(), failDesc); + assertFalse(SampleRestClientGroup.REQUEST_META_DATA.getValue(), failDesc); + assertTrue(SampleRestClientGroup.REQUEST_SUMMARY_DATA.getValue(), failDesc); + } + + @Test + public void testMinimumPropsAreSetViaCmdLineArgAndPropFile() { + + String[] cmdLineArgs = new String[] { + GROUP_PATH + ".CLASSPATH_PROP_FILE" + KVP_DELIMITER + CLASSPATH_BEGINNING + "minimum.props.speced.properties" + }; + + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .setCmdLineArgs(cmdLineArgs) + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + assertEquals("/org/yarnandtail/andhow/StdConfigSimulatedAppTest.minimum.props.speced.properties", + SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); + assertEquals("aquarius.usgs.gov", SampleRestClientGroup.REST_HOST.getValue()); + assertEquals(new Integer(8080), SampleRestClientGroup.REST_PORT.getValue()); + assertEquals("query/", SampleRestClientGroup.REST_SERVICE_NAME.getValue()); //a default value + assertEquals("abc123", SampleRestClientGroup.AUTH_KEY.getValue()); + assertEquals(new Integer(2), SampleRestClientGroup.RETRY_COUNT.getValue()); //a default + assertTrue(SampleRestClientGroup.REQUEST_META_DATA.getValue()); //a default + assertFalse(SampleRestClientGroup.REQUEST_SUMMARY_DATA.getValue()); //a default + + } + + @Test + public void testInvalidValuesViaCmdLineArgAndPropFile() throws Exception { + + SimpleNamingContextBuilder jndi = getJndi(); + jndi.activate(); + + String[] cmdLineArgs = new String[] { + GROUP_PATH + ".CLASSPATH_PROP_FILE" + KVP_DELIMITER + CLASSPATH_BEGINNING + "invalid.properties" + }; + + try { + + //Error expected b/c some values are invalid + AndHowConfiguration config = AndHowCoreTestConfig.instance() + .group(SampleRestClientGroup.class) + .setCmdLineArgs(cmdLineArgs) + .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) + .classpathPropertiesRequired(); + + AndHow.instance(config); + + } catch (AppFatalException e) { + + //Value Problems (validation) + //Due to loading from a prop file, the order of the file is not preserved, + //so we cannot know the order that problems were encountered. + ArrayList> expectedProblemPoints = new ArrayList(); + expectedProblemPoints.add(SampleRestClientGroup.REST_HOST); + expectedProblemPoints.add(SampleRestClientGroup.REST_PORT); + expectedProblemPoints.add(SampleRestClientGroup.REST_SERVICE_NAME); + + assertEquals(3, e.getProblems().filter(ValueProblem.class).size()); + assertTrue(expectedProblemPoints.contains(e.getProblems().filter(ValueProblem.class).get(0).getBadValueCoord().getProperty())); + assertTrue(expectedProblemPoints.contains(e.getProblems().filter(ValueProblem.class).get(1).getBadValueCoord().getProperty())); + assertTrue(expectedProblemPoints.contains(e.getProblems().filter(ValueProblem.class).get(2).getBadValueCoord().getProperty())); + + // + // Loader problems + assertEquals(1, e.getProblems().filter(LoaderProblem.class).size()); + assertEquals(SampleRestClientGroup.RETRY_COUNT, e.getProblems().filter(LoaderProblem.class).get(0).getBadValueCoord().getProperty()); + } + + + } + + interface SampleRestClientGroup { + + StrProp CLASSPATH_PROP_FILE = StrProp.builder().desc("Classpath location of a properties file w/ props").build(); + StrProp APP_NAME = StrProp.builder().aliasIn("app.name").aliasIn("app_name").build(); + StrProp REST_HOST = StrProp.builder().mustMatchRegex(".*\\.usgs\\.gov") .mustBeNonNull().build(); + IntProp REST_PORT = IntProp.builder().mustBeNonNull().mustBeGreaterThanOrEqualTo(80).mustBeLessThan(10000).build(); + StrProp REST_SERVICE_NAME = StrProp.builder().defaultValue("query/").mustEndWith("/").build(); + StrProp AUTH_KEY = StrProp.builder().mustBeNonNull().build(); + IntProp RETRY_COUNT = IntProp.builder().defaultValue(2).build(); + FlagProp REQUEST_META_DATA = FlagProp.builder().defaultValue(true).build(); + FlagProp REQUEST_SUMMARY_DATA = FlagProp.builder().build(); + } + +} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java deleted file mode 100644 index 52c3b676..00000000 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - */ -package org.yarnandtail.andhow; - -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.yarnandtail.andhow.StdConfig.StdConfigImpl; -import org.yarnandtail.andhow.api.Loader; -import org.yarnandtail.andhow.api.StandardLoader; -import org.yarnandtail.andhow.load.*; -import org.yarnandtail.andhow.load.std.*; -import org.yarnandtail.andhow.name.CaseInsensitiveNaming; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * - * @author ericeverman - */ -public class StdConfigTest { - - public StdConfigTest() { - } - - /** - * Test of getNamingStrategy method, of class BaseConfig. - */ - @Test - public void testGetNamingStrategy() { - StdConfigImpl std = StdConfig.instance(); - assertTrue(std.getNamingStrategy() instanceof CaseInsensitiveNaming); - } - - /** - * Test of buildLoaders method, of class BaseConfig. - */ - @Test - public void testBuildLoadersWithoutAnyChanges() { - StdConfigImpl std = StdConfig.instance(); - List loaders = std.buildLoaders(); - assertEquals(7, loaders.size()); - assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); - assertEquals(StdMainStringArgsLoader.class, loaders.get(1).getClass()); - assertEquals(StdSysPropLoader.class, loaders.get(2).getClass()); - assertEquals(StdEnvVarLoader.class, loaders.get(3).getClass()); - assertEquals(StdJndiLoader.class, loaders.get(4).getClass()); - assertEquals(StdPropFileOnFilesystemLoader.class, loaders.get(5).getClass()); - assertEquals(StdPropFileOnClasspathLoader.class, loaders.get(6).getClass()); - } - - @Test - public void testBuildLoadersWithCustomListOfStandardLoaders() { - StdConfigImpl std = StdConfig.instance(); - - List> newLoaders = new ArrayList(); - newLoaders.add(StdFixedValueLoader.class); - newLoaders.add(StdJndiLoader.class); - - std.setStandardLoaders(newLoaders); - List loaders = std.buildLoaders(); - assertEquals(2, loaders.size()); - assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); - assertEquals(StdJndiLoader.class, loaders.get(1).getClass()); - - std.setStandardLoaders(BaseConfig.getDefaultLoaderList()); - loaders = std.buildLoaders(); - assertEquals(7, loaders.size()); - assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); - assertEquals(StdPropFileOnClasspathLoader.class, loaders.get(6).getClass()); - - std.setStandardLoaders(StdSysPropLoader.class, StdPropFileOnFilesystemLoader.class); - loaders = std.buildLoaders(); - assertEquals(2, loaders.size()); - assertEquals(StdSysPropLoader.class, loaders.get(0).getClass()); - assertEquals(StdPropFileOnFilesystemLoader.class, loaders.get(1).getClass()); - } - - @Test - public void testBuildLoadersWithInsertingLoadersBeforeAndAfter() { - StdConfigImpl std = StdConfig.instance(); - - Loader loader1 = new MapLoader(); - Loader loader2 = new KeyValuePairLoader(); - Loader loader3 = new PropFileOnClasspathLoader(); - Loader loader4 = new PropFileOnFilesystemLoader(); - Loader loader5 = new FixedValueLoader(); - Loader loader6 = new MapLoader(); - Loader loader7 = new KeyValuePairLoader(); - Loader loader8 = new PropFileOnClasspathLoader(); - Loader loader9 = new PropFileOnFilesystemLoader(); - - //At the beginning of the list - std.insertLoaderBefore(StdFixedValueLoader.class, loader1); - std.insertLoaderBefore(StdFixedValueLoader.class, loader2); - std.insertLoaderAfter(StdFixedValueLoader.class, loader3); - std.insertLoaderAfter(StdFixedValueLoader.class, loader4); - std.insertLoaderBefore(StdMainStringArgsLoader.class, loader5); - - //At the end of the list - std.insertLoaderBefore(StdPropFileOnFilesystemLoader.class, loader6); - std.insertLoaderAfter(StdPropFileOnFilesystemLoader.class, loader7); - std.insertLoaderBefore(StdPropFileOnClasspathLoader.class, loader8); - std.insertLoaderAfter(StdPropFileOnClasspathLoader.class, loader9); - - List loaders = std.buildLoaders(); - assertEquals(16, loaders.size()); - assertEquals(loader1, loaders.get(0)); - assertEquals(loader2, loaders.get(1)); - assertEquals(StdFixedValueLoader.class, loaders.get(2).getClass()); - assertEquals(loader3, loaders.get(3)); - assertEquals(loader4, loaders.get(4)); - assertEquals(loader5, loaders.get(5)); - assertEquals(StdMainStringArgsLoader.class, loaders.get(6).getClass()); - assertEquals(StdSysPropLoader.class, loaders.get(7).getClass()); - assertEquals(StdEnvVarLoader.class, loaders.get(8).getClass()); - assertEquals(StdJndiLoader.class, loaders.get(9).getClass()); - assertEquals(loader6, loaders.get(10)); - assertEquals(StdPropFileOnFilesystemLoader.class, loaders.get(11).getClass()); - assertEquals(loader7, loaders.get(12)); - assertEquals(loader8, loaders.get(13)); - assertEquals(StdPropFileOnClasspathLoader.class, loaders.get(14).getClass()); - assertEquals(loader9, loaders.get(15)); - } - -} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java deleted file mode 100644 index 83665cb4..00000000 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientAppTest.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.yarnandtail.andhow.example.restclient; - -import java.util.ArrayList; - -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.mock.jndi.SimpleNamingContextBuilder; -import org.yarnandtail.andhow.*; -import org.yarnandtail.andhow.api.AppFatalException; -import org.yarnandtail.andhow.api.Property; -import org.yarnandtail.andhow.internal.LoaderProblem; -import org.yarnandtail.andhow.internal.ValueProblem; -import org.yarnandtail.andhow.load.KeyValuePairLoader; - -/** - * - * @author eeverman - */ -public class SampleRestClientAppTest extends AndHowCoreTestBase { - - String[] cmdLineArgs = new String[0]; - private static final String GROUP_PATH = "org.yarnandtail.andhow.example.restclient.SampleRestClientGroup"; - - - @BeforeEach - public void setup() { - - cmdLineArgs = new String[0]; - - } - - @Test - public void testAllValuesAreSet() { - - cmdLineArgs = new String[] { - GROUP_PATH + ".CLASSPATH_PROP_FILE" + KeyValuePairLoader.KVP_DELIMITER + - "/org/yarnandtail/andhow/example/restclient/all.points.speced.properties" - }; - - AndHowConfiguration config = AndHowCoreTestConfig.instance() - .group(SampleRestClientGroup.class) - .setCmdLineArgs(cmdLineArgs) - .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) - .classpathPropertiesRequired(); - - AndHow.instance(config); - - assertEquals("/org/yarnandtail/andhow/example/restclient/all.points.speced.properties", - SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); - assertEquals(" Big App ", SampleRestClientGroup.APP_NAME.getValue()); - assertEquals("aquarius.usgs.gov", SampleRestClientGroup.REST_HOST.getValue()); - assertEquals(new Integer(8080), SampleRestClientGroup.REST_PORT.getValue()); - assertEquals("doquery/", SampleRestClientGroup.REST_SERVICE_NAME.getValue()); - assertEquals("abc123", SampleRestClientGroup.AUTH_KEY.getValue()); - assertEquals(new Integer(4), SampleRestClientGroup.RETRY_COUNT.getValue()); - assertFalse(SampleRestClientGroup.REQUEST_META_DATA.getValue()); - assertTrue(SampleRestClientGroup.REQUEST_SUMMARY_DATA.getValue()); - - } - - @Test - public void testMinimumPropsAreSet() { - - cmdLineArgs = new String[] { - GROUP_PATH + ".CLASSPATH_PROP_FILE" + KeyValuePairLoader.KVP_DELIMITER + - "/org/yarnandtail/andhow/example/restclient/minimum.points.speced.properties" - }; - - AndHowConfiguration config = AndHowCoreTestConfig.instance() - .group(SampleRestClientGroup.class) - .setCmdLineArgs(cmdLineArgs) - .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) - .classpathPropertiesRequired(); - - AndHow.instance(config); - - assertEquals("/org/yarnandtail/andhow/example/restclient/minimum.points.speced.properties", - SampleRestClientGroup.CLASSPATH_PROP_FILE.getValue()); - assertEquals("aquarius.usgs.gov", SampleRestClientGroup.REST_HOST.getValue()); - assertEquals(new Integer(8080), SampleRestClientGroup.REST_PORT.getValue()); - assertEquals("query/", SampleRestClientGroup.REST_SERVICE_NAME.getValue()); //a default value - assertEquals("abc123", SampleRestClientGroup.AUTH_KEY.getValue()); - assertEquals(new Integer(2), SampleRestClientGroup.RETRY_COUNT.getValue()); //a default - assertTrue(SampleRestClientGroup.REQUEST_META_DATA.getValue()); //a default - assertFalse(SampleRestClientGroup.REQUEST_SUMMARY_DATA.getValue()); //a default - - } - - @Test - public void testInvalidValues() throws Exception { - - SimpleNamingContextBuilder jndi = getJndi(); - jndi.activate(); - - cmdLineArgs = new String[] { - GROUP_PATH + ".CLASSPATH_PROP_FILE" + KeyValuePairLoader.KVP_DELIMITER + - "/org/yarnandtail/andhow/example/restclient/invalid.properties" - }; - - try { - - //Error expected b/c some values are invalid - AndHowConfiguration config = AndHowCoreTestConfig.instance() - .group(SampleRestClientGroup.class) - .setCmdLineArgs(cmdLineArgs) - .setClasspathPropFilePath(SampleRestClientGroup.CLASSPATH_PROP_FILE) - .classpathPropertiesRequired(); - - AndHow.instance(config); - - } catch (AppFatalException e) { - - //Value Problems (validation) - //Due to loading from a prop file, the order of the file is not preserved, - //so we cannot know the order that problems were encountered. - ArrayList> expectedProblemPoints = new ArrayList(); - expectedProblemPoints.add(SampleRestClientGroup.REST_HOST); - expectedProblemPoints.add(SampleRestClientGroup.REST_PORT); - expectedProblemPoints.add(SampleRestClientGroup.REST_SERVICE_NAME); - - assertEquals(3, e.getProblems().filter(ValueProblem.class).size()); - assertTrue(expectedProblemPoints.contains(e.getProblems().filter(ValueProblem.class).get(0).getBadValueCoord().getProperty())); - assertTrue(expectedProblemPoints.contains(e.getProblems().filter(ValueProblem.class).get(1).getBadValueCoord().getProperty())); - assertTrue(expectedProblemPoints.contains(e.getProblems().filter(ValueProblem.class).get(2).getBadValueCoord().getProperty())); - - // - // Loader problems - assertEquals(1, e.getProblems().filter(LoaderProblem.class).size()); - assertEquals(SampleRestClientGroup.RETRY_COUNT, e.getProblems().filter(LoaderProblem.class).get(0).getBadValueCoord().getProperty()); - } - - - } - - -} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientGroup.java b/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientGroup.java deleted file mode 100644 index 37a93297..00000000 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/example/restclient/SampleRestClientGroup.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.yarnandtail.andhow.example.restclient; - -import org.yarnandtail.andhow.property.FlagProp; -import org.yarnandtail.andhow.property.IntProp; -import org.yarnandtail.andhow.property.StrProp; - -/** - * - * @author eeverman - */ -public interface SampleRestClientGroup { - - StrProp CLASSPATH_PROP_FILE = StrProp.builder().desc("Classpath location of a properties file w/ props").build(); - StrProp APP_NAME = StrProp.builder().aliasIn("app.name").aliasIn("app_name").build(); - StrProp REST_HOST = StrProp.builder().mustMatchRegex(".*\\.usgs\\.gov") .mustBeNonNull().build(); - IntProp REST_PORT = IntProp.builder().mustBeNonNull().mustBeGreaterThanOrEqualTo(80).mustBeLessThan(10000).build(); - StrProp REST_SERVICE_NAME = StrProp.builder().defaultValue("query/").mustEndWith("/").build(); - StrProp AUTH_KEY = StrProp.builder().mustBeNonNull().build(); - IntProp RETRY_COUNT = IntProp.builder().defaultValue(2).build(); - FlagProp REQUEST_META_DATA = FlagProp.builder().defaultValue(true).build(); - FlagProp REQUEST_SUMMARY_DATA = FlagProp.builder().build(); -} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/BaseForLoaderTests.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/BaseForLoaderTests.java new file mode 100644 index 00000000..5ed35801 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/BaseForLoaderTests.java @@ -0,0 +1,60 @@ +package org.yarnandtail.andhow.load; + +import org.junit.jupiter.api.BeforeEach; +import org.yarnandtail.andhow.api.GroupProxy; +import org.yarnandtail.andhow.internal.StaticPropertyConfigurationMutable; +import org.yarnandtail.andhow.internal.ValidatedValuesWithContextMutable; +import org.yarnandtail.andhow.name.CaseInsensitiveNaming; +import org.yarnandtail.andhow.property.*; +import org.yarnandtail.andhow.util.AndHowUtil; + +/** + * This is intended to be a base class for tests that directly test a single loader. + * As such, tests cannot apply default values or detect missing required values since + * that happens only after all loaders have run and higher level logic is applied. + * Thus, none of the example parameters have defaults or required flags. + * + * @author eeverman + */ +public class BaseForLoaderTests { + StaticPropertyConfigurationMutable appDef; + ValidatedValuesWithContextMutable appValuesBuilder; + + @BeforeEach + public void init() throws Exception { + appValuesBuilder = new ValidatedValuesWithContextMutable(); + CaseInsensitiveNaming bns = new CaseInsensitiveNaming(); + appDef = new StaticPropertyConfigurationMutable(bns); + GroupProxy proxy = AndHowUtil.buildGroupProxy(SimpleParams.class); + + appDef.addProperty(proxy, SimpleParams.STR_BOB); + appDef.addProperty(proxy, SimpleParams.STR_NULL); + appDef.addProperty(proxy, SimpleParams.STR_ENDS_WITH_XXX); + + appDef.addProperty(proxy, SimpleParams.LNG_TIME); + appDef.addProperty(proxy, SimpleParams.INT_NUMBER); + appDef.addProperty(proxy, SimpleParams.DBL_NUMBER); + + appDef.addProperty(proxy, SimpleParams.FLAG_FALSE); + appDef.addProperty(proxy, SimpleParams.FLAG_TRUE); + appDef.addProperty(proxy, SimpleParams.FLAG_NULL); + + } + + public interface SimpleParams { + //Strings + StrProp STR_BOB = StrProp.builder().aliasIn("String_Bob").aliasInAndOut("Stringy.Bob").defaultValue("bob").build(); + StrProp STR_NULL = StrProp.builder().aliasInAndOut("String_Null").build(); + StrProp STR_ENDS_WITH_XXX = StrProp.builder().mustEndWith("XXX").build(); + + //Some Numbers + LngProp LNG_TIME = LngProp.builder().aliasIn("lngIn").build(); + IntProp INT_NUMBER = IntProp.builder().aliasIn("intIn").build(); + DblProp DBL_NUMBER = DblProp.builder().aliasIn("dblIn").build(); + + //Flags + FlagProp FLAG_FALSE = FlagProp.builder().build(); + FlagProp FLAG_TRUE = FlagProp.builder().build(); + FlagProp FLAG_NULL = FlagProp.builder().build(); + } +} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/FixedValueLoaderTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/FixedValueLoaderTest.java new file mode 100644 index 00000000..afdd69ef --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/FixedValueLoaderTest.java @@ -0,0 +1,161 @@ +package org.yarnandtail.andhow.load; + +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.PropertyValue; +import org.yarnandtail.andhow.api.LoaderValues; +import org.yarnandtail.andhow.api.ParsingException; +import org.yarnandtail.andhow.api.Problem; +import org.yarnandtail.andhow.internal.LoaderProblem; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.yarnandtail.andhow.load.BaseForLoaderTests.SimpleParams.*; + +public class FixedValueLoaderTest extends BaseForLoaderTests { + + @Test + public void happyPathViaPropertyValues() { + + List props = new ArrayList(); + props.add(new PropertyValue(STR_BOB, "test")); + props.add(new PropertyValue(STR_NULL, "not_null")); + props.add(new PropertyValue(STR_ENDS_WITH_XXX, "XXX")); + + //spaces on non-string values should be trimmed and not matter + props.add(new PropertyValue(LNG_TIME, "60")); //as string + props.add(new PropertyValue(INT_NUMBER, 30)); //As exact value type (int) + props.add(new PropertyValue(DBL_NUMBER, "123.456D")); //as string + props.add(new PropertyValue(FLAG_TRUE, " false ")); + props.add(new PropertyValue(FLAG_FALSE, " true ")); + props.add(new PropertyValue(FLAG_NULL, " true ")); + + + FixedValueLoader loader = new FixedValueLoader(); + loader.setPropertyValues(props); + + LoaderValues result = loader.load(appDef, appValuesBuilder); + + assertEquals(0, result.getProblems().size()); + assertEquals(0L, result.getValues().stream().filter(p -> p.hasProblems()).count()); + assertEquals("test", result.getExplicitValue(SimpleParams.STR_BOB)); + assertEquals("not_null", result.getExplicitValue(SimpleParams.STR_NULL)); + assertEquals("XXX", result.getExplicitValue(STR_ENDS_WITH_XXX)); + assertEquals(60L, result.getExplicitValue(LNG_TIME)); + assertEquals(30, result.getExplicitValue(INT_NUMBER)); + assertEquals(Boolean.FALSE, result.getExplicitValue(FLAG_TRUE)); + assertEquals(Boolean.TRUE, result.getExplicitValue(FLAG_FALSE)); + assertEquals(Boolean.TRUE, result.getExplicitValue(FLAG_NULL)); + } + + @Test + public void happyPathViaKeyObjectPairs() throws ParsingException { + + String basePath = SimpleParams.class.getCanonicalName() + "."; + + List kops = new ArrayList(); + kops.add(new KeyObjectPair(basePath + "STR_BOB", "test")); + kops.add(new KeyObjectPair(basePath + "STR_NULL", "not_null")); + kops.add(new KeyObjectPair(basePath + "STR_ENDS_WITH_XXX", "XXX")); + kops.add(new KeyObjectPair(basePath + "LNG_TIME", 60L)); //As exact value type (Long) + kops.add(new KeyObjectPair(basePath + "INT_NUMBER", "30")); //Supply string for conversion + kops.add(new KeyObjectPair(basePath + "DBL_NUMBER", 123.456D)); //As exact value type (Double) + kops.add(new KeyObjectPair(basePath + "FLAG_TRUE", false)); + kops.add(new KeyObjectPair(basePath + "FLAG_FALSE", true)); + kops.add(new KeyObjectPair(basePath + "FLAG_NULL", Boolean.TRUE)); + + + FixedValueLoader loader = new FixedValueLoader(); + loader.setKeyObjectPairValues(kops); + + LoaderValues result = loader.load(appDef, appValuesBuilder); + + assertEquals(0, result.getProblems().size()); + assertEquals(0L, result.getValues().stream().filter(p -> p.hasProblems()).count()); + assertEquals("test", result.getExplicitValue(STR_BOB)); + assertEquals("not_null", result.getExplicitValue(STR_NULL)); + assertEquals("XXX", result.getExplicitValue(STR_ENDS_WITH_XXX)); + assertEquals(60L, result.getExplicitValue(LNG_TIME)); + assertEquals(30, result.getExplicitValue(INT_NUMBER)); + assertEquals(Boolean.FALSE, result.getExplicitValue(FLAG_TRUE)); + assertEquals(Boolean.TRUE, result.getExplicitValue(FLAG_FALSE)); + assertEquals(Boolean.TRUE, result.getExplicitValue(FLAG_NULL)); + } + + @Test + public void duplicatePropertyValuesCauseProblems() { + List props = new ArrayList(); + props.add(new PropertyValue(STR_BOB, "test")); + props.add(new PropertyValue(STR_BOB, "ignored because its a duplicate property entry")); + + FixedValueLoader loader = new FixedValueLoader(); + loader.setPropertyValues(props); + + LoaderValues result = loader.load(appDef, appValuesBuilder); + + //Make sure we have a duplicate problem reported + assertEquals(1, result.getProblems().size()); + assertEquals(0L, result.getValues().stream().filter(p -> p.hasProblems()).count()); //No problems at an ind level + Problem p = result.getProblems().get(0); + assertTrue(p instanceof LoaderProblem.DuplicatePropertyLoaderProblem); + assertEquals(STR_BOB, ((LoaderProblem.DuplicatePropertyLoaderProblem) p).getBadValueCoord().getProperty()); + + //The initial assignment should have still worked + assertEquals("test", result.getExplicitValue(SimpleParams.STR_BOB)); + } + + @Test + public void duplicateKeyObjectPairsCauseProblems() throws ParsingException { + String basePath = SimpleParams.class.getCanonicalName() + "."; + + List kops = new ArrayList(); + kops.add(new KeyObjectPair(basePath + "STR_BOB", "test")); + kops.add(new KeyObjectPair(basePath + "STR_BOB", "ignored because its a duplicate property entry")); + + + FixedValueLoader loader = new FixedValueLoader(); + loader.setKeyObjectPairValues(kops); + + LoaderValues result = loader.load(appDef, appValuesBuilder); + + //Make sure we have a duplicate problem reported + assertEquals(1, result.getProblems().size()); + assertEquals(0L, result.getValues().stream().filter(p -> p.hasProblems()).count()); //No problems at an ind level + Problem p = result.getProblems().get(0); + assertTrue(p instanceof LoaderProblem.DuplicatePropertyLoaderProblem); + assertEquals(STR_BOB, ((LoaderProblem.DuplicatePropertyLoaderProblem) p).getBadValueCoord().getProperty()); + + //The initial assignment should have still worked + assertEquals("test", result.getExplicitValue(SimpleParams.STR_BOB)); + } + + @Test + public void duplicateBetweenPVsAndKopsCauseProblems() throws ParsingException { + + //Set based on PropertyValues + List props = new ArrayList(); + props.add(new PropertyValue(STR_BOB, "test")); + + //Set based on KeyObjectPair + String basePath = SimpleParams.class.getCanonicalName() + "."; + List kops = new ArrayList(); + kops.add(new KeyObjectPair(basePath + "STR_BOB", "ignored because its a duplicate property entry")); + + FixedValueLoader loader = new FixedValueLoader(); + loader.setPropertyValues(props); + loader.setKeyObjectPairValues(kops); + + LoaderValues result = loader.load(appDef, appValuesBuilder); + + //Make sure we have a duplicate problem reported + assertEquals(1, result.getProblems().size()); + assertEquals(0L, result.getValues().stream().filter(p -> p.hasProblems()).count()); //No problems at an ind level + Problem p = result.getProblems().get(0); + assertTrue(p instanceof LoaderProblem.DuplicatePropertyLoaderProblem); + assertEquals(STR_BOB, ((LoaderProblem.DuplicatePropertyLoaderProblem) p).getBadValueCoord().getProperty()); + + //The initial assignment should have still worked + assertEquals("test", result.getExplicitValue(SimpleParams.STR_BOB)); + } +} diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyObjectPairTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyObjectPairTest.java new file mode 100644 index 00000000..261697a0 --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyObjectPairTest.java @@ -0,0 +1,56 @@ +package org.yarnandtail.andhow.load; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.api.ParsingException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class KeyObjectPairTest { + + @Test + public void simpleTest() throws ParsingException { + KeyObjectPair kop1 = new KeyObjectPair(" Name1 ", " Value1 "); //two spaces pad start & end of name & value + KeyObjectPair kop2 = new KeyObjectPair("Name2", ""); + KeyObjectPair kop3 = new KeyObjectPair("Name3", 23L); + KeyObjectPair kop4 = new KeyObjectPair(" Name4 "); + KeyObjectPair kop5 = new KeyObjectPair("Name5", null); + + assertEquals("Name1", kop1.getName()); + assertEquals(" Value1 ", kop1.getValue()); + assertEquals("Name1 : \" Value1 \"", kop1.toString()); + + assertEquals("Name2", kop2.getName()); + assertEquals("", kop2.getValue()); + assertEquals("Name2 : \"\"", kop2.toString()); + + assertEquals("Name3", kop3.getName()); + assertEquals(23L, kop3.getValue()); + assertEquals("Name3 : \"23\"", kop3.toString()); + + assertEquals("Name4", kop4.getName()); + assertNull(kop4.getValue()); + assertEquals("Name4 : \"[null]\"", kop4.toString()); + + assertEquals("Name5", kop5.getName()); + assertNull(kop5.getValue()); + assertEquals("Name5 : \"[null]\"", kop5.toString()); + } + + @Test + public void emptyNameShouldBeAnError() { + assertThrows(ParsingException.class, () -> { + new KeyObjectPair(" ", "SomeValue"); + }); + + assertThrows(ParsingException.class, () -> { + new KeyObjectPair("", "SomeValue"); + }); + + assertThrows(ParsingException.class, () -> { + new KeyObjectPair(null, "SomeValue"); + }); + } + +} \ No newline at end of file diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java index 43f86b25..44e675c5 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/load/KeyValuePairLoaderTest.java @@ -17,46 +17,12 @@ /** * Note: This directly tests a single loader so it is not possible to * test for missing required values. Loaders can't know if a value is missing - - * that only can be figured out after all loaders are comlete. + * that only can be figured out after all loaders are complete. * * @author eeverman */ -public class KeyValuePairLoaderTest { - - StaticPropertyConfigurationMutable appDef; - ValidatedValuesWithContextMutable appValuesBuilder; - - public interface SimpleParams { - //Strings - StrProp STR_BOB = StrProp.builder().aliasIn("String_Bob").aliasInAndOut("Stringy.Bob").defaultValue("bob").build(); - StrProp STR_NULL = StrProp.builder().aliasInAndOut("String_Null").build(); - StrProp STR_ENDS_WITH_XXX = StrProp.builder().mustEndWith("XXX").build(); - +public class KeyValuePairLoaderTest extends BaseForLoaderTests { - //Flags - FlagProp FLAG_FALSE = FlagProp.builder().defaultValue(false).build(); - FlagProp FLAG_TRUE = FlagProp.builder().defaultValue(true).build(); - FlagProp FLAG_NULL = FlagProp.builder().build(); - } - - @BeforeEach - public void init() throws Exception { - appValuesBuilder = new ValidatedValuesWithContextMutable(); - - CaseInsensitiveNaming bns = new CaseInsensitiveNaming(); - - GroupProxy proxy = AndHowUtil.buildGroupProxy(SimpleParams.class); - - appDef = new StaticPropertyConfigurationMutable(bns); - appDef.addProperty(proxy, SimpleParams.STR_BOB); - appDef.addProperty(proxy, SimpleParams.STR_NULL); - appDef.addProperty(proxy, SimpleParams.STR_ENDS_WITH_XXX); - appDef.addProperty(proxy, SimpleParams.FLAG_FALSE); - appDef.addProperty(proxy, SimpleParams.FLAG_TRUE); - appDef.addProperty(proxy, SimpleParams.FLAG_NULL); - - } - @Test public void testCmdLineLoaderHappyPathAsList() { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java index 494e0b91..01a38282 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/DblTypeTest.java @@ -47,6 +47,14 @@ public void testParseNotANumber() { type.parse("apple") ); } + + @Test + public void stringMarkedAsLongIsError() { + DblType type = DblType.instance(); + assertThrows(ParsingException.class, () -> + type.parse("34L") + ); + } @Test public void testParseEmpty() { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java index 2a17b876..5342b70d 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/valuetype/LngTypeTest.java @@ -49,6 +49,14 @@ public void testParseEmpty() throws ParsingException { type.parse("") ); } + + @Test + public void stringMarkedWithLSufixIsError() { + LngType type = LngType.instance(); + assertThrows(ParsingException.class, () -> + type.parse("34L") + ); + } @Test public void testParseTooBig() { diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties new file mode 100644 index 00000000..aae45645 --- /dev/null +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.all.props.speced.properties @@ -0,0 +1,8 @@ +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.APP_NAME = " Big App " +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_HOST = aquarius.usgs.gov +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_PORT = 8080 +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_SERVICE_NAME = doquery/ +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.AUTH_KEY = abc123 +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.RETRY_COUNT = 4 +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REQUEST_META_DATA = false +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REQUEST_SUMMARY_DATA = true \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.invalid.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.invalid.properties new file mode 100644 index 00000000..204d6ef7 --- /dev/null +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.invalid.properties @@ -0,0 +1,7 @@ +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_HOST = aquarius.usgs.com +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_PORT = 999999 +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_SERVICE_NAME = doquery +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.AUTH_KEY = abc123 +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.RETRY_COUNT = NotReallyANumber +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REQUEST_META_DATA = false +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REQUEST_SUMMARY_DATA = true \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.minimum.props.speced.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.minimum.props.speced.properties new file mode 100644 index 00000000..06e5c1f7 --- /dev/null +++ b/andhow-core/src/test/resources/org/yarnandtail/andhow/StdConfigSimulatedAppTest.minimum.props.speced.properties @@ -0,0 +1,11 @@ +# +# Only required points are specified +# + +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_HOST = aquarius.usgs.gov +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_PORT = 8080 +# org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REST_SERVICE_NAME = doquery +org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.AUTH_KEY = abc123 +# org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.RETRY_COUNT = 4 +# org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REQUEST_META_DATA = false +# org.yarnandtail.andhow.StdConfigSimulatedAppTest.SampleRestClientGroup.REQUEST_SUMMARY_DATA = true \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/all.points.speced.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/all.points.speced.properties deleted file mode 100644 index db9f8fd7..00000000 --- a/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/all.points.speced.properties +++ /dev/null @@ -1,8 +0,0 @@ -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.APP_NAME = " Big App " -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_HOST = aquarius.usgs.gov -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_PORT = 8080 -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_SERVICE_NAME = doquery/ -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.AUTH_KEY = abc123 -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.RETRY_COUNT = 4 -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REQUEST_META_DATA = false -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REQUEST_SUMMARY_DATA = true \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/invalid.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/invalid.properties deleted file mode 100644 index 340cac1a..00000000 --- a/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/invalid.properties +++ /dev/null @@ -1,7 +0,0 @@ -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_HOST = aquarius.usgs.com -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_PORT = 999999 -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_SERVICE_NAME = doquery -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.AUTH_KEY = abc123 -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.RETRY_COUNT = NotReallyANumber -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REQUEST_META_DATA = false -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REQUEST_SUMMARY_DATA = true \ No newline at end of file diff --git a/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/minimum.points.speced.properties b/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/minimum.points.speced.properties deleted file mode 100644 index 7331d91c..00000000 --- a/andhow-core/src/test/resources/org/yarnandtail/andhow/example/restclient/minimum.points.speced.properties +++ /dev/null @@ -1,11 +0,0 @@ -# -# Only required points are specified -# - -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_HOST = aquarius.usgs.gov -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_PORT = 8080 -# org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REST_SERVICE_NAME = doquery -org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.AUTH_KEY = abc123 -# org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.RETRY_COUNT = 4 -# org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REQUEST_META_DATA = false -# org.yarnandtail.andhow.example.restclient.SampleRestClientGroup.REQUEST_SUMMARY_DATA = true \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java index a0cde875..68d8729d 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep1/EarthMapMakerTest.java @@ -15,12 +15,19 @@ */ public class EarthMapMakerTest extends AndHowJunit5TestBase { + /** + * This test will see the properties set in the andhow.properties file on the classpath. + * Currently that file is at: /src/main/resources/andhow.properties
    + * Optionally, an set of properties used only during testing could be placed at: + * /src/test/resources/andhow.properties
    + * That file would then be used exclusively during testing. + */ @Test public void testConfigFromPropertiesFileOnly() { EarthMapMaker emm = new EarthMapMaker(); - //Actual values + //These values match what is loaded from the andhow.properties file assertEquals("My Map", emm.getMapName()); assertEquals(-125, emm.getWestBound()); assertEquals(51, emm.getNorthBound()); @@ -31,62 +38,44 @@ public void testConfigFromPropertiesFileOnly() { assertEquals("http://prod.mybiz.com.logger/EarthMapMaker/", emm.getLogServerUrl()); } - - @Test - public void testConfigFromSysProps() { - - System.setProperty("com.dep1.EarthMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep1.EarthMapMaker.EAST_BOUND", "-99"); - - NonProductionConfig.instance().forceBuild(); - - EarthMapMaker emm = new EarthMapMaker(); - - //Actual values - assertEquals("SysPropMapName", emm.getMapName()); - assertEquals(-99, emm.getEastBound()); - } - + + /** + * Sometimes a test will need custom configuration, such as wanting to + * test your app when it is configured a particular way. + * This example does that for just this one method. + * + * By using the base class AndHowJunit5TestBase, AndHow + * configuration will automatically revert to its original state + * (configured via the property file andhow.properties) + * when this method completes. + */ @Test - public void testConfigFromCmdLineThenSysPropsViaNonProdConfigForceBuild() { - - System.setProperty("com.dep1.EarthMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep1.EarthMapMaker.EAST_BOUND", "-99"); - + public void testUsingCustomConfiguration() { - NonProductionConfig.instance().setCmdLineArgs(new String[] { - "com.dep1.EarthMapMaker.MAP_NAME=CmdLineMapName", - "com.dep1.EarthMapMaker.WEST_BOUND=-179"}) + NonProductionConfig.instance() + .addFixedValue(EarthMapMaker.MAP_NAME, "SomeNameILike") /* via Property reference */ + .addFixedValue("com.dep1.EarthMapMaker.EAST_BOUND", 42) /* via Property name */ .forceBuild(); - - EarthMapMaker emm = new EarthMapMaker(); - - //Actual values - assertEquals("CmdLineMapName", emm.getMapName()); - assertEquals(-99, emm.getEastBound()); - assertEquals(-179, emm.getWestBound()); - } - - @Test - public void testConfigFromCmdLineThenSysPropsViaProdConfigUtilForceBuild() { - - System.setProperty("com.dep1.EarthMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep1.EarthMapMaker.EAST_BOUND", "-99"); - - AndHowNonProductionUtil.forceRebuild( - AndHow.findConfig().setCmdLineArgs(new String[] { - "com.dep1.EarthMapMaker.MAP_NAME=CmdLineMapName", - "com.dep1.EarthMapMaker.WEST_BOUND=-179"}) - ); - + EarthMapMaker emm = new EarthMapMaker(); - - //Actual values - assertEquals("CmdLineMapName", emm.getMapName()); - assertEquals(-99, emm.getEastBound()); - assertEquals(-179, emm.getWestBound()); + + //These values were customized above + assertEquals("SomeNameILike", emm.getMapName()); + assertEquals(42, emm.getEastBound()); + + //All other values still come from the andhow.properties file + assertEquals(51, emm.getNorthBound()); } - + + + /** + * This just demonstrates the order of loading. + * The System Properties Loader is earlier in the loader list than the + * JNDI loader, thus, values set as System Properties 'win' and override + * values found in JNDI. Properties files are read last of all. + * + * @throws Exception + */ @Test public void testOrderOfLoading() throws Exception { @@ -99,6 +88,8 @@ public void testOrderOfLoading() throws Exception { jndi.bind("java:" + "com.dep1.EarthMapMaker.SOUTH_BOUND", "7"); jndi.bind("java:comp/env/" + "org.dataprocess.ExternalServiceConnector.ConnectionConfig.SERVICE_URL", "test/"); jndi.activate(); + + NonProductionConfig.instance().forceBuild(); //VALUES IN THE PROPS FILE //org.dataprocess.ExternalServiceConnector.ConnectionConfig.SERVICE_URL = http://forwardcorp.com/service/ @@ -121,25 +112,19 @@ public void testOrderOfLoading() throws Exception { assertEquals(-99, emm.getEastBound()); assertEquals(7, emm.getSouthBound()); } - + + /** + * Validation is always enforced + */ @Test - public void testBadInt() { - - System.setProperty("com.dep1.EarthMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep1.EarthMapMaker.EAST_BOUND", "East"); - - try { + public void testAttemptToAssignAnInvalidValue() { + + //LOG_SERVER must start w/ "http://" + assertThrows(AppFatalException.class, () ->{ NonProductionConfig.instance() - .setCmdLineArgs(new String[] { - "com.dep1.EarthMapMaker.MAP_NAME=CmdLineMapName", - "com.dep1.EarthMapMaker.WEST_BOUND=-179"}) + .addFixedValue("com.dep1.EarthMapMaker.LOG_SERVER", "ftp://blah/") .forceBuild(); - - fail("Expected an exception"); - } catch (AppFatalException afe) { - assertEquals(1, afe.getProblems().size()); - assertTrue(afe.getProblems().get(0) instanceof LoaderProblem.StringConversionLoaderProblem); - } + }); } } diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java index b3ac61f0..e7d55601 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/com/dep2/MarsMapMakerTest.java @@ -15,13 +15,25 @@ */ public class MarsMapMakerTest extends AndHowJunit5TestBase { + /** + * Its OK to do nothing. AndHow will initialize itself at the point + * of the first Property value access. + * + * AndHowJunit5TestBase allows you to modify AndHow's state + * for each test class and/or for each test method - something + * that is NOT possible in production. + * If you use the AndHowJunit5TestBase for your tests, any changes + * you make to AndHow's state in a test class or during a test + * method are reverted after each method / test class completes. + */ @Test public void testConfigFromPropertiesFileOnly() { MarsMapMaker mmm = new MarsMapMaker(); - - //Actual values - assertEquals("My Map", mmm.getMapName()); + + //These values read from andhow.properties - see the EarthMapMakerTest for + //more details. + assertEquals("My Map", mmm.getMapName()); //AndHow initializes here if it hasn't already. assertEquals(-125, mmm.getWestBound()); assertEquals(51, mmm.getNorthBound()); assertEquals(-65, mmm.getEastBound()); @@ -31,95 +43,5 @@ public void testConfigFromPropertiesFileOnly() { assertEquals("http://prod.mybiz.com.logger/MarsMapMaker/", mmm.getLogServerUrl()); } - - @Test - public void testConfigFromSysProps() { - - System.setProperty("com.dep2.MarsMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep2.MarsMapMaker.EAST_BOUND", "-99"); - - NonProductionConfig.instance().forceBuild(); - - MarsMapMaker mmm = new MarsMapMaker(); - - //Actual values - assertEquals("SysPropMapName", mmm.getMapName()); - assertEquals(-99, mmm.getEastBound()); - } - - @Test - public void testConfigFromCmdLineThenSysProps() { - - System.setProperty("com.dep2.MarsMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep2.MarsMapMaker.EAST_BOUND", "-99"); - - - NonProductionConfig.instance().setCmdLineArgs(new String[] { - "com.dep2.MarsMapMaker.MAP_NAME=CmdLineMapName", - "com.dep2.MarsMapMaker.WEST_BOUND=-179"}) - .forceBuild(); - - MarsMapMaker mmm = new MarsMapMaker(); - - //Actual values - assertEquals("CmdLineMapName", mmm.getMapName()); - assertEquals(-99, mmm.getEastBound()); - assertEquals(-179, mmm.getWestBound()); - } - - @Test - public void testOrderOfLoading() throws Exception { - - System.setProperty("com.dep2.MarsMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep2.MarsMapMaker.EAST_BOUND", "-99"); - - - SimpleNamingContextBuilder jndi = getJndi(); - jndi.bind("java:" + "com.dep2.MarsMapMaker.MAP_NAME", "JndiPropMapName"); - jndi.bind("java:" + "com.dep2.MarsMapMaker.SOUTH_BOUND", "7"); - jndi.bind("java:comp/env/" + "org.dataprocess.ExternalServiceConnector.ConnectionConfig.SERVICE_URL", "test/"); - jndi.activate(); - - //VALUES IN THE PROPS FILE - //org.dataprocess.ExternalServiceConnector.ConnectionConfig.SERVICE_URL = http://forwardcorp.com/service/ - //org.dataprocess.ExternalServiceConnector.ConnectionConfig.TIMEOUT = 60 - //com.dep2.MarsMapMaker.EAST_BOUND = -65 - //com.dep2.MarsMapMaker.MAP_NAME = My Map - //com.dep2.MarsMapMaker.NORTH_BOUND = 51 - //com.dep2.MarsMapMaker.SOUTH_BOUND = 23 - //com.dep2.MarsMapMaker.WEST_BOUND = -125 - - - ExternalServiceConnector esc = new ExternalServiceConnector(); - assertEquals("test/", esc.getConnectionUrl()); - assertEquals(60, esc.getConnectionTimeout()); - - MarsMapMaker mmm = new MarsMapMaker(); - assertEquals("SysPropMapName", mmm.getMapName()); - assertEquals(-125, mmm.getWestBound()); - assertEquals(51, mmm.getNorthBound()); - assertEquals(-99, mmm.getEastBound()); - assertEquals(7, mmm.getSouthBound()); - } - - @Test - public void testBadInt() { - - System.setProperty("com.dep2.MarsMapMaker.MAP_NAME", "SysPropMapName"); - System.setProperty("com.dep2.MarsMapMaker.EAST_BOUND", "East"); - - try { - NonProductionConfig.instance() - .setCmdLineArgs(new String[] { - "com.dep2.MarsMapMaker.MAP_NAME=CmdLineMapName", - "com.dep2.MarsMapMaker.WEST_BOUND=-179"}) - .forceBuild(); - - fail("Expected an exception"); - } catch (AppFatalException afe) { - assertEquals(1, afe.getProblems().size()); - assertTrue(afe.getProblems().get(0) instanceof LoaderProblem.StringConversionLoaderProblem); - } - } } diff --git a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java index 60b0da14..b17c0d58 100644 --- a/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java +++ b/andhow-testing/andhow-simulated-app-tests/andhow-multimodule-dataprocess/andhow-default-behavior-test/src/test/java/org/dataprocess/ExternalServiceConnectorTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.*; /** + * The EarthMapMakerTest has more complete examples of testing. * * @author ericeverman */ @@ -13,11 +14,7 @@ public class ExternalServiceConnectorTest extends AndHowJunit5TestBase { public ExternalServiceConnectorTest() { } - - /** - * Test of getConnectionUrl method, of class EarthMapMaker. - */ @Test public void testAllConfigValues() { ExternalServiceConnector esc = new ExternalServiceConnector(); From d815e6ce6910c9aab566bdebe662ff00c5726fdb Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Sat, 10 Jul 2021 23:15:03 -0600 Subject: [PATCH 90/91] Testing improvements for StdConfig (#511) * Testing improvements for StdConfig * Fix build error * Add forgotten test annotation * More tests, improved docs * Improved javadocs Co-authored-by: Eric Everman --- .../org/yarnandtail/andhow/StdConfig.java | 57 ++++--- .../load/PropFileOnClasspathLoader.java | 4 +- .../std/StdPropFileOnClasspathLoader.java | 45 +++++- .../andhow/StdConfigGetterAndSetterTest.java | 141 +++++++++++++++++- .../java/org/yarnandtail/andhow/TestUtil.java | 92 ++++++++++++ 5 files changed, 309 insertions(+), 30 deletions(-) create mode 100644 andhow-core/src/test/java/org/yarnandtail/andhow/TestUtil.java diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java b/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java index e2ae1d3a..eb7364a7 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java @@ -116,12 +116,15 @@ public S setClasspathPropFilePath(String classpathPropFilePathString) { + "be specified as both a String and StrProp"); } - if (classpathPropFilePathString != null && !classpathPropFilePathString.startsWith("/") - && (classpathPropFilePathString.endsWith(".properties") || classpathPropFilePathString.endsWith(".xml"))) { - - throw new IllegalArgumentException("The path to the property file on " - + "the classpath should start with a '/' if the filename contains a dot."); + if ( + classpathPropFilePathString != null && + classpathPropFilePathString.contains(".") && + !classpathPropFilePathString.startsWith("/") + ) { + throw new IllegalArgumentException("A path to a property file on the classpath " + + "must start with a '/' if the filename contains a dot."); } + this.classpathPropFilePathStr = classpathPropFilePathString; return (S) this; @@ -156,10 +159,20 @@ public S setClasspathPropFilePath(StrProp classpathPropFilePathProperty) { } /** - * If set, the properties file loaded by StdPropFileOnClasspathLoader must - * be found and a RuntimeException will be thrown if it is not found. + * If called to set this to 'required', a classpath properties file must + * exist and be readable. This flag is used by the {@Code StdPropFileOnClasspathLoader}. + * + * Since the {@Code StdPropFileOnClasspathLoader} has a default property file name, + * {@Code /andhow.properties}, setting this to 'required' means that either that + * default file name or another that you configure instead must exist. + * + * @See setClasspathPropFilePath methods for details on using a non-default + * classpath properties file. * - * This is not set by default, allowing the properties file to be optional. + * A RuntimeException will be thrown if this is set to 'required' and there + * is no classpath properties file that can be read. + *
    + * This is NOT set by default, allowing the properties file to be optional. * * @return */ @@ -169,10 +182,9 @@ public S classpathPropertiesRequired() { } /** - * If set, the properties file loaded by StdPropFileOnClasspathLoader is - * optional and will not throw an error if it is not found. + * Sets the properties file on the classpath to be optional, the default. * - * This is set by default, so there is no need to explicitly call it. + * @See classpathPropertiesRequired * * @return */ @@ -202,10 +214,22 @@ public S setFilesystemPropFilePath(StrProp filesystemPropFilePath) { } /** - * If set, the properties file loaded by StdPropFileOnFilesystemLoader must - * be found and a RuntimeException will be thrown if it is not found. + * If called to set this to 'required', a non-null configured value for the + * filesystem properties file must point to an existing, readable properties + * file. This flag is used by the {@Code StdPropFileOnFilesystemLoader}. * - * This is not set by default, allowing the properties file to be optional. + * A RuntimeException will be thrown if this is set to 'required' and there + * is a path specified which points to a file that does not exist. + * Configuring a filesystem path is a two step process:
      + *
    • First, a StrProp Property must be specified for this configuration + * via the {@Code setFilesystemPropFilePath} method
    • + *
    • Then, a value must be configured for in an any way that AndHow + * reads and loads values, such as environment vars, system properties, etc..
    • + *
    + * If and non-null value is configured, its doesn't point to a readable properties + * file, AND this required flag is set, a RuntimeException will be thrown at startup. + *
    + * This is NOT set by default, allowing the properties file to be optional. * * @return */ @@ -215,10 +239,9 @@ public S filesystemPropFileRequired() { } /** - * If set, the properties file loaded by StdPropFileOnFilesystemLoader is - * optional and will not throw an error if it is not found. + * Sets the properties file on the filesystem to be optional, the default. * - * This is set by default, so there is no need to explicitly call it. + * @See setFilesystemPropFilePath * * @return */ diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoader.java b/andhow-core/src/main/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoader.java index 83d632e0..e6a8c1c3 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoader.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/load/PropFileOnClasspathLoader.java @@ -7,8 +7,8 @@ import org.yarnandtail.andhow.internal.LoaderProblem; /** - * Reads from a Java .property file on the classpath, following standard java - * conventions for the structure of those file. + * Reads from a Java .properties file on the classpath, following standard java + * conventions for the structure of those key:value pair files. * * This loader finds the properties file via either a String or StrProperty * specified in the constructor. If the StrProperty is used, an earlier an diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdPropFileOnClasspathLoader.java b/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdPropFileOnClasspathLoader.java index ff3b7166..c59cfa6c 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdPropFileOnClasspathLoader.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/load/std/StdPropFileOnClasspathLoader.java @@ -42,13 +42,39 @@ * Full details on how Java parses properties files can be found in the * properties file specification. *

    - * Configuring the name or classpath of the properties file can be used to - * enable different configuration profiles based on the environment. - * For instance, a system property could specify that {@code /test.properties} - * be used on a test server and {@code /production.properties} on a production - * server. An example of configuring the property file path: - *
    - *

    + * Since resources on the test classpath override those on the main classpath,
    + * its easy to have separate configurations for production and testing simply
    + * by having two {@code andhow.properties} files, one on the main classpath
    + * and one on test.  There are many other options with classpath properties files.
    + * 

    + * To change the name of the properties file used for testing, a class like the + * example below can be added to your test classpath: + *

    {@Code
    + * import org.yarnandtail.andhow.*;
    + * import org.yarnandtail.andhow.property.StrProp;
    + *
    + * public class UsePropertyFileOnClasspath implements AndHowTestInit {
    + *
    + *   {@literal @}Override
    + *   public AndHowConfiguration getConfiguration() {
    + *     return  StdConfig.instance()
    + *       .setClasspathPropFilePath("/my_test_configuration_properties_file.prop");
    + *   }
    + * }
    + * }
    + * If AndHow finds a {@Code AndHowTestInit} implementation on the classpath, + * it uses it in preference to other initiation points. + * With this only on the test classpath, AndHow will still use the default + * {@code andhow.properties} for production. + *

    + * With slightly fancier configuration, something like {@code /staging.properties} + * could be used in a staging environment and {@code /production.properties} + * used in production. In a case like this we need the classpath to be + * configurable, not hard-coded like the example above. To do that we need + * a Property to configure it with, then we tell AndHow which property was + * used.
    + * Here is an example of a configurable classpath: + *

    {@Code
      * import org.yarnandtail.andhow.*;
      * import org.yarnandtail.andhow.property.StrProp;
      * 
    @@ -63,7 +89,7 @@
      *       .setClasspathPropFilePath(MY_CLASSPATH);
      *   }
      * }
    - * 
    + * }
    * The code above adds the property {@code MY_CLASSPATH} * (the name is arbitrary) which is used to configure the * {@code StdPropFileOnClasspathLoader} with a custom property file location. @@ -71,6 +97,9 @@ * see if a value has been loaded for {@code MY_CLASSPATH} by any prior loader. * If a value is present, the loader tries to load from the configured classpath. * If no value is configured, the default classpath is assumed. + * Properties file loaders are the last loaders, so configuration the classpath + * can be done by virtually anything: environment vars, system properties, + * command line args, JNDI... * *

    This is a Standard Loader

    * Like all {@code StandardLoader}'s, this loader is intended to be auto-created diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java index 8ac06833..1a354d7b 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java @@ -6,9 +6,8 @@ import org.junit.jupiter.api.Test; import org.yarnandtail.andhow.StdConfig.StdConfigImpl; -import org.yarnandtail.andhow.api.Loader; -import org.yarnandtail.andhow.api.Property; -import org.yarnandtail.andhow.api.StandardLoader; +import org.yarnandtail.andhow.api.*; +import org.yarnandtail.andhow.internal.ValidatedValuesWithContextMutable; import org.yarnandtail.andhow.load.*; import org.yarnandtail.andhow.load.std.*; import org.yarnandtail.andhow.name.CaseInsensitiveNaming; @@ -19,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.hamcrest.Matchers.*; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @@ -232,6 +232,12 @@ public void setCmdLineArgsTest() { String[] args = new String[] {"abc=123", "xyz='456"}; config.setCmdLineArgs(args); assertThat(config.getCmdLineArgs().toArray(), arrayContainingInAnyOrder(args)); + + Object o = TestUtil.getField(config.buildStdMainStringArgsLoader(), "keyValuePairs"); + List kvps = (List)o; + + assertEquals(2, kvps.size()); + assertThat(kvps.toArray(), arrayContainingInAnyOrder(args)); } @Test @@ -262,6 +268,127 @@ public void setEnvironmentPropertiesTest() { } + @Test + public void setClasspathPropFilePathViaStringTest() throws Exception { + MyStdConfig config = new MyStdConfig(); + ValidatedValuesWithContextMutable vvs = new ValidatedValuesWithContextMutable(); + Class vvsClass = ValidatedValuesWithContext.class; //class of getEffectivePath argument + + assertNull(config.getClasspathPropFileProp()); + assertNull(config.getClasspathPropFilePath()); + assertEquals("/andhow.properties", + TestUtil.stringMethod(config.buildStdPropFileOnClasspathLoader(), "getEffectivePath", vvs, vvsClass), + "Loader should see the default value"); + + config.setClasspathPropFilePath("/andhow.test.properties"); + assertEquals("/andhow.test.properties", config.getClasspathPropFilePath()); + assertNull(config.getClasspathPropFileProp()); + assertEquals("/andhow.test.properties", + TestUtil.stringMethod(config.buildStdPropFileOnClasspathLoader(), "getEffectivePath", vvs, vvsClass), + "Loader should see this configured value"); + + config.setClasspathPropFilePath("/andhow.test.props"); + assertEquals("/andhow.test.props", config.getClasspathPropFilePath()); + assertEquals("/andhow.test.props", + TestUtil.stringMethod(config.buildStdPropFileOnClasspathLoader(), "getEffectivePath", vvs, vvsClass), + "Loader should see this configured value"); + + assertThrows(IllegalArgumentException.class, () -> { + config.setClasspathPropFilePath("andhow.test.props"); + }, "paths containing dots must start w/ a slash"); + + assertEquals("/andhow.test.props", config.getClasspathPropFilePath(), "Should be unchanged"); + + config.setClasspathPropFilePath("/org/comcorp/project/andhow.test.properties"); + assertEquals("/org/comcorp/project/andhow.test.properties", config.getClasspathPropFilePath()); + assertEquals("/org/comcorp/project/andhow.test.properties", + TestUtil.stringMethod(config.buildStdPropFileOnClasspathLoader(), "getEffectivePath", vvs, vvsClass), + "Loader should see this configured value"); + + config.setClasspathPropFilePath((String)null); + assertNull(config.getClasspathPropFilePath()); + assertEquals("/andhow.properties", + TestUtil.stringMethod(config.buildStdPropFileOnClasspathLoader(), "getEffectivePath", vvs, vvsClass), + "Loader should see the default value"); + + } + + @Test + public void setClasspathPropFilePathViaStrPropTest() throws Exception { + StrProp MY_PATH_PROPERTY = StrProp.builder().build(); + + MyStdConfig config = new MyStdConfig(); + + ValidatedValue validatedValue = new ValidatedValue(MY_PATH_PROPERTY, "/my.prop.file"); + List vvList = new ArrayList<>(); + vvList.add(validatedValue); + + LoaderValues loaderValues = new LoaderValues(new FixedValueLoader(), vvList, ProblemList.EMPTY_PROBLEM_LIST); + ValidatedValuesWithContextMutable validatedValues = new ValidatedValuesWithContextMutable(); + validatedValues.addValues(loaderValues); + Class vvsClass = ValidatedValuesWithContext.class; //class of getEffectivePath argument + + config.setClasspathPropFilePath(MY_PATH_PROPERTY); + assertNull(config.getClasspathPropFilePath()); + assertEquals(MY_PATH_PROPERTY, config.getClasspathPropFileProp()); + assertEquals("/my.prop.file", + TestUtil.stringMethod(config.buildStdPropFileOnClasspathLoader(), "getEffectivePath", validatedValues, vvsClass), + "Loader should see this configured value"); + + config.setClasspathPropFilePath((StrProp)null); + assertNull(config.getClasspathPropFilePath()); + assertNull(config.getClasspathPropFileProp()); + assertEquals("/andhow.properties", + TestUtil.stringMethod(config.buildStdPropFileOnClasspathLoader(), "getEffectivePath", validatedValues, vvsClass), + "Loader should revert to default"); + } + + @Test + public void setClasspathPropFilePathInteractionOfStringAndStrPropTest() { + StrProp MY_PATH_PROPERTY = StrProp.builder().build(); + MyStdConfig config = new MyStdConfig(); + + config.setClasspathPropFilePath(MY_PATH_PROPERTY); + assertThrows(IllegalArgumentException.class, () -> { + config.setClasspathPropFilePath("/some.other.path"); + }, "Can't set via String and StrProp at the same time"); + assertNull(config.getClasspathPropFilePath(), "Should not have set the value due to error"); + + config.setClasspathPropFilePath((StrProp)null); + config.setClasspathPropFilePath("/some.other.path"); //now its OK + assertEquals("/some.other.path", config.getClasspathPropFilePath()); + + assertThrows(IllegalArgumentException.class, () -> { + config.setClasspathPropFilePath(MY_PATH_PROPERTY); + }, "String version is set, so now this one causes the error"); + assertNull(config.getClasspathPropFileProp(), "Should not have set the value due to error"); + + } + + @Test void classpathPropertiesRequiredOrNotTest() { + MyStdConfig config = new MyStdConfig(); + + assertFalse(config.getClasspathPropFileRequired(), "False should be the default"); + assertFalse(config.buildStdPropFileOnClasspathLoader().isMissingFileAProblem(), "False should be the default"); + + config.classpathPropertiesRequired(); + + assertTrue(config.getClasspathPropFileRequired()); + assertTrue(config.buildStdPropFileOnClasspathLoader().isMissingFileAProblem()); + } + + @Test void filesystemPropertiesRequiredOrNotTest() { + MyStdConfig config = new MyStdConfig(); + + assertFalse(config.getFilesystemPropFileRequired(), "False should be the default"); + assertFalse(config.buildStdPropFileOnFilesystemLoader().isMissingFileAProblem(), "False should be the default"); + + config.filesystemPropFileRequired(); + + assertTrue(config.getFilesystemPropFileRequired()); + assertTrue(config.buildStdPropFileOnFilesystemLoader().isMissingFileAProblem()); + } + boolean containsPropertyAndValue(List propertyValues, Property property, T value) { PropertyValue pv = propertyValues.stream().filter(p -> p.getProperty().equals(property)).findFirst().get(); return pv != null && pv.getValue().equals(value); @@ -291,6 +418,14 @@ public List getCmdLineArgs() { public Map getEnvironmentProperties() { return envProperties; } + + public String getClasspathPropFilePath() { return classpathPropFilePathStr; } + + public StrProp getClasspathPropFileProp() { return this.classpathPropFilePathProp; } + + public boolean getClasspathPropFileRequired() { return _missingClasspathPropFileAProblem; } + + public boolean getFilesystemPropFileRequired() { return _missingFilesystemPropFileAProblem; } } } diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/TestUtil.java b/andhow-core/src/test/java/org/yarnandtail/andhow/TestUtil.java new file mode 100644 index 00000000..9b1ce7db --- /dev/null +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/TestUtil.java @@ -0,0 +1,92 @@ +package org.yarnandtail.andhow; + +import org.junit.platform.commons.function.Try; +import org.junit.platform.commons.support.HierarchyTraversalMode; +import org.junit.platform.commons.support.ReflectionSupport; +import org.junit.platform.commons.util.Preconditions; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP; + +/** + * Testing Utils + */ +public class TestUtil { + + /** + * Invokes the named String returning method on the instance via reflection, + * bypassing visibility. + * + * For some tests, this is an easy way test the internal effect of some actions. + * JUnit ReflectionSupport *should* find methods without having to pass types, but + * this bug prevents that: + * https://github.com/junit-team/junit5/issues/2663 + * + * @param instance The object instance to call the method on. + * @param name The name of a String returning method, which must be an instance method. + * @param args Arguments to the method + * @param types The argument types of the method which must match the method, not the args. + * @return The String returned by the method + * @throws Exception + */ + public static String stringMethod(Object instance, String name, Object[] args, Class[] types) { + return (String)invokeMethod(instance, name, args, types); + } + + public static String stringMethod(Object instance, String name, Object arg, Class type) { + return (String)invokeMethod(instance, name, new Object[]{arg}, new Class[]{type}); + } + + public static String stringMethod(Object instance, String name, Object... args) { + return (String)invokeMethod(instance, name, args, getTypes(args)); + } + + public static Object getField(Object instance, String name) { + List fields = ReflectionSupport.findFields(instance.getClass(), f -> f.getName().equals(name), HierarchyTraversalMode.BOTTOM_UP); + + if (fields.size() != 1) { + throw new IllegalArgumentException("Expected to find 1 field, instead found: " + fields.size()); + } + + Optional optObj = ReflectionSupport.tryToReadFieldValue(fields.get(0), instance).toOptional(); + + if (optObj.isPresent()) { + return optObj.get(); + } else { + throw new IllegalArgumentException("Unable to return a value"); + } + } + + public static Object invokeMethod(Object instance, String name, Object[] args, Class[] types) { + + Optional method = ReflectionSupport.findMethod(instance.getClass(), name, types); + + if (method.isPresent()) { + return ReflectionSupport.invokeMethod(method.get(), instance, args); + } else { + throw new IllegalArgumentException("Method not found"); + } + } + + public static Class[] getTypes(Object... args) { + List types = new ArrayList(); + + if (args != null) { + types = Arrays.stream(args).map(a -> a.getClass()).collect(Collectors.toList()); + } + + return types.toArray(new Class[types.size()]); + } + + + +} From 63404e77cb74c8e49c3cdf81bd03c8d19b565d2b Mon Sep 17 00:00:00 2001 From: Eric <24287468+eeverman@users.noreply.github.com> Date: Tue, 20 Jul 2021 12:46:32 -0600 Subject: [PATCH 91/91] Fixes Issue #512 to improve config and testing UX (#513) * Initial work * Improved test example * Add Junit extension * Large update to JUnit 5 testing utils and examples * Fixed test naming to ensure tests are run * Fixed a few broken tests * cleanup * Move all methods that are supported parts of StdConfig to the AndHowConfiguration interface. A lot of those methods evolved in StdConfig or in BaseConfig and were never brought back to the interface. Now that the interface needs to be functional on its own, the method signitures need to move. * the getDefaultLoaderList() method was static and now is an instance method as part of this move. Its a minor, potentially breaking change, but it seems really unlikely to affect anyone. * turned off deployment of some non-distributable artifacts * Add mockito testing of the JUnit extensions Co-authored-by: Eric Everman --- .../andhow/AndHowConfiguration.java | 171 +++++++++++++++++- .../org/yarnandtail/andhow/BaseConfig.java | 11 +- .../org/yarnandtail/andhow/StdConfig.java | 109 ++--------- .../andhow/StdConfigGetterAndSetterTest.java | 2 +- .../pom.xml | 11 ++ .../example-app-1/pom.xml | 45 +++++ .../src/main/java/com/bigcorp/Calculator.java | 91 ++++++++++ .../src/main/resources/andhow.properties | 8 + .../com/bigcorp/CalculatorDefaultTest.java | 48 +++++ .../bigcorp/CalculatorDoubleConfigTest.java | 50 +++++ .../src/test/resources/andhow.properties | 4 + .../example-app-2/pom.xml | 41 +++++ .../main/java/com/bigcorp/AppInitiation.java | 20 ++ .../src/main/java/com/bigcorp/Checker.java | 82 +++++++++ .../main/resources/checker.default.properties | 30 +++ .../resources/checker.production.properties | 30 +++ .../main/resources/checker.stage.properties | 30 +++ .../CheckerDefaultAndMainArgsTest.java | 74 ++++++++ .../com/bigcorp/CheckerFixedValuesTest.java | 85 +++++++++ .../test/resources/checker.test.properties | 30 +++ .../andhow-simulated-app-tests/pom.xml | 24 ++- .../junit5/KillAndHowBeforeAllTests.java | 45 +++++ .../KillAndHowBeforeAllTestsExtension.java | 70 +++++++ .../junit5/KillAndHowBeforeEachTest.java | 48 +++++ .../KillAndHowBeforeEachTestExtension.java | 50 +++++ .../junit5/KillAndHowBeforeThisTest.java | 38 ++++ .../KillAndHowBeforeThisTestExtension.java | 72 ++++++++ ...KillAndHowBeforeAllTestsExtensionTest.java | 120 ++++++++++++ ...KillAndHowBeforeEachTestExtensionTest.java | 128 +++++++++++++ ...KillAndHowBeforeThisTestExtensionTest.java | 125 +++++++++++++ pom.xml | 16 +- 31 files changed, 1598 insertions(+), 110 deletions(-) create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-1/pom.xml create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/java/com/bigcorp/Calculator.java create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/resources/andhow.properties create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDefaultTest.java create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDoubleConfigTest.java create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/resources/andhow.properties create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/pom.xml create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/AppInitiation.java create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/Checker.java create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.default.properties create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.production.properties create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.stage.properties create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerDefaultAndMainArgsTest.java create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerFixedValuesTest.java create mode 100644 andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/resources/checker.test.properties create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTests.java create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtension.java create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTest.java create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtension.java create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTest.java create mode 100644 andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtension.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtensionTest.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtensionTest.java create mode 100644 andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtensionTest.java diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java index 4ae1ff87..8c74ddee 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/AndHowConfiguration.java @@ -2,6 +2,7 @@ import java.util.List; import org.yarnandtail.andhow.api.*; +import org.yarnandtail.andhow.property.StrProp; /** * @@ -84,6 +85,174 @@ public interface AndHowConfiguration { * @return */ C removeFixedValue(String propertyNameOrAlias); - + + /** + * Sets the classpath path to a properties file for the + * {@Code StdPropFileOnClasspathLoader} to read and load from. + * + * If no path is specified via one of the two {@Code setClasspathPropFilePath} methods, + * the default classpath of '/andhow.properties' is used. + *

    + * As per Java convention, a path on the classpath can use dots or slashes to separate packages. + * However, if the file name itself contains dots, then the path must start with a slash and use + * slashes to separate packages. + *

    + * Valid Examples: + *

      + *
    • /andhow.property - The default. The file is at the root and the name contains a dot
    • + *
    • /org/ngo/config.props - Similar to above, but in the org.ngo package, the file name is 'config.props'
    • + *
    • org.ngo.props - The package is org.ngo, the file name is 'props'. + * There are no dots in the file name, so its OK to use dots to separate the packages
    • + *
    + * + * @param classpathPropFilePathString + * @return + */ + C setClasspathPropFilePath(String classpathPropFilePathString); + + /** + * Sets the classpath path via a StrProp (a Property of String type) to a + * properties file for the {@Code StdPropFileOnClasspathLoader} to read and load from. + * + * If no path is specified via one of the two {@Code setClasspathPropFilePath} methods, + * the default classpath of '/andhow.properties' is used. + *

    + * As per Java convention, a path on the classpath can use dots or slashes to separate packages. + * However, if the file name itself contains dots, then the path must start with a slash and use + * slashes to separate packages. Its common to have a '.properties' extension on the properties + * file, so its good practice to add a validation rule to the StrProp used here to ensure it + * {@Code mustStartWith("/")}. + *

    + * Valid Examples of configured values: + *

      + *
    • /andhow.property - The default. The file is at the root and the name contains a dot
    • + *
    • /org/ngo/config.props - Similar to above, but in the org.ngo package, the file name is 'config.props'
    • + *
    • org.ngo.props - The package is org.ngo, the file name is 'props'. + * There are no dots in the file name, so its OK to use dots to separate the packages
    • + *
    + * + * @param classpathPropFilePathProperty + * @return + */ + C setClasspathPropFilePath(StrProp classpathPropFilePathProperty); + + /** + * If called to set this to 'required', a classpath properties file must + * exist and be readable. This flag is used by the {@Code StdPropFileOnClasspathLoader}. + * + * Since the {@Code StdPropFileOnClasspathLoader} has a default property file name, + * {@Code /andhow.properties}, setting this to 'required' means that either that + * default file name or another that you configure instead must exist. + * + * @See setClasspathPropFilePath methods for details on using a non-default + * classpath properties file. + * + * A RuntimeException will be thrown if this is set to 'required' and there + * is no classpath properties file that can be read. + *
    + * This is NOT set by default, allowing the properties file to be optional. + * + * @return + */ + C classpathPropertiesRequired(); + + /** + * Sets the properties file on the classpath to be optional, the default. + * + * @See classpathPropertiesRequired + * + * @return + */ + C classpathPropertiesNotRequired(); + + /** + * Sets the filesystem path via a StrProp (a Property of String type) to a + * properties file for the StdPropFileOnFilesystemLoader to load. + * + * If no property is set to specify a path, or a property is set by has no + * value, this loader won't be used. If the property is specified but the + * specified file is missing, an error will be thrown based on the + * filesystemPropFileRequired flag. + * + * Paths should generally be absolute and correctly formed for the host + * environment. + * + * @param filesystemPropFilePath + * @return + */ + C setFilesystemPropFilePath(StrProp filesystemPropFilePath); + + /** + * If called to set this to 'required', a non-null configured value for the + * filesystem properties file must point to an existing, readable properties + * file. This flag is used by the {@Code StdPropFileOnFilesystemLoader}. + * + * A RuntimeException will be thrown if this is set to 'required' and there + * is a path specified which points to a file that does not exist. + * Configuring a filesystem path is a two step process:
      + *
    • First, a StrProp Property must be specified for this configuration + * via the {@Code setFilesystemPropFilePath} method
    • + *
    • Then, a value must be configured for in an any way that AndHow + * reads and loads values, such as environment vars, system properties, etc..
    • + *
    + * If and non-null value is configured, its doesn't point to a readable properties + * file, AND this required flag is set, a RuntimeException will be thrown at startup. + *
    + * This is NOT set by default, allowing the properties file to be optional. + * + * @return + */ + C filesystemPropFileRequired(); + + /** + * Sets the properties file on the filesystem to be optional, the default. + * + * @See setFilesystemPropFilePath + * + * @return + */ + C filesystemPropFileNotRequired(); + + // + //Loader related + + /** + * The default list of standard loaders, as a list of Classes that implement + * {@Code StandardLoader} + *

    + * The returned list is disconnected from the actual list of loaders - it is + * intended to be a starting point for applications that want to modify the + * list, then call setStandardLoaders(). + *

    + * Unlike other methods of this class, it does not fluently return a method + * to itself, so your code will need a AndHowConfiguration instance reference to + * use it, eg: + *

    {@Code
    +	 * public class MyAppInitiation implements AndHowInit {
    +	 *    @Override
    +	 *  	public AndHowConfiguration getConfiguration() {
    +	 * 			AndHowConfiguration config = AndHow.findConfig();
    +	 * 			List> sll = config.getDefaultLoaderList();
    +	 * 			...do some rearranging of the list...
    +	 *
    +	 * 			config.setStandardLoaders(sll) ...and go on to call other methods on config...
    +	 * 		}
    +	 * }
    +	 * }
    + *

    + * Note: AndHow version up to and including 0.4.1 had this method as a static + * method. + * @return + */ + List> getDefaultLoaderList(); + + C setStandardLoaders(List> newStandardLoaders); + + C setStandardLoaders(Class... newStandardLoaders); + + C insertLoaderBefore(Class insertBeforeThisLoader, Loader loaderToInsert); + + C insertLoaderAfter(Class insertAfterThisLoader, Loader loaderToInsert); + void build(); } diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java b/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java index e00681e4..92600621 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/BaseConfig.java @@ -169,14 +169,9 @@ public List getRegisteredGroups() { public void build() { AndHow.instance(this); } - - /** - * The list of default loaders as a list. - * - * This is a disconnected list from any instance of the BaseConfig. - * @return - */ - public static List> getDefaultLoaderList() { + + @Override + public List> getDefaultLoaderList() { List> loaders = new ArrayList(); diff --git a/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java b/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java index eb7364a7..d3f0c53e 100644 --- a/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java +++ b/andhow-core/src/main/java/org/yarnandtail/andhow/StdConfig.java @@ -93,20 +93,7 @@ public S setCmdLineArgs(String[] commandLineArgs) { return (S) this; } - /** - * Sets the classpath path to a properties file for the - * StdPropFileOnClasspathLoader to load. - * - * If no path is specified via either a String or StrProp, the path - * '/andhow.properties' is used.
    - * - * Paths should start with a forward slash and have packages delimited by - * forward slashes. If the file name contains a dot, the path must - * start with a forward slash. - * - * @param classpathPropFilePathString - * @return - */ + @Override public S setClasspathPropFilePath(String classpathPropFilePathString) { classpathPropFilePathString = TextUtil.trimToNull(classpathPropFilePathString); @@ -130,22 +117,7 @@ public S setClasspathPropFilePath(String classpathPropFilePathString) { return (S) this; } - /** - * Sets the classpath path via a StrProp (a Property of String type) to a - * properties file for the StdPropFileOnClasspathLoader to load. - * - * If no path is specified via either a String or StrProp, the path - * '/andhow.properties' is used.
    - * - * Paths should start with a forward slash and have packages delimited by - * forward slashes. If the file name contains a dot, the path must - * start with a forward slash. Thus, it is good practice to add a validation - * rule to the StrProp used here to ensure it - * mustStartWith("/"). - * - * @param classpathPropFilePathProperty - * @return - */ + @Override public S setClasspathPropFilePath(StrProp classpathPropFilePathProperty) { if (classpathPropFilePathStr != null && classpathPropFilePathProperty != null) { @@ -158,93 +130,31 @@ public S setClasspathPropFilePath(StrProp classpathPropFilePathProperty) { return (S) this; } - /** - * If called to set this to 'required', a classpath properties file must - * exist and be readable. This flag is used by the {@Code StdPropFileOnClasspathLoader}. - * - * Since the {@Code StdPropFileOnClasspathLoader} has a default property file name, - * {@Code /andhow.properties}, setting this to 'required' means that either that - * default file name or another that you configure instead must exist. - * - * @See setClasspathPropFilePath methods for details on using a non-default - * classpath properties file. - * - * A RuntimeException will be thrown if this is set to 'required' and there - * is no classpath properties file that can be read. - *
    - * This is NOT set by default, allowing the properties file to be optional. - * - * @return - */ + @Override public S classpathPropertiesRequired() { _missingClasspathPropFileAProblem = true; return (S) this; } - /** - * Sets the properties file on the classpath to be optional, the default. - * - * @See classpathPropertiesRequired - * - * @return - */ + @Override public S classpathPropertiesNotRequired() { _missingClasspathPropFileAProblem = false; return (S) this; } - /** - * Sets the filesystem path via a StrProp (a Property of String type) to a - * properties file for the StdPropFileOnFilesystemLoader to load. - * - * If no property is set to specify a path, or a property is set by has no - * value, this loader won't be used. If the property is specified but the - * specified file is missing, an error will be thrown based on the - * filesystemPropFileRequired flag. - * - * Paths should generally be absolute and correctly formed for the host - * environment. - * - * @param filesystemPropFilePath - * @return - */ + @Override public S setFilesystemPropFilePath(StrProp filesystemPropFilePath) { this.filesystemPropFilePathProp = filesystemPropFilePath; return (S) this; } - /** - * If called to set this to 'required', a non-null configured value for the - * filesystem properties file must point to an existing, readable properties - * file. This flag is used by the {@Code StdPropFileOnFilesystemLoader}. - * - * A RuntimeException will be thrown if this is set to 'required' and there - * is a path specified which points to a file that does not exist. - * Configuring a filesystem path is a two step process:

      - *
    • First, a StrProp Property must be specified for this configuration - * via the {@Code setFilesystemPropFilePath} method
    • - *
    • Then, a value must be configured for in an any way that AndHow - * reads and loads values, such as environment vars, system properties, etc..
    • - *
    - * If and non-null value is configured, its doesn't point to a readable properties - * file, AND this required flag is set, a RuntimeException will be thrown at startup. - *
    - * This is NOT set by default, allowing the properties file to be optional. - * - * @return - */ + @Override public S filesystemPropFileRequired() { _missingFilesystemPropFileAProblem = true; return (S) this; } - /** - * Sets the properties file on the filesystem to be optional, the default. - * - * @See setFilesystemPropFilePath - * - * @return - */ + @Override public S filesystemPropFileNotRequired() { _missingFilesystemPropFileAProblem = false; return (S) this; @@ -253,6 +163,7 @@ public S filesystemPropFileNotRequired() { /** * Allows system properties to be overridden. * + * @deprecated * @param properties */ public S setSystemProperties(Properties properties) { @@ -291,6 +202,7 @@ public S setEnvironmentProperties(Map newEnvProperties) { return (S) this; } + @Override public S setStandardLoaders(List> newStandardLoaders) { standardLoaders.clear(); @@ -299,6 +211,7 @@ public S setStandardLoaders(List> newStandardLoa return (S) this; } + @Override public S setStandardLoaders(Class... newStandardLoaders) { standardLoaders.clear(); @@ -310,6 +223,7 @@ public S setStandardLoaders(Class... newStandardLoader return (S) this; } + @Override public S insertLoaderBefore( Class insertBeforeThisLoader, Loader loaderToInsert) { @@ -324,6 +238,7 @@ public S insertLoaderBefore( return (S) this; } + @Override public S insertLoaderAfter( Class insertAfterThisLoader, Loader loaderToInsert) { diff --git a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java index 1a354d7b..63b1da97 100644 --- a/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java +++ b/andhow-core/src/test/java/org/yarnandtail/andhow/StdConfigGetterAndSetterTest.java @@ -69,7 +69,7 @@ public void testBuildLoadersWithCustomListOfStandardLoaders() { assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); assertEquals(StdJndiLoader.class, loaders.get(1).getClass()); - std.setStandardLoaders(BaseConfig.getDefaultLoaderList()); + std.setStandardLoaders(std.getDefaultLoaderList()); loaders = std.buildLoaders(); assertEquals(7, loaders.size()); assertEquals(StdFixedValueLoader.class, loaders.get(0).getClass()); diff --git a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml index be5d7112..59f4312c 100644 --- a/andhow-testing/andhow-annotation-processor-test-harness/pom.xml +++ b/andhow-testing/andhow-annotation-processor-test-harness/pom.xml @@ -28,4 +28,15 @@ true + + + + maven-deploy-plugin + + true + + + + + diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-1/pom.xml b/andhow-testing/andhow-simulated-app-tests/example-app-1/pom.xml new file mode 100644 index 00000000..ffa8b852 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-1/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + andhow-simulated-app-tests + org.yarnandtail + 0.4.2-SNAPSHOT + + example-app-1 + + + + + org.yarnandtail + andhow + ${project.version} + + + org.yarnandtail + andhow-test-harness + ${project.version} + + + + app + + + maven-assembly-plugin + + + + com.bigcorp.Checker + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/java/com/bigcorp/Calculator.java b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/java/com/bigcorp/Calculator.java new file mode 100644 index 00000000..2f61273c --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/java/com/bigcorp/Calculator.java @@ -0,0 +1,91 @@ +package com.bigcorp; + +import org.yarnandtail.andhow.property.StrProp; + +/** + * A simple calculator that can use two different modes, which an AndHow {@Code StrProp} allows + * selection of based on configuration from (in this case) a properties file. + * + * This isn't a complete application, but its easy to imagine {@Code doCalc} being called by an + * AWS Lambda function, part of a command line utility, or just some library part of a larger app. + */ +public class Calculator { + + //The AndHow configuration Property to select between two modes (doesn't have to be public) + public static final StrProp MODE = StrProp.builder() + .mustBeNonNull().mustEqual("DOUBLE", "FLOAT").build(); + + /** + * Do the calculation, but choose which implementation to use based on CALC_MODE + * @param a + * @param b + * @return + */ + public Number doCalc(Number a, Number b) { + + Number result = null; + + if (MODE.getValue().equals("DOUBLE")) { + result = doDoubleCalc(a, b); + } else if (MODE.getValue().equals("FLOAT")) { + result = doFloatCalc(a, b); + } else { + //throw new IllegalStateException("Validation on CALC_MODE ensures this never happens"); + } + + return result; + } + + protected Number doDoubleCalc(Number a, Number b) { + return a.doubleValue() / b.doubleValue(); + } + + protected Number doFloatCalc(Number a, Number b) { + return a.floatValue() / b.floatValue(); + } + + /** + * A main method to run this app as it would be in a 'production' environment. + * Run it from an IDE (configure the IDE to pass two numbers as args), or via command line. + *

    + * To run from command line, first use Maven to create a runnable jar. + * Here are the commands, executed from the root of the AndHow project, to build and run the jar:
    + *

    {@Code
    +	 * > mvn clean package -DskipTests -Dmaven.javadoc.skip=true
    +	 * > java -jar andhow-testing/andhow-simulated-app-tests/example-app-1/target/app.jar 1.23 4.56
    +	 * }
    + * The output of running this command will be: + *
    {@Code
    +	 * Result is 0.26973684210526316 (Double)
    +	 * }
    + * How did it know to use the Double implementation? AndHow finds the {@Code checker.production.properties} + * file on the classpath and reads the configured value for CALC_MODE. {@CODE CALC_MODE.getValue()} + * returns 'DOUBLE'. Compare this to the unit test for this class... + *

    + * Properties files are just one way AndHow reads configuration. Values in the properties file + * could be overwritten via env. vars., JNDI, system properties, etc.. + * Rerunning the main method with a system property like this: + *

    {@Code
    +	 * > java -Dcom.bigcorp.Calculator.CALC_MODE=FLOAT -jar andhow-testing/andhow-simulated-app-tests/example-app-1/target/app.jar 1.23 4.56
    +	 * }
    + * will result in {@Code Result is 0.26973686 (Float)} + * + * @param args Two arguments that are parsable to numbers. + */ + public static void main(String[] args) { + Double a = Double.parseDouble(args[0]); + Double b = Double.parseDouble(args[1]); + + Calculator mult = new Calculator(); + + Number result = mult.doCalc(a, b); + + System.out.println(mult.toString(result)); + } + + public String toString(Number result) { + return "Result is " + result + " (" + result.getClass().getSimpleName() + ")"; + } + + +} diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/resources/andhow.properties b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/resources/andhow.properties new file mode 100644 index 00000000..88c08e6d --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/main/resources/andhow.properties @@ -0,0 +1,8 @@ +# By default, AndHow will find and load a properties file named +# 'checker.production.properties' if it exists on the classpath. +# +# An additional 'checker.production.properties' file can be placed on the +# test classpath to provide a different configuration during +# testing, as this example project does. + +com.bigcorp.Calculator.MODE: DOUBLE \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDefaultTest.java b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDefaultTest.java new file mode 100644 index 00000000..82942a70 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDefaultTest.java @@ -0,0 +1,48 @@ +package com.bigcorp; + +import org.junit.jupiter.api.Test; + +import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut; +import static org.junit.jupiter.api.Assertions.*; + +/** + * In the test environment, the application is configured to use the 'FLOAT' + * implementation, as configured in the {@Code andhow.properties} on the TEST CLASSPATH. + * + * Resources on the test classpath override the main classpath, so this is a natural and useful + * outcome. Just like in production, AndHow will auto-discover its configuration during testing. + *

    + * Using the test {@Code andhow.properties} file, however, means that all the tests run with the + * same 'FLOAT' configuration value. How do we test the application with the DOUBLE configuration? + * See {@Code CalculatorDoubleConfigTest} for the answer... + *

    + * ...and much more flexible and complex configuration is possible for production and testing - + * be sure to look at other of the 'simulated-app-tests'. + */ +class CalculatorDefaultTest { + + /** + * Verify that the app runs in FLOAT mode in the test environment. + */ + @Test + public void verifyTestEnvironmentConfiguration() { + Calculator mult = new Calculator(); + + Number result = mult.doCalc(1.23D, 4.56D); + + assertEquals(Float.class, result.getClass(), "The result should be 'Float', as configured"); + assertEquals("FLOAT", Calculator.MODE.getValue()); + + //System.out.println(mult.toString(result)); + } + + @Test + public void mainMethodShouldPrintResultToSystemOut() throws Exception { + + String text = tapSystemOut(() -> { + Calculator.main(new String[] {"4", "2"}); + }); + + assertTrue(text.contains("2.0") && text.contains("Float")); + } +} \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDoubleConfigTest.java b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDoubleConfigTest.java new file mode 100644 index 00000000..36249a6d --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/java/com/bigcorp/CalculatorDoubleConfigTest.java @@ -0,0 +1,50 @@ +package com.bigcorp; + +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.junit5.KillAndHowBeforeEachTest; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@Code CalculatorDefaultTest} didn't fully test the app because only some code is executed in the + * FLOAT configuration. This test demos testing other configurations. + *

    + * The {@Code @KillAndHowBeforeEachTest} annotation erases the configured state of AndHow before + * each test so each test can specify its own AndHow configuration. If a test doesn't explicitly + * initialize AndHow, AndHow will initialize normally as soon as the first Property value is referenced. + *

    + * When all tests are complete, {@Code KillAndHowBeforeEachTest} resets the AndHow state + * back to what it was at the start of the test. + */ +@KillAndHowBeforeEachTest // <-- Uses the JUnit extension mechanism +class CalculatorDoubleConfigTest { + + /** + * Test the app with the DOUBLE configuration. + */ + @Test + public void testAppInDoubleConfiguration() { + + //Force AndHow to see DOUBLE for the duration of this test + AndHow.findConfig().addFixedValue(Calculator.MODE, "DOUBLE").build(); + + Calculator mult = new Calculator(); + Number result = mult.doCalc(1.23D, 4.56D); + assertEquals(Double.class, result.getClass(), "The result should now be a Double"); + } + + /** + * If the test method doesn't initialize AndHow, AndHow will initialize automatically as soon + * as a Property is accessed and load the default configuration from the test + * {@Code checker.production.properties} file. + */ + @Test + public void revertToDefaultConfigIfTheTestDoesNotInitializeAndHow() { + + Calculator mult = new Calculator(); + Number result = mult.doCalc(1.23D, 4.56D); + assertEquals(Float.class, result.getClass(), "The result should revert to Float"); + } + +} \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/resources/andhow.properties b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/resources/andhow.properties new file mode 100644 index 00000000..770dde19 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-1/src/test/resources/andhow.properties @@ -0,0 +1,4 @@ +# This properties file on the test classpath will override the +# one on the main classpath for testing. + +com.bigcorp.Calculator.MODE: FLOAT \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/pom.xml b/andhow-testing/andhow-simulated-app-tests/example-app-2/pom.xml new file mode 100644 index 00000000..340f4411 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + andhow-simulated-app-tests + org.yarnandtail + 0.4.2-SNAPSHOT + + example-app-2 + + + + + org.yarnandtail + andhow + ${project.version} + + + org.yarnandtail + andhow-test-harness + ${project.version} + + + + app + + + maven-assembly-plugin + + + + com.bigcorp.Checker + + + + + + + \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/AppInitiation.java b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/AppInitiation.java new file mode 100644 index 00000000..da340d69 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/AppInitiation.java @@ -0,0 +1,20 @@ +package com.bigcorp; + +import org.yarnandtail.andhow.*; +import org.yarnandtail.andhow.property.StrProp; + +public class AppInitiation implements AndHowInit { + + public static final StrProp ANDHOW_CLASSPATH_FILE = StrProp.builder().mustStartWith("/") + .aliasIn("AH_CLASSPATH") /* Make this easier to config by adding an alias */ + .defaultValue("/checker.default.properties") + .description("Path to a file on the classpath. Classpaths must start w/ a slash.").build(); + + @Override + public AndHowConfiguration getConfiguration() { + + return + StdConfig.instance().setClasspathPropFilePath(ANDHOW_CLASSPATH_FILE); + + } +} diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/Checker.java b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/Checker.java new file mode 100644 index 00000000..d0459844 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/java/com/bigcorp/Checker.java @@ -0,0 +1,82 @@ +package com.bigcorp; + +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.property.IntProp; +import org.yarnandtail.andhow.property.StrProp; + +/** + * A hypothetical class that checks if a configured service url is 'live'. + * The url of the external service is configured by AndHow properties. + *

    + * Of course, this isn't a complete application, but its easy to imagine {@Code doCheck} being + * called by an AWS Lambda function, as a command line utility, or as just a library in a larger app. + */ +public class Checker { + + //All the config Properties for this class + // - Its a best practice to group AndHow Properties into an interface. + interface Config { + StrProp PROTOCOL = StrProp.builder().mustBeNonNull(). + mustEqual("http", "https").build(); + StrProp SERVER = StrProp.builder().mustBeNonNull().build(); + IntProp PORT = IntProp.builder().mustBeNonNull(). + mustBeGreaterThanOrEqualTo(80).mustBeLessThanOrEqualTo(8888).build(); + StrProp PATH = StrProp.builder().mustStartWith("/").build(); + } + + /** + * Builds a url using the AndHow Properties. + * @return + */ + public String getServiceUrl() { + return Config.PROTOCOL.getValue() + "://" + Config.SERVER.getValue() + ":" + + Config.PORT.getValue() + Config.PATH.getValue(); + } + + /* + * In the real world, this method would verify the configured url works... + */ +// public boolean checkTheUrl() { +// ... verify the url return a http 200 code or something ... +// } + + /** + * A main method to run this app as it might be in a real environment. + *

    + * In this main method the {@Code AndHow.findConfig()} method is used to append the cmd line + * arguments to the AndHow configuration. This allows Property values to be configured from + * these arguments, in addition to all the other way they could be configured. + *

    > + * Run it from an IDE , or via command line. + *

    + * To run from command line, first use Maven to create a runnable jar. + * Here are the commands, executed from the root of the AndHow project, to build and run the jar:
    + *

    {@Code
    +	 * > mvn clean package -DskipTests -Dmaven.javadoc.skip=true
    +	 * > java -jar andhow-testing/andhow-simulated-app-tests/example-app-2/target/app.jar
    +	 * }
    + * The output of this command will match the config in {@Code checker.default.properties}: + *
    {@Code
    +	 * Service url: https://default.bigcorp.com:80/validate
    +	 * }
    + * + *
    {@Code
    +	 * > java -Dcom.bigcorp.Calculator.CALC_MODE=FLOAT -jar andhow-testing/andhow-simulated-app-tests/example-app-2/target/app.jar 1.23 4.56
    +	 * }
    + * will result in {@Code Result is 0.26973686 (Float)} + * + * @param args Two arguments that are parsable to numbers. + */ + public static void main(String[] args) { + + //Find the AndHow Configuration and add to it the commandline args so they can be used for + //configuration as well. + AndHow.findConfig().setCmdLineArgs(args).build(); //Have to call build here or it doesn't work!! + + Checker v = new Checker(); + System.out.println("Service url: " + v.getServiceUrl()); // <--display the configured url + } + + + +} diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.default.properties b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.default.properties new file mode 100644 index 00000000..84f787b5 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.default.properties @@ -0,0 +1,30 @@ +# AndHow knows all the Property metadata, so it can create this file w/ place-holders for values. + +# ########################################################################################## +# Sample properties file generated by AndHow! +# strong.simple.valid.AppConfiguration - https://github.com/eeverman/andhow +# ########################################################################################## + +# ########################################################################################## +# Property Group com.bigcorp.Checker.Config + +# +# PATH (String) +# The property value must start with '/' +com.bigcorp.Checker.Config.PATH = /validate + +# +# PORT (Integer) NON-NULL +# The property value must: +# - be greater than or equal to 80 +# - be less than or equal to 8888 +com.bigcorp.Checker.Config.PORT = 80 + +# +# PROTOCOL (String) NON-NULL +# The property value must be equal to one of '[http, https]' +com.bigcorp.Checker.Config.PROTOCOL = https + +# +# SERVER (String) NON-NULL +com.bigcorp.Checker.Config.SERVER = default.bigcorp.com diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.production.properties b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.production.properties new file mode 100644 index 00000000..4cb1a073 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.production.properties @@ -0,0 +1,30 @@ +# AndHow knows all the Property metadata, so it can create this file w/ place-holders for values. + +# ########################################################################################## +# Sample properties file generated by AndHow! +# strong.simple.valid.AppConfiguration - https://github.com/eeverman/andhow +# ########################################################################################## + +# ########################################################################################## +# Property Group com.bigcorp.Checker.Config + +# +# PATH (String) +# The property value must start with '/' +com.bigcorp.Checker.Config.PATH = /validate + +# +# PORT (Integer) NON-NULL +# The property value must: +# - be greater than or equal to 80 +# - be less than or equal to 8888 +com.bigcorp.Checker.Config.PORT = 80 + +# +# PROTOCOL (String) NON-NULL +# The property value must be equal to one of '[http, https]' +com.bigcorp.Checker.Config.PROTOCOL = https + +# +# SERVER (String) NON-NULL +com.bigcorp.Checker.Config.SERVER = prod.bigcorp.com diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.stage.properties b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.stage.properties new file mode 100644 index 00000000..41abd034 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/main/resources/checker.stage.properties @@ -0,0 +1,30 @@ +# AndHow knows all the Property metadata, so it can create this file w/ place-holders for values. + +# ########################################################################################## +# Sample properties file generated by AndHow! +# strong.simple.valid.AppConfiguration - https://github.com/eeverman/andhow +# ########################################################################################## + +# ########################################################################################## +# Property Group com.bigcorp.Checker.Config + +# +# PATH (String) +# The property value must start with '/' +com.bigcorp.Checker.Config.PATH = /validate + +# +# PORT (Integer) NON-NULL +# The property value must: +# - be greater than or equal to 80 +# - be less than or equal to 8888 +com.bigcorp.Checker.Config.PORT = 80 + +# +# PROTOCOL (String) NON-NULL +# The property value must be equal to one of '[http, https]' +com.bigcorp.Checker.Config.PROTOCOL = https + +# +# SERVER (String) NON-NULL +com.bigcorp.Checker.Config.SERVER = stage.bigcorp.com diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerDefaultAndMainArgsTest.java b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerDefaultAndMainArgsTest.java new file mode 100644 index 00000000..a8d245d3 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerDefaultAndMainArgsTest.java @@ -0,0 +1,74 @@ +package com.bigcorp; + +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.junit5.KillAndHowBeforeThisTest; + +import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut; +import static org.junit.jupiter.api.Assertions.*; + +public class CheckerDefaultAndMainArgsTest { + + /** + * Verify the the app sees its default configuration. + *

    + * In the {@Code AppInitialtion.ANDHOW_CLASSPATH_FILE} Property, the default value is + * 'checker.default.properties'. In that same class we also tell AndHow to use the value of that + * Property to decide which property file to use, thus property values are read from + * 'checker.default.properties'. + */ + @Test + public void verifyTestEnvironmentConfiguration() { + Checker v = new Checker(); + + String url = v.getServiceUrl(); + + assertEquals("https://default.bigcorp.com:80/validate", url, + "The url should match the configuration in checker.default.properties"); + } + + /** + * Since the main method calls {@Code AndHow.findConfig()...build()}, forcing AndHow to build and + * initialize itself, we must 'kill' the AndHow configured state before calling main. + * Otherwise AndHow would throw a RuntimeException. + *

    + * In production, its AndHow's job to enforce a single, stable configuration state and it complains + * loudly if application code tries to re-initialize it. During testing, however, we need to + * 'break the rules' with things like {@Code @KillAndHowBeforeThisTest} and other AndHow test + * helpers. + * @throws Exception + */ + @KillAndHowBeforeThisTest + @Test + public void mainMethodAlsoSeesDefaultProperties() throws Exception { + + String text = tapSystemOut(() -> { + Checker.main(new String[]{}); + }); + + assertTrue(text.contains("https://default.bigcorp.com:80/validate"), + "The url should match the configuration in checker.default.properties"); + } + + /** + * The main method includes {@Code AndHow.findConfig().setCmdLineArgs(args)...} to allow AndHow + * to process the main args. + *

    + * This test shows how a key=value pair can be passed as an argument to main. Looking at the + * code in {@Code AppInitiation}, the key matches the {@Code ANDHOW_CLASSPATH_FILE} Property we + * told AndHow to use to decide which file to read. To make it easier to specify that Property, + * we added the alias 'AH_CLASSPATH' so we don't need to use the full class name of that Property. + * @throws Exception + */ + @KillAndHowBeforeThisTest + @Test + public void mainMethodWillAcceptConfigForFilePath() throws Exception { + + String text = tapSystemOut(() -> { + //pass the alias name of ANDHOW_CLASSPATH_FILE and a value as a main arg + Checker.main(new String[]{ "AH_CLASSPATH=/checker.test.properties"}); + }); + + assertTrue(text.contains("http://localhost:8888/localValidate"), + "The url should match the configuration in checker.test.properties"); + } +} \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerFixedValuesTest.java b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerFixedValuesTest.java new file mode 100644 index 00000000..d4dc8a6e --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/java/com/bigcorp/CheckerFixedValuesTest.java @@ -0,0 +1,85 @@ +package com.bigcorp; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.api.AppFatalException; +import org.yarnandtail.andhow.junit5.KillAndHowBeforeAllTests; +import org.yarnandtail.andhow.junit5.KillAndHowBeforeThisTest; + +import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Lets say there is a very specific configuration state we need to test our app in. + * We could do that with a separate properties file, but another way is to configure AndHow with + * 'fixed values' right in a test. + *

    + * The {@Code @KillAndHowBeforeAllTests} annotation resets AndHow before the start of testing and + * restores it after all tests are complete. The setup method uses + * {@Code AndHow.findConfig()...build()} to initialize AndHow with several 'fixed values' for the + * test scenario. The AndHow state is not modified between tests, so all tests share the same + * AndHow configuration state. + */ +@KillAndHowBeforeAllTests +public class CheckerFixedValuesTest { + + /** + * AndHow was 'killed' at the start of the test class, so this setup method can create a new + * AndHow configuration state for all the tests in this class. When all the tests are complete, + * the original state is restored. + */ + @BeforeAll + public static void setup() { + AndHow.findConfig() + .addFixedValue(Checker.Config.PROTOCOL, "https") + .addFixedValue(Checker.Config.SERVER, "imgs.xkcd.com") + .addFixedValue(Checker.Config.PORT, 80) + .addFixedValue(Checker.Config.PATH, "/comics/the_mother_of_all_suspicious_files.png") + .build(); //build() initializes AndHow, otherwise it would wait for the 1st Property access. + } + + /** + * Verify the the app only sees the 'fixed' values from above. + */ + @Test + public void verifyTestEnvironmentConfiguration() { + Checker v = new Checker(); + + String url = v.getServiceUrl(); + + assertEquals("https://imgs.xkcd.com:80/comics/the_mother_of_all_suspicious_files.png", url, + "The url is build based on the fixed values in the 'BeforeAll' method"); + } + + /** + * All the tests in this class will see the same AndHow configuration... + */ + @Test + public void verifyTestEnvironmentConfiguration2() { + assertEquals("imgs.xkcd.com", Checker.Config.SERVER.getValue()); + } + + /** + * One of AndHow's main jobs is to enforce a single, stable configuration state and complain + * loudly (with an AppFatalException) if application code tries to change that state by + * re-initializing AndHow. This test verifies that. + *

    + * The main() method explicitly initializes AndHow and so does this test class. Thus, the main + * method will throw a {@Code AppFatalException} because AndHow detects the attempt to + * reinitialize it. + *

    + * If we did want to call the main method here, @{Code @KillAndHowBeforeThisTest} could be added + * to this test method to start over with an unconfigured state for just this test. + * @throws Exception + */ + @Test + public void mainMethodShouldThrowAnErrorBecauseAndHowIsAlreadyInitialized() throws Exception { + + assertThrows(AppFatalException.class, () -> + Checker.main(new String[]{}) + ); + + } + +} \ No newline at end of file diff --git a/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/resources/checker.test.properties b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/resources/checker.test.properties new file mode 100644 index 00000000..1fcd74a2 --- /dev/null +++ b/andhow-testing/andhow-simulated-app-tests/example-app-2/src/test/resources/checker.test.properties @@ -0,0 +1,30 @@ +# AndHow knows all the Property metadata, so it can create this file w/ place-holders for values. + +# ########################################################################################## +# Sample properties file generated by AndHow! +# strong.simple.valid.AppConfiguration - https://github.com/eeverman/andhow +# ########################################################################################## + +# ########################################################################################## +# Property Group com.bigcorp.Checker.Config + +# +# PATH (String) +# The property value must start with '/' +com.bigcorp.Checker.Config.PATH = /localValidate + +# +# PORT (Integer) NON-NULL +# The property value must: +# - be greater than or equal to 80 +# - be less than or equal to 8888 +com.bigcorp.Checker.Config.PORT = 8888 + +# +# PROTOCOL (String) NON-NULL +# The property value must be equal to one of '[http, https]' +com.bigcorp.Checker.Config.PROTOCOL = http + +# +# SERVER (String) NON-NULL +com.bigcorp.Checker.Config.SERVER = localhost diff --git a/andhow-testing/andhow-simulated-app-tests/pom.xml b/andhow-testing/andhow-simulated-app-tests/pom.xml index f3b5e160..55dd0793 100644 --- a/andhow-testing/andhow-simulated-app-tests/pom.xml +++ b/andhow-testing/andhow-simulated-app-tests/pom.xml @@ -11,12 +11,34 @@ pom andhow-multimodule-dataprocess + example-app-1 + example-app-2 + + + + maven-assembly-plugin + + + jar-with-dependencies + + false + + + + package + + single + + + + + + - true maven-deploy-plugin true diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTests.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTests.java new file mode 100644 index 00000000..b8b6c00a --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTests.java @@ -0,0 +1,45 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.ExtendWith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotation that can be placed on a test class to reset AndHow to its unconfigured state + * (only) before the first test runs. When all tests in the class are done, the + * original AndHow configured state is restored, which may be unconfigured. + *

    + * Example usage: + *

    {@Code
    + * @KillAndHowBeforeAllTests
    + * public class MyJunit5TestClass {
    + *
    + *   @BeforeAll
    + *   public static void configAndHowForAllTests(){
    + * 		AndHow.findConfig()
    + * 				.addFixedValue([AndHowProperty reference or name], [Value for that Property])
    + * 				.addFixedValue(...)
    + * 				.build();
    + *    }
    + *
    + *   ...tests that will all share the same configuration...
    + *
    + * }
    + * }
    + *

    + * Note: Using this annotation on a JUnit test class is the same as using + * {@Code @ExtendWith(KillAndHowBeforeAllTestsExtension.class)} on a class, but this annotation is + * safer because it cannot be put on a method. '@ExtendWith' allows placement on a method, + * but the extension will only work properly on a class. + */ +@Target({ TYPE, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@ExtendWith(KillAndHowBeforeAllTestsExtension.class) +public @interface KillAndHowBeforeAllTests { + +} diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtension.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtension.java new file mode 100644 index 00000000..b22ca81a --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtension.java @@ -0,0 +1,70 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.yarnandtail.andhow.AndHowNonProductionUtil; +import org.yarnandtail.andhow.internal.AndHowCore; + +/** + * JUnit Extension that can be placed on a test class to reset AndHow to its unconfigured + * state (only) before the first test runs. When all tests in the class are done, the + * original AndHow configured state is restored, which may be unconfigured. + *

    + * With this extension, all tests in the class share the same AndHow configuration, which + * can be set in a {@Code @BeforeAll} setup method. + *

    + * It is easier and safer to use the @{Code @KillAndHowBeforeAllTests} annotation. + * That annotation uses this class and prevents this extension from being used on a test method + * (this extension only works correctly on a test class). + *

    + * Usage example: + *

    {@Code
    + * @ExtendWith(KillAndHowBeforeAllTestsExtension.class)
    + * public class MyJunit5TestClass {
    + *
    + *   @BeforeAll
    + *   public static void configAndHowForAllTests(){
    + * 		AndHow.findConfig()
    + * 				.addFixedValue([AndHowProperty reference or name], [Value for that Property])
    + * 				.addFixedValue(...)
    + * 				.build();
    + *    }
    + *
    + *   ...tests that will all share the same configuration...
    + *
    + * }
    + * }
    + */ +public class KillAndHowBeforeAllTestsExtension implements BeforeAllCallback, AfterAllCallback { + + protected static final String CORE_KEY = "core_key"; + + /** + * Store the state of AndHow before any test is run, then destroy the state + * so AndHow is unconfigured. + * @param extensionContext + * @throws Exception + */ + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + AndHowCore core = AndHowNonProductionUtil.getAndHowCore(); + getStore(extensionContext).put(CORE_KEY, core); + AndHowNonProductionUtil.destroyAndHowCore(); + } + + @Override + public void afterAll(ExtensionContext extensionContext) throws Exception { + AndHowCore core = getStore(extensionContext).remove(CORE_KEY, AndHowCore.class); + AndHowNonProductionUtil.setAndHowCore(core); + } + + /** + * Create or return the unique storage space for this test class for this extension. + * @param context + * @return + */ + protected ExtensionContext.Store getStore(ExtensionContext context) { + return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestClass())); + } +} diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTest.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTest.java new file mode 100644 index 00000000..3d11463d --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTest.java @@ -0,0 +1,48 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.yarnandtail.andhow.junit5.KillAndHowBeforeEachTestExtension; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotation that can be used on a JUnit test class to reset AndHow to its unconfigured state + * prior to each individual test. When all tests in the class are done, the original + * configured state of AndHow is restored, which may be unconfigured. + *

    + * Example usage: + *

    {@Code
    + * @KillAndHowBeforeEachTest
    + * public class MyJunit5TestClass {
    + *
    + *   @Test
    + *   public void doATest(){
    + * 		AndHow.findConfig()
    + * 				.addFixedValue([AndHowProperty reference or name], [Value for that Property])
    + * 				.addFixedValue(...)
    + * 				.build();
    + *
    + * 		  ...	code for this test...
    + *    }
    + *
    + *   ...other tests that can each configure AndHow...
    + *
    + * }
    + * }
    + *

    + * Note: Using this annotation on a JUnit test class is the same as using + * {@Code @ExtendWith(KillAndHowBeforeEachTestExtension.class)} on a class, but this annotation is + * safer because it cannot be put on a method. '@ExtendWith' allows placement on a method, + * but the extension will only work properly on a class. + */ +@Target({ TYPE, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@ExtendWith(KillAndHowBeforeEachTestExtension.class) +public @interface KillAndHowBeforeEachTest { + +} diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtension.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtension.java new file mode 100644 index 00000000..4c034b22 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtension.java @@ -0,0 +1,50 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.yarnandtail.andhow.AndHowNonProductionUtil; + + +/** + * JUnit Extension that can be placed on a test class to reset AndHow to its + * unconfigured state before each test runs. When all tests in the class are done, the + * original AndHow configured state is restored, which may be unconfigured. + *

    + * It is easier and safer to use the @{Code @KillAndHowBeforeEachTest} annotation. + * That annotation uses this class and prevents this extension from being used on a test method + * (this extension only works correctly on a test class). + *

    + * Usage example: + *

    {@Code
    + * @ExtendWith(KillAndHowBeforeEachTestExtension.class)
    + * public class MyJunit5TestClass {
    + *
    + *   @Test
    + *   public void doATest(){
    + * 		AndHow.findConfig()
    + * 				.addFixedValue([AndHowProperty reference or name], [Value for that Property])
    + * 				.addFixedValue(...)
    + * 				.build();
    + *
    + * 		  ...	code for this test...
    + *    }
    + *
    + *   ...other tests that can each configure AndHow...
    + *
    + * }
    + * }
    + */ +public class KillAndHowBeforeEachTestExtension extends KillAndHowBeforeAllTestsExtension + implements BeforeEachCallback { + + /** + * Destroy the AndHow state before each test so that each starts with AndHow unconfigured. + * @param extensionContext + * @throws Exception + */ + @Override + public void beforeEach(ExtensionContext extensionContext) throws Exception { + AndHowNonProductionUtil.destroyAndHowCore(); + } + +} diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTest.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTest.java new file mode 100644 index 00000000..c3ba7260 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTest.java @@ -0,0 +1,38 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.ExtendWith; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotation that can be placed on an individual test method to reset AndHow to its + * unconfigured state before the test runs. When the test is done, the original AndHow configured + * state is restored, which may be unconfigured. + *

    + * Example usage: + *

    {@Code
    + * public class MyJunit5TestClass {
    + *
    + *   @Test
    + *   @KillAndHowBeforeThisTest
    + *   public void aTest() { .... }
    + *
    + * }
    + * }
    + *

    + * Note: Using this annotation on a JUnit test method is the same as using + * {@Code @ExtendWith(KillAndHowBeforeThisTestExtension.class)} on a method, but this annotation is + * safer because it cannot be put on a class. '@ExtendWith' allows placement on a class, + * but the extension will only work properly on a method. + */ +@Target({ METHOD, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@ExtendWith(KillAndHowBeforeThisTestExtension.class) +public @interface KillAndHowBeforeThisTest { + +} diff --git a/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtension.java b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtension.java new file mode 100644 index 00000000..0dbabc5e --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/main/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtension.java @@ -0,0 +1,72 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.*; +import org.yarnandtail.andhow.AndHowNonProductionUtil; +import org.yarnandtail.andhow.internal.AndHowCore; + +import java.lang.reflect.Method; +import java.util.Optional; + +/** + * JUnit Extension that can be placed on an individual test method to reset AndHow to its + * unconfigured state before the test runs. When the test is done, the original AndHow configured + * state is restored, which may be unconfigured. + *

    + * It is easier and safer to use the @{Code @KillAndHowBeforeThisTest} annotation. + * That annotation uses this class and prevents this extension from being used on a test class + * (this extension only works correctly on a test method). + *

    + * Usage example: + *

    {@Code
    + * public class MyJunit5TestClass {
    + *
    + *   @Test
    + *   @ExtendWith(KillAndHowBeforeThisTestExtension.class)
    + *   public void aTest() { .... }
    + *
    + * }
    + * }
    + */ +public class KillAndHowBeforeThisTestExtension implements BeforeEachCallback, AfterEachCallback { + + public static final String CORE_KEY = "core_key"; + + /** + * The state of AndHow, stored before any tests in the current test class are run. + */ + private AndHowCore beforeAllAndHowCore; + + /** + * Store the state of AndHow before this test, then destroy the state so AndHow is unconfigured. + * @param extensionContext + * @throws Exception + */ + @Override + public void beforeEach(ExtensionContext extensionContext) throws Exception { + AndHowCore core = AndHowNonProductionUtil.getAndHowCore(); + getStore(extensionContext).put(CORE_KEY, core); + AndHowNonProductionUtil.destroyAndHowCore(); + } + + /** + * Restore the state of AndHow to what it was before this test. + * @param context + * @throws Exception + */ + @Override + public void afterEach(ExtensionContext context) throws Exception { + AndHowCore core = getStore(context).remove(CORE_KEY, AndHowCore.class); + AndHowNonProductionUtil.setAndHowCore(core); + } + + /** + * Create or return the unique storage space for this test class for this extension. + * @param context + * @return + */ + private ExtensionContext.Store getStore(ExtensionContext context) { + return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestInstance(), context.getRequiredTestMethod())); + } + + +} diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtensionTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtensionTest.java new file mode 100644 index 00000000..6e9f98be --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeAllTestsExtensionTest.java @@ -0,0 +1,120 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.AndHowNonProductionUtil; +import org.yarnandtail.andhow.internal.AndHowCore; + + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * Unit tests of this JUnit Extension. + * Beyond unit testing, the extension is used in many of the simulated app tests + * (see that module for usage examples). + */ +class KillAndHowBeforeAllTestsExtensionTest { + + AndHowCore andHowCoreCreatedDuringTest; + + //The expected namespace used to store values within the Extension + ExtensionContext.Namespace expectedNamespace; + + //Store for state variables within the context + ExtensionContext.Store store; + + //The context object that is passed to the test extension + ExtensionContext extensionContext; + + + @BeforeEach + void setUp() { + + // Setup AndHow for the test + assertNull(AndHowNonProductionUtil.getAndHowCore(), + "Just checking - no test should leave AndHow initialized"); + + AndHow.instance(); //force creation + + andHowCoreCreatedDuringTest = AndHowNonProductionUtil.getAndHowCore(); + + assertNotNull(andHowCoreCreatedDuringTest, "Should be non-null because we forced creation"); + + // + // Setup mockito for the test + + store = mock(ExtensionContext.Store.class); + when(store.remove(any(), any())).thenReturn(andHowCoreCreatedDuringTest); + + extensionContext = mock(ExtensionContext.class); + when(extensionContext.getRequiredTestClass()).thenReturn((Class)(this.getClass())); + when(extensionContext.getStore(any())).thenReturn(store); + } + + @AfterEach + void tearDown() { + AndHowNonProductionUtil.destroyAndHowCore(); + } + + @Test + void completeWorkflow() throws Exception { + + KillAndHowBeforeAllTestsExtension theExt = new KillAndHowBeforeAllTestsExtension(); + + // The initial event called on extension by JUnit + theExt.beforeAll(extensionContext); + + assertNull(AndHowNonProductionUtil.getAndHowCore(), + "Extension should have killed the core"); + + // The final event called on the extension by Junit + theExt.afterAll(extensionContext); + + // + // Verify the overall outcome + assertEquals(andHowCoreCreatedDuringTest, AndHowNonProductionUtil.getAndHowCore(), + "Extension should have reinstated the same core instance created in setup"); + + + // + // Verify actions on the store + ArgumentCaptor keyForPut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForRemove = ArgumentCaptor.forClass(Object.class); + + InOrder orderedStoreCalls = inOrder(store); + orderedStoreCalls.verify(store).put(keyForPut.capture(), eq(andHowCoreCreatedDuringTest)); + orderedStoreCalls.verify(store).remove(keyForRemove.capture(), eq(AndHowCore.class)); + verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store + + assertEquals(keyForPut.getValue(), keyForRemove.getValue(), + "The keys used for put & remove should be the same"); + + // + // Verify actions on the ExtensionContext + ArgumentCaptor namespace = + ArgumentCaptor.forClass(ExtensionContext.Namespace.class); + + //Each method is called once in beforeAll and afterAll + verify(extensionContext, times(2)).getRequiredTestClass(); //Must be called to figure out the Test class + verify(extensionContext, times(2)).getStore(namespace.capture()); + + //Verify the namespace used + // The namespace is an implementation detail, but must include the Extension class and the + // Tested class (this class in this case). There isn't an easy way to test that minimum spec, + // so here just check for a specific namespace. + expectedNamespace = ExtensionContext.Namespace.create( + KillAndHowBeforeAllTestsExtension.class, + (Class)(this.getClass())); + assertEquals(expectedNamespace, namespace.getValue()); + + + } + + +} \ No newline at end of file diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtensionTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtensionTest.java new file mode 100644 index 00000000..c43ba8da --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeEachTestExtensionTest.java @@ -0,0 +1,128 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.AndHowNonProductionUtil; +import org.yarnandtail.andhow.internal.AndHowCore; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * Unit tests of this JUnit Extension. + * Beyond unit testing, the extension is used in many of the simulated app tests + * (see that module for usage examples). + */ +class KillAndHowBeforeEachTestExtensionTest { + + AndHowCore andHowCoreCreatedDuringTest; + + //The expected namespace used to store values within the Extension + ExtensionContext.Namespace expectedNamespace; + + //Store for state variables within the context + ExtensionContext.Store store; + + //The context object that is passed to the test extension + ExtensionContext extensionContext; + + + @BeforeEach + void setUp() { + + // Setup AndHow for the test + assertNull(AndHowNonProductionUtil.getAndHowCore(), + "Just checking - no test should leave AndHow initialized"); + + AndHow.instance(); //force creation + + andHowCoreCreatedDuringTest = AndHowNonProductionUtil.getAndHowCore(); + + assertNotNull(andHowCoreCreatedDuringTest, "Should be non-null because we forced creation"); + + // + // Setup mockito for the test + + store = mock(ExtensionContext.Store.class); + when(store.remove(any(), any())).thenReturn(andHowCoreCreatedDuringTest); + + extensionContext = mock(ExtensionContext.class); + when(extensionContext.getRequiredTestClass()).thenReturn((Class)(this.getClass())); + when(extensionContext.getStore(any())).thenReturn(store); + } + + @AfterEach + void tearDown() { + AndHowNonProductionUtil.destroyAndHowCore(); + } + + @Test + void completeWorkflow() throws Exception { + + KillAndHowBeforeEachTestExtension theExt = new KillAndHowBeforeEachTestExtension(); + + // The initial event called on extension by JUnit + theExt.beforeAll(extensionContext); + + assertNull(AndHowNonProductionUtil.getAndHowCore(), + "Extension should have killed the core"); + + AndHow.instance(); //force creation again! + + theExt.beforeEach(extensionContext); + + assertNull(AndHowNonProductionUtil.getAndHowCore(), + "Extension should have killed the core... again!"); + + //Note: after each is not implemented b/c its not needed + + // The final event called on the extension by Junit + theExt.afterAll(extensionContext); + + // + // Verify the overall outcome + assertEquals(andHowCoreCreatedDuringTest, AndHowNonProductionUtil.getAndHowCore(), + "Extension should have reinstated the same core instance created in setup"); + + + // + // Verify actions on the store + ArgumentCaptor keyForPut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForRemove = ArgumentCaptor.forClass(Object.class); + + InOrder orderedStoreCalls = inOrder(store); + orderedStoreCalls.verify(store).put(keyForPut.capture(), eq(andHowCoreCreatedDuringTest)); + orderedStoreCalls.verify(store).remove(keyForRemove.capture(), eq(AndHowCore.class)); + verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store + + assertEquals(keyForPut.getValue(), keyForRemove.getValue(), + "The keys used for put & remove should be the same"); + + // + // Verify actions on the ExtensionContext + ArgumentCaptor namespace = + ArgumentCaptor.forClass(ExtensionContext.Namespace.class); + + //Each method is called once in beforeAll and afterAll + verify(extensionContext, times(2)).getRequiredTestClass(); //Must be called to figure out the Test class + verify(extensionContext, times(2)).getStore(namespace.capture()); + + //Verify the namespace used + // The namespace is an implementation detail, but must include the Extension class and the + // Tested class (this class in this case). There isn't an easy way to test that minimum spec, + // so here just check for a specific namespace. + expectedNamespace = ExtensionContext.Namespace.create( + KillAndHowBeforeEachTestExtension.class, + (Class)(this.getClass())); + assertEquals(expectedNamespace, namespace.getValue()); + + + } + + +} \ No newline at end of file diff --git a/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtensionTest.java b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtensionTest.java new file mode 100644 index 00000000..890b5f11 --- /dev/null +++ b/andhow-testing/andhow-test-harness/src/test/java/org/yarnandtail/andhow/junit5/KillAndHowBeforeThisTestExtensionTest.java @@ -0,0 +1,125 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.AndHowNonProductionUtil; +import org.yarnandtail.andhow.internal.AndHowCore; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * Unit tests of this JUnit Extension. + * Beyond unit testing, the extension is used in many of the simulated app tests + * (see that module for usage examples). + */ +class KillAndHowBeforeThisTestExtensionTest { + + AndHowCore andHowCoreCreatedDuringTest; + + //The expected namespace used to store values within the Extension + ExtensionContext.Namespace expectedNamespace; + + //Store for state variables within the context + ExtensionContext.Store store; + + //The context object that is passed to the test extension + ExtensionContext extensionContext; + + + @BeforeEach + void setUp() throws NoSuchMethodException { + + // Setup AndHow for the test + assertNull(AndHowNonProductionUtil.getAndHowCore(), + "Just checking - no test should leave AndHow initialized"); + + AndHow.instance(); //force creation + + andHowCoreCreatedDuringTest = AndHowNonProductionUtil.getAndHowCore(); + + assertNotNull(andHowCoreCreatedDuringTest, "Should be non-null because we forced creation"); + + // + // Setup mockito for the test + + store = mock(ExtensionContext.Store.class); + when(store.remove(any(), any())).thenReturn(andHowCoreCreatedDuringTest); + + extensionContext = mock(ExtensionContext.class); + when(extensionContext.getRequiredTestInstance()).thenReturn(this); + when(extensionContext.getRequiredTestMethod()).thenReturn(this.getClass().getMethod("completeWorkflow")); + when(extensionContext.getStore(any())).thenReturn(store); + } + + @AfterEach + void tearDown() { + AndHowNonProductionUtil.destroyAndHowCore(); + } + + @Test + public void completeWorkflow() throws Exception { + + KillAndHowBeforeThisTestExtension theExt = new KillAndHowBeforeThisTestExtension(); + + // The initial event called on extension by JUnit + theExt.beforeEach(extensionContext); + + assertNull(AndHowNonProductionUtil.getAndHowCore(), + "Extension should have killed the core"); + + // The final event called on the extension by Junit + theExt.afterEach(extensionContext); + + // + // Verify the overall outcome + assertEquals(andHowCoreCreatedDuringTest, AndHowNonProductionUtil.getAndHowCore(), + "Extension should have reinstated the same core instance created in setup"); + + + // + // Verify actions on the store + ArgumentCaptor keyForPut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForRemove = ArgumentCaptor.forClass(Object.class); + + InOrder orderedStoreCalls = inOrder(store); + orderedStoreCalls.verify(store).put(keyForPut.capture(), eq(andHowCoreCreatedDuringTest)); + orderedStoreCalls.verify(store).remove(keyForRemove.capture(), eq(AndHowCore.class)); + verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store + + assertEquals(keyForPut.getValue(), keyForRemove.getValue(), + "The keys used for put & remove should be the same"); + + // + // Verify actions on the ExtensionContext + ArgumentCaptor namespace = + ArgumentCaptor.forClass(ExtensionContext.Namespace.class); + + //Each method is called once in beforeAll and afterAll + verify(extensionContext, times(2)).getRequiredTestInstance(); //Must be called to figure out the Test class + verify(extensionContext, times(2)).getStore(namespace.capture()); + + //Verify the namespace used + // The namespace is an implementation detail, but must include the Extension class, the + // instance of the test (this instance)*, and since this extension is specific to the actual + // test method called, the Method of the test (this method). + // There isn't an easy way to test that minimum spec, so here just check for a specific namespace. + // * This extension is different from the other in needing the test instance rather than the + // test class. Since this extension stores values at the instance method level, it is unique + // to that level rather than to the class level. + expectedNamespace = ExtensionContext.Namespace.create( + KillAndHowBeforeThisTestExtension.class, + this, + this.getClass().getMethod("completeWorkflow")); + assertEquals(expectedNamespace, namespace.getValue()); + + + } + + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9c612087..f4c41da7 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,12 @@ 1.10.19 test + + com.github.stefanbirkner + system-lambda + 1.2.0 + test + @@ -197,6 +203,10 @@ org.mockito mockito-all + + com.github.stefanbirkner + system-lambda + @@ -211,7 +221,6 @@ org.apache.maven.plugins maven-javadoc-plugin 3.3.0 - true javadoc-jar @@ -229,10 +238,13 @@ - true maven-deploy-plugin 2.8.2 + + maven-assembly-plugin + 3.3.0 +