Merge lp:~bac/launchpad/retest into lp:launchpad

Proposed by Brad Crittenden
Status: Merged
Approved by: Barry Warsaw
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~bac/launchpad/retest
Merge into: lp:launchpad
Diff against target: 83 lines
1 file modified
utilities/retest.py (+79/-0)
To merge this branch: bzr merge lp:~bac/launchpad/retest
Reviewer Review Type Date Requested Status
Barry Warsaw (community) Approve
Review via email: mp+12421@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

= Summary =

Adding a new utility to make re-running a failed test set easy.

== Proposed fix ==

utilities/retest.py will take the output of a previous run with test failures and
re-run them all. It looks for numbered tests 'stories/foo/01-dosomething.txt' and
will run the entire story directory.

== Pre-implementation notes ==

Talked briefly to Gary who thought the script belongs in utilities.

== Implementation details ==

None.

== Tests ==

Take a previous run with failures and feed it to the script. You can use the email
sent from ec2 as input, e.g.

% utilities/retest.py test.out

== Demo and Q/A ==

As above.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  utilities/retest.py

Revision history for this message
Barry Warsaw (barry) wrote :

On Sep 25, 2009, at 02:40 PM, Brad Crittenden wrote:

>Adding a new utility to make re-running a failed test set easy.

Okay, you are my new best friend! Thanks for writing this awesome utility.
I have some minor comments about style, but merge-conditional r=me with their
consideration.

 review approve
 status approve

=== added file 'utilities/retest.py'
--- utilities/retest.py 1970-01-01 00:00:00 +0000
+++ utilities/retest.py 2009-09-25 14:40:29 +0000
> @@ -0,0 +1,80 @@
> +#!/usr/bin/env python
> +#
> +# 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.
> +% utilities/retest.py test.out
> +"""
> +
> +import subprocess
> +import sys
> +import re
> +from pprint import pprint
> +
> +# Regular expression to match numbered stories.
> +STORY_RE = re.compile("(.*)/\d{2}-.*")
> +
> +
> +def getTestName(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)
> + return test
> +
> +
> +def extractTests(fd):
> + """Get the set of tests to be run.
> +
> + Given an open file descriptor pointing to a test summary report, find all
> + of the tests to be run.
> + """

Isn't fd really a file object, not a file descriptor? Also, to avoid
abbreviations maybe 'input_file' is a better name?

> + failed_tests = set()
> + reading_tests = False
> + line = True
> + while (line):
> + line = fd.readline()

You can do this more efficiently like so

    for line in input_file:

> + if line.startswith('Tests with failures:'):
> + reading_tests = True
> + continue
> + if reading_tests:
> + if line.startswith('Total:'):
> + break
> + test = getTestName(line.strip())
> + failed_tests.add(test)
> + return failed_tests
> +
> +
> +def run_tests(tests):
> + """Given a set of tests, run them as one group."""
> + cmd = ['bin/test', '-vv']
> + print "Running tests:"
> + pprint(sorted(list(tests)))
> + for test in tests:
> + cmd.append('-t')
> + cmd.append(test)
> + p = subprocess.Popen(cmd)
> + p.wait()
> +
> +
> +if __name__ == '__main__':
> + try:
> + log_file = sys.argv[1]
> + except IndexError:
> + print "Usage: %s test_output_file" % (sys.argv[0])
> + sys.exit(-1)
> + fd = open(log_file, 'r')
> + failed_tests = extractTests(fd)
> + run_tests(failed_tests)

review: Approve
Revision history for this message
Brad Crittenden (bac) wrote :

On Sep 25, 2009, at 11:39 , Barry Warsaw wrote:

> Review: Approve
> On Sep 25, 2009, at 02:40 PM, Brad Crittenden wrote:
>
>> Adding a new utility to make re-running a failed test set easy.
>
> Okay, you are my new best friend! Thanks for writing this awesome
> utility.
> I have some minor comments about style, but merge-conditional r=me
> with their
> consideration.
>
> review approve
> status approve

Thanks!

>
> === added file 'utilities/retest.py'
> --- utilities/retest.py 1970-01-01 00:00:00 +0000
> +++ utilities/retest.py 2009-09-25 14:40:29 +0000
>> +def extractTests(fd):
>> + """Get the set of tests to be run.
>> +
>> + Given an open file descriptor pointing to a test summary
>> report, find all
>> + of the tests to be run.
>> + """
>
> Isn't fd really a file object, not a file descriptor? Also, to avoid
> abbreviations maybe 'input_file' is a better name?

Right. Changed.

>
>> + failed_tests = set()
>> + reading_tests = False
>> + line = True
>> + while (line):
>> + line = fd.readline()
>
> You can do this more efficiently like so
>
> for line in input_file:

Fixed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'utilities/retest.py'
--- utilities/retest.py 1970-01-01 00:00:00 +0000
+++ utilities/retest.py 2009-09-26 01:04:14 +0000
@@ -0,0 +1,79 @@
1#!/usr/bin/env python
2#
3# Copyright 2009 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).
5
6"""
7Given an error report, run all of the failed tests again.
8
9For instance, it can be used in the following scenario:
10% bin/test -vvm lp.registry | tee test.out
11% # Oh nos! Failures!
12% # Fix tests.
13% utilities/retest.py test.out
14"""
15
16import subprocess
17import sys
18import re
19from pprint import pprint
20
21
22# Regular expression to match numbered stories.
23STORY_RE = re.compile("(.*)/\d{2}-.*")
24
25
26def getTestName(test):
27 """Get the test name of a failed test.
28
29 If the test is part of a numbered story,
30 e.g. 'stories/gpg-coc/01-claimgpgp.txt', then return the directory name
31 since all of the stories must be run together.
32 """
33 match = STORY_RE.match(test)
34 if match:
35 return match.group(1)
36 return test
37
38
39def extractTests(input_file):
40 """Get the set of tests to be run.
41
42 Given a file object for a test summary report, find all of the tests to be
43 run.
44 """
45 failed_tests = set()
46 reading_tests = False
47 for line in input_file:
48 if line.startswith('Tests with failures:'):
49 reading_tests = True
50 continue
51 if reading_tests:
52 if line.startswith('Total:'):
53 break
54 test = getTestName(line.strip())
55 failed_tests.add(test)
56 return failed_tests
57
58
59def run_tests(tests):
60 """Given a set of tests, run them as one group."""
61 cmd = ['bin/test', '-vv']
62 print "Running tests:"
63 pprint(sorted(list(tests)))
64 for test in tests:
65 cmd.append('-t')
66 cmd.append(test)
67 p = subprocess.Popen(cmd)
68 p.wait()
69
70
71if __name__ == '__main__':
72 try:
73 log_file = sys.argv[1]
74 except IndexError:
75 print "Usage: %s test_output_file" % (sys.argv[0])
76 sys.exit(-1)
77 fd = open(log_file, 'r')
78 failed_tests = extractTests(fd)
79 run_tests(failed_tests)