/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.lib;

import java.io.File;
import com.sun.javatest.Script;
import com.sun.javatest.Status;
import com.sun.javatest.TestDescription;
import com.sun.javatest.TestEnvironment;
import com.sun.javatest.util.StringArray;

/**
 * A Script to compile/execute a standard test.
 */
public class StdTestScript extends Script
{
    public Status run(String[] args, TestDescription td, TestEnvironment env) {
        try {
            String[] m = env.lookup("script.mode");
            if (m != null && m.length == 1)
                setMode(m[0]);
        }
        catch (TestEnvironment.Fault e) {
            return Status.failed("error determining script mode: " + e.getMessage());
        }

        boolean compile = false;
        boolean execute = false;
        boolean expectFail = false;

        for (int i = 0; i < args.length; i++) {
            String arg = args[i];

            if (arg.equals("-certify")) {
                compile = false;
                execute = true;
            }
            else if (arg.equals("-precompile")) {
                compile = true;
                execute = false;
            }
            else if (arg.equals("-developer")) {
                compile = true;
                execute = true;
            }
            else if (arg.equals("-compile")) {
                compile = true;
            }
            else if (arg.equals("-execute")) {
                execute = true;
            }
            else if (arg.equals("-expectFail")) {
                expectFail = true;
            }
            else
                return Status.failed("bad arg for script: `" + arg + "'");
        }

        if (compile == false && execute == false) {
            // not set in args, so set from mode
            compile = (mode == DEVELOPER || mode == PRECOMPILE);
            execute = (mode == DEVELOPER || mode == CERTIFY);
        }

        if (compile) {
            String srcsParameter = td.getParameter("sources");
            if (srcsParameter == null)
                // check "source" for backwards compatibility
                srcsParameter = td.getParameter("source");
            String[] srcs = StringArray.split(srcsParameter);
            File[] files = new File[srcs.length];
            File tdDir  = td.getDir();
            for (int i = 0; i < files.length; i++)
                files[i] = new File(tdDir, srcs[i].replace('/', File.separatorChar));

            Status compileStatus = compileTogether(files);

            // if we're not going to execute the test, this is the end of the task
            if (!execute)  {
                if (expectFail) {
                    // backwards compatibility for negative compiler tests,
                    // for which we expect the compilation to fail,
                    // so verify that it did, and return accordingly
                    if (compileStatus.getType() == Status.FAILED)
                        return pass_compFailExp.augment(compileStatus);
                    else
                        return fail_compSuccUnexp.augment(compileStatus);
                } else
                    // normal exit for compile-only tests
                    return compileStatus;
            }

            // if we want to execute the test, but the compilation failed, we can't go on
            if (compileStatus.isFailed())
                return fail_compFailUnexp.augment(compileStatus);
        }

        if (execute) {
            String executeClass = td.getParameter("executeClass");
            if (executeClass == null)
                return error_noExecuteClass;

            Status executeStatus = execute(executeClass, td.getParameter("executeArgs"));

            if (expectFail) {
                // backwards compatibility for negative execution tests,
                // for which we expect the execution to fail,
                // so verify that it did, and return accordingly
                if (executeStatus.getType() == Status.FAILED)
                    return pass_execFailExp.augment(executeStatus);
                else
                    return fail_execSuccUnexp.augment(executeStatus);
            } else
                // normal exit for (compile and) execute tests
                return executeStatus;
        }

        return error_noActionSpecified;
    }

    /**
     * Get the execution mode for this script. The default mode is CERTIFY.
     * @return an integer signifying the execution mode for this script
     * @see #setMode
     * @see #UNKNOWN
     * @see #CERTIFY
     * @see #PRECOMPILE
     * @see #DEVELOPER
     */
    public int getMode() {
        return mode;
    }

    /**
     * Set the execution mode for this script.
     * @param mode an integer signifying the execution mode for this script
     * @see #getMode
     * @see #UNKNOWN
     * @see #CERTIFY
     * @see #PRECOMPILE
     * @see #DEVELOPER
     */
    public void setMode(int mode) {
        switch (mode) {
        case CERTIFY:
        case PRECOMPILE:
        case DEVELOPER:
            this.mode = mode;
            break;

        default:
            throw new IllegalArgumentException();
        }
    }

    private void setMode(String mode) {
        setMode(parseMode(mode));
    }

    private static int parseMode(String m) {
        if (m == null || m.equals("certify"))
            return CERTIFY;
        else if (m.equals("precompile"))
            return PRECOMPILE;
        else if (m.equals("developer"))
            return DEVELOPER;
        else
            return UNKNOWN;
    }

    private static int getDefaultMode() {
        return parseMode(System.getProperty("javatest.stdTestScript.defaultMode"));
    }

    /**
     * An integer signifying that the execution mode is unknown.
     */
    public static final int UNKNOWN = 0;

    /**
     * An integer signifying that the execution mode is to perform
     * a certification run, executing precompiled classes.
     */
    public static final int CERTIFY = 1;

    /**
     * An integer signifying that the execution mode is to precompile
     * but not otherwise execute the tests.
     */
    public static final int PRECOMPILE = 2;

    /**
     * An integer signifying that the execution mode is to compile
     * and execute the tests.
     */
    public static final int DEVELOPER = 3;

    private int mode = getDefaultMode();
}
