Преглед изворни кода

new: support for Function<int[], Interface>

Ranides Atterwim пре 10 година
родитељ
комит
dc83443572

+ 161 - 58
assira.junit/src/main/java/net/ranides/assira/junit/InterfaceSuite.java

@@ -6,8 +6,6 @@
  */
 package net.ranides.assira.junit;
 
-import java.io.IOException;
-import java.io.OutputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
@@ -15,16 +13,16 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.function.Function;
 import java.util.function.Supplier;
 import javax.annotation.Resource;
-import junit.framework.Assert;
+import org.junit.Assert;
 import org.junit.Ignore;
 
 /**
@@ -44,11 +42,11 @@ public class InterfaceSuite {
     
     private final List<TesterWrapper> testers = new ArrayList<>();
     
-    private final OutputStream output;
+    private final PrintStream output;
     
     private boolean debug;
 
-    public InterfaceSuite(OutputStream output) {
+    public InterfaceSuite(PrintStream output) {
         this.output = output;
     }
     
@@ -75,54 +73,96 @@ public class InterfaceSuite {
             if(!m.getReturnType().equals(void.class)) {
                 continue;
             }
-            methods.add(new MethodRunner(tester, m));
+            MethodRunner mr = runner(tester, m);
+            if(null != mr) {
+                methods.add(mr);
+            }
         }
 		return this;
 	}
     
     public InterfaceSuite param(String name, Object value) {
-        for(TesterWrapper t : testers) {
+        testers.stream().forEach((t) -> {
             t.param(name, value);
-        }
+        });
         return this;
     }
     
+    public boolean run(Function<int[], ?> generator) {
+        return irun(new TGeneratorF(generator));
+    }
+    
     public boolean run(Supplier<?> generator) {
+        return irun(new TGeneratorS(generator));
+    }
+    
+    private boolean irun(TGenerator generator) {
         Class<?> type = generator.get().getClass();
         Counter counter = new Counter();
         List<Exception> errors = new ArrayList<>();
-        for(MethodRunner m : methods) {
-            if( m.accept(type)) {
-                try {
-                    m.run(counter, generator);
-                } catch(Exception cause) {
-                    errors.add(cause);
-                }
+        methods.stream().filter((m)->m.accept(type)).forEach((m)->{
+            try {
+                m.run(counter, generator);
+            } catch(Exception cause) {
+                errors.add(cause);
             }
-        }
-        for(Exception cause : errors) {
+        });
+        errors.stream().forEach((cause) -> {
             cause.printStackTrace(new PrintStream(output));
-        }
+        });
         if(!errors.isEmpty()) {
             Assert.fail("Failed interface tests: " + errors.size() + "/"+counter.value);
         }
 		return true;
 	}
     
-    private final class MethodRunner implements Comparable<MethodRunner> {
+    private MethodRunner runner(Object that, Method method) {
+        if( method.getParameterTypes()[0].equals(Supplier.class) ) {
+            return new SMethodRunner(that, method);
+        }
+        if( method.getParameterTypes()[0].equals(Function.class) ) {
+            return new SMethodRunner(that, method);
+        }
+        return new CMethodRunner(that, method);
+    }
+    
+    private class CMethodRunner extends MethodRunner {
+
+        public CMethodRunner(Object that, Method method) {
+            super(that, method);
+        }
+
+        @Override
+        protected void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+            method.invoke(that, argument.get());
+        }
+        
+    }
+    
+    private class SMethodRunner extends MethodRunner {
+
+        public SMethodRunner(Object that, Method method) {
+            super(that, method);
+        }
+
+        @Override
+        protected void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+            method.invoke(that, argument);
+        }
         
-        private final String uid;
+    }
+    
+    private abstract class MethodRunner implements Comparable<MethodRunner> {
         
-        private final boolean supp;
+        protected final String uid;
         
-        private final Class<?> type;
+        protected final Class<?> type;
         
-        private final Object that;
+        protected final Object that;
         
-        private final Method method;
+        protected final Method method;
 
         public MethodRunner(Object that, Method method) {
-            this.supp = method.getParameterTypes()[0].equals(Supplier.class);
             this.type = typeof(method);
             this.that = that;
             this.method = method;
@@ -138,15 +178,12 @@ public class InterfaceSuite {
             return type.isAssignableFrom(param);
         }
         
-        public void run(Counter counter, Supplier<?> supplier) throws RuntimeException {
+        protected abstract void invoke(TGenerator argument) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
+        
+        public void run(Counter counter, TGenerator param) throws RuntimeException {
             try {
-                method.invoke(that, supp ? supplier : supplier.get());
-                printf(" - SUITE: %s [%d] %s.%s%n",
-                    type.getName(),
-                    counter.next(),
-                    method.getDeclaringClass().getSimpleName(),
-                    method.getName()
-                );
+                invoke(param);
+                printf(type, counter.next(), method, "");
             } catch (IllegalAccessException | IllegalArgumentException ex) {
                 throw NewAssert.rethrow(ex);
             } catch (InvocationTargetException ex) {
@@ -155,45 +192,48 @@ public class InterfaceSuite {
                     
                     if( !"TEST.SILENT".equals(uoe.getMessage())) {
                         if(debug) {
-                            ex.printStackTrace();
+                            ex.printStackTrace(output);
                         }
                         String comment = "TEST.IGNORE".equals(uoe.getMessage()) ? "IGNORED" : "UNSUPPORTED";
-                        printf(" - SUITE: %s [%d] %s.%s - %s%n",
-                            type.getName(),
-                            counter.next(),
-                            method.getDeclaringClass().getSimpleName(),
-                            method.getName(),
-                            comment
-                        );
+                        printf(type, counter.next(), method, comment);
                     }
                     
                 } else {
-                    printf(" - SUITE: %s [%d] %s.%s - FAILED%n",
-                        type.getName(),
-                        counter.next(),
-                        method.getDeclaringClass().getSimpleName(),
-                        method.getName()
-                    );
+                    printf(type, counter.next(), method, "FAILED");
                     throw NewAssert.rethrow(ex.getTargetException());
                 }
             }
         }
         
-        private void printf(String format, Object... values) {
-            try {
-                output.write(String.format(format, values).getBytes(StandardCharsets.UTF_8));
-            } catch (IOException ex) {
-                throw new Error(ex); // NOPMD
+        private void printf(Class<?> type, int index, Method method, String comment) {
+            if(null == comment || comment.isEmpty()) {
+                comment = "";
+            } else {
+                comment = " - " + comment;
             }
+            output.printf(" - SUITE: %s [%d] %s.%s%s%n",
+                type.getName(),
+                index,
+                method.getDeclaringClass().getSimpleName(),
+                method.getName(),
+                comment
+            );
         }
         
     }
     
     static Class<?> typeof(Method method) {
         Class<?> cparam = method.getParameterTypes()[0];
-        if(!Supplier.class.equals(cparam)) {
-            return cparam;
+        if(Supplier.class.isAssignableFrom(cparam)) {
+            return typeofSupplier(method);
         }
+        if(Function.class.isAssignableFrom(cparam)) {
+            return typeofFunction(method);
+        }
+        return cparam;
+    }
+
+    private static Class<?> typeofSupplier(Method method) throws AssertionError {
         ParameterizedType gparam = (ParameterizedType)method.getGenericParameterTypes()[0];
         Type tparam = gparam.getActualTypeArguments()[0];
         if(tparam instanceof Class<?>) {
@@ -202,13 +242,32 @@ public class InterfaceSuite {
         if(tparam instanceof ParameterizedType) {
             return (Class<?>)(((ParameterizedType)tparam).getRawType());
         }
+        throw new AssertionError("You can't use wildcard generic types in method: " + method, causeForMethod(method));
+    }
+    
+    private static Class<?> typeofFunction(Method method) throws AssertionError {
+        ParameterizedType gparam = (ParameterizedType)method.getGenericParameterTypes()[0];
+        if(!gparam.getActualTypeArguments()[0].equals(int[].class)) {
+            throw new AssertionError("Function must accept int[] in method: " + method, causeForMethod(method));
+        }
+        Type tparam = gparam.getActualTypeArguments()[1];
+        if(tparam instanceof Class<?>) {
+            return (Class<?>)tparam;
+        }
+        if(tparam instanceof ParameterizedType) {
+            return (Class<?>)(((ParameterizedType)tparam).getRawType());
+        }
+        throw new AssertionError("You can't use wildcard generic types in method: " + method, causeForMethod(method));
+    }
+
+    private static Exception causeForMethod(Method method) {
         Exception e = new Exception("Invalid declaration");
         e.setStackTrace(new StackTraceElement[]{
             new StackTraceElement(
-                    method.getDeclaringClass().getName(), 
-                    method.getName(), "?", -1)
+                method.getDeclaringClass().getName(),
+                method.getName(), "?", -1)
         });
-        throw new AssertionError("You can't use wildcard generic types in method: " + method, e);
+        return e;
     }
    
     private static final class Counter {
@@ -253,5 +312,49 @@ public class InterfaceSuite {
         }
         
     }
+    
+    private interface TGenerator extends Function<int[], Object>, Supplier<Object>  {
+        
+    }
+    
+    private static class TGeneratorF implements TGenerator {
+        
+        private final Function<int[], ?> function;
+
+        public TGeneratorF(Function<int[], ?> function) {
+            this.function = function;
+        }
+
+        @Override
+        public Object get() {
+            return function.apply(new int[0]);
+        }
+
+        @Override
+        public Object apply(int[] t) {
+            return function.apply(t);
+        }
+        
+    }
+    
+    private static class TGeneratorS implements TGenerator {
+        
+        private final Supplier<?> function;
+
+        public TGeneratorS(Supplier<?> function) {
+            this.function = function;
+        }
+
+        @Override
+        public Object get() {
+            return function.get();
+        }
+
+        @Override
+        public Object apply(int[] t) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+        
+    }
 	
 }

+ 18 - 2
assira.junit/src/test/java/net/ranides/assira/junit/QTesterSuiteTest.java

@@ -10,8 +10,10 @@ import java.io.ByteArrayOutputStream;
 import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.TreeMap;
@@ -25,7 +27,7 @@ import org.junit.Test;
  *
  * @author Ranides Atterwim <ranides@gmail.com>
  */
-public class QTesterSuiteTest {
+public class InterfaceSuiteTest {
     
     private static final List<String> EXPECTED = Arrays.asList(
         "ABC.findFirst",
@@ -66,6 +68,11 @@ public class QTesterSuiteTest {
         "n=ot c=34"
     );
 
+    private static final List<String> EXPECTED_FUNCTION = Arrays.asList(
+        "ForFunction.function", 
+        "ForFunction.list", 
+        "ForFunction.supplier"
+    );
 
     @Test
     public void testRun() {
@@ -103,7 +110,7 @@ public class QTesterSuiteTest {
     @Test
     public void testSupport() {
         TeeStream out = new TeeStream(System.err);
-        InterfaceSuite suite = new InterfaceSuite(out)
+        InterfaceSuite suite = new InterfaceSuite(new PrintStream(out))
             .append(new Testers.ForSupport());
 
         TesterObjects.OUT.clear();
@@ -131,6 +138,15 @@ public class QTesterSuiteTest {
         });
     }
     
+    @Test
+    public void testFunction() {
+        TesterObjects.OUT.clear();
+        new InterfaceSuite(System.err)
+            .append(new Testers.ForFunction())
+            .run((int[] args) -> new ArrayList<>());
+        Assert.assertEquals(EXPECTED_FUNCTION, TesterObjects.OUT);
+    }
+    
     @Test
     public void testResources() {
         TesterObjects.OUT.clear();

+ 21 - 0
assira.junit/src/test/java/net/ranides/assira/junit/mockup/Testers.java

@@ -6,7 +6,9 @@
  */
 package net.ranides.assira.junit.mockup;
 
+import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 import java.util.function.Supplier;
 import javax.annotation.Resource;
 import net.ranides.assira.junit.InterfaceTest;
@@ -40,6 +42,25 @@ public class Testers {
 
     }
     
+    public static class ForFunction {
+
+        @InterfaceTest
+        public void supplier(Supplier<List<String>> s) {
+            TesterObjects.OUT.add("ForFunction.supplier");
+        }
+        
+        @InterfaceTest
+        public void function(Function<int[], List<String>> f) {
+            TesterObjects.OUT.add("ForFunction.function");
+        }
+        
+        @InterfaceTest
+        public void list(List<String> list) {
+            TesterObjects.OUT.add("ForFunction.list");
+        }
+
+    }
+    
     public static class ForSupport {
 
         @InterfaceTest