package test.beast.integration; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import org.junit.Test; import beast.core.BEASTInterface; import beast.core.BEASTObject; import beast.core.Input; import beast.core.Param; import beast.util.AddOnManager; import junit.framework.TestCase; public class InputTypeTest extends TestCase { { System.setProperty("beast.is.junit.testing", "true"); } /* * Test that the type of an input can be determined, If not, the programmer * has to manually initialise the type of the input (most easily done * through a constructor of Input) */ @Test public void testInputTypeCanBeSet() throws Exception { List<String> beastObjectNames = AddOnManager.find(beast.core.BEASTObject.class, AddOnManager.IMPLEMENTATION_DIR); List<String> failingInputs = new ArrayList<String>(); for (String beastObjectName : beastObjectNames) { try { BEASTObject beastObject = (BEASTObject) Class.forName(beastObjectName).newInstance(); List<Input<?>> inputs = beastObject.listInputs(); for (Input<?> input : inputs) { if (input.getType() == null) { try { input.determineClass(beastObject); if (input.getType() == null) { failingInputs.add(beastObject + ":" + input.getName()); } } catch (Exception e2) { failingInputs.add(beastObject + ":" + input.getName()); } } } } catch (Exception e) { // ignore } } assertTrue( "Type of input could not be set for these inputs (probably requires to be set by using the appropriate constructure of Input): " + failingInputs.toString(), failingInputs.size() == 0); } @Test public void testAnnotatedInputHasGetters() throws Exception { testAnnotatedInputHasGetters(AddOnManager.IMPLEMENTATION_DIR); } public void testAnnotatedInputHasGetters(String [] packages) throws Exception { List<String> beastObjectNames = AddOnManager.find(Object.class, packages); System.err.println("Testing " + beastObjectNames.size() + " classes"); List<String> failingInputs = new ArrayList<String>(); for (String beastObject : beastObjectNames) { try { Class<?> _class = Class.forName(beastObject); Constructor<?>[] allConstructors = _class.getDeclaredConstructors(); for (Constructor<?> ctor : allConstructors) { Annotation[][] annotations = ctor.getParameterAnnotations(); List<Param> paramAnnotations = new ArrayList<>(); for (Annotation [] a0 : annotations) { for (Annotation a : a0) { if (a instanceof Param) { paramAnnotations.add((Param) a); } } } if (paramAnnotations.size() > 0 && !BEASTInterface.class.isAssignableFrom(_class)) { failingInputs.add(_class.getName() + " has Param annotations but does not implement BEASTInterface\n"); } Class<?>[] types = ctor.getParameterTypes(); Type[] gtypes = ctor.getGenericParameterTypes(); if (types.length > 0 && paramAnnotations.size() > 0) { int offset = 0; if (types.length == paramAnnotations.size() + 1) { offset = 1; } for (int i = 0; i < paramAnnotations.size(); i++) { Class<?> type; Class<?> clazz = null; boolean isList = false; String typeName = types[i + offset].getTypeName(); if (typeName.endsWith("[]")) { failingInputs.add(_class.getName() + " constructor has arrray as argument, should be a List\n"); } else { switch (typeName) { case "int" : type = Integer.class; break; case "long" : type = Long.class; break; case "float" : type = Float.class; break; case "double" : type = Double.class; break; case "boolean" : type = Boolean.class; break; default: clazz = Class.forName(typeName); if (clazz.isAssignableFrom(List.class)) { Type[] genericTypes2 = ((ParameterizedType) gtypes[i + offset]).getActualTypeArguments(); type = (Class<?>) genericTypes2[0]; isList = true; } else { type = types[i + offset]; } } String name = paramAnnotations.get(i).name(); String getter = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); try { Method method = _class.getMethod(getter); if (isList) { if (!method.getReturnType().isAssignableFrom(List.class)) { failingInputs.add(_class.getName() + ":" + getter + "() should return List<" + type.getName() + "> instead of " + method.getReturnType().getName() + "\n"); } } else if (!method.getReturnType().isAssignableFrom(type)) { failingInputs.add(_class.getName() + ":" + getter + "() should return " + type.getName() + " instead of " + method.getReturnType().getName() + "\n"); } } catch (NoSuchMethodException e) { failingInputs.add(_class.getName() + ":" + getter + "() missing\n"); } String setter = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); try { _class.getMethod(setter, type); } catch (NoSuchMethodException e) { failingInputs.add(_class.getName() + ":" + setter + "("+ type.getName() + ") missing, or does not have correct argument\n"); } } } } } } catch (Exception e) { System.err.println(beastObject + " " + e.getClass().getName() + " " + e.getMessage()); } } System.err.println("Done!"); assertTrue( "Something is wrong with these annotated constructor(s): \n" + failingInputs.toString(), failingInputs.size() == 0); } }