/*
 * $Id$
 *
 * Copyright (c) 1996, 2009, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.javatest;

import com.sun.javatest.util.BackupPolicy;

import java.util.Iterator;

/**
 * TestRunner is the abstract base class providing the ability to
 * control how tests are run. A default is provided that executes
 * each test by means of a script class.
 * <p>If a test suite does not wish tom use the default test runner,
 * it should provide its own implementation of this class. The primary
 * method which should be implemented is {@link #runTests}.
 */
public abstract class TestRunner {
    private WorkDirectory workDir;
    private BackupPolicy backupPolicy;
    private TestEnvironment env;
    private ExcludeList excludeList;
    private int concurrency;
    private Harness.Observer notifier;

    /**
     * Get the work directory to be used to store the test results generated
     * by this test runner.
     *
     * @return the work directory used to store the test results generated
     * by this test runner
     */
    public WorkDirectory getWorkDirectory() {
        return workDir;
    }

    /**
     * Set the work directory used to store the test results generated
     * by this test runner.
     *
     * @param wd the work directory used to store the test results generated
     *           by this test runner
     * @see #getWorkDirectory
     */
    void setWorkDirectory(WorkDirectory wd) {
        workDir = wd;
    }

    /**
     * Get the test suite containing the tests to be run by this test runner.
     *
     * @return the test suite containing the tests to be run by this test runner
     */
    public TestSuite getTestSuite() {
        return workDir.getTestSuite();
    }

    /**
     * Get the backup policy to be used when creating the test result files
     * generated by this test runner.
     *
     * @return the backup policy to be used when creating the
     * test result files generated by this test runner
     * @see TestResult#writeResults(WorkDirectory, BackupPolicy)
     */
    public BackupPolicy getBackupPolicy() {
        return backupPolicy;
    }

    /**
     * Set the backup policy to be used when creating the test result files
     * generated by this test runner.
     *
     * @param backupPolicy the backup policy to be used when creating the
     *                     test result files generated by this test runner
     * @see #getBackupPolicy
     * @see TestResult#writeResults(WorkDirectory, BackupPolicy)
     */
    void setBackupPolicy(BackupPolicy backupPolicy) {
        this.backupPolicy = backupPolicy;
    }

    /**
     * Get the test environment to be used to execute the tests that will
     * be run by this test runner.
     *
     * @return the test environment to be used to execute the tests that will
     * be run by this test runner
     * @see #getEnvironment
     */
    public TestEnvironment getEnvironment() {
        return env;
    }

    /**
     * Set the test environment to be used to execute the tests that will
     * be run by this test runner.
     *
     * @param env the test environment to be used to execute the tests that will
     *            be run by this test runner
     * @see #getEnvironment
     */
    void setEnvironment(TestEnvironment env) {
        this.env = env;
    }

    /**
     * Get the exclude list to be used to identify the test cases to be
     * excluded when running tests.
     *
     * @return the exclude list to be used to identify the test cases to be
     * excluded when running tests
     * @see #getExcludedTestCases
     */
    public ExcludeList getExcludeList() {
        return excludeList;
    }

    /**
     * Set the exclude list to be used to identify the test cases to be
     * excluded when running tests.
     *
     * @param excludeList the exclude list to be used to identify the test cases to be
     *                    excluded when running tests
     * @see #getExcludeList
     * @see #getExcludedTestCases
     */
    void setExcludeList(ExcludeList excludeList) {
        this.excludeList = excludeList;
    }

    /**
     * Get the names of the test cases to be excluded when running a specific test.
     *
     * @param td the test for which the excluded test cases should be obtained
     * @return the names of the test cases to be excluded when running a specific test.
     * The result is null if the test is not found or is
     * completely excluded without specifying test cases.  This may be
     * a mix of single TC strings or a comma separated list of them.
     */
    public String[] getExcludedTestCases(TestDescription td) {
        return excludeList.getTestCases(td);
    }

    /**
     * Get the concurrency to be used when running the tests, if applicable.
     *
     * @return the concurrency to be used when running the tests, if applicable
     * @see #getConcurrency
     */
    public int getConcurrency() {
        return concurrency;
    }

    /**
     * Set the concurrency to be used when running the tests, if applicable.
     *
     * @param conc the concurrency to be used when running the tests, if applicable
     * @see #getConcurrency
     */
    void setConcurrency(int conc) {
        concurrency = conc;
    }

    /**
     * Set the notifier to be used when running the tests.
     *
     * @param notifier the notifier to be used when running the tests
     * @see #notifyStartingTest
     * @see #notifyFinishedTest
     */
    void setNotifier(Harness.Observer notifier) {
        this.notifier = notifier;
    }

    /**
     * Delegate the notifier to be used when running the tests to the Script
     *
     * @param script the script to be used to execute the test
     */
    void delegateNotifier(Script script) {
        script.setNotifier(this.notifier);
    }

    /**
     * Run the tests obtained from an iterator. The iterator returns
     * TestDescription objects for the tests that have been selected to be run.
     * The iterator supports the standard hasNext() and next() methods;
     * it does not support remove(), which throws UnsupportedOperationException.
     * Each test description gives the details of the test to be run.
     * As each test is started, the implementation of this method must create
     * a new TestResult object and call {@link #notifyStartingTest}.
     * When the test completes (however it completes) the implementation of
     * this method must call {@link #notifyFinishedTest}.
     *
     * @param testIter the iterator to be used to obtain the tests to be run
     * @return true if and only if all the tests executed successfully and passed
     * @throws InterruptedException if the test run was interrupted
     */
    protected abstract boolean runTests(Iterator<TestDescription> testIter) throws InterruptedException;

    /**
     * This method must be called as each test is started, to notify any
     * registered observers.
     *
     * @param tr the TestResult object for the test that has been started
     * @see Harness.Observer
     */
    protected void notifyStartingTest(TestResult tr) {
        notifier.startingTest(tr);
    }

    /**
     * This method must be called when each test is finished, to notify any
     * registered observers.
     *
     * @param tr the TestResult object for the test that has been started
     * @see Harness.Observer
     */
    protected void notifyFinishedTest(TestResult tr) {
        // this must do the following:
        // numTestsDone++;
        // resultTable.update(result);
        // notify observers
        notifier.finishedTest(tr);
    }
}
