#!${buildout:executable}
#
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""
Given an error report, run all of the failed tests again.

For instance, it can be used in the following scenario:

  % bin/test -vvm lp.registry | tee test.out
  % # Oh nos!  Failures!
  % # Fix tests.
  % bin/retest test.out

Or, when run without arguments (or if any argument is "-"), a test
report (or a part of) can be piped in, for example by pasting it:

  % bin/retest
  Tests with failures:
     lib/lp/registry/browser/tests/sourcepackage-views.txt
     lib/lp/registry/tests/../stories/product/xx-product-package-pages.txt
  Total: ... tests, 2 failures, 0 errors in ...

"""

import fileinput
import os
import re
import sys
from itertools import takewhile
from pprint import pprint

${python-relative-path-setup}

# The test script for this branch.
TEST = "${buildout:directory/bin/test}"

# Regular expression to match numbered stories.
STORY_RE = re.compile("(.*)/\d{2}-.*")


def get_test_name(test):
    """Get the test name of a failed test.

    If the test is part of a numbered story,
    e.g. 'stories/gpg-coc/01-claimgpgp.txt', then return the directory name
    since all of the stories must be run together.
    """
    match = STORY_RE.match(test)
    if match:
        return match.group(1)
    # Otherwise split the test and return the first portion.  The split will
    # chop off windmill descriptions.
    return test.split()[0]


def gen_test_lines(lines):
    def p_start(line):
        return line.startswith('Tests with failures:')
    def p_take(line):
        return not line.startswith('Total:')
    lines = iter(lines)
    for line in lines:
        if p_start(line):
            for line in takewhile(p_take, lines):
                yield line


def gen_tests(test_lines):
    for test_line in test_lines:
        yield get_test_name(test_line.strip())


def extract_tests(lines):
    return set(gen_tests(gen_test_lines(lines)))


def run_tests(tests):
    """Given a set of tests, run them as one group."""
    print "Running tests:"
    pprint(sorted(tests))
    args = ['-vv']
    for test in tests:
        args.append('-t')
        args.append(test)
    os.execl(TEST, TEST, *args)


if __name__ == '__main__':
    tests = extract_tests(fileinput.input())
    if len(tests) >= 1:
        run_tests(tests)
    else:
        sys.stdout.write(
            "Error: no tests found\n"
            "Usage: %s [test_output_file|-] ...\n\n%s\n\n" % (
                sys.argv[0], __doc__.strip()))
        sys.exit(1)
