All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dakusui.jcunit.core.JCUnit Maven / Gradle / Ivy

package com.github.dakusui.jcunit.core;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.junit.runner.Runner;
import org.junit.runners.Suite;
import org.junit.runners.model.TestClass;

import com.github.dakusui.jcunit.exceptions.JCUnitEnvironmentException;
import com.github.dakusui.jcunit.exceptions.JCUnitException;
import com.github.dakusui.jcunit.exceptions.JCUnitPluginException;
import com.github.dakusui.jcunit.exceptions.ObjectUnderFrameworkException;
import com.github.dakusui.jcunit.generators.SimpleTestArrayGenerator;
import com.github.dakusui.jcunit.generators.TestArrayGenerator;

public class JCUnit extends Suite {
	private final ArrayList runners= new ArrayList();

	/**
	 * Only called reflectively. Do not use programmatically.
	 */
	public JCUnit(Class klass) throws Throwable {
		super(klass, Collections.emptyList());
		List parametersList= getParametersList(getTestClass());
		for (int i= 0; i < parametersList.size(); i++)
			runners.add(
					new JCUnitRunner(
							getTestClass().getJavaClass(),
							parametersList, 
							i)
					);
	}

	@SuppressWarnings("unchecked")
	static Map cast(Object values) {
		return (Map) values;
	}

	@Override
	protected List getChildren() {
		return runners;
	}

	private List getParametersList(TestClass klass)
			throws Throwable {
		@SuppressWarnings("rawtypes")
		Class generatorClass = getTestArrayGeneratorClass(klass.getJavaClass());
		if (generatorClass == null) {
			generatorClass = SimpleTestArrayGenerator.class;
		}
		return JCUnit.composeTestArray(
				klass.getJavaClass(),
				generatorClass
				);
	}

	static Method domainMethod(Class cut, Field inField) {
		Method ret;
		try {
			try {
				ret = cut.getMethod(inField.getName());
			} catch (NoSuchMethodException e) {
				ret = cut.getDeclaredMethod(inField.getName());
			}
		} catch (SecurityException e) {
			String msg = String.format("JCUnit cannot be run in this environment. (%s:%s)", e.getClass().getName(), e.getMessage());
			throw new JCUnitEnvironmentException(msg, e);
		} catch (NoSuchMethodException e) {
			String msg = String.format("Method to generate a domain for '%s' isn't defined in class '%s' or not visible.", inField, cut);
			throw new ObjectUnderFrameworkException(msg, e);
		}
		if (!validateDomainMethod(inField, ret)) {
			String msg = String.format("Domain method '%s' isn't compatible with field '%s'", ret, inField);
			throw new IllegalArgumentException(msg, null);
		}
		return ret;
	}

	public static boolean checkIfStatic(Method domainMethod) {
		return Modifier.isStatic(domainMethod.getModifiers());
	}

	private static boolean checkIfTypeCompatible(Field inField,
			Method domainMethod) {
		if (!domainMethod.getReturnType().isArray()) {
			return false;
		}
		if (domainMethod.getReturnType().getComponentType() != inField.getType()) {
			return false;
		}
		return true;
	}

	private static boolean checkIfAnyParameterExists(Method domainMethod) {
		return domainMethod.getParameterTypes().length != 0;
	}

	private static boolean checkIfReturnTypeIsArray(Method domainMethod) {
		return domainMethod.getReturnType().isArray();
	}

	private static boolean validateDomainMethod(Field inField, Method domainMethod) {
		boolean ret = true;
		ret &= JCUnit.checkIfStatic(domainMethod);
		ret &= !JCUnit.checkIfAnyParameterExists(domainMethod);
		ret &= JCUnit.checkIfReturnTypeIsArray(domainMethod);
		ret &= JCUnit.checkIfTypeCompatible(inField, domainMethod);
		return ret;
	}

	public static DomainGenerator domainGenerator(Class cut, Field inField) {
		DomainGenerator ret = null;
		In.Domain inType = inField.getAnnotation(In.class).domain();
		assert inType != null;
		
		if (inType  == In.Domain.Method) {
			final Method m = JCUnit.domainMethod(cut, inField);
			ret = new DomainGenerator() {
				@Override
				public Object[] domain() throws JCUnitException {
					return Utils.invokeDomainMethod(m);
				}
			};
		} else if (inType == In.Domain.Default) {
			Class inFieldType = inField.getType();
			Object[] tmpvalues = null;
			if (inFieldType == Integer.TYPE || inFieldType == Integer.class) {
				tmpvalues = new Object[]{1, 0, -1, 100, -100, Integer.MAX_VALUE, Integer.MIN_VALUE};
			} else if (inFieldType == Long.TYPE || inFieldType == Long.class) {
				tmpvalues = new Object[]{1L, 0L, -1L, 100L, -100L, Long.MAX_VALUE, Long.MIN_VALUE};
			} else if (inFieldType == Short.TYPE || inFieldType == Short.class) {
				tmpvalues = new Object[]{(short)1, (short)0, (short)-1, (short)100, (short)-100, Short.MAX_VALUE, Short.MIN_VALUE};
			} else if (inFieldType == Byte.TYPE || inFieldType == Byte.class) {
				tmpvalues = new Object[]{(byte)1, (byte)0, (byte)-1, (byte)100, (byte)-100, Byte.MAX_VALUE, Byte.MIN_VALUE};
			} else if (inFieldType == Float.TYPE || inFieldType == Float.class) {
				tmpvalues = new Object[]{1.0f, 0f, -1.0f, 100.0f, -100.0f, Float.MAX_VALUE, Float.MIN_VALUE};
			} else if (inFieldType == Double.TYPE || inFieldType == Double.class) {
				tmpvalues = new Object[]{1.0d, 0d, -1.0d, 100.0d, -100.0d, Double.MAX_VALUE, Double.MIN_VALUE};
			} else if (inFieldType == Character.TYPE || inFieldType == Character.class) {
				tmpvalues = new Object[]{'a', 'あ', (char)1, Character.MAX_VALUE, Character.MIN_VALUE};
			} else if (inFieldType == Boolean.TYPE || inFieldType == Boolean.class) {
				tmpvalues = new Object[]{true, false};
			} else if (inFieldType == String.class) {
				tmpvalues = new Object[]{"Hello world", "こんにちは世界", ""};
			} else if (Enum.class.isAssignableFrom(inFieldType)) {
				try {
					tmpvalues = (Object[]) inFieldType.getMethod("values").invoke(null);
				} catch (IllegalArgumentException e) {
				} catch (SecurityException e) {
				} catch (IllegalAccessException e) {
				} catch (InvocationTargetException e) {
				} catch (NoSuchMethodException e) {
				}
			} else {
				String msg = String.format(
						"Only primitive type fields and fields whose class are wrappers " + 
						"of primitive types can use 'Default' domain type. (field:%s, type:%s)",
						inField.getName(),
						inFieldType.getName()
				);
				throw new IllegalArgumentException(msg);
			}
			assert tmpvalues != null;
			// if the field isn't a primitive, null is added as a possible value.
			if (!inFieldType.isPrimitive()) {
				Object[] values2 = new Object[tmpvalues.length + 1];
				System.arraycopy(tmpvalues, 0, values2, 0, tmpvalues.length);
				values2[tmpvalues.length] = null;
				tmpvalues = values2;
			}
			final Object[] values = tmpvalues; 
			ret = new DomainGenerator() {
				@Override
				public Object[] domain() {
					return values;
				}
			};
		} else if (inType == In.Domain.None) {
			ret = new DomainGenerator() {
				@Override
				public Object[] domain() throws JCUnitException {
					return new Object[]{};
				}
			};
		}
		assert ret != null;
		return ret;
	}

	@SuppressWarnings("unchecked")
	public
	static TestArrayGenerator newTestArrayGenerator(
			@SuppressWarnings("rawtypes") Class enumeratorClass,
			Map domains) {
		TestArrayGenerator ret = null;
		try {
			ret = (TestArrayGenerator) enumeratorClass.newInstance();
			ret.init(domains);
		} catch (InstantiationException e) {
			throw new JCUnitPluginException(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			throw new JCUnitPluginException(e.getMessage(), e);
		}
		return ret;
	}

	public static List composeTestArray(
			Class cut, 
			@SuppressWarnings("rawtypes") Class generatorClass) throws JCUnitException {
		if (generatorClass == null) throw new NullPointerException();
		
		Field[] fields = Utils.getInFieldsFromClassUnderTest(cut);
		
		Map domains = new LinkedHashMap();
		for (Field f : fields) {
			DomainGenerator domainGenerator = JCUnit.domainGenerator(cut, f);
			if (domainGenerator != null) {
				Object[] domain = domainGenerator.domain();
				domains.put(f, domain);
			}
		}
		
		List ret = new ArrayList();
		
		TestArrayGenerator patternEnumerator = JCUnit.newTestArrayGenerator(generatorClass, domains);
		for (Map pattern : patternEnumerator) {
			ret.add(pattern);
		}
		return ret;
	}

	@SuppressWarnings("rawtypes")
	private static Class getTestArrayGeneratorClass(Class ouf) {
		Generator an = ouf.getAnnotation(Generator.class);
		Class ret =  an != null ? an.value() : null;
		if (ret != null) {
			return ret;
		} else {
			Class superClass = ouf.getSuperclass();
			if  (superClass == null) {
				return null;
			}
			return getTestArrayGeneratorClass(superClass);
		}
	}
}