From 413d02c694ff81031f0befa50514bfd69d7cd7e9 Mon Sep 17 00:00:00 2001 From: Peridot Date: Tue, 18 Jul 2023 16:22:50 +0200 Subject: [PATCH 1/9] Create test for InjectorProcessor --- .../utilities/inject/InjectorProcessor.java | 2 +- .../inject/InjectorProcessorTest.java | 96 +++++++++++++++++++ .../inject/shared/AnnotationUtils.java | 34 +++++++ .../inject/shared/DummyProperty.java | 61 ++++++++++++ 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java create mode 100644 di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java create mode 100644 di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java diff --git a/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java b/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java index f7aa62f..32ff4b9 100644 --- a/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java +++ b/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java @@ -35,7 +35,7 @@ final class InjectorProcessor { private final Injector injector; private final Map injectableCache = new HashMap<>(); - private final Bind autoConstructBind; + final Bind autoConstructBind; InjectorProcessor(Injector injector) { this.injector = injector; diff --git a/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java b/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java new file mode 100644 index 0000000..ed3ddbe --- /dev/null +++ b/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java @@ -0,0 +1,96 @@ +package org.panda_lang.utilities.inject; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Constructor; +import org.junit.jupiter.api.Test; +import org.panda_lang.utilities.inject.annotations.AutoConstruct; +import org.panda_lang.utilities.inject.annotations.Injectable; +import org.panda_lang.utilities.inject.shared.AnnotationUtils; +import org.panda_lang.utilities.inject.shared.DummyProperty; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +final class InjectorProcessorTest { + + private final Injector injector = DependencyInjection.createInjector(resources -> { + resources.on(int.class).assignInstance(2023); + resources.on(String.class).assignInstance("Test"); + resources.annotatedWithTested(TestAnnotation.class).assignInstance("TestAnnotation"); + }); + private final InjectorProcessor processor = new InjectorProcessor(this.injector); + + @Injectable + @Retention(RetentionPolicy.RUNTIME) + @interface TestAnnotation { + + } + + private static class TestClass { + + public TestClass(int intProp, @TestAnnotation String stringAnnotatedProp) { } + + public TestClass(int intProp, float floatProp) { } + + } + + @Test + void shouldFetchAnnotations() { + Annotation[] annotations = this.processor.fetchAnnotations(TestClass.class.getConstructors()[0]); + assertEquals(2, annotations.length); + assertNull(annotations[0]); + assertTrue(annotations[1] instanceof TestAnnotation); + } + + @Test + void shouldFetchBinds() throws Exception { + // Fetch single + Property intProperty = new DummyProperty<>("intProp", int.class); + assertDoesNotThrow(() -> this.processor.fetchBind(null, intProperty)); + + Property stringProperty = new DummyProperty<>("stringProp", String.class); + Bind stringBind = assertDoesNotThrow(() -> this.processor.fetchBind(null, stringProperty)); + assertEquals("Test", stringBind.getValue(stringProperty, null)); + + Property stringAnnotatedProperty = new DummyProperty<>("stringAnnotatedProp", String.class, TestAnnotation.class); + TestAnnotation testAnnotation = AnnotationUtils.instanceAnnotation(TestAnnotation.class); + Bind annotatedStringBind = assertDoesNotThrow(() -> this.processor.fetchBind(testAnnotation, stringAnnotatedProperty)); + assertEquals("TestAnnotation", annotatedStringBind.getValue(stringAnnotatedProperty, testAnnotation)); + + Property autoConstructProperty = new DummyProperty<>("autoConstructProp", Object.class, AutoConstruct.class); + Bind autoConstructBind = assertDoesNotThrow(() -> this.processor.fetchBind(null, autoConstructProperty)); + assertEquals(this.processor.autoConstructBind, autoConstructBind); + + // Fetch multiple + Constructor testConstructor = TestClass.class.getConstructors()[0]; + Annotation[] annotations = this.processor.fetchAnnotations(testConstructor); + Bind[] fetchedBinds = assertDoesNotThrow(() -> this.processor.fetchBinds(annotations, testConstructor)); + + assertEquals(2, fetchedBinds.length); + assertNotNull(fetchedBinds[0]); + assertNotNull(fetchedBinds[1]); + } + + @Test + void shouldNotFetchBinds() { + // Fetch single + Property floatProperty = new DummyProperty<>("floatProp", float.class); + Property doubleProperty = new DummyProperty<>("doubleProp", double.class); + + assertThrows(MissingBindException.class, () -> this.processor.fetchBind(null, floatProperty)); + assertThrows(MissingBindException.class, () -> this.processor.fetchBind(null, doubleProperty)); + + // Fetch multiple + Constructor testConstructor = TestClass.class.getConstructors()[1]; + Annotation[] annotations = this.processor.fetchAnnotations(testConstructor); + + assertThrows(MissingBindException.class, () -> this.processor.fetchBinds(annotations, testConstructor)); + } + +} diff --git a/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java b/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java new file mode 100644 index 0000000..043dfc0 --- /dev/null +++ b/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java @@ -0,0 +1,34 @@ +package org.panda_lang.utilities.inject.shared; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Proxy; + +public final class AnnotationUtils { + + private AnnotationUtils() { + } + + @SuppressWarnings("unchecked") + public static A instanceAnnotation(Class annotationClass) { + return (A) Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class[] { annotationClass }, (proxy, method, args) -> { + if (method.getName().equals("annotationType")) { + return annotationClass; + } + + if (method.getName().equals("toString")) { + return "@" + annotationClass.getName() + "()"; + } + + if (method.getName().equals("hashCode")) { + return 0; + } + + if (method.getName().equals("equals")) { + return proxy == args[0]; + } + + throw new UnsupportedOperationException("Unsupported method: " + method); + }); + } + +} diff --git a/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java b/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java new file mode 100644 index 0000000..89bed96 --- /dev/null +++ b/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java @@ -0,0 +1,61 @@ +package org.panda_lang.utilities.inject.shared; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.panda_lang.utilities.inject.Property; +import panda.std.stream.PandaStream; + +public class DummyProperty implements Property { + + private final String name; + private final Class type; + private final Map, Annotation> annotations; + + public DummyProperty(String name, Class type, List> annotations) { + this.name = name; + this.type = type; + this.annotations = PandaStream.of(annotations) + .toMap(annotation -> annotation, annotation -> { + try { + return AnnotationUtils.instanceAnnotation(annotation); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }); + } + + @SafeVarargs + public DummyProperty(String name, Class type, Class... annotations) { + this(name, type, Arrays.asList(annotations)); + } + + @Override + public String getName() { + return this.name; + } + + @Override + public Class getType() { + return this.type; + } + + @Override + public Type getParametrizedType() { + return this.type; + } + + @Override + @SuppressWarnings("unchecked") + public A getAnnotation(Class annotation) { + return (A) this.annotations.get(annotation); + } + + @Override + public Annotation[] getAnnotations() { + return this.annotations.values().toArray(new Annotation[0]); + } + +} From 1e00302c0127bf13988241b9ec3ee588455ec09f Mon Sep 17 00:00:00 2001 From: Peridot Date: Tue, 18 Jul 2023 16:24:54 +0200 Subject: [PATCH 2/9] Make utility classes in tests private --- .../inject/DependencyInjectionFieldsTest.java | 6 +++--- .../inject/DependencyInjectionHandlerTest.java | 8 ++++---- .../inject/DependencyInjectionInstancesTest.java | 8 ++++---- .../utilities/inject/DependencyInjectionUtilsTest.java | 10 +++++----- .../utilities/inject/DependencyInjectionWikiTest.java | 2 +- .../utilities/inject/InjectorProcessorTest.java | 4 +--- .../inject/annotations/AutoConstructTest.java | 6 +++--- .../inject/annotations/PostConstructTest.java | 4 ++-- 8 files changed, 23 insertions(+), 25 deletions(-) diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java index 6cf031c..140704a 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java @@ -13,9 +13,9 @@ final class DependencyInjectionFieldsTest { @Injectable @Retention(RetentionPolicy.RUNTIME) - @interface Custom { } + private @interface Custom { } - static class Service extends AbstractService { + private static class Service extends AbstractService { public final boolean throughConstructor; public final String customArgument; @@ -41,7 +41,7 @@ public String serve() { } - static abstract class AbstractService { + private static abstract class AbstractService { @Inject protected float abstractFieldOne; diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java index 1a42857..ef84829 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java @@ -16,13 +16,13 @@ public class DependencyInjectionHandlerTest { @Injectable @Retention(RetentionPolicy.RUNTIME) - @interface Custom { } + private @interface Custom { } @Injectable @Retention(RetentionPolicy.RUNTIME) - @interface TestAnnotation { } + private @interface TestAnnotation { } - public static class Service1 { + private static class Service1 { @Inject @Custom @@ -61,7 +61,7 @@ void shouldCreateInstance() { injector.newInstanceWithFields(Service1.class); } - public static class Service2 { + private static class Service2 { @Inject public String fieldOne; diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java index 9c11b35..9659814 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java @@ -34,12 +34,12 @@ void shouldNotInjectInstances() { assertThrows(DependencyInjectionException.class, () -> injector.newInstance(Service.class)); } - public static class Bean { } + private static class Bean { } - public interface Custom { } - static class CustomImpl implements Custom { } + private interface Custom { } + private static class CustomImpl implements Custom { } - static class Service { + private static class Service { public Service(Bean bean, Custom custom) { assertNotNull(bean); assertNotNull(custom); diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java index ec5544e..26133f9 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java @@ -36,18 +36,18 @@ void testAnnotation() { assertDoesNotThrow(() -> DependencyInjectionUtils.testAnnotation(InjectableAnnotation.class)); } - static class NotAnAnnotation { } + private static class NotAnAnnotation { } - @interface DefaultAnnotation { } + private @interface DefaultAnnotation { } @Retention(RetentionPolicy.CLASS) - @interface ClassAnnotation { } + private @interface ClassAnnotation { } @Retention(RetentionPolicy.RUNTIME) - @interface RuntimeAnnotation { } + private @interface RuntimeAnnotation { } @Injectable @Retention(RetentionPolicy.RUNTIME) - @interface InjectableAnnotation { } + private @interface InjectableAnnotation { } } \ No newline at end of file diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java index 8fd2364..49ec0b3 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java @@ -53,7 +53,7 @@ void testWikiExample() { @Injectable // mark annotation as DI ready annotation @Retention(RetentionPolicy.RUNTIME) // make sure that the annotation is visible at runtime - @interface AwesomeRandom { } + private @interface AwesomeRandom { } private static final class Entity { private final UUID id; diff --git a/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java b/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java index ed3ddbe..184076c 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java @@ -28,9 +28,7 @@ final class InjectorProcessorTest { @Injectable @Retention(RetentionPolicy.RUNTIME) - @interface TestAnnotation { - - } + private @interface TestAnnotation { } private static class TestClass { diff --git a/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java b/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java index 73e6c51..56f0fe5 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java @@ -10,7 +10,7 @@ final class AutoConstructTest { - static class Service { + private static class Service { @AutoConstruct private Repository repository; @@ -26,7 +26,7 @@ public void testMethod(@AutoConstruct DataProvider dataProvider) { } - static class Repository { + private static class Repository { @AutoConstruct private DataProvider dataProvider; @@ -42,7 +42,7 @@ private void construct() { } - static class DataProvider { + private static class DataProvider { @Inject private String testValue; diff --git a/di/src/test/java/org/panda_lang/utilities/inject/annotations/PostConstructTest.java b/di/src/test/java/org/panda_lang/utilities/inject/annotations/PostConstructTest.java index cab1ad1..28326c9 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/annotations/PostConstructTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/annotations/PostConstructTest.java @@ -9,7 +9,7 @@ final class PostConstructTest { - static class Service extends AbstractService { + private static class Service extends AbstractService { @Inject public String value; @@ -24,7 +24,7 @@ private void construct() { } - abstract static class AbstractService { + private abstract static class AbstractService { @Inject private float abstractValue; From f4f0dc0dba1145ae3423a67fa389f82d82c62e00 Mon Sep 17 00:00:00 2001 From: Peridot Date: Tue, 18 Jul 2023 18:15:55 +0200 Subject: [PATCH 3/9] Create tests for methods invocation --- .../DependencyInjectionInstancesTest.java | 13 ++++ .../inject/MethodsInvocationTest.java | 72 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java index 9659814..086c679 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java @@ -1,5 +1,6 @@ package org.panda_lang.utilities.inject; +import java.security.InvalidParameterException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -31,6 +32,7 @@ void shouldNotInjectInstances() { }); }); + assertThrows(InvalidParameterException.class, () -> injector.forConstructor(InvalidClass.class).newInstance(), "Class has contain one and only one constructor"); assertThrows(DependencyInjectionException.class, () -> injector.newInstance(Service.class)); } @@ -46,4 +48,15 @@ public Service(Bean bean, Custom custom) { } } + private static class InvalidClass { // 2 constructors (only 1 is allowed) + public InvalidClass(Bean bean, Custom custom) { + assertNotNull(bean); + assertNotNull(custom); + } + + public InvalidClass(Bean bean) { + assertNotNull(bean); + } + } + } diff --git a/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java new file mode 100644 index 0000000..610fa66 --- /dev/null +++ b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java @@ -0,0 +1,72 @@ +package org.panda_lang.utilities.inject; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class MethodsInvocationTest { + + private static class TestClass { + + private String testString; + private int testInt = 0; + + public void testMethod() { + this.testString = "test"; + } + + @TestAnnotation + public void annotatedMethod() { + this.testInt += 2; + } + + @TestAnnotation + public void annotatedMethod(int value) { + this.testInt *= value; + } + + @TestAnnotation2 + public void annotatedMethod2() { + throw new IllegalStateException("This method should not be invoked"); + } + + public void failMethod() { + throw new RuntimeException(); + } + + } + + @Retention(RetentionPolicy.RUNTIME) + private @interface TestAnnotation { } + + @Retention(RetentionPolicy.RUNTIME) + private @interface TestAnnotation2 {} + + @Test + void shouldInvokeMethods() throws NoSuchMethodException { + Injector injector = DependencyInjection.createInjector(resources -> { + resources.on(int.class).assignInstance(3); + }); + + TestClass testClass = new TestClass(); + injector.invokeMethod(TestClass.class.getMethod("testMethod"), testClass); + assertEquals("test", testClass.testString); + + injector.invokeAnnotatedMethods(TestAnnotation.class, testClass); + assertEquals(6, testClass.testInt); + } + + @Test + void shouldNotInvokeMethods() throws NoSuchMethodException { + Injector injector = DependencyInjection.createInjector(resources -> { + resources.on(int.class).assignInstance(3); + }); + + TestClass testClass = new TestClass(); + assertThrows(DependencyInjectionException.class, () -> injector.invokeMethod(TestClass.class.getMethod("failMethod"), testClass)); + } + +} From c94515343ac46b0e728c973534b10477593c8511 Mon Sep 17 00:00:00 2001 From: Peridot Date: Tue, 18 Jul 2023 18:22:45 +0200 Subject: [PATCH 4/9] Slightly improve AnnotationUtils --- .../panda_lang/utilities/inject/shared/AnnotationUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java b/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java index 043dfc0..cad68cb 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java @@ -20,11 +20,11 @@ public static A instanceAnnotation(Class annotationCla } if (method.getName().equals("hashCode")) { - return 0; + return annotationClass.hashCode() * 127; } if (method.getName().equals("equals")) { - return proxy == args[0]; + return args[0] instanceof Annotation && annotationClass.equals(((Annotation) args[0]).annotationType()); } throw new UnsupportedOperationException("Unsupported method: " + method); From a454de0da8087618abe18ea2c279aacd2edcddf8 Mon Sep 17 00:00:00 2001 From: Peridot Date: Tue, 18 Jul 2023 22:29:47 +0200 Subject: [PATCH 5/9] Remove unused DependencyInjectionException constructor --- .../utilities/inject/DependencyInjectionException.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/di/src/main/java/org/panda_lang/utilities/inject/DependencyInjectionException.java b/di/src/main/java/org/panda_lang/utilities/inject/DependencyInjectionException.java index b617c75..f47ca5b 100644 --- a/di/src/main/java/org/panda_lang/utilities/inject/DependencyInjectionException.java +++ b/di/src/main/java/org/panda_lang/utilities/inject/DependencyInjectionException.java @@ -28,10 +28,6 @@ public class DependencyInjectionException extends RuntimeException { super(message); } - DependencyInjectionException(Throwable cause) { - super(cause); - } - DependencyInjectionException(String message, Throwable cause) { super(message, cause); } From 66d76a1e1295dc1c0f13654eee88317ad964f218 Mon Sep 17 00:00:00 2001 From: Peridot Date: Tue, 18 Jul 2023 22:35:51 +0200 Subject: [PATCH 6/9] Assert that method invocation does not throw --- .../utilities/inject/DependencyInjectionHandlerTest.java | 2 -- .../panda_lang/utilities/inject/MethodsInvocationTest.java | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java index ef84829..93e116f 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java @@ -2,7 +2,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.concurrent.ThreadLocalRandom; import org.junit.jupiter.api.Test; import org.panda_lang.utilities.inject.annotations.Inject; import org.panda_lang.utilities.inject.annotations.Injectable; @@ -10,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; public class DependencyInjectionHandlerTest { diff --git a/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java index 610fa66..674ae79 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java @@ -4,6 +4,7 @@ import java.lang.annotation.RetentionPolicy; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -43,7 +44,7 @@ public void failMethod() { private @interface TestAnnotation { } @Retention(RetentionPolicy.RUNTIME) - private @interface TestAnnotation2 {} + private @interface TestAnnotation2 { } @Test void shouldInvokeMethods() throws NoSuchMethodException { @@ -52,10 +53,10 @@ void shouldInvokeMethods() throws NoSuchMethodException { }); TestClass testClass = new TestClass(); - injector.invokeMethod(TestClass.class.getMethod("testMethod"), testClass); + assertDoesNotThrow(() -> injector.invokeMethod(TestClass.class.getMethod("testMethod"), testClass)); assertEquals("test", testClass.testString); - injector.invokeAnnotatedMethods(TestAnnotation.class, testClass); + assertDoesNotThrow(() -> injector.invokeAnnotatedMethods(TestAnnotation.class, testClass)); assertEquals(6, testClass.testInt); } From 0c2ddb2b02b7a587bc01b0e5992ba9bfd123d389 Mon Sep 17 00:00:00 2001 From: Peridot Date: Tue, 18 Jul 2023 22:39:27 +0200 Subject: [PATCH 7/9] Better line breakings --- .../panda_lang/utilities/inject/shared/DummyProperty.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java b/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java index 89bed96..bafa960 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java @@ -17,14 +17,16 @@ public class DummyProperty implements Property { public DummyProperty(String name, Class type, List> annotations) { this.name = name; this.type = type; - this.annotations = PandaStream.of(annotations) - .toMap(annotation -> annotation, annotation -> { + this.annotations = PandaStream.of(annotations).toMap( + annotation -> annotation, + annotation -> { try { return AnnotationUtils.instanceAnnotation(annotation); } catch (Exception ex) { throw new RuntimeException(ex); } - }); + } + ); } @SafeVarargs From 398cc02af91e566680fca765edcff0184cd3f27c Mon Sep 17 00:00:00 2001 From: Peridot Date: Wed, 19 Jul 2023 04:01:59 +0200 Subject: [PATCH 8/9] Do not rely on order of execution --- .../panda_lang/utilities/inject/MethodsInvocationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java index 674ae79..4b1a3a6 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java @@ -26,7 +26,7 @@ public void annotatedMethod() { @TestAnnotation public void annotatedMethod(int value) { - this.testInt *= value; + this.testInt += value; } @TestAnnotation2 @@ -57,7 +57,7 @@ void shouldInvokeMethods() throws NoSuchMethodException { assertEquals("test", testClass.testString); assertDoesNotThrow(() -> injector.invokeAnnotatedMethods(TestAnnotation.class, testClass)); - assertEquals(6, testClass.testInt); + assertEquals(5, testClass.testInt); } @Test From 046c10ba83ae944870cc8d24f28e3748662aa719 Mon Sep 17 00:00:00 2001 From: Peridot Date: Wed, 19 Jul 2023 15:45:52 +0200 Subject: [PATCH 9/9] General tests cleanup --- .../utilities/inject/InjectorProcessor.java | 6 +- .../inject/DependencyInjectionFieldsTest.java | 12 ++-- .../DependencyInjectionHandlerTest.java | 4 +- .../DependencyInjectionInstancesTest.java | 13 ++-- .../inject/DependencyInjectionUtilsTest.java | 18 +++--- .../inject/DependencyInjectionWikiTest.java | 11 ++-- .../inject/InjectorProcessorTest.java | 42 ++++++++----- .../inject/MethodsInvocationTest.java | 8 +-- .../inject/annotations/AutoConstructTest.java | 1 - .../inject/shared/AnnotationUtils.java | 34 ---------- .../inject/shared/DummyProperty.java | 63 ------------------- 11 files changed, 66 insertions(+), 146 deletions(-) delete mode 100644 di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java delete mode 100644 di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java diff --git a/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java b/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java index 32ff4b9..47b1717 100644 --- a/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java +++ b/di/src/main/java/org/panda_lang/utilities/inject/InjectorProcessor.java @@ -35,7 +35,7 @@ final class InjectorProcessor { private final Injector injector; private final Map injectableCache = new HashMap<>(); - final Bind autoConstructBind; + private final Bind autoConstructBind; InjectorProcessor(Injector injector) { this.injector = injector; @@ -188,4 +188,8 @@ protected Bind fetchBind(@Nullable Annotation annotation, Property p return handlers; } + Bind getAutoConstructBind() { + return this.autoConstructBind; + } + } diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java index 140704a..39ae1d6 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionFieldsTest.java @@ -1,19 +1,19 @@ package org.panda_lang.utilities.inject; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import org.junit.jupiter.api.Test; import org.panda_lang.utilities.inject.annotations.Inject; import org.panda_lang.utilities.inject.annotations.Injectable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; final class DependencyInjectionFieldsTest { @Injectable @Retention(RetentionPolicy.RUNTIME) - private @interface Custom { } + private @interface Custom {} private static class Service extends AbstractService { @@ -68,7 +68,7 @@ void shouldInjectFields() { resources.annotatedWithTested(Custom.class).assignHandler((property, custom, objects) -> objects[0].toString()); }); - Service service = injector.newInstanceWithFields(Service.class,"custom argument"); + Service service = injector.newInstanceWithFields(Service.class, "custom argument"); assertEquals("Hello Field 7", service.serve()); assertEquals("1.2 254623242914889729", service.serveAbstract()); } diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java index 93e116f..0ebadb1 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionHandlerTest.java @@ -14,11 +14,11 @@ public class DependencyInjectionHandlerTest { @Injectable @Retention(RetentionPolicy.RUNTIME) - private @interface Custom { } + private @interface Custom {} @Injectable @Retention(RetentionPolicy.RUNTIME) - private @interface TestAnnotation { } + private @interface TestAnnotation {} private static class Service1 { diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java index 086c679..7945b28 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionInstancesTest.java @@ -32,23 +32,27 @@ void shouldNotInjectInstances() { }); }); - assertThrows(InvalidParameterException.class, () -> injector.forConstructor(InvalidClass.class).newInstance(), "Class has contain one and only one constructor"); + assertThrows(InvalidParameterException.class, () -> injector.forConstructor(InvalidClass.class).newInstance(), "Class has to contain one and only constructor"); assertThrows(DependencyInjectionException.class, () -> injector.newInstance(Service.class)); } - private static class Bean { } + private static class Bean {} - private interface Custom { } - private static class CustomImpl implements Custom { } + private interface Custom {} + + private static class CustomImpl implements Custom {} private static class Service { + public Service(Bean bean, Custom custom) { assertNotNull(bean); assertNotNull(custom); } + } private static class InvalidClass { // 2 constructors (only 1 is allowed) + public InvalidClass(Bean bean, Custom custom) { assertNotNull(bean); assertNotNull(custom); @@ -57,6 +61,7 @@ public InvalidClass(Bean bean, Custom custom) { public InvalidClass(Bean bean) { assertNotNull(bean); } + } } diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java index 26133f9..7f82aea 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionUtilsTest.java @@ -16,13 +16,13 @@ package org.panda_lang.utilities.inject; -import org.junit.jupiter.api.Test; -import org.panda_lang.utilities.inject.annotations.Injectable; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import org.junit.jupiter.api.Test; +import org.panda_lang.utilities.inject.annotations.Injectable; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; class DependencyInjectionUtilsTest { @@ -36,18 +36,18 @@ void testAnnotation() { assertDoesNotThrow(() -> DependencyInjectionUtils.testAnnotation(InjectableAnnotation.class)); } - private static class NotAnAnnotation { } + private static class NotAnAnnotation {} - private @interface DefaultAnnotation { } + private @interface DefaultAnnotation {} @Retention(RetentionPolicy.CLASS) - private @interface ClassAnnotation { } + private @interface ClassAnnotation {} @Retention(RetentionPolicy.RUNTIME) - private @interface RuntimeAnnotation { } + private @interface RuntimeAnnotation {} @Injectable @Retention(RetentionPolicy.RUNTIME) - private @interface InjectableAnnotation { } + private @interface InjectableAnnotation {} } \ No newline at end of file diff --git a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java index 49ec0b3..9b78dd6 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/DependencyInjectionWikiTest.java @@ -16,13 +16,12 @@ package org.panda_lang.utilities.inject; -import org.junit.jupiter.api.Test; -import org.panda_lang.utilities.inject.annotations.Inject; -import org.panda_lang.utilities.inject.annotations.Injectable; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.panda_lang.utilities.inject.annotations.Inject; +import org.panda_lang.utilities.inject.annotations.Injectable; final class DependencyInjectionWikiTest { @@ -53,9 +52,10 @@ void testWikiExample() { @Injectable // mark annotation as DI ready annotation @Retention(RetentionPolicy.RUNTIME) // make sure that the annotation is visible at runtime - private @interface AwesomeRandom { } + private @interface AwesomeRandom {} private static final class Entity { + private final UUID id; @Inject //it's not required, but it might be useful to disable "unused method" warnings/scanning for annotations @@ -66,6 +66,7 @@ private Entity(@AwesomeRandom UUID random) { public UUID getId() { return id; } + } } diff --git a/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java b/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java index 184076c..6d48ba6 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java @@ -4,11 +4,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import org.junit.jupiter.api.Test; import org.panda_lang.utilities.inject.annotations.AutoConstruct; import org.panda_lang.utilities.inject.annotations.Injectable; -import org.panda_lang.utilities.inject.shared.AnnotationUtils; -import org.panda_lang.utilities.inject.shared.DummyProperty; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -28,13 +27,26 @@ final class InjectorProcessorTest { @Injectable @Retention(RetentionPolicy.RUNTIME) - private @interface TestAnnotation { } + private @interface TestAnnotation {} private static class TestClass { - public TestClass(int intProp, @TestAnnotation String stringAnnotatedProp) { } + private int intProp; + private float floatProp; + private String stringProp; + @TestAnnotation + private String stringAnnotatedProp; + @AutoConstruct + private Object autoConstructProp; - public TestClass(int intProp, float floatProp) { } + public TestClass(int intProp, @TestAnnotation String stringAnnotatedProp) {} + + public TestClass(int intProp, float floatProp) {} + + private static PropertyField getProperty(String fieldName) throws NoSuchFieldException { + Field field = TestClass.class.getDeclaredField(fieldName); + return new PropertyField(field); + } } @@ -49,21 +61,21 @@ void shouldFetchAnnotations() { @Test void shouldFetchBinds() throws Exception { // Fetch single - Property intProperty = new DummyProperty<>("intProp", int.class); + Property intProperty = TestClass.getProperty("intProp"); assertDoesNotThrow(() -> this.processor.fetchBind(null, intProperty)); - Property stringProperty = new DummyProperty<>("stringProp", String.class); + Property stringProperty = TestClass.getProperty("stringProp"); Bind stringBind = assertDoesNotThrow(() -> this.processor.fetchBind(null, stringProperty)); assertEquals("Test", stringBind.getValue(stringProperty, null)); - Property stringAnnotatedProperty = new DummyProperty<>("stringAnnotatedProp", String.class, TestAnnotation.class); - TestAnnotation testAnnotation = AnnotationUtils.instanceAnnotation(TestAnnotation.class); + Property stringAnnotatedProperty = TestClass.getProperty("stringAnnotatedProp"); + TestAnnotation testAnnotation = stringAnnotatedProperty.getAnnotation(TestAnnotation.class); Bind annotatedStringBind = assertDoesNotThrow(() -> this.processor.fetchBind(testAnnotation, stringAnnotatedProperty)); assertEquals("TestAnnotation", annotatedStringBind.getValue(stringAnnotatedProperty, testAnnotation)); - Property autoConstructProperty = new DummyProperty<>("autoConstructProp", Object.class, AutoConstruct.class); + Property autoConstructProperty = TestClass.getProperty("autoConstructProp"); Bind autoConstructBind = assertDoesNotThrow(() -> this.processor.fetchBind(null, autoConstructProperty)); - assertEquals(this.processor.autoConstructBind, autoConstructBind); + assertEquals(this.processor.getAutoConstructBind(), autoConstructBind); // Fetch multiple Constructor testConstructor = TestClass.class.getConstructors()[0]; @@ -76,18 +88,14 @@ void shouldFetchBinds() throws Exception { } @Test - void shouldNotFetchBinds() { + void shouldNotFetchBinds() throws Exception { // Fetch single - Property floatProperty = new DummyProperty<>("floatProp", float.class); - Property doubleProperty = new DummyProperty<>("doubleProp", double.class); - + Property floatProperty = TestClass.getProperty("floatProp"); assertThrows(MissingBindException.class, () -> this.processor.fetchBind(null, floatProperty)); - assertThrows(MissingBindException.class, () -> this.processor.fetchBind(null, doubleProperty)); // Fetch multiple Constructor testConstructor = TestClass.class.getConstructors()[1]; Annotation[] annotations = this.processor.fetchAnnotations(testConstructor); - assertThrows(MissingBindException.class, () -> this.processor.fetchBinds(annotations, testConstructor)); } diff --git a/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java index 4b1a3a6..338b0d3 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java @@ -41,13 +41,13 @@ public void failMethod() { } @Retention(RetentionPolicy.RUNTIME) - private @interface TestAnnotation { } + private @interface TestAnnotation {} @Retention(RetentionPolicy.RUNTIME) - private @interface TestAnnotation2 { } + private @interface TestAnnotation2 {} @Test - void shouldInvokeMethods() throws NoSuchMethodException { + void shouldInvokeMethods() { Injector injector = DependencyInjection.createInjector(resources -> { resources.on(int.class).assignInstance(3); }); @@ -61,7 +61,7 @@ void shouldInvokeMethods() throws NoSuchMethodException { } @Test - void shouldNotInvokeMethods() throws NoSuchMethodException { + void shouldNotInvokeMethods() { Injector injector = DependencyInjection.createInjector(resources -> { resources.on(int.class).assignInstance(3); }); diff --git a/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java b/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java index 56f0fe5..0626f72 100644 --- a/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java +++ b/di/src/test/java/org/panda_lang/utilities/inject/annotations/AutoConstructTest.java @@ -6,7 +6,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; final class AutoConstructTest { diff --git a/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java b/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java deleted file mode 100644 index cad68cb..0000000 --- a/di/src/test/java/org/panda_lang/utilities/inject/shared/AnnotationUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.panda_lang.utilities.inject.shared; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Proxy; - -public final class AnnotationUtils { - - private AnnotationUtils() { - } - - @SuppressWarnings("unchecked") - public static A instanceAnnotation(Class annotationClass) { - return (A) Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class[] { annotationClass }, (proxy, method, args) -> { - if (method.getName().equals("annotationType")) { - return annotationClass; - } - - if (method.getName().equals("toString")) { - return "@" + annotationClass.getName() + "()"; - } - - if (method.getName().equals("hashCode")) { - return annotationClass.hashCode() * 127; - } - - if (method.getName().equals("equals")) { - return args[0] instanceof Annotation && annotationClass.equals(((Annotation) args[0]).annotationType()); - } - - throw new UnsupportedOperationException("Unsupported method: " + method); - }); - } - -} diff --git a/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java b/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java deleted file mode 100644 index bafa960..0000000 --- a/di/src/test/java/org/panda_lang/utilities/inject/shared/DummyProperty.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.panda_lang.utilities.inject.shared; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import org.panda_lang.utilities.inject.Property; -import panda.std.stream.PandaStream; - -public class DummyProperty implements Property { - - private final String name; - private final Class type; - private final Map, Annotation> annotations; - - public DummyProperty(String name, Class type, List> annotations) { - this.name = name; - this.type = type; - this.annotations = PandaStream.of(annotations).toMap( - annotation -> annotation, - annotation -> { - try { - return AnnotationUtils.instanceAnnotation(annotation); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - ); - } - - @SafeVarargs - public DummyProperty(String name, Class type, Class... annotations) { - this(name, type, Arrays.asList(annotations)); - } - - @Override - public String getName() { - return this.name; - } - - @Override - public Class getType() { - return this.type; - } - - @Override - public Type getParametrizedType() { - return this.type; - } - - @Override - @SuppressWarnings("unchecked") - public A getAnnotation(Class annotation) { - return (A) this.annotations.get(annotation); - } - - @Override - public Annotation[] getAnnotations() { - return this.annotations.values().toArray(new Annotation[0]); - } - -}