/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package javax.jdo.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.io.PrintStream;

import junit.framework.Test;
import junit.framework.TestResult;
import junit.framework.TestSuite;
import junit.textui.ResultPrinter;
import junit.textui.TestRunner;

/**
 * TestRunner class for running a single test or a test suite in batch
 * mode. The format of the test output is specified by the result printer
 * class. The main method sets an exit code according to the test result:
 * <ul>
 * <li><code>0</code>: success
 * <li><code>1</code>: failure, the test shows an unexpected behavior
 * <li><code>2</code>: exception, the test throws an unhandled excption 
 * </ul>
 * 
 * @author Michael Bouschen
 */
public class BatchTestRunner
    extends TestRunner
{
    /** Name of the system property to specify the result printer class. */
    public static final String RESULTPRINTER_PROPERTY = "ResultPrinterClass"; 
    
    /** Default of the system property ResultPrinterClass. */
    public static final String RESULTPRINTER_DEFAULT = BatchResultPrinter.class.getName();
    
    /** 
     * Constructor. 
     * It creates a result printer instance based on the system property
     * and delegates to the constructor taking a result printer argument. 
     */
    public BatchTestRunner() {
    	super();
        setPrinter(getResultPrinter());
    }
    
    /**  
     * Constructor. USes teh specified resultPrinter to format the test result.
     */
    public BatchTestRunner(ResultPrinter resultPrinter) {
        super(resultPrinter);
    }

    /** Runs all test methods from the specified class. */
    public static void run(Class clazz) {
        run(new TestSuite(clazz));
    }
    
    /** Runs the specified test. */
    public static TestResult run(Test test) {
		return new BatchTestRunner().doRun(test);
    }

	/**	Runs the specified test and waits until the user types RETURN. */
	public static void runAndWait(Test suite) {
		new BatchTestRunner().doRun(suite, true);
	}

	/** 
     * Runs in batch mode and sets an exit code. If the specified String
     * array includes a single fully qualified class name, this test class
     * is executed. If it is empty it runs the TestListSuite.
     */
    public static void main(String args[]) {
		BatchTestRunner aTestRunner= new BatchTestRunner();
		try {
            /*
            if ((args == null) || args.length == 0)
                args = new String[] { TestListSuite.class.getName() };
            */
			TestResult r = aTestRunner.start(args);
			if (!r.wasSuccessful()) 
				System.exit(FAILURE_EXIT);
			System.exit(SUCCESS_EXIT);
		} catch(Exception e) {
			System.err.println(e.getMessage());
			System.exit(EXCEPTION_EXIT);
		}
	}
    
    /** Returns a result printer instance. n instance of tCheck the system property */
    protected ResultPrinter getResultPrinter() {
      	String className =  System.getProperty(RESULTPRINTER_PROPERTY);
        if (className != null) {
            className = className.trim();
            if (className.length() != 0) {
                String msg = null;
                try {
                    // get class instance
                    Class<?> clazz = Class.forName(className);
                    // constructor taking PrintStream arg
                    Constructor ctor = clazz.getConstructor(
                        new Class[] { PrintStream.class } );
                    // create instance
                    return (ResultPrinter)ctor.newInstance(
                        new Object[] { System.out });
                }
                catch (ClassNotFoundException ex) {
                    // specified ResultPrinter class not 
                    msg = "Cannot find specified result printer class " + 
                        className + ".";
                }
                catch (NoSuchMethodException ex) {
                    msg = "Class " + className + 
                        " does not provide constructor taking a PrintStream.";
                }
                catch (InstantiationException ex) {
                    msg = "Class " + className + " is abstract.";
                }
                catch (IllegalAccessException ex) {
                    msg = "Constructor taking a PrintStream of class " + 
                        className + " is not accessible.";
                }
                catch (InvocationTargetException ex) {
                    msg = "Constructor call results in exception " + ex + ".";
                }

                // ResultPrinter class specified, but not avaiable
                System.out.println(msg);
                ResultPrinter printer = getDefaultResultPrinter();
                System.out.println("Using default result printer of class " + 
                                   printer.getClass().getName());
            }
        }
        
        // ResultPrinter class not specified => use default
        return getDefaultResultPrinter();
    }

    /** 
     * Returns an instance of the default result printer class
     * BatchResultPrinter.
     */
    protected ResultPrinter getDefaultResultPrinter() {
        return new BatchResultPrinter(System.out);
    }
    

}

