#!/usr/bin/env python

"""Run all test scripts in a directory tree and report the results.
Searches for any file "test_*.py" in any subdirectory "test" under the
current directory (or a starting point given on the command line),
and runs them all.
"""

# created 2000/07/10, Greg Ward

__revision__ = "$Id: run_sancho_tests,v 1.8 2002/01/29 16:14:09 akuchlin Exp $"

import sys, os, re
import getopt
import new
from sancho.unittest import TestOptions, \
     find_scenarios, run_scenarios, \
     sum_test_counts, summarize_results, tests_successful

USAGE = """\
usage: %(prog)s [ -q ] -r [start_dir ...]
   or: %(prog)s [ -q ] test_script ...
"""

VERBOSE = 1

def write_now(msg):
    sys.stdout.write(msg)
    sys.stdout.flush()


def find_test_scripts (start_dir):

    """Recursively explore from 'start_dir', looking for all
    "test/test_*.py" files.  Return the list of matches.
    """

    filename_re = re.compile('^test_\w+\.py$')

    def visit (matches, dirname, names, filename_re=filename_re):
        if os.path.basename(dirname) == 'test':
            for name in names:
                if filename_re.match(name):
                    name = os.path.normpath(os.path.join(dirname, name))
                    matches.append(name)

    matches = []
    os.path.walk(start_dir, visit, matches)
    return matches


def run_test_script (script):
    """Run all the tests in a test script.
    """

    # XXX should catch exceptions from execfile here, but that would
    # mean duplicating logic from TestScenario.run(); I'll wait 'till I
    # have a decent "test collection" abstraction and just do it once.

    test_name = os.path.splitext(os.path.basename(script))[0]
    test_mod = new.module(test_name)
    test_mod.__file__ = script + " (fake)"
    execfile(script, test_mod.__dict__)
    scenarios = find_scenarios(test_mod)

    options = TestOptions(verbosity=TestOptions.LOW, show_coverage=0)
    stats = run_scenarios(scenarios, options)
    if VERBOSE or not tests_successful(stats):
        print "%s: %s" % summarize_results(stats, script)
    return stats


def run_all_scripts (scripts):
    script_stats = []                   # list of test count 3-tuples
    for script in scripts:
        script_stats.append(run_test_script(script))

    totals = sum_test_counts(script_stats)
    print "%s: %s" % summarize_results(totals)
    return tests_successful(totals)
    


def main (prog, args):
    global VERBOSE

    usage = USAGE % vars()
    try:
        (opts, args) = getopt.getopt(args, 'rq')
    except getopt.error, msg:
        raise SystemExit, usage + "\n" + str(msg)
    recursive = ('-r', '') in opts
    VERBOSE = ('-q', '') not in opts

    if recursive:
        start_dirs = args or ["."]
        scripts = []
        if VERBOSE:
            write_now("looking for test scripts...")
        for start_dir in start_dirs:
            scripts.extend(find_test_scripts(start_dir))
        if VERBOSE:
            write_now("found %d\n" % len(scripts))
    else:
        if len(args) == 0:
            raise SystemExit, usage + "\nNot enough arguments"
        scripts = args

    # If MX_DB not defined, tests will blow up (because we'll be running
    # them against an actual database)... so fix it!  This lets us run
    # tests against an actual database if we want, just by defining
    # MX_DB.
    if not os.environ.has_key('MX_DB'):
        os.environ['MX_DB'] = "none"

    success = run_all_scripts(scripts)
    if not success:
        sys.exit(2) # some tests failed

if __name__ == "__main__":
    main(os.path.basename(sys.argv[0]), sys.argv[1:])
