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

lambda type deduction
TestContractRunner: small refactoring

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

+ 4 - 0
assira.junit/pom.xml

@@ -31,6 +31,10 @@
             <groupId>net.ranides</groupId>
             <artifactId>caliper</artifactId>
         </dependency>
+		<dependency>
+			<groupId>net.ranides</groupId>
+			<artifactId>assira.asm</artifactId>
+		</dependency>
         <dependency>
             <groupId>com.google.code.findbugs</groupId>
             <artifactId>annotations</artifactId>

+ 3 - 18
assira.junit/src/main/java/net/ranides/assira/junit/NewAssert.java

@@ -33,7 +33,7 @@ public final class NewAssert extends org.junit.Assert {
             fail(error + " expected");
         } catch(Throwable cause) { // NOPMD
             if( !error.isInstance(cause)) {
-                throw rethrow(cause);
+                throw TestUtils.rethrow(cause);
             }
         }
     }
@@ -48,7 +48,7 @@ public final class NewAssert extends org.junit.Assert {
                 assertEquals(value, istream.readObject());
             }
         } catch(IOException | ClassNotFoundException cause) {
-            throw rethrow(cause);
+            throw TestUtils.rethrow(cause);
         }
     }
     
@@ -179,7 +179,7 @@ public final class NewAssert extends org.junit.Assert {
             try {
                 mValueOf.invoke(null, "__NONAME_IN_ENUM__");
             } catch(InvocationTargetException cause) {
-                throw rethrow(cause.getCause());
+                throw TestUtils.rethrow(cause.getCause());
             }
         });
         
@@ -192,20 +192,5 @@ public final class NewAssert extends org.junit.Assert {
         void run() throws Exception;
         
     }
-    
-    static RuntimeException rethrow(Throwable cause) {
-        new ExceptionErasure<RuntimeException>().rethrow(cause);
-        
-        // not reachable, wyjątek bez wrappowania leci linię wyżej
-        throw new RuntimeException(cause); // NOPMD
-    }
-    
-    private static class ExceptionErasure<T extends Throwable> { // NOPMD
-        
-        @SuppressWarnings("unchecked")
-        private void rethrow(Throwable exception) throws T {
-            throw (T) exception;
-        }
 
-    }
 }

+ 82 - 177
assira.junit/src/main/java/net/ranides/assira/junit/TestContractRunner.java

@@ -7,12 +7,9 @@
 package net.ranides.assira.junit;
 
 import java.io.PrintStream;
-import java.lang.reflect.Field;
 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.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -20,10 +17,13 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
-import javax.annotation.Resource;
+import net.ranides.assira.junit.TestUtils.ICounter;
+import net.ranides.assira.junit.TestUtils.IField;
+import net.ranides.assira.junit.TestUtils.IPatterns;
 import org.junit.Assert;
 import org.junit.Ignore;
 
@@ -37,13 +37,27 @@ import org.junit.Ignore;
  */
 public final class TestContractRunner {
 	
+	// @todo (assira.junit #3) obsługa wielu funkcji i supplierów
+	// możemy pobierać typy argumentów i returnów funkcji za pomocą TestUtils#typeof(Function)
+	// jeśli tę funkcję rozszerzyć, żeby zwracała Tuple<Param,Result> to mamy wszystko
+	// potem to można trzymać w jakiejś mapie
+	// a potem można matchować przy uruchamianiu metod
+	//
+	// w sumie przydatne, jeśli nasza klasa chce ruszyć na kilku testerach, które
+	// pobierają różne inputy:
+	//		Function<int[], MyInterface>
+	//		Function<char[], MyInterface>
+	//		Function<byte[], MyInterface>
+	//		Function<String, MyInterface>
+	//		Supplier<MyInterface>
+	
 	private static final boolean DEBUG = "true".equals(System.getProperty("assira.junit.debug"));
 	
 	private final SortedSet<MethodRunner> methods = new TreeSet<>();
     
     private final List<TesterWrapper> testers = new ArrayList<>();
     
-    private final List<Pattern> ignored = new ArrayList<>();
+    private final IPatterns ignored = new IPatterns();
         
     private final PrintStream output;
 	
@@ -62,15 +76,11 @@ public final class TestContractRunner {
     
     public TestContractRunner(TestContractRunner runner) {
         this(runner.output);
-        try {
-            for(TesterWrapper w : runner.testers) {
-                this.appendCopy(w);
-            }
-            this.ignored.addAll(runner.ignored);
-            this.debug = runner.debug;
-        } catch(ReflectiveOperationException cause) {
-            throw NewAssert.rethrow(cause);
-        }
+		for(TesterWrapper w : runner.testers) {
+			appendWrapper(TestUtils.copy(w.object)).setParams(w);
+		}
+		this.ignored.add(runner.ignored);
+		this.debug = runner.debug;
     }
     
 	public TestContractRunner debug(boolean value) {
@@ -83,7 +93,7 @@ public final class TestContractRunner {
 	}
 	
     public TestContractRunner ignore(String method) {
-        this.ignored.add(Pattern.compile(Pattern.quote(method)));
+        this.ignored.add(method);
         return this;
     }
 	
@@ -96,16 +106,6 @@ public final class TestContractRunner {
         appendWrapper(tester);
         return this;
     }
-    
-    private void appendCopy(TesterWrapper source) throws ReflectiveOperationException {
-        Object tester = source.object.getClass().newInstance();
-        TesterWrapper target = appendWrapper(tester);
-        for(Entry<String, IParam> entry : source.resources.entrySet()) {
-            String key = entry.getKey();
-            target.resources.get(key).set(entry.getValue().get());
-        }
-    }
-    
 	
 	public TesterWrapper appendWrapper(Object tester) {
         TesterWrapper wrapper = new TesterWrapper(tester);
@@ -126,18 +126,13 @@ public final class TestContractRunner {
             if(!m.getReturnType().equals(void.class)) {
                 continue;
             }
-            MethodRunner mr = runner(tester, m);
-            if(null != mr) {
-                methods.add(mr);
-            }
+			methods.add(runner(tester, m));
         }
 		return wrapper;
 	}
     
     public TestContractRunner param(String name, Object value) {
-        testers.stream().forEach((t) -> {
-            t.param(name, value);
-        });
+        testers.stream().forEach(t -> t.param(name, value));
         return this;
     }
 	
@@ -148,8 +143,13 @@ public final class TestContractRunner {
 	}
 	
 	@SuppressWarnings("unchecked")
-	public <P> TestContractRunner function(P ident, Function<P, ?> function) {
+	public <T,R> TestContractRunner function(Function<T, R> function) {
 		this.function = (Function)function;
+		return this;
+	}
+	
+	public <T,R> TestContractRunner function(T ident, Function<T, R> function) {
+		function(function);
 		this.supplier = () -> this.function.apply(ident);
 		return this;
 	}
@@ -158,19 +158,22 @@ public final class TestContractRunner {
         for(TesterWrapper tester : testers) {
             tester.bind(this);
         }
-		if(null == supplier) {
-			throw new AssertionError("Supplier is not set.");
+		if(null == supplier && null == function) {
+			throw new AssertionError("You have to set Supplier or Function.");
 		}
-        Class<?> type = supplier.get().getClass();
-        Counter counter = new Counter();
+		Class<?> type;
+		if(null != supplier) {
+			type = supplier.get().getClass();
+		} else {
+			type = TestUtils.typeof(function);
+		}
+        ICounter counter = new ICounter();
         List<String> failed = new ArrayList<>();
-        methods.stream().filter((m)->m.accept(type)).forEach((m)->{
-            try {
-                m.run(counter);
-            } catch(Throwable cause) {
-                failed.add(m.label);
-                cause.printStackTrace(output);
-            }
+        methods.stream().filter(m -> m.accept(type)).forEach(m -> {
+			m.run(counter, cause -> {
+				failed.add(m.label);
+				cause.printStackTrace(output);
+			});
         });
         ireset();
         if(!failed.isEmpty()) {
@@ -180,7 +183,7 @@ public final class TestContractRunner {
                 output.print(f);
             }
             output.println();
-            Assert.fail("Failed interface tests: " + failed.size() + "/"+counter.value);
+            Assert.fail("Failed interface tests: " + failed.size() + "/"+counter.value());
         }
 		return true;
 	}
@@ -196,10 +199,10 @@ public final class TestContractRunner {
     }
     
     private MethodRunner runner(Object that, Method method) {
-        if( method.getParameterTypes()[0].equals(Supplier.class) ) {
+        if( TestUtils.acceptsSupplier(method) ) {
             return new SMethodRunner(that, method);
         }
-        if( method.getParameterTypes()[0].equals(Function.class) ) {
+        if( TestUtils.acceptsFunction(method) ) {
             return new SMethodRunner(that, method);
         }
         return new CMethodRunner(that, method);
@@ -213,6 +216,9 @@ public final class TestContractRunner {
 
         @Override
         protected void invoke() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+			if(null == supplier) {
+				throw new AssertionError("Supplier is required by method: " + nid, TestUtils.cause4method(method, "Supplier required."));
+			}
             method.invoke(that, supplier.get());
         }
         
@@ -246,11 +252,11 @@ public final class TestContractRunner {
         protected String label;
 
         public MethodRunner(Object that, Method method) {
-            this.type = typeof(method);
+            this.type = TestUtils.typeof(method);
             this.that = that;
             this.method = method;
-            this.uid = type.getName()+"#"+method.getDeclaringClass().getName() +"#" + method.getName();
-            this.nid = method.getDeclaringClass().getSimpleName() + "." + method.getName();
+            this.uid = TestUtils.uid(method);
+            this.nid = TestUtils.nid(method);
         }
         
         @Override
@@ -264,14 +270,12 @@ public final class TestContractRunner {
         
         protected abstract void invoke() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
         
-        public void run(Counter counter) throws RuntimeException {
-			for(Pattern p : ignored) {
-				if(p.matcher(nid).matches()) {
-					if(debug) {
-						printf(type, counter.next(), method, "IGNORED");
-					}
-					return;
+        public void run(ICounter counter, Consumer<Throwable> handler) {
+			if(ignored.matches(nid)) {
+				if(debug) {
+					printf(type, counter.next(), method, "IGNORED");
 				}
+				return;
 			}
             int index = counter.next();
             try {
@@ -280,10 +284,10 @@ public final class TestContractRunner {
 					printf(type, index, method, "");
 				}
             } catch (IllegalAccessException | IllegalArgumentException ex) {
-                throw NewAssert.rethrow(ex);
+                handler.accept(ex);
             } catch (InvocationTargetException ex) {
 				printf(type, index, method, "FAILED");
-				throw NewAssert.rethrow(ex.getTargetException());
+				handler.accept(ex.getTargetException());
             }
         }
         
@@ -305,93 +309,27 @@ public final class TestContractRunner {
         
     }
     
-    static Class<?> typeof(Method method) {
-        Class<?> cparam = method.getParameterTypes()[0];
-        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<?>) {
-            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 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)
-        });
-        return e;
-    }
-   
-    private static final class Counter {
-        private int value = 0;
-        
-        public int next() {
-            return ++value;
-        }
-    }
-    
     private static final class TesterWrapper {
         
         private final Object object;
         
-        private final Map<String, IParam> resources = new HashMap<>();
+        private final Map<String, IField> resources = new HashMap<>();
         
-        private final List<IParam> bindings = new ArrayList<>();
+        private final List<IField> bindings = new ArrayList<>();
         
         public TesterWrapper(Object object) {
             this.object = object;
-            for(Class<?> c=object.getClass(); c!=null; c=c.getSuperclass()) {
-                for(Field f : c.getDeclaredFields()) {
-                    Resource info = f.getAnnotation(Resource.class);
-                    if(null != info) {
-                        String name = info.name();
-                        if("".equals(name)) {
-                            name = f.getName();
-                        }
-                        f.setAccessible(true);
-                        if( f.getType().equals(TestContractRunner.class) ) {
-                            bindings.add(new IParam(object, f));
-                        } else {
-                            resources.put(name, new IParam(object, f));
-                        }
-                    }
-                }
-            }
+			for(IField f : TestUtils.getFields(object)) {
+				if( TestContractRunner.class.equals(f.type()) ) {
+					bindings.add(f);
+				} else {
+					resources.put(f.name(), f);
+				}
+			}
         }
                 
         public void bind(TestContractRunner runner) {
-            for(IParam p : bindings) {
+            for(IField p : bindings) {
                 p.set(runner);
             }
         }
@@ -401,9 +339,17 @@ public final class TestContractRunner {
                 resources.get(name).set(value);
             }
         }
+		
+		public void setParams(TesterWrapper source) {
+			for(Entry<String, IField> entry : source.resources.entrySet()) {
+				String key = entry.getKey();
+				Object val = entry.getValue().get();
+				resources.get(key).set(val);
+			}
+		}
         
         public void resetParams() {
-            for(IParam param : resources.values()) {
+            for(IField param : resources.values()) {
                 param.reset();
             }
         }
@@ -429,46 +375,5 @@ public final class TestContractRunner {
 		}
 		
 	}
-    
-    private static final class IParam {
-        private final Object object;
-        private final Field field;
-        private final Object value;
-
-        public IParam(Object object, Field field) {
-            try {
-                this.object = object;
-                this.field = field;
-                this.value = field.get(object);
-            } catch(ReflectiveOperationException cause) {
-                throw NewAssert.rethrow(cause);
-            }
-        }
-
-        public void set(Object value) {
-            try {
-                field.set(object, value);
-            } catch(ReflectiveOperationException cause) {
-                throw NewAssert.rethrow(cause);
-            }
-        }
-        
-        public Object get() {
-            try {
-                return field.get(object);
-            } catch(ReflectiveOperationException cause) {
-                throw NewAssert.rethrow(cause);
-            }
-        }
-        
-        public void reset() {
-            try {
-                field.set(object, value);
-            } catch(ReflectiveOperationException cause) {
-                throw NewAssert.rethrow(cause);
-            }
-        }
-        
-    }
 	
 }

+ 331 - 0
assira.junit/src/main/java/net/ranides/assira/junit/TestUtils.java

@@ -0,0 +1,331 @@
+/*
+ * @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.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.regex.Pattern;
+import javax.annotation.Resource;
+import sun.reflect.ConstantPool;
+
+/**
+ *
+ * @author Ranides Atterwim <ranides@gmail.com>
+ */
+@SuppressWarnings("PMD.MethodNameRules")
+final class TestUtils {
+
+	private TestUtils() { 
+		/* utility class */
+	}
+
+	public static Object copy(Object object) {
+		try {
+			return object.getClass().newInstance();
+		} catch (ReflectiveOperationException cause) {
+			return rethrow(cause);
+		}
+	}
+	
+	public static Class<?> typeof(Method method) {
+        Class<?> cparam = method.getParameterTypes()[0];
+        if(Supplier.class.isAssignableFrom(cparam)) {
+            return typeof_supplier(method);
+        }
+        if(Function.class.isAssignableFrom(cparam)) {
+            return typeof_function(method);
+        }
+        return cparam;
+    }
+	
+	public static Class<?> typeof(Function<?,?> function) {
+//		if(!function.getClass().isSynthetic()) {
+//			return typeof_generic(function);
+//		} else {
+			return typeof_lambda(function);
+//		}
+	}
+	
+	private static Class<?> typeof_lambda(Function<?,?> function) {
+		ConstantPool cp = Li.get(function.getClass());
+		
+		for(int i=0; i<cp.getSize(); i++) {
+			String[] info;
+			try {
+				info = cp.getMemberRefInfoAt(i);
+			} catch(Exception ex) {
+				continue;
+			}
+			
+			String parent = info[0];
+			if("java/lang/Object".equals(parent) || "java/lang/Class".equals(parent)) {
+				continue;
+			}
+			try {
+				if(1 != net.ranides.asm.Type.getArgumentTypes(info[2]).length) {
+					continue;
+				}
+			} catch(Exception ex) {
+				continue;
+			}
+			
+			return vm2class(function.getClass().getClassLoader(), net.ranides.asm.Type.getReturnType(info[2]).getDescriptor());
+		}
+		throw new AssertionError("Deduction of return type failed. Unsupported lambda: " + function);
+	}
+	
+	private static Class<?> typeof_supplier(Method method) throws AssertionError {
+        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());
+        }
+        throw new AssertionError("You can't use wildcard generic types in method: " + method, cause4method(method, "Invalid declaration"));
+    }
+    
+    private static Class<?> typeof_function(Method method) throws AssertionError {
+        ParameterizedType gparam = (ParameterizedType)method.getGenericParameterTypes()[0];
+
+        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, cause4method(method, "Invalid declaration"));
+    }
+	
+	@SuppressWarnings("PMD")
+    private static Class<?> vm2class(ClassLoader loader, String vmtype) {
+		try {
+			if(vmtype.startsWith("[")) {
+				return Array.newInstance(vm2class(loader, vmtype.substring(1)), 0).getClass();
+			}
+			if(vmtype.startsWith("L") && vmtype.endsWith(";")) {
+				return Class.forName(vmtype.substring(1,vmtype.length()-1).replace('/', '.'), false, loader);
+			}
+			if("Z".equals(vmtype)) {
+				return boolean.class;
+			}
+			if("B".equals(vmtype)) {
+				return byte.class;
+			}
+			if("C".equals(vmtype)) {
+				return char.class;
+			}
+			if("S".equals(vmtype)) {
+				return short.class;
+			}
+			if("I".equals(vmtype)) {
+				return int.class;
+			}
+			if("J".equals(vmtype)) {
+				return long.class;
+			}
+			if("F".equals(vmtype)) {
+				return float.class;
+			}
+			if("D".equals(vmtype)) {
+				return double.class;
+			}
+			if("V".equals(vmtype)) {
+				return void.class;
+			}
+		} catch(ClassNotFoundException cause) {
+			throw rethrow(cause);
+		}
+		throw new AssertionError("Unknown type: " + vmtype);
+    }
+	
+	public static Exception cause4method(Method method, String message) {
+        Exception e = new Exception(message); // NOPMD
+        e.setStackTrace(new StackTraceElement[]{
+            new StackTraceElement(
+                method.getDeclaringClass().getName(),
+                method.getName(), "?", -1)
+        });
+        return e;
+    }
+	
+	public static boolean acceptsSupplier(Method method) {
+		return method.getParameterTypes()[0].equals(Supplier.class);
+	}
+	
+	public static boolean acceptsFunction(Method method) {
+		return method.getParameterTypes()[0].equals(Function.class);
+	}
+	
+	public static String uid(Method method) {
+		return TestUtils.typeof(method).getName() + "#" + method.getDeclaringClass().getName() + "#" + method.getName();
+	}
+	
+	public static String nid(Method method) {
+		return method.getDeclaringClass().getSimpleName() + "." + method.getName();
+	}
+	
+	public static List<IField> getFields(Object object) {
+		List<IField> ret = new ArrayList<>();
+		for(Class<?> c=object.getClass(); c!=null; c=c.getSuperclass()) {
+			for(Field f : c.getDeclaredFields()) {
+				Resource info = f.getAnnotation(Resource.class);
+				if(null != info) {
+					String name = info.name();
+					if("".equals(name)) {
+						name = f.getName();
+					}
+					f.setAccessible(true);
+					ret.add(new IField(object, f, name));
+				}
+			}
+		}
+		return ret;
+	}
+	
+	public static final class ICounter {
+		
+        private int value = 0;
+        
+        public int next() {
+            return ++value;
+        }
+		
+		public int value() {
+			return value;
+		}
+    }
+	
+	public static final class IPatterns {
+		
+		private final List<Pattern> patterns = new ArrayList<>();
+		
+		public IPatterns add(IPatterns values) {
+			patterns.addAll(values.patterns);
+			return this;
+		}
+		
+		public IPatterns add(String value) {
+			return add(Pattern.compile(Pattern.quote(value)));
+		}
+		
+		public IPatterns add(Pattern value) {
+			patterns.add(value);
+			return this;
+		}
+		
+		public IPatterns clear() {
+			patterns.clear();
+			return this;
+		}
+		
+		public boolean matches(String value) {
+			return patterns.stream().anyMatch(p -> p.matcher(value).matches());
+		}
+	}
+	
+	public static final class IField {
+		
+        private final Object object;
+        private final Field field;
+        private final String name;
+        private final Object value;
+
+        public IField(Object object, Field field, String name) {
+            try {
+                this.object = object;
+                this.field = field;
+				this.name = name;
+                this.value = field.get(object);
+            } catch(ReflectiveOperationException cause) {
+                throw rethrow(cause);
+            }
+        }
+		
+		public Class<?> type() {
+			 return field.getType();
+		}
+		
+		public String name() {
+			return name;
+		}
+
+        public void set(Object value) {
+            try {
+                field.set(object, value);
+            } catch(ReflectiveOperationException cause) {
+                throw rethrow(cause);
+            }
+        }
+        
+        public Object get() {
+            try {
+                return field.get(object);
+            } catch(ReflectiveOperationException cause) {
+                throw rethrow(cause);
+            }
+        }
+        
+        public void reset() {
+            try {
+                field.set(object, value);
+            } catch(ReflectiveOperationException cause) {
+                throw rethrow(cause);
+            }
+        }
+        
+    }
+	
+	public static RuntimeException rethrow(Throwable cause) {
+        new ExceptionErasure<RuntimeException>().rethrow(cause);
+        
+        // not reachable, wyjątek bez wrappowania leci linię wyżej
+        throw new RuntimeException(cause); // NOPMD
+    }
+    
+    private static class ExceptionErasure<T extends Throwable> { // NOPMD
+        
+        @SuppressWarnings("unchecked")
+        private void rethrow(Throwable exception) throws T {
+            throw (T) exception;
+        }
+
+    }
+	
+	private static final class Li { // NOPMD - lazy init idiom
+		
+		public static final Method GET_CONSTANT_POOL;
+		
+		static {
+			try {
+				GET_CONSTANT_POOL = Class.class.getDeclaredMethod("getConstantPool");
+				GET_CONSTANT_POOL.setAccessible(true);
+			} catch(ReflectiveOperationException cause) {
+				throw rethrow(cause);
+			}
+		}
+		
+		public static ConstantPool get(Class<?> type) {
+			try {
+				return (ConstantPool) GET_CONSTANT_POOL.invoke(type);
+			} catch (ReflectiveOperationException cause) {
+				throw rethrow(cause);
+			}
+		}
+		
+	}
+}

+ 17 - 2
assira.junit/src/test/java/net/ranides/assira/junit/TestContractRunnerTest.java

@@ -77,6 +77,11 @@ public class TestContractRunnerTest {
         "ForFunction.list", 
         "ForFunction.supplier"
     );
+	
+	private static final List<String> EXPECTED_FUNCTION_I = Arrays.asList(
+        "ForFunction.function", 
+        "ForFunction.supplier=null"
+    );
 
     @Test
     public void testRun() {
@@ -149,10 +154,20 @@ public class TestContractRunnerTest {
     
     @Test
     public void testFunction() {
+		
+		TesterObjects.OUT.clear();
+		new TestContractRunner(System.err)
+			.ignore("ForFunction.list")
+            .append(new Testers.ForFunction())
+            .function((args) -> new ArrayList<>())
+			.run();
+		assertEquals(EXPECTED_FUNCTION_I, TesterObjects.OUT);
+		
         TesterObjects.OUT.clear();
         new TestContractRunner(System.err)
             .append(new Testers.ForFunction())
-            .function(new int[0], (args) -> new ArrayList<>())
+			.supplier(ArrayList::new)
+            .function((Integer size) -> new ArrayList<>(size))
 			.run();
         assertEquals(EXPECTED_FUNCTION, TesterObjects.OUT);
     }
@@ -203,7 +218,7 @@ public class TestContractRunnerTest {
             try {
                 return bytes.toString(StandardCharsets.UTF_8.name());
             } catch (UnsupportedEncodingException ex) {
-                throw NewAssert.rethrow(ex);
+                throw TestUtils.rethrow(ex);
             }
         }
         

+ 7 - 1
assira.junit/src/test/java/net/ranides/assira/junit/mockup/Testers.java

@@ -47,7 +47,13 @@ public class Testers {
 
         @TestContract
         public void supplier(Supplier<List<String>> s) {
-            TesterObjects.OUT.add("ForFunction.supplier");
+			try {
+				s.get();
+				TesterObjects.OUT.add("ForFunction.supplier");
+			} catch(AssertionError ae) {
+				TesterObjects.OUT.add("ForFunction.supplier=null");
+			}
+            
         }
         
         @TestContract