Kaynağa Gözat

QAssert#assertEquals - 3 argument version
QTesterSuite - refactoring
* remove @Tester annotation
* use of non-static methods in "testers"
* @Test annotation is required

Ranides Atterwim 10 yıl önce
ebeveyn
işleme
0b21cd4be8

+ 36 - 1
assira.junit/src/main/java/net/ranides/assira/junit/QAssert.java

@@ -26,7 +26,7 @@ public final class QAssert {
         try {
             action.run();
             fail(error + " expected");
-        } catch(Throwable cause) {
+        } catch(Throwable cause) { // NOPMD
             if( !error.equals(cause.getClass())) {
                 throw rethrow(cause);
             }
@@ -90,6 +90,41 @@ public final class QAssert {
         assertFalse("d != object", d.equals(new Object()));
     }
     
+    /**
+     * Requirements (object : hash / pseudo-value)
+     * <pre>
+     *  A : 1 / 1
+     *  B : 1 / 1
+     *  D : 2 / 2
+     * </pre>
+     * @param a1
+     * @param a2
+     * @param b
+     * @param c 
+     */
+    @SuppressWarnings("PMD.ShortVar")
+    public static void assertEquality(Object a, Object b, Object d) {
+        int ha = a.hashCode();
+        int hb = b.hashCode();
+        int hd = d.hashCode();
+        
+        assertTrue("hashcode: a==b", ha == hb);
+        assertTrue("hashcode: a!=d", ha != hd);
+        
+        assertTrue("a==a", a.equals(a));
+        assertTrue("b==b", b.equals(b));
+        assertTrue("d==d", d.equals(d));
+        
+        assertSymEquals("a == b", a,b);
+        
+        assertSymNotEquals("a != d", a,d);
+        assertSymNotEquals("b != d", b,d);
+        
+        assertFalse("a != object", a.equals(new Object()));
+        assertFalse("b != object", b.equals(new Object()));
+        assertFalse("d != object", d.equals(new Object()));
+    }
+    
     private static void assertSymEquals(String message, Object value1, Object value2) {
         assertTrue(message, value1.equals(value2) );
         assertTrue(message, value2.equals(value1) );

+ 0 - 24
assira.junit/src/main/java/net/ranides/assira/junit/QTester.java

@@ -1,24 +0,0 @@
-/*
- * @author Ranides Atterwim <ranides@gmail.com>
- * @copyright Ranides Atterwim
- * @license WTFPL
- * @url http://ranides.net/projects/assira.junit
- */
-package net.ranides.assira.junit;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Marks class as usable by {@link QReflectiveSuite}. Marked class
- * should have public static methods accepting instances of class specified in annotation.
- * 
- * @see QReflectiveSuite
- * @author Ranides Atterwim <ranides@gmail.com>
- */
-@Retention(RetentionPolicy.RUNTIME)
-public @interface QTester {
-	
-	Class<?> value();
-	
-}

+ 71 - 84
assira.junit/src/main/java/net/ranides/assira/junit/QTesterSuite.java

@@ -11,17 +11,14 @@ import java.io.OutputStream;
 import java.lang.reflect.InvocationTargetException;
 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.Arrays;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.function.Supplier;
-import org.junit.Assert;
 import org.junit.Ignore;
+import org.junit.Test;
 
 /**
  * Inspects inheritance tree of objects passed to {@link #run} method and invokes
@@ -33,7 +30,7 @@ import org.junit.Ignore;
  */
 public class QTesterSuite {
 	
-	private final Map<Class<?>, List<TesterRunner>> testers = new IdentityHashMap<>();
+	private final SortedSet<MethodRunner> methods = new TreeSet<>();
     
     private final OutputStream output;
 
@@ -41,82 +38,69 @@ public class QTesterSuite {
         this.output = output;
     }
 	
-	public QTesterSuite append(Class<?> tester) {
-        Class<?> target = tester.getAnnotation(QTester.class).value();
-        if(!testers.containsKey(target)) {
-            testers.put(target, new ArrayList<>());
+	public QTesterSuite append(Object tester) {
+        for(Method m : tester.getClass().getMethods()) {
+            if(Modifier.isStatic(m.getModifiers()) ) {
+                continue;
+            }
+            if(1 != m.getParameterCount()) {
+                continue;
+            }
+            if(null != m.getAnnotation(Ignore.class)) {
+                continue;
+            }
+            if(null == m.getAnnotation(Test.class)) {
+                continue;
+            }
+            if(!m.getReturnType().equals(void.class)) {
+                continue;
+            }
+            methods.add(new MethodRunner(tester, m));
         }
-		testers.get(target).add(new TesterRunner(tester, target));
 		return this;
 	}
     
     public boolean run(Supplier<?> generator) {
         Class<?> type = generator.get().getClass();
         Counter counter = new Counter();
-        match(type).stream().forEach((runner) -> {
-            runner.run(counter, type, generator);
-        });
+        for(MethodRunner m : methods) {
+            if( m.accept(type)) {
+                m.run(counter, generator);
+            }
+        }
 		return true;
 	}
     
-    private Set<TesterRunner> match(Class<?> type) {
-        Set<TesterRunner> result = new TreeSet<>();
-		for(Class<?> clazz = type; clazz!=Object.class; clazz=clazz.getSuperclass()) {
-            matchInsert(result, clazz);
-            matchInsert(result, clazz.getInterfaces());
-		}
-        return result;
-    }
-    
-    private void matchInsert(Set<TesterRunner> collection, Class[] list) {
-        for(Class<?> clazz : list) {
-            matchInsert(collection, clazz);
-        }
-    }
-    
-    private void matchInsert(Set<TesterRunner> collection, Class<?> clazz) {
-        if( testers.containsKey(clazz)) {
-            collection.addAll(testers.get(clazz));
-        }
-    }
-    
-    private final class TesterRunner implements Comparable<TesterRunner> {
+    private final class MethodRunner implements Comparable<MethodRunner> {
         
-        private final Class<?> tester;
+        private final String uid;
         
-        private final List<Method> methods = new ArrayList<>();
-
-        public TesterRunner(Class<?> tester, Class<?> target) {
-            this.tester = tester;
+        private final boolean supp;
+        
+        private final Class<?> type;
+        
+        private final Object that;
+        
+        private final Method method;
 
-            Method[] array = tester.getMethods();
-            Arrays.sort(array, 
-                (method1,  method2) -> method1.getName().compareTo(method2.getName())
-            );
-            for(Method method : array) {
-                if(method.getDeclaringClass().equals(Object.class)) {
-                    continue;
-                }
-                if(!accept(method, target)) {
-                    Assert.assertNotNull("public method should have compatible signature: "+method,  method.getAnnotation(Ignore.class));
-                    continue;
-                }
-                methods.add(method);
-            }
+        public MethodRunner(Object that, Method method) {
+            this.supp = method.getParameterTypes()[0].equals(Supplier.class);
+            this.type = param(method);
+            this.that = that;
+            this.method = method;
+            this.uid = type.getName()+"#"+method.getDeclaringClass().getName() +"#" + method.getName();
         }
 
         @Override
-        public int compareTo(TesterRunner other) {
-            return tester.getSimpleName().compareTo(other.tester.getSimpleName());
+        public int compareTo(MethodRunner object) {
+            return uid.compareTo(object.uid);
         }
         
-        public void run(Counter counter, Class<?> type, Supplier<?> generator) {
-            for(Method method : methods) {
-                run(counter, method, type, param(method, generator));
-            }
+        public boolean accept(Class<?> param) {
+            return type.isAssignableFrom(param);
         }
         
-        private void run(Counter counter, Method method, Class<?> type, Object object) throws RuntimeException {
+        public void run(Counter counter, Supplier<?> supplier) throws RuntimeException {
             try {
                 printf(" - SUITE: %s [%d] %s.%s%n",
                     type.getName(),
@@ -124,7 +108,7 @@ public class QTesterSuite {
                     method.getDeclaringClass().getSimpleName(),
                     method.getName()
                 );
-                method.invoke(null, object);
+                method.invoke(that, supp ? supplier : supplier.get());
             } catch (IllegalAccessException | IllegalArgumentException ex) {
                 throw QAssert.rethrow(ex);
             } catch (InvocationTargetException ex) {
@@ -141,25 +125,6 @@ public class QTesterSuite {
             }
         }
         
-        private boolean accept(Method method, Class<?> target) {
-            return 
-                Modifier.isStatic(method.getModifiers()) &&
-                1 == method.getParameterCount() &&
-                accept(method.getParameterTypes()[0], target);
-        }
-        
-        private boolean accept(Class<?> param, Class<?> target) {
-            return param.equals(target) || param.equals(Supplier.class);
-        }
-        
-        private Object param(Method method, Supplier<?> generator) {
-            if(method.getParameterTypes()[0].equals(Supplier.class)) {
-                return generator;
-            } else {
-                return generator.get();
-            }
-        }
-        
         private void printf(String format, Object... values) {
             try {
                 output.write(String.format(format, values).getBytes(StandardCharsets.UTF_8));
@@ -170,6 +135,28 @@ public class QTesterSuite {
         
     }
     
+    static Class<?> param(Method method) {
+        Class<?> cparam = method.getParameterTypes()[0];
+        if(!Supplier.class.equals(cparam)) {
+            return cparam;
+        }
+        ParameterizedType gparam = (ParameterizedType)method.getGenericParameterTypes()[0];
+        Type tparam = gparam.getActualTypeArguments()[0];
+        if(tparam instanceof Class<?>) {
+            return (Class<?>)tparam;
+        }
+        if(tparam instanceof ParameterizedType) {
+            return (Class<?>)(((ParameterizedType)tparam).getRawType());
+        }
+        Exception e = new Exception("Invalid declaration");
+        e.setStackTrace(new StackTraceElement[]{
+            new StackTraceElement(
+                    method.getDeclaringClass().getName(), 
+                    method.getName(), "?", -1)
+        });
+        throw new AssertionError("You can't use wildcard generic types in method: " + method, e);
+    }
+   
     private static final class Counter {
         private int value = 0;
         

+ 31 - 13
assira.junit/src/test/java/net/ranides/assira/junit/QTesterSuiteTest.java

@@ -14,6 +14,7 @@ import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
+import java.util.TreeMap;
 import net.ranides.assira.junit.mockup.TesterObjects;
 import net.ranides.assira.junit.mockup.TesterObjects.*;
 import net.ranides.assira.junit.mockup.Testers;
@@ -43,9 +44,9 @@ public class QTesterSuiteTest {
         "A.findFirst",
         "A.findAll",
         "ForA2.exec",
-        "ForAbstractAB.exec",
         "Abstract.close",
-        "Abstract.open"
+        "Abstract.open",
+        "ForAbstractAB.exec"
     );
     
     private static final List<String> EXPECTED_A = Arrays.asList(
@@ -54,20 +55,20 @@ public class QTesterSuiteTest {
     );
     
     private static final String EXPECTED_OUT = String.format(
-        " - SUITE: net.ranides.assira.junit.mockup.TesterObjects$ObjectA [1] ForSupport.run%n"+
-        " - SUITE: net.ranides.assira.junit.mockup.TesterObjects$ObjectA [2] ForSupport.unsupported%n"+
-        " - SUITE: net.ranides.assira.junit.mockup.TesterObjects$ObjectA [2] ForSupport.unsupported - UNSUPPORTED%n"
+        " - SUITE: net.ranides.assira.junit.mockup.TesterInterfaces$IntA [1] ForSupport.run%n"+
+        " - SUITE: net.ranides.assira.junit.mockup.TesterInterfaces$IntA [2] ForSupport.unsupported%n"+
+        " - SUITE: net.ranides.assira.junit.mockup.TesterInterfaces$IntA [2] ForSupport.unsupported - UNSUPPORTED%n"
     );
 
 
     @Test
     public void testRun() {
         QTesterSuite suite = new QTesterSuite(System.err)
-            .append(Testers.ForA1.class)
-            .append(Testers.ForB.class)
-            .append(Testers.ForC.class)
-            .append(Testers.ForAbstractAB.class)
-            .append(Testers.ForA2.class);
+            .append(new Testers.ForA1())
+            .append(new Testers.ForB())
+            .append(new Testers.ForC())
+            .append(new Testers.ForAbstractAB())
+            .append(new Testers.ForA2());
 
         TesterObjects.OUT.clear();
         suite.run(() -> new ObjectABC());
@@ -79,14 +80,14 @@ public class QTesterSuiteTest {
     @Test
     public void testInvalid() {
         QAssert.assertThrows(AssertionError.class, () ->
-            new QTesterSuite(System.err).append(Testers.Invalid.class)
+            new QTesterSuite(System.err).append(new Testers.Invalid())
         );
     }
     
     @Test
     public void testCorrect() {
         QTesterSuite suite = new QTesterSuite(System.err)
-            .append(Testers.Correct.class);
+            .append(new Testers.Correct());
 
         TesterObjects.OUT.clear();
         suite.run(() -> new ObjectA());
@@ -97,7 +98,7 @@ public class QTesterSuiteTest {
     public void testSupport() {
         TeeStream out = new TeeStream(System.err);
         QTesterSuite suite = new QTesterSuite(out)
-            .append(Testers.ForSupport.class);
+            .append(new Testers.ForSupport());
 
         TesterObjects.OUT.clear();
         suite.run(() -> new ObjectA());
@@ -105,6 +106,23 @@ public class QTesterSuiteTest {
         Assert.assertEquals(EXPECTED_A, TesterObjects.OUT);
     }
     
+    @Test
+    public void testMapSupplier() {
+        new QTesterSuite(System.err)
+            .append(new Testers.ForMap())
+            .run(() -> new TreeMap<>());
+        Assert.assertEquals(Arrays.asList("map"), TesterObjects.OUT);
+    }
+    
+    @Test
+    public void testWildcardSupplier() {
+        QAssert.assertThrows(AssertionError.class, ()->{
+            new QTesterSuite(System.err)
+                .append(new Testers.ForWildcard())
+                .run(() -> new TreeMap<>());
+        });
+    }
+    
     private static class TeeStream extends FilterOutputStream {
 
         ByteArrayOutputStream bytes = new ByteArrayOutputStream();

+ 48 - 23
assira.junit/src/test/java/net/ranides/assira/junit/mockup/Testers.java

@@ -6,11 +6,12 @@
  */
 package net.ranides.assira.junit.mockup;
 
+import java.util.Map;
 import java.util.function.Supplier;
 import net.ranides.assira.junit.mockup.TesterObjects.*;
 import net.ranides.assira.junit.mockup.TesterInterfaces.*;
-import net.ranides.assira.junit.QTester;
 import org.junit.Ignore;
+import org.junit.Test;
 
 /**
  *
@@ -19,107 +20,131 @@ import org.junit.Ignore;
 @SuppressWarnings("PMD")
 public class Testers {
     
-    @QTester(IntA.class)
     public static class ForA1 {
 
-        public static void run(IntA object) {
+        @Test
+        public void run(IntA object) {
             object.findFirst();
             object.findAll();
         }
 
     }
         
-    @QTester(IntA.class)
     public static class ForA2 {
 
-        public static void exec(IntA object) {
+        @Test
+        public void exec(IntA object) {
             TesterObjects.OUT.add("ForA2.exec");
         }
 
     }
     
-    @QTester(IntA.class)
     public static class ForSupport {
 
-        public static void run(IntA object) {
+        @Test
+        public void run(IntA object) {
             object.findFirst();
             object.findAll();
         }
         
-        public static void unsupported(IntA object) {
+        @Test
+        public void unsupported(IntA object) {
             throw new UnsupportedOperationException("NSE");
         }
 
     }
     
-    @QTester(IntA.class)
     public static class Invalid {
 
-        public static void run(IntA object) {
+        @Test
+        public void run(IntA object) {
             object.findFirst();
             object.findAll();
         }
         
-        public static void other(IntA object, int params) {
+        @Test
+        public void other(IntA object, int params) {
             object.findFirst();
             object.findAll();
         }
 
     }
     
-    @QTester(IntA.class)
     public static class Correct {
 
-        public static void run(IntA object) {
+        @Test
+        public void run(IntA object) {
             object.findFirst();
             object.findAll();
         }
         
         @Ignore
-        public static void other(IntA object, int params) {
+        @Test
+        public void other(IntA object, int params) {
             object.findFirst();
             object.findAll();
         }
 
     }
 
-    @QTester(IntB.class)
     public static class ForB {
 
-        public static void runO(IntB object) {
+        @Test
+        public void runO(IntB object) {
             object.open();
         }
 
-        public static void runC(IntB object) {
+        @Test
+        public void runC(IntB object) {
             object.close();
         }
 
     }
 
-    @QTester(IntC.class)
     public static class ForC {
 
-        public static void runR(IntC object) {
+        @Test
+        public void runR(IntC object) {
             object.read();
         }
 
-        public static void runW(IntC object) {
+        @Test
+        public void runW(IntC object) {
             object.write();
         }
         
-        public static void runZ(Supplier<IntC> function) {
+        @Test
+        public void runZ(Supplier<IntC> function) {
             function.get().next();
         }
 
     }
     
-    @QTester(AbstractAB.class)
     public static class ForAbstractAB {
         
-        public static void exec(AbstractAB that) {
+        @Test
+        public void exec(AbstractAB that) {
             TesterObjects.OUT.add("ForAbstractAB.exec");
         }
                 
     }
     
+    public static class ForMap {
+        
+        @Test
+        public void exec(Supplier<Map<Integer, String>> supplier) {
+            TesterObjects.OUT.add("map");
+        }
+ 
+    }
+    
+    public static class ForWildcard {
+        
+        @Test
+        public void wildcard(Supplier<? extends Map<Integer, String>> supplier) {
+            TesterObjects.OUT.add("map");
+        }
+        
+    }
+    
 }