package kuleuven.group2.runner;

import java.util.ArrayList;

import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;

import kuleuven.group2.data.Test;

/**
 * A class of test runners used for running single test methods
 * in a predefined order. This only violates the JUnit's fixed
 * order execution. Set-ups and tear-downs of test methods and
 * test fixture are executed properly.
 * 
 * @author	Group 2
 * @version	16 November 2013
 */
public class TestRunner {
	
	/**
	 * Creates a new test runner with no class loader.
	 */
	public TestRunner() {
		this(null);
	}
	
	/**
	 * Creates a new test runner with the given class loader
	 * as its class loader.
	 * 
	 * @param	classLoader
	 * 			The new class loader of this test runner.
	 */
	public TestRunner(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
	
	/**
	 * Returns the class loader of this test runner.
	 * 
	 * @return	The class loader of this test runner.
	 */
	public ClassLoader getClassLoader() {
		return this.classLoader;
	}
	
	/**
	 * Sets the class loader of this test runner
	 * to the given class loader.
	 * 
	 * @param	classLoader
	 * 			The class loader that has the class
	 * 			loader of this test runner to be set to.
	 */
	public void setClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
	
	/**
	 * The class loader of this test runner.
	 */
	private ClassLoader classLoader;
	
	/**
	 * The default runner used for running tests.
	 * In this way object creation can be decreased.
	 */
	private static final TestRunner DEFAULT_RUNNER = new TestRunner();
	
	/**
	 * Runs the given tests. This means that all the methods referred to
	 * in the Test objects will be ran separately.
	 * 
	 * @param	ClassLoader
	 * 			The class loader needed to obtain the class references.
	 * @param	tests
	 * 			The tests that has to be ran.
	 */
	@SuppressWarnings("static-access")
	public static Result[] runTests(ClassLoader classLoader, Test[] tests) {
		return DEFAULT_RUNNER.runTests(classLoader, tests);
	}
	
	/**
	 * Runs the given tests. This means that all the methods referred to
	 * in the Test objects will be ran separately.
	 * 
	 * @param	ClassLoader
	 * 			The class loader needed to obtain the class references.
	 * @param	tests
	 * 			The tests that has to be ran.
	 */
	public Result[] runTestMethods(ArrayList<Test> tests) {
		return runTestMethods(getClassLoader(), tests.toArray(new Test[tests.size()]));
	}
	
	/**
	 * Runs the given tests. This means that all the methods referred to
	 * in the Test objects will be ran separately.
	 * 
	 * @param	ClassLoader
	 * 			The class loader needed to obtain the class references.
	 * @param	tests
	 * 			The tests that has to be ran.
	 */
	public Result[] runTestMethods(Test[] tests) {
		return runTestMethods(getClassLoader(), tests);
	}
	
	/**
	 * Runs the given tests. This means that all the methods referred to
	 * in the Test objects will be ran separately.
	 * 
	 * @param	ClassLoader
	 * 			The class loader needed to obtain the class references.
	 * @param	tests
	 * 			The tests that has to be ran.
	 */
	public Result[] runTestMethods(ClassLoader classLoader, Test[] tests) {
		Result[] results = new Result[tests.length];
		for (int i=0; i<tests.length; i++) {
			try {
				// Obtain the class with the specified binary name in the test object.
				Class<?> klass = classLoader.loadClass(tests[i].getTestClassName());
				// Create a request for a single test method
				Request request = requestTestMethod(klass, tests[i].getTestMethodName());
				// Run the request and obtain the request.
				Result result = runTestMethod(request);
				// Store the result.
				results[i] = result;
			} catch (ClassNotFoundException e) {
				// If class cannot be found, a null reference is stored.
				// results[i] == null;
			}
		}
		return results;
	}
	
	/**
	 * The JUnitCore object (facade) used for running tests.
	 */
	private JUnitCore jUntiCore = new JUnitCore();
	
	/**
	 * Create a Request that, when processed, will run a single test.
	 * This is done by filtering out all other tests.
	 * This method is used to support rerunning single tests.
	 * 
	 * @param	clazz
	 * 			The class of the test.
	 * @param	methodName
	 * 			The name of the test
	 * @return	A Request that will cause a single test be run
	 */
	public static Request requestTestMethod(Class<?> clazz, String methodName) {
		return Request.method(clazz, methodName);
	}
	
	/**
	 * Runs all the tests contained in request.
	 * 
	 * @param	request
	 * 			The request describing tests.
	 * @return	A Result describing the details of
	 * 			the test run and the failed tests.
	 */
	public Result runTestMethod(Request request) {
		return this.jUntiCore.run(request);
	}
}

Test runner added for single methods