Simplify ClassPathScanner tests
authorStephen Kitt <skitt@redhat.com>
Fri, 12 Oct 2018 07:40:27 +0000 (09:40 +0200)
committerMichael Vorburger <mike@vorburger.ch>
Wed, 1 Jul 2020 23:37:26 +0000 (01:37 +0200)
By isolating the Guice-specific aspects of ClassPathScanner, and
making the latter more generic, we can greatly simplify the
ClassPathScanner UT and isolate it from Guice dependencies.

Signed-off-by: Stephen Kitt <skitt@redhat.com>
src/main/java/org/opendaylight/genius/simple/AlivenessMonitorWiring.java
src/main/java/org/opendaylight/genius/simple/GeniusMain.java
src/main/java/org/opendaylight/genius/simple/GeniusWiring.java
src/main/java/org/opendaylight/infrautils/inject/ClassPathScanner.java
src/main/java/org/opendaylight/infrautils/inject/guice/GuiceClassPathBinder.java [new file with mode: 0644]
src/main/java/org/opendaylight/netvirt/simple/NetvirtMain.java
src/main/java/org/opendaylight/netvirt/simple/NetvirtWiring.java
src/test/java/org/opendaylight/genius/simple/test/GeniusSimpleDistributionTest.java
src/test/java/org/opendaylight/infrautils/inject/tests/ClassPathScannerTest.java

index cb09ad6a0b3539c3734e5c8ded18835074c8a157..cb1811a98ec2b923d02e4f4ccda03c3fb3046eba 100644 (file)
@@ -8,20 +8,20 @@
 package org.opendaylight.genius.simple;
 
 import com.google.inject.AbstractModule;
-import org.opendaylight.infrautils.inject.ClassPathScanner;
+import org.opendaylight.infrautils.inject.guice.GuiceClassPathBinder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.alivenessmonitor.rev160411.AlivenessMonitorService;
 
 public class AlivenessMonitorWiring extends AbstractModule {
 
-    private final ClassPathScanner scanner;
+    private final GuiceClassPathBinder classPathBinder;
 
-    public AlivenessMonitorWiring(ClassPathScanner scanner) {
-        this.scanner = scanner;
+    public AlivenessMonitorWiring(GuiceClassPathBinder classPathBinder) {
+        this.classPathBinder = classPathBinder;
     }
 
     @Override
     protected void configure() {
-        scanner.bind(binder(), AlivenessMonitorService.class);
+        classPathBinder.bind(binder(), AlivenessMonitorService.class);
     }
 
 }
index 4db2d63ed0a77086cbfb42e5d530d71327f055e2..11ac369abec61c8d3f65353fac62c3c2d6a54faa 100644 (file)
@@ -7,7 +7,7 @@
  */
 package org.opendaylight.genius.simple;
 
-import org.opendaylight.infrautils.inject.ClassPathScanner;
+import org.opendaylight.infrautils.inject.guice.GuiceClassPathBinder;
 import org.opendaylight.infrautils.simple.ShellMain;
 
 public final class GeniusMain {
@@ -15,7 +15,7 @@ public final class GeniusMain {
     private GeniusMain() { }
 
     public static void main(String[] args) {
-        ClassPathScanner scanner = new ClassPathScanner("org.opendaylight");
-        new ShellMain(new GeniusWiring(scanner)).awaitShutdown();
+        GuiceClassPathBinder classPathBinder = new GuiceClassPathBinder("org.opendaylight");
+        new ShellMain(new GeniusWiring(classPathBinder)).awaitShutdown();
     }
 }
index 8b1d81bf734903b4094d418a94b6809ac2463a3a..0b36c8e3f3705aad2955f27d71f4b1ce546485f3 100644 (file)
@@ -9,7 +9,7 @@ package org.opendaylight.genius.simple;
 
 import com.google.inject.AbstractModule;
 import org.opendaylight.daexim.DataImportBootReady;
-import org.opendaylight.infrautils.inject.ClassPathScanner;
+import org.opendaylight.infrautils.inject.guice.GuiceClassPathBinder;
 import org.opendaylight.infrautils.inject.guice.testutils.AnnotationsModule;
 import org.opendaylight.infrautils.simple.InfraUtilsWiring;
 import org.opendaylight.mdsal.simple.MdsalWiring;
@@ -19,10 +19,10 @@ import org.ops4j.pax.cdi.api.OsgiService;
 
 public class GeniusWiring extends AbstractModule {
 
-    private final ClassPathScanner scanner;
+    private final GuiceClassPathBinder classPathBinder;
 
-    public GeniusWiring(ClassPathScanner scanner) {
-        this.scanner = scanner;
+    public GeniusWiring(GuiceClassPathBinder classPathBinder) {
+        this.classPathBinder = classPathBinder;
     }
 
     @Override
@@ -50,7 +50,7 @@ public class GeniusWiring extends AbstractModule {
         install(new MdsalUtilWiring());
         install(new LockManagerWiring());
         install(new IdManagerWiring());
-        install(new AlivenessMonitorWiring(scanner));
+        install(new AlivenessMonitorWiring(classPathBinder));
         install(new InterfaceManagerWiring());
         install(new ItmWiring());
         install(new DatastoreUtilsWiring());
index 66e0d7079381b0642c6d2c87bb99db033d4e99cc..28244ec1ea3dfa01d75145021820220708d98321 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.infrautils.inject;
 
-import com.google.inject.Binder;
 import io.github.classgraph.ClassGraph;
 import io.github.classgraph.ClassInfo;
 import io.github.classgraph.ScanResult;
@@ -17,6 +16,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiConsumer;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.slf4j.Logger;
@@ -68,10 +68,10 @@ public class ClassPathScanner {
     /**
      * Binds the given interfaces in the given binder, using implementations discovered by scanning the class path.
      *
-     * @param binder The binder.
+     * @param binder The binder (modeled as a generic consumer).
      * @param interfaces The requested interfaces.
      */
-    public void bind(Binder binder, Class... interfaces) {
+    public <T> void bind(BiConsumer<Class<T>, Class<? extends T>> binder, Class... interfaces) {
         for (Class requestedInterface : interfaces) {
             bindImplementationFor(binder, requestedInterface);
         }
@@ -79,10 +79,10 @@ public class ClassPathScanner {
     }
 
     @SuppressWarnings("unchecked")
-    private void bindImplementationFor(Binder binder, Class requestedInterface) {
+    private <T> void bindImplementationFor(BiConsumer<Class<T>, Class<? extends T>> binder, Class requestedInterface) {
         Class implementation = implementations.get(requestedInterface.getName());
         if (implementation != null) {
-            binder.bind(requestedInterface).to(implementation);
+            binder.accept(requestedInterface, implementation);
             for (Constructor constructor : implementation.getDeclaredConstructors()) {
                 Annotation injectAnnotation = constructor.getAnnotation(Inject.class);
                 if (injectAnnotation != null) {
diff --git a/src/main/java/org/opendaylight/infrautils/inject/guice/GuiceClassPathBinder.java b/src/main/java/org/opendaylight/infrautils/inject/guice/GuiceClassPathBinder.java
new file mode 100644 (file)
index 0000000..cc20e63
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.infrautils.inject.guice;
+
+import com.google.inject.Binder;
+import org.opendaylight.infrautils.inject.ClassPathScanner;
+
+/**
+ * Binds interfaces to implementations in Guice by scanning the classpath.
+ */
+public class GuiceClassPathBinder {
+    private final ClassPathScanner scanner;
+
+    public GuiceClassPathBinder(String prefix) {
+        this.scanner = new ClassPathScanner(prefix);
+    }
+
+    /**
+     * Binds the implementation of the given interface, if any, in the given binder, along with all dependencies.
+     *
+     * @param binder The binder to set up.
+     * @param requestedInterface The requested interface.
+     */
+    public void bind(Binder binder, Class<?> requestedInterface) {
+        scanner.bind((contract, implementation) -> binder.bind(contract).to(implementation), requestedInterface);
+    }
+}
index 65e7a9b123f8a742280cd3eede6cfc6f5b599c11..326dc45668f5f92c00d79cd32e40035188f0eb23 100644 (file)
@@ -7,7 +7,7 @@
  */
 package org.opendaylight.netvirt.simple;
 
-import org.opendaylight.infrautils.inject.ClassPathScanner;
+import org.opendaylight.infrautils.inject.guice.GuiceClassPathBinder;
 import org.opendaylight.infrautils.simple.Main;
 
 public final class NetvirtMain {
@@ -15,7 +15,7 @@ public final class NetvirtMain {
     private NetvirtMain() { }
 
     public static void main(String[] args) {
-        ClassPathScanner scanner = new ClassPathScanner("org.opendaylight");
-        new Main(new NetvirtWiring(scanner)).awaitShutdown();
+        GuiceClassPathBinder classPathBinder = new GuiceClassPathBinder("org.opendaylight");
+        new Main(new NetvirtWiring(classPathBinder)).awaitShutdown();
     }
 }
index 69f0eb93887682ab656436eccf0b441e5594f53e..ec38744bcafc94752b4cb0eb10edb03091c2370a 100644 (file)
@@ -9,19 +9,19 @@ package org.opendaylight.netvirt.simple;
 
 import com.google.inject.AbstractModule;
 import org.opendaylight.genius.simple.GeniusWiring;
-import org.opendaylight.infrautils.inject.ClassPathScanner;
+import org.opendaylight.infrautils.inject.guice.GuiceClassPathBinder;
 
 public class NetvirtWiring extends AbstractModule {
 
-    private final ClassPathScanner scanner;
+    private final GuiceClassPathBinder classPathBinder;
 
-    public NetvirtWiring(ClassPathScanner scanner) {
-        this.scanner = scanner;
+    public NetvirtWiring(GuiceClassPathBinder classPathBinder) {
+        this.classPathBinder = classPathBinder;
     }
 
     @Override
     protected void configure() {
-        install(new GeniusWiring(scanner));
+        install(new GeniusWiring(classPathBinder));
 
         install(new AclServiceWiring());
     }
index 4d80c4b49b4bb85a3b6313e0c3630c3cd40b713c..2b6dbb306941ff9a687cb5b6280757f2afb20a76 100644 (file)
@@ -12,7 +12,7 @@ import org.junit.Rule;
 import org.opendaylight.genius.interfacemanager.interfaces.InterfaceManagerService;
 import org.opendaylight.genius.itm.api.IITMProvider;
 import org.opendaylight.genius.simple.GeniusWiring;
-import org.opendaylight.infrautils.inject.ClassPathScanner;
+import org.opendaylight.infrautils.inject.guice.GuiceClassPathBinder;
 import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule2;
 import org.opendaylight.infrautils.simple.ShellTestWiring;
 import org.opendaylight.infrautils.simple.testutils.AbstractSimpleDistributionTest;
@@ -27,9 +27,9 @@ public class GeniusSimpleDistributionTest extends AbstractSimpleDistributionTest
     // TODO https://github.com/google/guice/wiki/Grapher ...
     // TODO https://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/tools/jmx/Manager.html
 
-    private static final ClassPathScanner SCANNER = new ClassPathScanner("org.opendaylight");
+    private static final GuiceClassPathBinder CLASS_PATH_BINDER = new GuiceClassPathBinder("org.opendaylight");
 
-    public @Rule GuiceRule2 guice = new GuiceRule2(new GeniusWiring(SCANNER), new ShellTestWiring());
+    public @Rule GuiceRule2 guice = new GuiceRule2(new GeniusWiring(CLASS_PATH_BINDER), new ShellTestWiring());
 
     @SuppressWarnings("unused")
     private @Inject InterfaceManagerService interfaceManagerService;
index 3413a362df4e1fb8b009839231853637b4629399..c5966d9daf150401ecb40d6a3d35c0cb98aedd11 100644 (file)
@@ -8,75 +8,24 @@
 package org.opendaylight.infrautils.inject.tests;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.CALLS_REAL_METHODS;
-import static org.mockito.Mockito.mock;
 
-import com.google.inject.Binder;
-import com.google.inject.binder.AnnotatedBindingBuilder;
-import com.google.inject.binder.ScopedBindingBuilder;
 import java.util.HashMap;
 import java.util.Map;
-import javax.annotation.Nullable;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Mockito;
 import org.opendaylight.infrautils.inject.ClassPathScanner;
 
 public class ClassPathScannerTest {
-    private final TestBinder testBinder = mock(TestBinder.class, Mockito.CALLS_REAL_METHODS);
+    private final Map<Class<?>, Class<?>> bindings = new HashMap<>();
 
     @Before
     public void setup() {
-        new ClassPathScanner("org.opendaylight.infrautils.inject.tests").bind(testBinder,
+        new ClassPathScanner("org.opendaylight.infrautils.inject.tests").bind(bindings::put,
             ClassPathScannerTestTopInterface.class);
     }
 
     @Test
     public void verifyImplementationBinding() {
-        assertEquals(ClassPathScannerTestImplementation.class,
-            testBinder.getImplementation(ClassPathScannerTestTopInterface.class));
-    }
-
-    private abstract static class TestBinder implements Binder {
-        private Map<Class<?>, Class<?>> bindings;
-
-        private <T> void storeBinding(Class<T> type, Class<? extends T> implementation) {
-            if (bindings == null) {
-                bindings = new HashMap<>();
-            }
-            bindings.put(type, implementation);
-        }
-
-        <T> Class<? extends T> getImplementation(Class<T> type) {
-            return (Class<? extends T>) bindings.get(type);
-        }
-
-        @Override
-        public <T> AnnotatedBindingBuilder<T> bind(Class<T> type) {
-            TestAnnotatedBindingBuilder builder = mock(TestAnnotatedBindingBuilder.class, CALLS_REAL_METHODS);
-            builder.setType(type);
-            builder.setBinder(this);
-            return builder;
-        }
-
-        private abstract static class TestAnnotatedBindingBuilder<T> implements AnnotatedBindingBuilder<T> {
-            private Class<T> type;
-            private TestBinder binder;
-
-            @Nullable
-            @Override
-            public ScopedBindingBuilder to(Class<? extends T> implementation) {
-                binder.storeBinding(type, implementation);
-                return null;
-            }
-
-            void setType(Class<T> type) {
-                this.type = type;
-            }
-
-            void setBinder(TestBinder binder) {
-                this.binder = binder;
-            }
-        }
+        assertEquals(ClassPathScannerTestImplementation.class, bindings.get(ClassPathScannerTestTopInterface.class));
     }
 }