package junit.textui;


import java.lang.reflect.* ;
import java.text.NumberFormat;
import java.util.* ;
import java.io.PrintStream;

import junit.framework.* ;
import junit.runner.* ;

/**
 * A command line based tool to run tests.
 * <pre>
 * java junit.textui.TestRunner [-wait] TestCaseClass
 * </pre>
 * TestRunner expects the name of a TestCase class as argument.
 * If this class defines a static <code>suite</code> method it 
 * will be invoked and the returned test is run. Otherwise all 
 * the methods starting with "test" having no arguments are run.
 * <p>
 * When the wait command line argument is given TestRunner
 * waits until the users types RETURN.
 * <p>
 * TestRunner prints a trace as the tests are executed followed by a
 * summary at the end. 
 */
public class TestRunner extends BaseTestRunner {
        PrintStream fWriter = System.out;

        int fColumn = 0;

        /**
         * Constructs a TestRunner.
         */
        public TestRunner() {
        }

        /**
         * Constructs a TestRunner using the given stream for all the output
         */
        public TestRunner(PrintStream writer) {
                this();
                if (writer == null)
                        throw new IllegalArgumentException("Writer can\'t be null");
                fWriter = writer;
        }

        /**
         * Always use the StandardTestSuiteLoader. Overridden from
         * BaseTestRunner.
         */
        public TestSuiteLoader getLoader() {
                return new StandardTestSuiteLoader();
        }

        public synchronized void addError(Test test, Throwable t) {
                writer().print("E");
        }

        public synchronized void addFailure(Test test, AssertionFailedError t) {
                writer().print("F");
        }

        /**
         * Creates the TestResult to be used for the test run.
         */
        protected TestResult createTestResult() {
                return new TestResult();
        }

        public TestResult doRun(Test suite, boolean wait) {
                TestResult result = createTestResult();
                result.addListener(this );
                long startTime = System.currentTimeMillis();
                suite.run(result);
                long endTime = System.currentTimeMillis();
                long runTime = endTime - startTime;
                writer().println();
                writer().println("Time: " + elapsedTimeAsString(runTime));
                print(result);

                writer().println();

                pause(wait);
                return result;
        }

        protected void pause(boolean wait) {
                if (wait) {
                        writer().println("<RETURN> to continue");
                        try {
                                System.in.read();
                        }
                        catch (Exception e) {
                        }
                }
        }

        public synchronized void startTest(Test test) {
                writer().print(".");
                if (fColumn++ >= 40) {
                        writer().println();
                        fColumn = 0;
                }
        }

        public void endTest(Test test) {
        }

        public static void main(String args[]) {
                TestRunner aTestRunner = new TestRunner();
                try {
                        TestResult r = aTestRunner.start(args);
                        if ( !r.wasSuccessful())
                                System.exit(-1);
                        System.exit(0);
                } catch (Exception e) {
                        System.err.println(e.getMessage());
                        System.exit(-2);
                }
        }

        /**
         * Prints failures to the standard output
         */
        public synchronized void print(TestResult result) {
            printErrors(result);
            printFailures(result);
            printHeader(result);
        }

        /**
         * Prints the errors to the standard output
         */
        public void printErrors(TestResult result) {
            if (result.errorCount() != 0) {
                if (result.errorCount() == 1)
                        writer().println("There was " + result.errorCount() + " error:");
                else
                        writer().println("There were " + result.errorCount() + " errors:");

                        int i = 1;
                        for (Enumeration e = result.errors(); e.hasMoreElements(); i++) {
                            TestFailure failure = (TestFailure) e.nextElement();
                                writer().println(i + ") " + failure.failedTest());
                                writer().print(getFilteredTrace(failure.thrownException()));
                    }
                }
        }

        /**
         * Prints failures to the standard output
         */
        public void printFailures(TestResult result) {
                if (result.failureCount() != 0) {
                        if (result.failureCount() == 1)
                                writer().println("There was " + result.failureCount() + " failure:");
                        else
                                writer().println("There were " + result.failureCount() + " failures:");
                        int i = 1;
                        for (Enumeration e = result.failures(); e.hasMoreElements(); i++) {
                                TestFailure failure = (TestFailure) e.nextElement();
                                writer().print(i + ") " + failure.failedTest());
                                Throwable t = failure.thrownException();
                                writer().print(getFilteredTrace(failure.thrownException()));
                        }
                }
        }

        /**
         * Prints the header of the report
         */
        public void printHeader(TestResult result) {
                if (result.wasSuccessful()) {
                        writer().println();
                        writer().print("OK");
                        writer().println (" (" + result.runCount() + " tests)");

                } else {
                        writer().println();
                        writer().println("FAILURES!!!");
                        writer().println("Tests run: " + result.runCount() + 
                                         ",  Failures: " + result.failureCount() + 
                                         ",  Errors: " + result.errorCount());
                }
        }

        /**
         * Runs a suite extracted from a TestCase subclass.
         */
        static public void run(Class testClass) {
                run(new TestSuite(testClass));
        }

        /**
         * Runs a single test and collects its results.
         * This method can be used to start a test run
         * from your program.
         * <pre>
         * public static void main (String[] args) {
         *     test.textui.TestRunner.run(suite());
         * }
         * </pre>
         */
        static public void run(Test suite) {
                TestRunner aTestRunner = new TestRunner();
                aTestRunner.doRun(suite, false);
        }

        /**
         * Runs a single test and waits until the user
         * types RETURN.
         */
        static public void runAndWait(Test suite) {
                TestRunner aTestRunner = new TestRunner();
                aTestRunner.doRun(suite, true);
        }

        /**
         * Starts a test run. Analyzes the command line arguments
         * and runs the given test suite.
         */
        protected TestResult start(String args[]) throws Exception {
                String testCase = "";
                boolean wait = false;

                for (int i = 0; i < args.length; i++) {
                        if (args[i].equals("-wait"))
                                wait = true;
                        else if (args[i].equals("-c"))
                                testCase = extractClassName(args[ ++i]);
                        else if (args[i].equals("-v"))
                                System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
                        else
                                testCase = args[i];
                }

                if (testCase.equals(""))
                        throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");

                try {
                        Test suite = getTest(testCase);
                        return doRun(suite, wait);
                }
                catch (Exception e) {
                        throw new Exception("Could not create and run test suite: " + e);
                }
        }

        protected void runFailed(String message) {
                System.err.println(message);
                System.exit(-1);
        }

        protected PrintStream writer() {
                return fWriter;
        }
}