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); } 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..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 @@ -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 6cf031c..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,21 +1,21 @@ 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) - @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; @@ -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 1a42857..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 @@ -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,19 +9,18 @@ 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 { @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 +59,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..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 @@ -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,19 +32,36 @@ void shouldNotInjectInstances() { }); }); + assertThrows(InvalidParameterException.class, () -> injector.forConstructor(InvalidClass.class).newInstance(), "Class has to contain one and only constructor"); 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 {} + + private static class Service { - 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); + } + + 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 ec5544e..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)); } - 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..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 - @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 new file mode 100644 index 0000000..6d48ba6 --- /dev/null +++ b/di/src/test/java/org/panda_lang/utilities/inject/InjectorProcessorTest.java @@ -0,0 +1,102 @@ +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 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 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) + private @interface TestAnnotation {} + + private static class TestClass { + + private int intProp; + private float floatProp; + private String stringProp; + @TestAnnotation + private String stringAnnotatedProp; + @AutoConstruct + private Object autoConstructProp; + + 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); + } + + } + + @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 = TestClass.getProperty("intProp"); + assertDoesNotThrow(() -> this.processor.fetchBind(null, intProperty)); + + Property stringProperty = TestClass.getProperty("stringProp"); + Bind stringBind = assertDoesNotThrow(() -> this.processor.fetchBind(null, stringProperty)); + assertEquals("Test", stringBind.getValue(stringProperty, null)); + + 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 = TestClass.getProperty("autoConstructProp"); + Bind autoConstructBind = assertDoesNotThrow(() -> this.processor.fetchBind(null, autoConstructProperty)); + assertEquals(this.processor.getAutoConstructBind(), 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() throws Exception { + // Fetch single + Property floatProperty = TestClass.getProperty("floatProp"); + assertThrows(MissingBindException.class, () -> this.processor.fetchBind(null, floatProperty)); + + // 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 new file mode 100644 index 0000000..338b0d3 --- /dev/null +++ b/di/src/test/java/org/panda_lang/utilities/inject/MethodsInvocationTest.java @@ -0,0 +1,73 @@ +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.assertDoesNotThrow; +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() { + Injector injector = DependencyInjection.createInjector(resources -> { + resources.on(int.class).assignInstance(3); + }); + + TestClass testClass = new TestClass(); + assertDoesNotThrow(() -> injector.invokeMethod(TestClass.class.getMethod("testMethod"), testClass)); + assertEquals("test", testClass.testString); + + assertDoesNotThrow(() -> injector.invokeAnnotatedMethods(TestAnnotation.class, testClass)); + assertEquals(5, testClass.testInt); + } + + @Test + void shouldNotInvokeMethods() { + 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)); + } + +} 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..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,11 +6,10 @@ 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 { - static class Service { + private static class Service { @AutoConstruct private Repository repository; @@ -26,7 +25,7 @@ public void testMethod(@AutoConstruct DataProvider dataProvider) { } - static class Repository { + private static class Repository { @AutoConstruct private DataProvider dataProvider; @@ -42,7 +41,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;