package com.ibm.emb.junit;

import java.io.BufferedWriter;
import java.util.Enumeration;

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

/**
 * An extension of junit.textui.TestRunner which overrides several functions to
 * provide logging of failed tests to configurable files.
 */
public class EMBTestRunner extends TestRunner {

    protected EMBTestConfiguration embTestConfig = null;

    ResultPrinter rprinter = new ResultPrinter(System.out);

    int fColumn = 0;

    /**
     * overwritten method
     */
    public synchronized void addError(Test test, Throwable t) {
        super.addError(test, t);
    }

    /**
     * overwritten method
     */
    public synchronized void addFailure(Test test, AssertionFailedError t) {
        super.addFailure(test, t);
    }

    /**
     * Constructs a TestRunner.
     */
    public EMBTestRunner() {
        embTestConfig = EMBTestConfiguration.instance();
    }

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

    /**
     * overwritten doRun method which also logs failed tests to a file
     */
    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;
        System.out.println("Time: " + elapsedTimeAsString(runTime));
        try {
            fileWriter().newLine();
            fileWriter().write("Time: " + elapsedTimeAsString(runTime));
            fileWriter().newLine();
            print(result);
            System.out.println();
            fileWriter().newLine();
        } catch (java.io.IOException e) {
            System.out.println("Error writing result in " + this.getClass().getName());
        }
        pause(wait);
        return result;
    }

    public synchronized void startTest(Test test) {
        super.startTest(test);
        try {
            if (fColumn++ >= 80) {
                fileWriter().newLine();
                fColumn = 0;
            }
        } catch (java.io.IOException e) {
            System.out.println("Error writing result in " + this.getClass().getName());
        }
    }

    /**
     * overwritten method which logs summary to a file and prints summary to the
     * standard output
     */
    public synchronized void print(TestResult result) {
        try {
            fileHeader(result);
            fileErrors(result);
            fileFailures(result);
            fileWriter().flush();
        } catch (java.io.IOException e) {
            System.out.println("Error writng result in " + this.getClass().getName());
        }
    }

    /**
     * overwritten method which logs errors to a file and prints the errors to
     * the standard output
     */
    public void fileErrors(TestResult result) throws java.io.IOException {
        if (result.errorCount() != 0) {
            if (result.errorCount() == 1) {
                fileWriter().write("There was " + result.errorCount() + " error:");
                fileWriter().newLine();
            } else {
                fileWriter().write("There were " + result.errorCount() + " errors:");
                fileWriter().newLine();
            }
            int i = 1;
            for (Enumeration e = result.errors(); e.hasMoreElements(); i++) {
                TestFailure failure = (TestFailure) e.nextElement();
                fileWriter().write(i + ") " + failure.failedTest());
                fileWriter().newLine();
                fileWriter().write(getFilteredTrace(failure.thrownException()));
                fileWriter().newLine();
            }
        }
    }

    public void endTest(Test test) {
    }

    /**
     * overwritten method which logs failures to a file and prints failures to
     * the standard output
     */
    public void fileFailures(TestResult result) throws java.io.IOException {
        if (result.failureCount() != 0) {
            if (result.failureCount() == 1) {
                fileWriter().write("There was " + result.failureCount() + " failure:");
                fileWriter().newLine();
            } else {
                fileWriter().write("There were " + result.failureCount() + " failures:");
                fileWriter().newLine();
            }
            int i = 1;
            for (Enumeration e = result.failures(); e.hasMoreElements(); i++) {
                TestFailure failure = (TestFailure) e.nextElement();
                fileWriter().write(i + ") " + failure.failedTest());
                fileWriter().newLine();
                Throwable t = failure.thrownException();
                fileWriter().write(getFilteredTrace(failure.thrownException()));
                fileWriter().newLine();
            }
        }
    }

    /**
     * overwritten method which prints the header to a file and prints the
     * header of the report
     */
    public void fileHeader(TestResult result) throws java.io.IOException {
        if (result.wasSuccessful()) {
            fileWriter().newLine();
            fileWriter().write("OK");
            fileWriter().write(" (" + result.runCount() + " tests)");
            fileWriter().newLine();

            // added to print summary to console
            System.out.println();
            System.out.println("OK (" + result.runCount() + " tests)");
            System.out.println();

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

            // print to console
            System.out.println("FAILURES!!!");
            System.out.println("Tests run: " + result.runCount() + ", Failures: " + result.failureCount()
                    + ", Errors: " + result.errorCount());
            System.out.println();
        }
    }

    /**
     * overwritten run method which also logs errors to a file 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) {
     *     com.ibm.emb.meb.junit.EMBTestRunner.run(suite());
     * }
     * </pre>
     */
    static public TestResult run(Test suite) {
        EMBTestRunner aTestRunner = new EMBTestRunner();
        return aTestRunner.doRun(suite, false);
    }

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

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

    /**
     * fileWriter used to log the results obtained from the EMBTestConfiguration
     * singleton
     */
    protected BufferedWriter fileWriter() {
        return embTestConfig.getJunitWriter();
    }
}