/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.tools.ant.taskdefs.optional;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Path;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.util.Enumeration;
import java.util.Vector;

/**
 * Ant task to run JUnit tests.
 *
 * JUnit is a framework to create unit test. It has been initially
 * created by Erich Gamma and Kent Beck.
 * JUnit can be found at <a
 * href="ftp://www.armaties.com/D/home/armaties/ftp/TestingFramework/JUnit/"
 * >ftp://www.armaties.com/D/home/armaties/ftp/TestingFramework/JUnit/</a>.
 * <p>
 * This ant task runs a single TestCase. By default it spans a new Java VM
 * to prevent interferences between different testcases, unless
 * <code>fork</code> has been disabled.
 *
 * @author Thomas Haas
 */
public class JUnitTask extends Task {

    private final static int HALT_NOT = 1;
    private final static int HALT_IMMEDIATLY = 2;
    private final static int HALT_AT_END = 3;

    private CommandlineJava commandline = new CommandlineJava();
    private Vector tests = new Vector();

    private JUnitTest defaults = new JUnitTest();
    private boolean defaultOutfile = true;

    private int haltOnError = HALT_IMMEDIATLY;
    private int haltOnFailure = HALT_AT_END;

    /**
     * Set the path to junit classes.
     * @param junit path to junit classes.
     */
    public void setJunit(String junit) {
        commandline.createClasspath().createElement().setLocation(junit);
    }


    public void setDefaulthaltonerror(String value) {
        defaults.setHaltonerror(value);
    }

    public void setDefaulthaltonfailure(String value) {
        defaults.setHaltonfailure(value);
    }

    public void setDefaultprintsummary(String value) {
        defaults.setPrintsummary(value);
    }

    public void setDefaultprintxml(String value) {
        defaults.setPrintxml(value);
    }

    public void setDefaultOutFile(String value) {
        defaultOutfile = Project.toBoolean(value);
    }

    public void setFork(String value) {
        defaults.setFork(value);
    }


    /**
     * Set the classpath to be used by the testcase.
     * @param classpath the classpath used to run the testcase.
     */
    /*public void setClasspath(String classpath) {
        this.classpath = classpath;
    }*/


    public Path createClasspath() {
        return commandline.createClasspath();
    }

    public JUnitTest createTest() {
        final JUnitTest result;
        result = new JUnitTest(
            defaults.getFork(),
            defaults.getHaltonerror(),
            defaults.getHaltonfailure(),
            defaults.getPrintsummary(),
            defaults.getPrintxml(),
            null, null);

        tests.addElement(result);
        return result;
    }


    /**
     * Creates a new JUnitRunner and enables fork of a new Java VM.
     */
    public JUnitTask() throws Exception {
        commandline.setClassname("org.apache.tools.ant.taskdefs.optional.JUnitTestRunner");
    }

    /**
     * Runs the testcase.
     */
    public void execute() throws BuildException {
        boolean errorOcured = false;
        boolean failureOccured = false;

        final String oldclasspath = System.getProperty("java.class.path");

        commandline.createClasspath().createElement().setPath(oldclasspath);
        System.setProperty("java.class.path", commandline.createClasspath().toString());

        Enumeration list = tests.elements();
        while (list.hasMoreElements()) {
            final JUnitTest test = (JUnitTest)list.nextElement();

            final String filename = "TEST-" + test.getName() + ".xml";
            if (new File(filename).exists()) {
                project.log("Skipping " + test.getName());
                continue;
            }
            project.log("Running " + test.getName());

            if (defaultOutfile && (test.getOutfile() == null ||
                test.getOutfile().length() == 0)) {

                test.setOutfile("RUNNING-" + filename);
            }

            int exitValue = 2;

            if (test.getFork()) {
                try {
                    final Execute execute = new Execute();
                    final Commandline cmdl = new Commandline();
                    cmdl.addLine(commandline.getCommandline());
                    cmdl.addLine(test.getCommandline());
                    execute.setCommandline(cmdl.getCommandline());
                    project.log("Execute JUnit: " + cmdl, Project.MSG_VERBOSE);
                    exitValue = execute.execute();
                }
                catch (IOException e) {
                    throw new BuildException("Process fork failed.");
                }
            } else {
                final Object[] arg = { test };
                final Class[] argType = { arg[0].getClass() };
                try {
                    final Class target = Class.forName("org.apache.tools.ant.taskdefs.optional.JUnitTestRunner");
                    final Method main = target.getMethod("runTest", argType);
                    project.log("Load JUnit: " + test, Project.MSG_VERBOSE);
                    exitValue = ((Integer)main.invoke(null, arg)).intValue();
                } catch (InvocationTargetException e) {
                    e.getTargetException().printStackTrace();
                    throw new BuildException("Running test failed: " + e.getTargetException());
                } catch (Exception e) {
                    throw new BuildException("Running test failed: " + e);
                }
            }

            errorOcured = errorOcured || exitValue == 2;
            failureOccured = failureOccured || exitValue == 1;
            if (errorOcured || failureOccured) {
                rename("RUNNING-" + filename, "ERROR-" + filename);
            } else {
                rename("RUNNING-" + filename, filename);
            }
            if (errorOcured && haltOnError == HALT_IMMEDIATLY ||
                failureOccured && haltOnFailure == HALT_IMMEDIATLY) {
                throw new BuildException("JUNIT FAILED");
            }
        }

        if (errorOcured && haltOnError == HALT_AT_END ||
            failureOccured && haltOnFailure == HALT_AT_END) {
            throw new BuildException("JUNIT FAILED");
        }
    }

    private void rename(String source, String destination) throws BuildException {
        final File src = new File(source);
        final File dest = new File(destination);

        if (dest.exists()) dest.delete();
        src.renameTo(dest);
    }
}
