From 5e88fd7441ec8edbb7f1c08dac2829e1398b7da6 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:01:02 +0200 Subject: [PATCH 01/10] Extending Collection-Builders to have constructors with initial values --- .../core/builders/ArrayListBuilder.java | 14 ++++++++++++++ .../builders/core/builders/HashMapBuilder.java | 14 ++++++++++++++ .../builders/core/builders/HashSetBuilder.java | 17 +++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilder.java b/core/src/main/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilder.java index 6b5d13d6..8d94a172 100644 --- a/core/src/main/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilder.java +++ b/core/src/main/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilder.java @@ -36,6 +36,20 @@ public class ArrayListBuilder { private final List mList = new ArrayList<>(); + /** Creates an empty ArrayListBuilder. */ + public ArrayListBuilder() {} + + /** + * Creates an ArrayListBuilder initialized with the elements from the given list. + * + * @param initialList list to initialize from + */ + public ArrayListBuilder(List initialList) { + if (initialList != null) { + mList.addAll(initialList); + } + } + /** * Appends the element to the end of the internal list. Calling add-function on inner ArrayList * field. diff --git a/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashMapBuilder.java b/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashMapBuilder.java index f3dfc190..e66e41ad 100644 --- a/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashMapBuilder.java +++ b/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashMapBuilder.java @@ -37,6 +37,20 @@ public class HashMapBuilder { private final Map mMap = new HashMap<>(); + /** Creates an empty HashMapBuilder. */ + public HashMapBuilder() {} + + /** + * Creates a HashMapBuilder initialized with the mappings from the given map. + * + * @param initialMap map to initialize from + */ + public HashMapBuilder(Map initialMap) { + if (initialMap != null) { + mMap.putAll(initialMap); + } + } + /** * Associates the specified value with the specified key in this map. Using an inner HashMap * implementation. If the key already exists in the map, the old value is replaced. diff --git a/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java b/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java index 57448b23..d49dcb4f 100644 --- a/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java +++ b/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java @@ -36,9 +36,22 @@ public class HashSetBuilder { private final Set mSet = new HashSet<>(); + /** Creates an empty HashSetBuilder. */ + public HashSetBuilder() {} + + /** + * Creates a HashSetBuilder initialized with the elements from the given set. + * + * @param initialSet set to initialize from + */ + public HashSetBuilder(Set initialSet) { + if (initialSet != null) { + mSet.addAll(initialSet); + } + } + /** - * Adds the specified element to this set if it is not already present. Calling add-function on - * inner HashSet field. + * Appends the element to the internal set. Calling add-function on inner HashSet field. * * @param element element to be appended * @return current instance of ArrayListBuilder for using in stream-notation From 597254ee075041cd3385eb88ea3d81b96737be6a Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:03:10 +0200 Subject: [PATCH 02/10] Reusing values in consumers and builders which are providing a builder via consumer --- .../builders/processor/util/BuilderDefinitionCreator.java | 4 ++-- .../simple/builders/processor/BuilderProcessorTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index 0cbf7220..1960942a 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -489,7 +489,7 @@ private static MethodDto createFieldConsumer(String fieldName, TypeName fieldTyp methodDto.setMethodType(MethodTypes.CONSUMER); methodDto.setCode( """ - $helperType:T consumer = new $helperType:T(); + $helperType:T consumer = this.$fieldName:N.isChanged() ? this.$fieldName:N.value() : new $helperType:T(); $dtoMethodParam:N.accept(consumer); this.$fieldName:N = $builderFieldWrapper:T.changedValue(consumer); return this; @@ -545,7 +545,7 @@ private static MethodDto createFieldConsumerWithBuilder(String fieldName, TypeNa methodDto.setMethodType(MethodTypes.CONSUMER_BY_BUILDER); methodDto.setCode( """ - $helperType:T builder = new $helperType:T(); + $helperType:T builder = this.$fieldName:N.isChanged() ? new $helperType:T(this.$fieldName:N.value()) : new $helperType:T(); $dtoMethodParam:N.accept(builder); this.$fieldName:N = $builderFieldWrapper:T.changedValue(builder.build()); return this; diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index 6db98c43..df1c0095 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -454,7 +454,7 @@ public Address() {} "public RequiredCustomTypeViaConstructorBuilder address(Supplier
addressSupplier)", // Consumer method for constructor field (the key coverage addition!) "public RequiredCustomTypeViaConstructorBuilder address(Consumer
addressConsumer)", - "Address consumer = new Address();", + "Address consumer = this.address.isChanged() ? this.address.value() : new Address();", "addressConsumer.accept(consumer);"); } @@ -1206,7 +1206,7 @@ public HelperAnno() {} generatedCode, "private TrackedValue helper = unsetValue();", "helperBuilderConsumer", - "HelperAnnoBuilder builder = new HelperAnnoBuilder();", + "HelperAnnoBuilder builder = this.helper.isChanged() ? new HelperAnnoBuilder(this.helper.value()) : new HelperAnnoBuilder();", "helperBuilderConsumer.accept(builder);", "this.helper = changedValue(builder.build());"); } @@ -1248,7 +1248,7 @@ public HelperPlain() {} // Expect consumer method with local var named 'consumer' of HelperPlain ProcessorAsserts.assertContaining( generatedCode, - "HelperPlain consumer = new HelperPlain();", + "HelperPlain consumer = this.helperPlain.isChanged() ? this.helperPlain.value() : new HelperPlain();", "helperPlainConsumer.accept(consumer);", "private TrackedValue helperPlain = unsetValue();"); } From cde284d4c179349685349fdcf8c60a6a2f2322b4 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:11:52 +0200 Subject: [PATCH 03/10] Extending PersonDtoBuilderTest with an example of extending arrays --- .../simple/builders/example/PersonDtoBuilderTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java b/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java index ba234ce5..92a5cc07 100644 --- a/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java +++ b/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java @@ -38,6 +38,7 @@ void testBuilder() { PersonDtoBuilder.create() .birthdate(LocalDate.now()) .nickNames(b -> b.add("Test1").add("Test2")) + .nickNames(b -> b.add("Test3").add("Test4")) .mannschaft( mb -> mb.name("Testmannschaft") @@ -50,6 +51,7 @@ void testBuilder() { assertEquals("Testname", personDto.getName()); assertNotNull(personDto.getMannschaft()); assertEquals("Testmannschaft", personDto.getMannschaft().getName()); + assertEquals(4, personDto.getNickNames().size()); } private String nameSupplier() { From aa722d0cf3cb7aaf66740395cc6ef24aee1d1c88 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:19:30 +0200 Subject: [PATCH 04/10] Changing functionality, the value should be taken if already set --- .../simple/builders/core/util/TrackedValue.java | 8 ++++---- .../simple/builders/example/PersonDtoBuilderTest.java | 1 + .../builders/processor/util/BuilderDefinitionCreator.java | 4 ++-- .../simple/builders/processor/BuilderProcessorTest.java | 6 +++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java b/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java index 8d4f6854..251638d4 100644 --- a/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java +++ b/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java @@ -44,7 +44,7 @@ * @param value the underlying value (may be {@code null}) * @param isChanged whether the value was explicitly changed by the builder API */ -public record TrackedValue(T value, boolean isChanged) { +public record TrackedValue(T value, boolean isChanged, boolean isUnset) { /** * Executes the provided {@link Consumer} only if this value has been explicitly changed. @@ -65,7 +65,7 @@ public void ifChanged(Consumer consumer) { * @return an instance with {@code value == null} and {@code isChanged == false} */ public static TrackedValue unsetValue() { - return new TrackedValue<>(null, false); + return new TrackedValue<>(null, false, true); } /** @@ -77,7 +77,7 @@ public static TrackedValue unsetValue() { * @return an instance with the given value and {@code isChanged == false} */ public static TrackedValue initialValue(T value) { - return new TrackedValue<>(value, false); + return new TrackedValue<>(value, false, false); } /** @@ -88,6 +88,6 @@ public static TrackedValue initialValue(T value) { * @return an instance with the given value and {@code isChanged == true} */ public static TrackedValue changedValue(T value) { - return new TrackedValue<>(value, true); + return new TrackedValue<>(value, true, false); } } diff --git a/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java b/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java index 92a5cc07..fffdd113 100644 --- a/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java +++ b/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java @@ -45,6 +45,7 @@ void testBuilder() { .sponsoren( sb -> sb.add(SponsorDtoBuilder.create().name("TestSponsor").build()))) .name(this::nameSupplier) + .mannschaft(mb -> mb.sponsoren(sb -> sb.add(SponsorDtoBuilder.create().name("TestSponsor2").build()))) .build(); assertNotNull(personDto); assertNotNull(personDto.getBirthdate()); diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index 1960942a..a81c865b 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -489,7 +489,7 @@ private static MethodDto createFieldConsumer(String fieldName, TypeName fieldTyp methodDto.setMethodType(MethodTypes.CONSUMER); methodDto.setCode( """ - $helperType:T consumer = this.$fieldName:N.isChanged() ? this.$fieldName:N.value() : new $helperType:T(); + $helperType:T consumer = !this.$fieldName:N.isUnset() ? this.$fieldName:N.value() : new $helperType:T(); $dtoMethodParam:N.accept(consumer); this.$fieldName:N = $builderFieldWrapper:T.changedValue(consumer); return this; @@ -545,7 +545,7 @@ private static MethodDto createFieldConsumerWithBuilder(String fieldName, TypeNa methodDto.setMethodType(MethodTypes.CONSUMER_BY_BUILDER); methodDto.setCode( """ - $helperType:T builder = this.$fieldName:N.isChanged() ? new $helperType:T(this.$fieldName:N.value()) : new $helperType:T(); + $helperType:T builder = !this.$fieldName:N.isUnset() ? new $helperType:T(this.$fieldName:N.value()) : new $helperType:T(); $dtoMethodParam:N.accept(builder); this.$fieldName:N = $builderFieldWrapper:T.changedValue(builder.build()); return this; diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index df1c0095..ccf23d8a 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -454,7 +454,7 @@ public Address() {} "public RequiredCustomTypeViaConstructorBuilder address(Supplier
addressSupplier)", // Consumer method for constructor field (the key coverage addition!) "public RequiredCustomTypeViaConstructorBuilder address(Consumer
addressConsumer)", - "Address consumer = this.address.isChanged() ? this.address.value() : new Address();", + "Address consumer = !this.address.isUnset() ? this.address.value() : new Address();", "addressConsumer.accept(consumer);"); } @@ -1206,7 +1206,7 @@ public HelperAnno() {} generatedCode, "private TrackedValue helper = unsetValue();", "helperBuilderConsumer", - "HelperAnnoBuilder builder = this.helper.isChanged() ? new HelperAnnoBuilder(this.helper.value()) : new HelperAnnoBuilder();", + "HelperAnnoBuilder builder = !this.helper.isUnset() ? new HelperAnnoBuilder(this.helper.value()) : new HelperAnnoBuilder();", "helperBuilderConsumer.accept(builder);", "this.helper = changedValue(builder.build());"); } @@ -1248,7 +1248,7 @@ public HelperPlain() {} // Expect consumer method with local var named 'consumer' of HelperPlain ProcessorAsserts.assertContaining( generatedCode, - "HelperPlain consumer = this.helperPlain.isChanged() ? this.helperPlain.value() : new HelperPlain();", + "HelperPlain consumer = !this.helperPlain.isUnset() ? this.helperPlain.value() : new HelperPlain();", "helperPlainConsumer.accept(consumer);", "private TrackedValue helperPlain = unsetValue();"); } From 15d6c3d51531d366af6dc7879f29ca0a7b81820b Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:38:19 +0200 Subject: [PATCH 05/10] Fixing handling of initial values --- .../builders/core/util/TrackedValue.java | 23 +++++++++++-------- .../util/BuilderDefinitionCreator.java | 4 ++-- .../processor/util/JavaCodeGenerator.java | 4 ++-- .../processor/BuilderProcessorTest.java | 12 +++++----- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java b/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java index 251638d4..ff10fd1e 100644 --- a/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java +++ b/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java @@ -43,17 +43,18 @@ * @param the value type * @param value the underlying value (may be {@code null}) * @param isChanged whether the value was explicitly changed by the builder API + * @param isInitial whether the value is an initial value from an existing instance */ -public record TrackedValue(T value, boolean isChanged, boolean isUnset) { +public record TrackedValue(T value, boolean isChanged, boolean isInitial) { /** - * Executes the provided {@link Consumer} only if this value has been explicitly changed. + * Executes the provided {@link Consumer} only if this value has been set (initial value or + * changed). * - * @param consumer action to perform with the current {@link #value()} when {@link #isChanged()} - * is {@code true} + * @param consumer action to perform with the current {@link #value()} */ - public void ifChanged(Consumer consumer) { - if (isChanged) { + public void ifSet(Consumer consumer) { + if (isChanged || isInitial) { consumer.accept(value); } } @@ -62,10 +63,11 @@ public void ifChanged(Consumer consumer) { * Creates a tracked value representing an unset state (no change made). * * @param the value type - * @return an instance with {@code value == null} and {@code isChanged == false} + * @return an instance with {@code value == null}, {@code isChanged == false}, and {@code + * isInitial == false} */ public static TrackedValue unsetValue() { - return new TrackedValue<>(null, false, true); + return new TrackedValue<>(null, false, false); } /** @@ -74,10 +76,11 @@ public static TrackedValue unsetValue() { * * @param the value type * @param value the initial value (may be {@code null}) - * @return an instance with the given value and {@code isChanged == false} + * @return an instance with the given value, {@code isChanged == false}, and {@code isInitial == + * true} */ public static TrackedValue initialValue(T value) { - return new TrackedValue<>(value, false, false); + return new TrackedValue<>(value, false, true); } /** diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index a81c865b..d9a8a327 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -489,7 +489,7 @@ private static MethodDto createFieldConsumer(String fieldName, TypeName fieldTyp methodDto.setMethodType(MethodTypes.CONSUMER); methodDto.setCode( """ - $helperType:T consumer = !this.$fieldName:N.isUnset() ? this.$fieldName:N.value() : new $helperType:T(); + $helperType:T consumer = this.$fieldName:N.ifSet() ? this.$fieldName:N.value() : new $helperType:T(); $dtoMethodParam:N.accept(consumer); this.$fieldName:N = $builderFieldWrapper:T.changedValue(consumer); return this; @@ -545,7 +545,7 @@ private static MethodDto createFieldConsumerWithBuilder(String fieldName, TypeNa methodDto.setMethodType(MethodTypes.CONSUMER_BY_BUILDER); methodDto.setCode( """ - $helperType:T builder = !this.$fieldName:N.isUnset() ? new $helperType:T(this.$fieldName:N.value()) : new $helperType:T(); + $helperType:T builder = this.$fieldName:N.ifSet() ? new $helperType:T(this.$fieldName:N.value()) : new $helperType:T(); $dtoMethodParam:N.accept(builder); this.$fieldName:N = $builderFieldWrapper:T.changedValue(builder.build()); return this; diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaCodeGenerator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaCodeGenerator.java index 2a99207b..5d261417 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaCodeGenerator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaCodeGenerator.java @@ -281,9 +281,9 @@ private MethodSpec createMethodBuild( mb.addStatement("$1T result = new $2T<>($3L)", returnType, dtoBaseClass, ctorArgs); } - // Apply setter-based fields only when changed + // Apply setter-based fields only when set for (FieldDto f : setterFields) { - mb.addStatement("this.$N.ifChanged(result::$N)", f.getFieldName(), f.getSetterName()); + mb.addStatement("this.$N.ifSet(result::$N)", f.getFieldName(), f.getSetterName()); } mb.addStatement("return result"); return mb.build(); diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index ccf23d8a..eb66b85f 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -201,7 +201,7 @@ void shouldHandleFieldsOfConstructorAndSetters() { "public CtorAndSetterBuilder a(int a)", // helpermethod for constructor param "public CtorAndSetterBuilder a(Supplier aSupplier)", // supplier for constructor "CtorAndSetter result = new CtorAndSetter(this.a.value());", - "this.name.ifChanged(result::setName);"); + "this.name.ifSet(result::setName);"); } @Test @@ -247,7 +247,7 @@ void shouldPreferConstructorOverSetterWhenBothExist() { "public CtorAndSetterSameFieldBuilder name(String name)", // Expectations on build function "CtorAndSetterSameField result = new CtorAndSetterSameField(this.a.value());", - "this.name.ifChanged(result::setName);"); + "this.name.ifSet(result::setName);"); // expect no setter application for 'a' on build-function ProcessorAsserts.assertNotContaining(generatedCode, "result.setA(this.a);"); } @@ -454,7 +454,7 @@ public Address() {} "public RequiredCustomTypeViaConstructorBuilder address(Supplier
addressSupplier)", // Consumer method for constructor field (the key coverage addition!) "public RequiredCustomTypeViaConstructorBuilder address(Consumer
addressConsumer)", - "Address consumer = !this.address.isUnset() ? this.address.value() : new Address();", + "Address consumer = this.address.ifSet() ? this.address.value() : new Address();", "addressConsumer.accept(consumer);"); } @@ -882,7 +882,7 @@ void shouldGenerateBuildOverrideAndReturnAnInstanceWithCalledSetters() { @Override public BuildDoc build() { BuildDoc result = new BuildDoc(); - this.x.ifChanged(result::setX); + this.x.ifSet(result::setX); return result; } """); @@ -1206,7 +1206,7 @@ public HelperAnno() {} generatedCode, "private TrackedValue helper = unsetValue();", "helperBuilderConsumer", - "HelperAnnoBuilder builder = !this.helper.isUnset() ? new HelperAnnoBuilder(this.helper.value()) : new HelperAnnoBuilder();", + "HelperAnnoBuilder builder = this.helper.ifSet() ? new HelperAnnoBuilder(this.helper.value()) : new HelperAnnoBuilder();", "helperBuilderConsumer.accept(builder);", "this.helper = changedValue(builder.build());"); } @@ -1248,7 +1248,7 @@ public HelperPlain() {} // Expect consumer method with local var named 'consumer' of HelperPlain ProcessorAsserts.assertContaining( generatedCode, - "HelperPlain consumer = !this.helperPlain.isUnset() ? this.helperPlain.value() : new HelperPlain();", + "HelperPlain consumer = this.helperPlain.ifSet() ? this.helperPlain.value() : new HelperPlain();", "helperPlainConsumer.accept(consumer);", "private TrackedValue helperPlain = unsetValue();"); } From f56ea738f35dae5452e6779f82e7b1938ea2bac3 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:40:49 +0200 Subject: [PATCH 06/10] Fixing code to use one helper function instead of two --- .../simple/builders/core/util/TrackedValue.java | 11 ++++++++++- .../processor/util/BuilderDefinitionCreator.java | 4 ++-- .../builders/processor/BuilderProcessorTest.java | 6 +++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java b/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java index ff10fd1e..8cc2a919 100644 --- a/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java +++ b/core/src/main/java/org/javahelpers/simple/builders/core/util/TrackedValue.java @@ -47,6 +47,15 @@ */ public record TrackedValue(T value, boolean isChanged, boolean isInitial) { + /** + * Checks if this value has been set (either as initial value or changed). + * + * @return true if the value is set (initial or changed), false if unset + */ + public boolean isSet() { + return isChanged || isInitial; + } + /** * Executes the provided {@link Consumer} only if this value has been set (initial value or * changed). @@ -54,7 +63,7 @@ public record TrackedValue(T value, boolean isChanged, boolean isInitial) { * @param consumer action to perform with the current {@link #value()} */ public void ifSet(Consumer consumer) { - if (isChanged || isInitial) { + if (isSet()) { consumer.accept(value); } } diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index d9a8a327..1499b520 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -489,7 +489,7 @@ private static MethodDto createFieldConsumer(String fieldName, TypeName fieldTyp methodDto.setMethodType(MethodTypes.CONSUMER); methodDto.setCode( """ - $helperType:T consumer = this.$fieldName:N.ifSet() ? this.$fieldName:N.value() : new $helperType:T(); + $helperType:T consumer = this.$fieldName:N.isSet() ? this.$fieldName:N.value() : new $helperType:T(); $dtoMethodParam:N.accept(consumer); this.$fieldName:N = $builderFieldWrapper:T.changedValue(consumer); return this; @@ -545,7 +545,7 @@ private static MethodDto createFieldConsumerWithBuilder(String fieldName, TypeNa methodDto.setMethodType(MethodTypes.CONSUMER_BY_BUILDER); methodDto.setCode( """ - $helperType:T builder = this.$fieldName:N.ifSet() ? new $helperType:T(this.$fieldName:N.value()) : new $helperType:T(); + $helperType:T builder = this.$fieldName:N.isSet() ? new $helperType:T(this.$fieldName:N.value()) : new $helperType:T(); $dtoMethodParam:N.accept(builder); this.$fieldName:N = $builderFieldWrapper:T.changedValue(builder.build()); return this; diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index eb66b85f..c86be7db 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -454,7 +454,7 @@ public Address() {} "public RequiredCustomTypeViaConstructorBuilder address(Supplier
addressSupplier)", // Consumer method for constructor field (the key coverage addition!) "public RequiredCustomTypeViaConstructorBuilder address(Consumer
addressConsumer)", - "Address consumer = this.address.ifSet() ? this.address.value() : new Address();", + "Address consumer = this.address.isSet() ? this.address.value() : new Address();", "addressConsumer.accept(consumer);"); } @@ -1206,7 +1206,7 @@ public HelperAnno() {} generatedCode, "private TrackedValue helper = unsetValue();", "helperBuilderConsumer", - "HelperAnnoBuilder builder = this.helper.ifSet() ? new HelperAnnoBuilder(this.helper.value()) : new HelperAnnoBuilder();", + "HelperAnnoBuilder builder = this.helper.isSet() ? new HelperAnnoBuilder(this.helper.value()) : new HelperAnnoBuilder();", "helperBuilderConsumer.accept(builder);", "this.helper = changedValue(builder.build());"); } @@ -1248,7 +1248,7 @@ public HelperPlain() {} // Expect consumer method with local var named 'consumer' of HelperPlain ProcessorAsserts.assertContaining( generatedCode, - "HelperPlain consumer = this.helperPlain.ifSet() ? this.helperPlain.value() : new HelperPlain();", + "HelperPlain consumer = this.helperPlain.isSet() ? this.helperPlain.value() : new HelperPlain();", "helperPlainConsumer.accept(consumer);", "private TrackedValue helperPlain = unsetValue();"); } From 4efd6ee9182e31a44be46fe59f3e88d01c53b6a5 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:42:47 +0200 Subject: [PATCH 07/10] Adding assert for the rechange of mannschaft by builder --- .../simple/builders/example/PersonDtoBuilderTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java b/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java index fffdd113..75e9ab50 100644 --- a/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java +++ b/example/src/test/java/org/javahelpers/simple/builders/example/PersonDtoBuilderTest.java @@ -53,6 +53,7 @@ void testBuilder() { assertNotNull(personDto.getMannschaft()); assertEquals("Testmannschaft", personDto.getMannschaft().getName()); assertEquals(4, personDto.getNickNames().size()); + assertEquals(2, personDto.getMannschaft().getSponsoren().size()); } private String nameSupplier() { From 1dee9cf27de80a5d03910bd59d249d739a8e838b Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:51:23 +0200 Subject: [PATCH 08/10] Adding tests in core-module --- core/pom.xml | 16 ++ .../core/builders/ArrayListBuilderTest.java | 139 ++++++++++++ .../core/builders/HashMapBuilderTest.java | 172 ++++++++++++++ .../core/builders/HashSetBuilderTest.java | 151 +++++++++++++ .../builders/core/util/TrackedValueTest.java | 210 ++++++++++++++++++ 5 files changed, 688 insertions(+) create mode 100644 core/src/test/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilderTest.java create mode 100644 core/src/test/java/org/javahelpers/simple/builders/core/builders/HashMapBuilderTest.java create mode 100644 core/src/test/java/org/javahelpers/simple/builders/core/builders/HashSetBuilderTest.java create mode 100644 core/src/test/java/org/javahelpers/simple/builders/core/util/TrackedValueTest.java diff --git a/core/pom.xml b/core/pom.xml index f3c0f8e2..a57bb7b9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -64,6 +64,9 @@ false + + 6.0.0 + 0.9.0 2.29 @@ -76,6 +79,19 @@ + + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + diff --git a/core/src/test/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilderTest.java b/core/src/test/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilderTest.java new file mode 100644 index 00000000..47603b17 --- /dev/null +++ b/core/src/test/java/org/javahelpers/simple/builders/core/builders/ArrayListBuilderTest.java @@ -0,0 +1,139 @@ +/* + * MIT License + * + * Copyright (c) 2025 Andreas Igel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.javahelpers.simple.builders.core.builders; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +class ArrayListBuilderTest { + + @Test + void shouldCreateEmptyBuilder() { + ArrayListBuilder builder = new ArrayListBuilder<>(); + List result = builder.build(); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void shouldCreateBuilderWithInitialList() { + List initial = Arrays.asList("item1", "item2", "item3"); + + ArrayListBuilder builder = new ArrayListBuilder<>(initial); + List result = builder.build(); + + assertNotNull(result); + assertEquals(3, result.size()); + assertEquals("item1", result.get(0)); + assertEquals("item2", result.get(1)); + assertEquals("item3", result.get(2)); + } + + @Test + void shouldHandleNullInitialList() { + ArrayListBuilder builder = new ArrayListBuilder<>(null); + List result = builder.build(); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void shouldAllowAddingToInitializedList() { + List initial = Arrays.asList("item1", "item2"); + + ArrayListBuilder builder = new ArrayListBuilder<>(initial); + builder.add("item3").add("item4"); + List result = builder.build(); + + assertNotNull(result); + assertEquals(4, result.size()); + assertEquals("item1", result.get(0)); + assertEquals("item2", result.get(1)); + assertEquals("item3", result.get(2)); + assertEquals("item4", result.get(3)); + } + + @Test + void shouldAddSingleElement() { + ArrayListBuilder builder = new ArrayListBuilder<>(); + builder.add("test"); + List result = builder.build(); + + assertEquals(1, result.size()); + assertEquals("test", result.get(0)); + } + + @Test + void shouldAddMultipleElements() { + ArrayListBuilder builder = new ArrayListBuilder<>(); + builder.add("first").add("second").add("third"); + List result = builder.build(); + + assertEquals(3, result.size()); + assertEquals("first", result.get(0)); + assertEquals("second", result.get(1)); + assertEquals("third", result.get(2)); + } + + @Test + void shouldAddAllFromCollection() { + ArrayListBuilder builder = new ArrayListBuilder<>(); + builder.addAll(Arrays.asList(1, 2, 3)); + List result = builder.build(); + + assertEquals(3, result.size()); + assertEquals(1, result.get(0)); + assertEquals(2, result.get(1)); + assertEquals(3, result.get(2)); + } + + @Test + void shouldChainAddAndAddAll() { + ArrayListBuilder builder = new ArrayListBuilder<>(); + builder.add("first").addAll(Arrays.asList("second", "third")).add("fourth"); + List result = builder.build(); + + assertEquals(4, result.size()); + assertEquals("first", result.get(0)); + assertEquals("second", result.get(1)); + assertEquals("third", result.get(2)); + assertEquals("fourth", result.get(3)); + } + + @Test + void shouldReturnThisForFluentInterface() { + ArrayListBuilder builder = new ArrayListBuilder<>(); + ArrayListBuilder result = builder.add("test"); + + assertEquals(builder, result); + } +} diff --git a/core/src/test/java/org/javahelpers/simple/builders/core/builders/HashMapBuilderTest.java b/core/src/test/java/org/javahelpers/simple/builders/core/builders/HashMapBuilderTest.java new file mode 100644 index 00000000..9b47c95f --- /dev/null +++ b/core/src/test/java/org/javahelpers/simple/builders/core/builders/HashMapBuilderTest.java @@ -0,0 +1,172 @@ +/* + * MIT License + * + * Copyright (c) 2025 Andreas Igel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.javahelpers.simple.builders.core.builders; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class HashMapBuilderTest { + + @Test + void shouldCreateEmptyBuilder() { + HashMapBuilder builder = new HashMapBuilder<>(); + Map result = builder.build(); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void shouldCreateBuilderWithInitialMap() { + Map initial = new HashMap<>(); + initial.put("one", 1); + initial.put("two", 2); + initial.put("three", 3); + + HashMapBuilder builder = new HashMapBuilder<>(initial); + Map result = builder.build(); + + assertNotNull(result); + assertEquals(3, result.size()); + assertEquals(1, result.get("one")); + assertEquals(2, result.get("two")); + assertEquals(3, result.get("three")); + } + + @Test + void shouldHandleNullInitialMap() { + HashMapBuilder builder = new HashMapBuilder<>(null); + Map result = builder.build(); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void shouldAllowAddingToInitializedMap() { + Map initial = new HashMap<>(); + initial.put("one", 1); + initial.put("two", 2); + + HashMapBuilder builder = new HashMapBuilder<>(initial); + builder.put("three", 3).put("four", 4); + Map result = builder.build(); + + assertNotNull(result); + assertEquals(4, result.size()); + assertEquals(1, result.get("one")); + assertEquals(2, result.get("two")); + assertEquals(3, result.get("three")); + assertEquals(4, result.get("four")); + } + + @Test + void shouldPutSingleEntry() { + HashMapBuilder builder = new HashMapBuilder<>(); + builder.put("key", "value"); + Map result = builder.build(); + + assertEquals(1, result.size()); + assertEquals("value", result.get("key")); + } + + @Test + void shouldPutMultipleEntries() { + HashMapBuilder builder = new HashMapBuilder<>(); + builder.put("first", 1).put("second", 2).put("third", 3); + Map result = builder.build(); + + assertEquals(3, result.size()); + assertEquals(1, result.get("first")); + assertEquals(2, result.get("second")); + assertEquals(3, result.get("third")); + } + + @Test + void shouldOverwriteExistingKey() { + HashMapBuilder builder = new HashMapBuilder<>(); + builder.put("key", 1).put("key", 2); + Map result = builder.build(); + + assertEquals(1, result.size()); + assertEquals(2, result.get("key")); + } + + @Test + void shouldPutAllFromMap() { + HashMapBuilder builder = new HashMapBuilder<>(); + Map additional = new HashMap<>(); + additional.put("a", 1); + additional.put("b", 2); + + builder.putAll(additional); + Map result = builder.build(); + + assertEquals(2, result.size()); + assertEquals(1, result.get("a")); + assertEquals(2, result.get("b")); + } + + @Test + void shouldChainPutAndPutAll() { + HashMapBuilder builder = new HashMapBuilder<>(); + Map additional = new HashMap<>(); + additional.put("two", 2); + additional.put("three", 3); + + builder.put("one", 1).putAll(additional).put("four", 4); + Map result = builder.build(); + + assertEquals(4, result.size()); + assertEquals(1, result.get("one")); + assertEquals(2, result.get("two")); + assertEquals(3, result.get("three")); + assertEquals(4, result.get("four")); + } + + @Test + void shouldHandleNullValues() { + HashMapBuilder builder = new HashMapBuilder<>(); + builder.put("key", null); + Map result = builder.build(); + + assertEquals(1, result.size()); + assertTrue(result.containsKey("key")); + assertEquals(null, result.get("key")); + } + + @Test + void shouldReturnThisForFluentInterface() { + HashMapBuilder builder = new HashMapBuilder<>(); + HashMapBuilder result = builder.put("key", 1); + + assertEquals(builder, result); + } +} diff --git a/core/src/test/java/org/javahelpers/simple/builders/core/builders/HashSetBuilderTest.java b/core/src/test/java/org/javahelpers/simple/builders/core/builders/HashSetBuilderTest.java new file mode 100644 index 00000000..883f3d24 --- /dev/null +++ b/core/src/test/java/org/javahelpers/simple/builders/core/builders/HashSetBuilderTest.java @@ -0,0 +1,151 @@ +/* + * MIT License + * + * Copyright (c) 2025 Andreas Igel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.javahelpers.simple.builders.core.builders; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.Test; + +class HashSetBuilderTest { + + @Test + void shouldCreateEmptyBuilder() { + HashSetBuilder builder = new HashSetBuilder<>(); + Set result = builder.build(); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void shouldCreateBuilderWithInitialSet() { + Set initial = new HashSet<>(Arrays.asList("item1", "item2", "item3")); + + HashSetBuilder builder = new HashSetBuilder<>(initial); + Set result = builder.build(); + + assertNotNull(result); + assertEquals(3, result.size()); + assertTrue(result.contains("item1")); + assertTrue(result.contains("item2")); + assertTrue(result.contains("item3")); + } + + @Test + void shouldHandleNullInitialSet() { + HashSetBuilder builder = new HashSetBuilder<>(null); + Set result = builder.build(); + + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + void shouldAllowAddingToInitializedSet() { + Set initial = new HashSet<>(Arrays.asList("item1", "item2")); + + HashSetBuilder builder = new HashSetBuilder<>(initial); + builder.add("item3").add("item4"); + Set result = builder.build(); + + assertNotNull(result); + assertEquals(4, result.size()); + assertTrue(result.contains("item1")); + assertTrue(result.contains("item2")); + assertTrue(result.contains("item3")); + assertTrue(result.contains("item4")); + } + + @Test + void shouldAddSingleElement() { + HashSetBuilder builder = new HashSetBuilder<>(); + builder.add("test"); + Set result = builder.build(); + + assertEquals(1, result.size()); + assertTrue(result.contains("test")); + } + + @Test + void shouldAddMultipleElements() { + HashSetBuilder builder = new HashSetBuilder<>(); + builder.add("first").add("second").add("third"); + Set result = builder.build(); + + assertEquals(3, result.size()); + assertTrue(result.contains("first")); + assertTrue(result.contains("second")); + assertTrue(result.contains("third")); + } + + @Test + void shouldHandleDuplicateElements() { + HashSetBuilder builder = new HashSetBuilder<>(); + builder.add("duplicate").add("duplicate").add("unique"); + Set result = builder.build(); + + assertEquals(2, result.size()); + assertTrue(result.contains("duplicate")); + assertTrue(result.contains("unique")); + } + + @Test + void shouldAddAllFromSet() { + HashSetBuilder builder = new HashSetBuilder<>(); + builder.addAll(new HashSet<>(Arrays.asList(1, 2, 3))); + Set result = builder.build(); + + assertEquals(3, result.size()); + assertTrue(result.contains(1)); + assertTrue(result.contains(2)); + assertTrue(result.contains(3)); + } + + @Test + void shouldChainAddAndAddAll() { + HashSetBuilder builder = new HashSetBuilder<>(); + builder.add("first").addAll(new HashSet<>(Arrays.asList("second", "third"))).add("fourth"); + Set result = builder.build(); + + assertEquals(4, result.size()); + assertTrue(result.contains("first")); + assertTrue(result.contains("second")); + assertTrue(result.contains("third")); + assertTrue(result.contains("fourth")); + } + + @Test + void shouldReturnThisForFluentInterface() { + HashSetBuilder builder = new HashSetBuilder<>(); + HashSetBuilder result = builder.add("test"); + + assertEquals(builder, result); + } +} diff --git a/core/src/test/java/org/javahelpers/simple/builders/core/util/TrackedValueTest.java b/core/src/test/java/org/javahelpers/simple/builders/core/util/TrackedValueTest.java new file mode 100644 index 00000000..46aab1cf --- /dev/null +++ b/core/src/test/java/org/javahelpers/simple/builders/core/util/TrackedValueTest.java @@ -0,0 +1,210 @@ +/* + * MIT License + * + * Copyright (c) 2025 Andreas Igel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.javahelpers.simple.builders.core.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.jupiter.api.Test; + +class TrackedValueTest { + + @Test + void shouldCreateUnsetValue() { + TrackedValue tracked = TrackedValue.unsetValue(); + + assertNull(tracked.value()); + assertFalse(tracked.isChanged()); + assertFalse(tracked.isInitial()); + assertFalse(tracked.isSet()); + } + + @Test + void shouldCreateInitialValue() { + TrackedValue tracked = TrackedValue.initialValue("test"); + + assertEquals("test", tracked.value()); + assertFalse(tracked.isChanged()); + assertTrue(tracked.isInitial()); + assertTrue(tracked.isSet()); + } + + @Test + void shouldCreateInitialValueWithNull() { + TrackedValue tracked = TrackedValue.initialValue(null); + + assertNull(tracked.value()); + assertFalse(tracked.isChanged()); + assertTrue(tracked.isInitial()); + assertTrue(tracked.isSet()); + } + + @Test + void shouldCreateChangedValue() { + TrackedValue tracked = TrackedValue.changedValue("modified"); + + assertEquals("modified", tracked.value()); + assertTrue(tracked.isChanged()); + assertFalse(tracked.isInitial()); + assertTrue(tracked.isSet()); + } + + @Test + void shouldCreateChangedValueWithNull() { + TrackedValue tracked = TrackedValue.changedValue(null); + + assertNull(tracked.value()); + assertTrue(tracked.isChanged()); + assertFalse(tracked.isInitial()); + assertTrue(tracked.isSet()); + } + + @Test + void shouldReturnTrueForIsSetWhenChanged() { + TrackedValue tracked = TrackedValue.changedValue("value"); + + assertTrue(tracked.isSet()); + } + + @Test + void shouldReturnTrueForIsSetWhenInitial() { + TrackedValue tracked = TrackedValue.initialValue("value"); + + assertTrue(tracked.isSet()); + } + + @Test + void shouldReturnFalseForIsSetWhenUnset() { + TrackedValue tracked = TrackedValue.unsetValue(); + + assertFalse(tracked.isSet()); + } + + @Test + void shouldExecuteConsumerWhenChanged() { + TrackedValue tracked = TrackedValue.changedValue("test"); + AtomicBoolean executed = new AtomicBoolean(false); + AtomicReference capturedValue = new AtomicReference<>(); + + tracked.ifSet( + value -> { + executed.set(true); + capturedValue.set(value); + }); + + assertTrue(executed.get()); + assertEquals("test", capturedValue.get()); + } + + @Test + void shouldExecuteConsumerWhenInitial() { + TrackedValue tracked = TrackedValue.initialValue("initial"); + AtomicBoolean executed = new AtomicBoolean(false); + AtomicReference capturedValue = new AtomicReference<>(); + + tracked.ifSet( + value -> { + executed.set(true); + capturedValue.set(value); + }); + + assertTrue(executed.get()); + assertEquals("initial", capturedValue.get()); + } + + @Test + void shouldNotExecuteConsumerWhenUnset() { + TrackedValue tracked = TrackedValue.unsetValue(); + AtomicBoolean executed = new AtomicBoolean(false); + + tracked.ifSet(value -> executed.set(true)); + + assertFalse(executed.get()); + } + + @Test + void shouldPassNullToConsumerWhenValueIsNull() { + TrackedValue tracked = TrackedValue.changedValue(null); + AtomicBoolean executed = new AtomicBoolean(false); + AtomicReference capturedValue = new AtomicReference<>("not-null"); + + tracked.ifSet( + value -> { + executed.set(true); + capturedValue.set(value); + }); + + assertTrue(executed.get()); + assertNull(capturedValue.get()); + } + + @Test + void shouldSupportRecordEquality() { + TrackedValue tracked1 = new TrackedValue<>("test", true, false); + TrackedValue tracked2 = new TrackedValue<>("test", true, false); + + assertEquals(tracked1, tracked2); + } + + @Test + void shouldSupportRecordHashCode() { + TrackedValue tracked1 = new TrackedValue<>("test", true, false); + TrackedValue tracked2 = new TrackedValue<>("test", true, false); + + assertEquals(tracked1.hashCode(), tracked2.hashCode()); + } + + @Test + void shouldDistinguishDifferentStates() { + TrackedValue unset = TrackedValue.unsetValue(); + TrackedValue initial = TrackedValue.initialValue("test"); + TrackedValue changed = TrackedValue.changedValue("test"); + + // Unset vs Initial + assertFalse(unset.isSet()); + assertTrue(initial.isSet()); + + // Initial vs Changed + assertTrue(initial.isInitial()); + assertFalse(changed.isInitial()); + assertFalse(initial.isChanged()); + assertTrue(changed.isChanged()); + } + + @Test + void shouldWorkWithComplexTypes() { + TrackedValue intTracked = TrackedValue.changedValue(42); + TrackedValue doubleTracked = TrackedValue.initialValue(3.14); + + assertEquals(42, intTracked.value()); + assertEquals(3.14, doubleTracked.value()); + assertTrue(intTracked.isSet()); + assertTrue(doubleTracked.isSet()); + } +} From 4d5bb0e6ce3f7cef922cdb00cd692d29a1292b81 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:55:03 +0200 Subject: [PATCH 09/10] Adding test-setup --- core/pom.xml | 2 +- core/src/test/java/.gitkeep | 0 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 core/src/test/java/.gitkeep diff --git a/core/pom.xml b/core/pom.xml index a57bb7b9..538175c6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -65,7 +65,7 @@ false - 6.0.0 + 5.11.4 0.9.0 diff --git a/core/src/test/java/.gitkeep b/core/src/test/java/.gitkeep deleted file mode 100644 index e69de29b..00000000 From 13fb306040c6b6d0e92413c97adb40c90cf6aabd Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Sun, 19 Oct 2025 22:59:47 +0200 Subject: [PATCH 10/10] Adding jacoco configuration to core module to retrieve coverage --- core/pom.xml | 32 ++++++++++++++++++++++++++++++++ pom.xml | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 538175c6..c29417c8 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -76,6 +76,7 @@ 3.2.8 3.1.4 3.21.0 + 0.8.14 @@ -215,6 +216,37 @@ + + metrics + + + + org.jacoco + jacoco-maven-plugin + ${plugin.jacoco.version} + + + prepare-agent + + prepare-agent + + + + report + verify + + report + + + + + ${project.build.directory}/jacoco.exec + ${project.reporting.outputDirectory}/jacoco + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index e1ba8ab2..09fa2537 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ Simple Builders https://sonarcloud.io ${project.basedir}/core/target/classes,${project.basedir}/processor/target/classes - ${project.basedir}/processor/target/site/jacoco/jacoco.xml + ${project.basedir}/core/target/site/jacoco/jacoco.xml,${project.basedir}/processor/target/site/jacoco/jacoco.xml **/example/** **/*Mapper*.java,**/processor/dtos/**,**/generated/**