Merge lp:~jml/launchpad/subunit-by-default into lp:launchpad

Proposed by Jonathan Lange
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~jml/launchpad/subunit-by-default
Merge into: lp:launchpad
Diff against target: 461 lines (+146/-141)
3 files modified
lib/devscripts/ec2test/builtins.py (+24/-21)
lib/devscripts/ec2test/ec2test-remote.py (+120/-104)
lib/devscripts/ec2test/testrunner.py (+2/-16)
To merge this branch: bzr merge lp:~jml/launchpad/subunit-by-default
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+18449@code.launchpad.net

Commit message

Generate subunit output on ec2 test, leaving nicer summary output and more analyzable test failures. Make --headless the default for ec2 test.

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

= Purpose =

This branch is intended to make debugging of test failures on EC2 easier by providing subunit output and by providing better error messages.

It also changes the default behaviour of the 'ec2 test' command to run in headless mode, deletes the '--headless' option and creates a new '--attached' option that is the inverse of the old '--headless' option.

= Implementation =

Most of the command changes are fairly boring. The interesting changes are in ec2test-remote and testrunner.py

In ec2test-remote, I deleted the JS checker, which is no longer used. I also removed the regex-based mechanic for producing output summary.

Instead, the full output is produced via subunit and interpreted using a component from subunit that drives a Python standard library TestResult object. I made my own TestResult object that prints out errors and failures as they occur.

I also rejiggered the subprocess code to have less nesting. I think this has made the behaviour clearer.

Finally, the subunit output is attached to the failure email. The attachment should have the correct mime type and a useful filename.

= Testing =

I've done several test runs through ec2 at various stages of development, including the final stage.

Revision history for this message
Jonathan Lange (jml) wrote :

I forgot to mention. There is an evil hack that makes sure that subunit is available to ec2test-remote.py. I'd love to be able to get rid of it, but I don't know how.

Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (7.1 KiB)

Looks good. I have a couple of questions/suggestions. I'm going to
borrow SummaryResult if that's okay, for my very-long-in-the-tooth-
but-still-going parallelization branch.

> === modified file 'lib/devscripts/ec2test/ec2test-remote.py'
> --- lib/devscripts/ec2test/ec2test-remote.py 2009-10-09 15:04:24 +0000
> +++ lib/devscripts/ec2test/ec2test-remote.py 2010-02-02 13:45:04 +0000
> @@ -106,80 +129,82 @@
> call = self.build_test_command()
>
> try:
> + popen = subprocess.Popen(
> + call, bufsize=-1,
> + stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
> + cwd=self.test_dir)
> +
> + self._gather_test_output(popen.stdout, summary_file, out_file)
> +
> + # Grab the testrunner exit status
> + result = popen.wait()
> +
> + if self.pqm_message is not None:
> + subject = self.pqm_message.get('Subject')
> + if result:
> + # failure
> + summary_file.write(
> + '\n\n**NOT** submitted to PQM:\n%s\n' % (subject,))
> + else:
> + # success
> + conn = bzrlib.smtp_connection.SMTPConnection(config)
> + conn.send_email(self.pqm_message)
> + summary_file.write(
> + '\n\nSUBMITTED TO PQM:\n%s\n' % (subject,))
> + except:
> + summary_file.write('\n\nERROR IN TESTRUNNER\n\n')
> + traceback.print_exc(file=summary_file)
> + result = 1
> + raise
> + finally:
> + # It probably isn't safe to close the log files ourselves,
> + # since someone else might try to write to them later.
> try:
> - try:
> - popen = subprocess.Popen(
> - call, bufsize=-1,
> - stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
> - cwd=self.test_dir)
> -
> - self._gather_test_output(popen, summary_file, out_file)
> -
> - # Grab the testrunner exit status
> - result = popen.wait()
> -
> - if self.pqm_message is not None:
> - subject = self.pqm_message.get('Subject')
> - if result:
> - # failure
> - summary_file.write(
> - '\n\n**NOT** submitted to PQM:\n%s\n' %
> - (subject,))
> - else:
> - # success
> - conn = bzrlib.smtp_connection.SMTPConnection(
> - config)
> - conn.send_email(self.pqm_message)
> - summary_file.write('\n\nSUBMITTED TO PQM:\n%s\n' %
> - (subject,))
> - except:
> - summary_file.write('\n\nERROR IN TESTRUNNER\n\n')
> - traceback.print_exc(file=summary_file)
> - ...

Read more...

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

Maybe it's worth adding the subunit PPA <https://launchpad.net/~subunit/+archive/ppa> to the EC2 host and installing from there, instead of doing the sys.path hack? It's not clear that it's a lot cleaner though.

Revision history for this message
Jonathan Lange (jml) wrote :
Download full text (7.6 KiB)

On Tue, Feb 2, 2010 at 2:01 PM, Gavin Panella
<email address hidden> wrote:
> Review: Approve
> Looks good. I have a couple of questions/suggestions. I'm going to
> borrow SummaryResult if that's okay, for my very-long-in-the-tooth-
> but-still-going parallelization branch.
>

It's fine. It'll be in trunk, of course, by the time you get there :)

>
>> === modified file 'lib/devscripts/ec2test/ec2test-remote.py'
>> --- lib/devscripts/ec2test/ec2test-remote.py  2009-10-09 15:04:24 +0000
>> +++ lib/devscripts/ec2test/ec2test-remote.py  2010-02-02 13:45:04 +0000
>> @@ -106,80 +129,82 @@
>>          call = self.build_test_command()
>>
>>          try:
>> +            popen = subprocess.Popen(
>> +                call, bufsize=-1,
>> +                stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
>> +                cwd=self.test_dir)
>> +
>> +            self._gather_test_output(popen.stdout, summary_file, out_file)
>> +
>> +            # Grab the testrunner exit status
>> +            result = popen.wait()
>> +
>> +            if self.pqm_message is not None:
>> +                subject = self.pqm_message.get('Subject')
>> +                if result:
>> +                    # failure
>> +                    summary_file.write(
>> +                        '\n\n**NOT** submitted to PQM:\n%s\n' % (subject,))
>> +                else:
>> +                    # success
>> +                    conn = bzrlib.smtp_connection.SMTPConnection(config)
>> +                    conn.send_email(self.pqm_message)
>> +                    summary_file.write(
>> +                        '\n\nSUBMITTED TO PQM:\n%s\n' % (subject,))
>> +        except:
>> +            summary_file.write('\n\nERROR IN TESTRUNNER\n\n')
>> +            traceback.print_exc(file=summary_file)
>> +            result = 1
>> +            raise
>> +        finally:
>> +            # It probably isn't safe to close the log files ourselves,
>> +            # since someone else might try to write to them later.
>>              try:
>> -                try:
>> -                    popen = subprocess.Popen(
>> -                        call, bufsize=-1,
>> -                        stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
>> -                        cwd=self.test_dir)
>> -
>> -                    self._gather_test_output(popen, summary_file, out_file)
>> -
>> -                    # Grab the testrunner exit status
>> -                    result = popen.wait()
>> -
>> -                    if self.pqm_message is not None:
>> -                        subject = self.pqm_message.get('Subject')
>> -                        if result:
>> -                            # failure
>> -                            summary_file.write(
>> -                                '\n\n**NOT** submitted to PQM:\n%s\n' %
>> -                                (subject,))
>> -                        else:
>> -                            # success
>> -                            conn = bzrlib.smtp_connection.SMTPConnection(
>> -                                config)
>> -                            conn.send_email(self.pqm_message)
>> -                            summary_file.write('\n\nSUBMITTED TO PQM:\n%s\n' %
>...

Read more...

Revision history for this message
Gavin Panella (allenap) wrote :

> > An alternative for all the logs is to open them line buffered,
> > assuming that works as advertised.
>
> How do you do that?
>
> jml

For stdout I think you can open a new file object with line buffering
enabled (untested):

  my_stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)

For regular files, it's similar:

  fd = open('fred.txt', 'w', 1)

Revision history for this message
Jonathan Lange (jml) wrote :

Gavin,

Thanks for the reviews & for the line buffering tip.

I don't think the output of this branch is really where I want it to be for landing, and I don't even think it's an incremental improvement. Also, with further integration testing, I've noticed odd behaviours (although these may be the xvfb bugs people keep mentioning).

In any case, it's not ready to land yet.

jml

Revision history for this message
Jonathan Lange (jml) wrote :

Hello,

Now that the new zope.testing with improved subunit support has landed, I feel comfortable and confident that this branch is ready to land also.

Leaving it up for review, since there has been some change since it was last reviewed.

jml

Revision history for this message
Gavin Panella (allenap) wrote :

Revs 10207, 10208, 10210 and 10211 all look good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/devscripts/ec2test/builtins.py'
--- lib/devscripts/ec2test/builtins.py 2010-03-18 10:38:14 +0000
+++ lib/devscripts/ec2test/builtins.py 2010-04-14 13:31:40 +0000
@@ -109,6 +109,12 @@
109 'and/or of this script.'))109 'and/or of this script.'))
110110
111111
112attached_option = Option(
113 'attached',
114 help=("Remain attached, i.e. do not go headless. Implied by --postmortem "
115 "and --file."))
116
117
112def filename_type(filename):118def filename_type(filename):
113 """An option validator for filenames.119 """An option validator for filenames.
114120
@@ -182,6 +188,10 @@
182 return branches, test_branch188 return branches, test_branch
183189
184190
191
192DEFAULT_TEST_OPTIONS = '--subunit -vvv'
193
194
185class cmd_test(EC2Command):195class cmd_test(EC2Command):
186 """Run the test suite in ec2."""196 """Run the test suite in ec2."""
187197
@@ -240,11 +250,7 @@
240 'consulted; for remote branches "Launchpad PQM '250 'consulted; for remote branches "Launchpad PQM '
241 '<launchpad@pqm.canonical.com>" is used by default.')),251 '<launchpad@pqm.canonical.com>" is used by default.')),
242 postmortem_option,252 postmortem_option,
243 Option(253 attached_option,
244 'headless',
245 help=('After building the instance and test, run the remote '
246 'tests headless. Cannot be used with postmortem '
247 'or file.')),
248 debug_option,254 debug_option,
249 Option(255 Option(
250 'open-browser',256 'open-browser',
@@ -256,10 +262,10 @@
256262
257 def run(self, test_branch=None, branch=None, trunk=False, machine=None,263 def run(self, test_branch=None, branch=None, trunk=False, machine=None,
258 instance_type=DEFAULT_INSTANCE_TYPE,264 instance_type=DEFAULT_INSTANCE_TYPE,
259 file=None, email=None, test_options='-vv', noemail=False,265 file=None, email=None, test_options=DEFAULT_TEST_OPTIONS,
260 submit_pqm_message=None, pqm_public_location=None,266 noemail=False, submit_pqm_message=None, pqm_public_location=None,
261 pqm_submit_location=None, pqm_email=None, postmortem=False,267 pqm_submit_location=None, pqm_email=None, postmortem=False,
262 headless=False, debug=False, open_browser=False,268 attached=False, debug=False, open_browser=False,
263 include_download_cache_changes=False):269 include_download_cache_changes=False):
264 if debug:270 if debug:
265 pdb.set_trace()271 pdb.set_trace()
@@ -267,10 +273,8 @@
267 branch = []273 branch = []
268 branches, test_branch = _get_branches_and_test_branch(274 branches, test_branch = _get_branches_and_test_branch(
269 trunk, branch, test_branch)275 trunk, branch, test_branch)
270 if ((postmortem or file) and headless):276 if (postmortem or file):
271 raise BzrCommandError(277 attached = True
272 'Headless mode currently does not support postmortem or file '
273 ' options.')
274 if noemail:278 if noemail:
275 if email:279 if email:
276 raise BzrCommandError(280 raise BzrCommandError(
@@ -279,12 +283,13 @@
279 if email == []:283 if email == []:
280 email = True284 email = True
281285
282 if headless and not (email or submit_pqm_message):286 if not attached and not (email or submit_pqm_message):
283 raise BzrCommandError(287 raise BzrCommandError(
284 'You have specified no way to get the results '288 'You have specified no way to get the results '
285 'of your headless test run.')289 'of your headless test run.')
286290
287 if test_options != '-vv' and submit_pqm_message is not None:291 if (test_options != DEFAULT_TEST_OPTIONS
292 and submit_pqm_message is not None):
288 raise BzrCommandError(293 raise BzrCommandError(
289 "Submitting to PQM with non-default test options isn't "294 "Submitting to PQM with non-default test options isn't "
290 "supported")295 "supported")
@@ -295,7 +300,7 @@
295300
296 runner = EC2TestRunner(301 runner = EC2TestRunner(
297 test_branch, email=email, file=file,302 test_branch, email=email, file=file,
298 test_options=test_options, headless=headless,303 test_options=test_options, headless=(not attached),
299 branches=branches, pqm_message=submit_pqm_message,304 branches=branches, pqm_message=submit_pqm_message,
300 pqm_public_location=pqm_public_location,305 pqm_public_location=pqm_public_location,
301 pqm_submit_location=pqm_submit_location,306 pqm_submit_location=pqm_submit_location,
@@ -304,7 +309,7 @@
304 instance=instance, launchpad_login=instance._launchpad_login,309 instance=instance, launchpad_login=instance._launchpad_login,
305 timeout=480)310 timeout=480)
306311
307 instance.set_up_and_run(postmortem, not headless, runner.run_tests)312 instance.set_up_and_run(postmortem, attached, runner.run_tests)
308313
309314
310class cmd_land(EC2Command):315class cmd_land(EC2Command):
@@ -325,9 +330,7 @@
325 Option(330 Option(
326 'force',331 'force',
327 help="Land the branch even if the proposal is not approved."),332 help="Land the branch even if the proposal is not approved."),
328 Option(333 attached_option,
329 'attached',
330 help="Remain attached, i.e. do not go headless."),
331 ]334 ]
332335
333 takes_args = ['merge_proposal?']336 takes_args = ['merge_proposal?']
@@ -337,8 +340,8 @@
337 """Return the command that would need to be run to submit with ec2."""340 """Return the command that would need to be run to submit with ec2."""
338 ec2_path = os.path.join(get_launchpad_root(), 'utilities', 'ec2')341 ec2_path = os.path.join(get_launchpad_root(), 'utilities', 'ec2')
339 command = [ec2_path, 'test']342 command = [ec2_path, 'test']
340 if not attached:343 if attached:
341 command.extend(['--headless'])344 command.extend(['--attached'])
342 command.extend(['--email=%s' % email for email in emails])345 command.extend(['--email=%s' % email for email in emails])
343 # 'ec2 test' has a bug where you cannot pass full URLs to branches to346 # 'ec2 test' has a bug where you cannot pass full URLs to branches to
344 # the -b option. It has special logic for 'launchpad' branches, so we347 # the -b option. It has special logic for 'launchpad' branches, so we
345348
=== modified file 'lib/devscripts/ec2test/ec2test-remote.py'
--- lib/devscripts/ec2test/ec2test-remote.py 2010-03-18 11:21:40 +0000
+++ lib/devscripts/ec2test/ec2test-remote.py 2010-04-14 13:31:40 +0000
@@ -10,12 +10,12 @@
10import optparse10import optparse
11import os11import os
12import pickle12import pickle
13import re
14import subprocess13import subprocess
15import sys14import sys
16import textwrap15import textwrap
17import time16import time
18import traceback17import traceback
18import unittest
1919
20from xml.sax.saxutils import escape20from xml.sax.saxutils import escape
2121
@@ -26,6 +26,66 @@
26import bzrlib.smtp_connection26import bzrlib.smtp_connection
27import bzrlib.workingtree27import bzrlib.workingtree
2828
29import subunit
30
31
32class SummaryResult(unittest.TestResult):
33 """Test result object used to generate the summary."""
34
35 double_line = '=' * 70 + '\n'
36 single_line = '-' * 70 + '\n'
37
38 def __init__(self, output_stream):
39 super(SummaryResult, self).__init__()
40 self.stream = output_stream
41
42 def printError(self, flavor, test, error):
43 """Print an error to the output stream."""
44 self.stream.write(self.double_line)
45 self.stream.write('%s: %s\n' % (flavor, test))
46 self.stream.write(self.single_line)
47 self.stream.write('%s\n' % (error,))
48 self.stream.flush()
49
50 def addError(self, test, error):
51 super(SummaryResult, self).addError(test, error)
52 self.printError('ERROR', test, self._exc_info_to_string(error, test))
53
54 def addFailure(self, test, error):
55 super(SummaryResult, self).addFailure(test, error)
56 self.printError(
57 'FAILURE', test, self._exc_info_to_string(error, test))
58
59
60class FlagFallStream:
61 """Wrapper around a stream that only starts forwarding after a flagfall.
62 """
63
64 def __init__(self, stream, flag):
65 """Construct a `FlagFallStream` that wraps 'stream'.
66
67 :param stream: A stream, a file-like object.
68 :param flag: A string that needs to be written to this stream before
69 we start forwarding the output.
70 """
71 self._stream = stream
72 self._flag = flag
73 self._flag_fallen = False
74
75 def write(self, bytes):
76 if self._flag_fallen:
77 self._stream.write(bytes)
78 else:
79 index = bytes.find(self._flag)
80 if index == -1:
81 return
82 else:
83 self._stream.write(bytes[index:])
84 self._flag_fallen = True
85
86 def flush(self):
87 self._stream.flush()
88
2989
30class BaseTestRunner:90class BaseTestRunner:
3191
@@ -35,10 +95,6 @@
35 self.pqm_message = pqm_message95 self.pqm_message = pqm_message
36 self.public_branch = public_branch96 self.public_branch = public_branch
37 self.public_branch_revno = public_branch_revno97 self.public_branch_revno = public_branch_revno
38
39 # Set up the testrunner options.
40 if test_options is None:
41 test_options = '-vv'
42 self.test_options = test_options98 self.test_options = test_options
4399
44 # Configure paths.100 # Configure paths.
@@ -106,80 +162,75 @@
106 call = self.build_test_command()162 call = self.build_test_command()
107163
108 try:164 try:
165 popen = subprocess.Popen(
166 call, bufsize=-1,
167 stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
168 cwd=self.test_dir)
169
170 self._gather_test_output(popen.stdout, summary_file, out_file)
171
172 # Grab the testrunner exit status
173 result = popen.wait()
174
175 if self.pqm_message is not None:
176 subject = self.pqm_message.get('Subject')
177 if result:
178 # failure
179 summary_file.write(
180 '\n\n**NOT** submitted to PQM:\n%s\n' % (subject,))
181 else:
182 # success
183 conn = bzrlib.smtp_connection.SMTPConnection(config)
184 conn.send_email(self.pqm_message)
185 summary_file.write(
186 '\n\nSUBMITTED TO PQM:\n%s\n' % (subject,))
187 except:
188 summary_file.write('\n\nERROR IN TESTRUNNER\n\n')
189 traceback.print_exc(file=summary_file)
190 result = 1
191 raise
192 finally:
193 # It probably isn't safe to close the log files ourselves,
194 # since someone else might try to write to them later.
109 try:195 try:
110 try:
111 popen = subprocess.Popen(
112 call, bufsize=-1,
113 stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
114 cwd=self.test_dir)
115
116 self._gather_test_output(popen, summary_file, out_file)
117
118 # Grab the testrunner exit status
119 result = popen.wait()
120
121 if self.pqm_message is not None:
122 subject = self.pqm_message.get('Subject')
123 if result:
124 # failure
125 summary_file.write(
126 '\n\n**NOT** submitted to PQM:\n%s\n' %
127 (subject,))
128 else:
129 # success
130 conn = bzrlib.smtp_connection.SMTPConnection(
131 config)
132 conn.send_email(self.pqm_message)
133 summary_file.write('\n\nSUBMITTED TO PQM:\n%s\n' %
134 (subject,))
135 except:
136 summary_file.write('\n\nERROR IN TESTRUNNER\n\n')
137 traceback.print_exc(file=summary_file)
138 result = 1
139 raise
140 finally:
141 # It probably isn't safe to close the log files ourselves,
142 # since someone else might try to write to them later.
143 summary_file.close()196 summary_file.close()
144 if self.email is not None:197 if self.email is not None:
145 subject = 'Test results: %s' % (198 subject = 'Test results: %s' % (
146 result and 'FAILURE' or 'SUCCESS')199 result and 'FAILURE' or 'SUCCESS')
147 summary_file = open(self.logger.summary_filename, 'r')200 summary_file = open(self.logger.summary_filename, 'r')
201 out_file = open(self.logger.out_filename, 'r')
148 bzrlib.email_message.EmailMessage.send(202 bzrlib.email_message.EmailMessage.send(
149 config, config.username(), self.email,203 config, config.username(), self.email,
150 subject, summary_file.read())204 subject, summary_file.read(),
205 attachment=out_file.read(),
206 attachment_filename='%s.log' % self.get_nick(),
207 attachment_mime_subtype='subunit')
151 summary_file.close()208 summary_file.close()
152 finally:209 finally:
153 # we do this at the end because this is a trigger to ec2test.py210 # we do this at the end because this is a trigger to
154 # back at home that it is OK to kill the process and take control211 # ec2test.py back at home that it is OK to kill the process
155 # itself, if it wants to.212 # and take control itself, if it wants to.
156 out_file.close()213 out_file.close()
157 self.logger.close_logs()214 self.logger.close_logs()
158215
159 def _gather_test_output(self, test_process, summary_file, out_file):216 def get_nick(self):
217 """Return the nick name of the branch that we are testing."""
218 return self.public_branch.strip('/').split('/')[-1]
219
220 def _gather_test_output(self, input_stream, summary_file, out_file):
160 """Write the testrunner output to the logs."""221 """Write the testrunner output to the logs."""
161 # Only write to stdout if we are running as the foreground process.222 # Only write to stdout if we are running as the foreground process.
162 echo_to_stdout = not self.daemonized223 echo_to_stdout = not self.daemonized
163224 result = SummaryResult(summary_file)
164 last_line = ''225 subunit_server = subunit.TestProtocolServer(
165 while 1:226 result, FlagFallStream(summary_file, 'Running tests.'))
166 data = test_process.stdout.read(256)227 for line in input_stream:
167 if data:228 subunit_server.lineReceived(line)
168 out_file.write(data)229 out_file.write(line)
169 out_file.flush()230 out_file.flush()
170 if echo_to_stdout:231 if echo_to_stdout:
171 sys.stdout.write(data)232 sys.stdout.write(line)
172 sys.stdout.flush()233 sys.stdout.flush()
173 lines = data.split('\n')
174 lines[0] = last_line + lines[0]
175 last_line = lines.pop()
176 for line in lines:
177 if not self.ignore_line(line):
178 summary_file.write(line + '\n')
179 summary_file.flush()
180 else:
181 summary_file.write(last_line)
182 break
183234
184235
185class TestOnMergeRunner(BaseTestRunner):236class TestOnMergeRunner(BaseTestRunner):
@@ -190,31 +241,6 @@
190 command = ['make', 'check', 'VERBOSITY=' + self.test_options]241 command = ['make', 'check', 'VERBOSITY=' + self.test_options]
191 return command242 return command
192243
193 # Used to filter lines in the summary log. See
194 # `BaseTestRunner.ignore_line()`.
195 ignore_line = re.compile(
196 r'( [\w\.\/\-]+( ?\([\w\.\/\-]+\))?|'
197 r'\s*Running.*|'
198 r'\d{4}\-\d{2}\-\d{2} \d{2}\:\d{2}\:\d{2} INFO.+|'
199 r'\s*Set up .+|'
200 r'\s*Tear down .*|'
201 r' Ran \d+ tests with .+)$').match
202
203
204class JSCheckTestRunner(BaseTestRunner):
205 """Executes the Launchpad JavaScript integration test suite."""
206
207 def build_test_command(self):
208 """See BaseTestRunner.build_test_command()."""
209 # We use the xvfb server's convenience script, xvfb-run, to
210 # automagically set the display, start the command, shut down the
211 # display, and return the exit code. (See the xvfb-run man page for
212 # details.)
213 return [
214 'xvfb-run',
215 '-s', '-screen 0 1024x768x24',
216 'make', 'jscheck']
217
218244
219class WebTestLogger:245class WebTestLogger:
220 """Logs test output to disk and a simple web page."""246 """Logs test output to disk and a simple web page."""
@@ -428,9 +454,6 @@
428 '--public-branch-revno', dest='public_branch_revno',454 '--public-branch-revno', dest='public_branch_revno',
429 type="int", default=None,455 type="int", default=None,
430 help=('The revision number of the public branch being tested.'))456 help=('The revision number of the public branch being tested.'))
431 parser.add_option(
432 '--jscheck', dest='jscheck', default=False, action='store_true',
433 help=('Run the JavaScript integration test suite.'))
434457
435 options, args = parser.parse_args()458 options, args = parser.parse_args()
436459
@@ -442,19 +465,12 @@
442 else:465 else:
443 pqm_message = None466 pqm_message = None
444467
445 if options.jscheck:468 runner = TestOnMergeRunner(
446 runner_type = JSCheckTestRunner
447 else:
448 # Use the default testrunner.
449 runner_type = TestOnMergeRunner
450
451 runner = runner_type(
452 options.email,469 options.email,
453 pqm_message,470 pqm_message,
454 options.public_branch,471 options.public_branch,
455 options.public_branch_revno,472 options.public_branch_revno,
456 ' '.join(args)473 ' '.join(args))
457 )
458474
459 try:475 try:
460 try:476 try:
461477
=== modified file 'lib/devscripts/ec2test/testrunner.py'
--- lib/devscripts/ec2test/testrunner.py 2010-03-31 20:52:55 +0000
+++ lib/devscripts/ec2test/testrunner.py 2010-04-14 13:31:40 +0000
@@ -109,7 +109,7 @@
109 message = image = None109 message = image = None
110 _running = False110 _running = False
111111
112 def __init__(self, branch, email=False, file=None, test_options='-vv',112 def __init__(self, branch, email=False, file=None, test_options=None,
113 headless=False, branches=(),113 headless=False, branches=(),
114 pqm_message=None, pqm_public_location=None,114 pqm_message=None, pqm_public_location=None,
115 pqm_submit_location=None,115 pqm_submit_location=None,
@@ -483,7 +483,7 @@
483 cmd.append('--public-branch-revno=%d' % branch_revno)483 cmd.append('--public-branch-revno=%d' % branch_revno)
484484
485 # Add any additional options for ec2test-remote.py485 # Add any additional options for ec2test-remote.py
486 cmd.extend(self.get_remote_test_options())486 cmd.extend(['--', self.test_options])
487 self.log(487 self.log(
488 'Running tests... (output is available on '488 'Running tests... (output is available on '
489 'http://%s/)\n' % self._instance.hostname)489 'http://%s/)\n' % self._instance.hostname)
@@ -519,17 +519,3 @@
519 'Writing abridged test results to %s.\n' % self.file)519 'Writing abridged test results to %s.\n' % self.file)
520 user_connection.sftp.get('/var/www/summary.log', self.file)520 user_connection.sftp.get('/var/www/summary.log', self.file)
521 user_connection.close()521 user_connection.close()
522
523 def get_remote_test_options(self):
524 """Return the test command that will be passed to ec2test-remote.py.
525
526 Returns a tuple of command-line options and switches.
527 """
528 if '--jscheck' in self.test_options:
529 # We want to run the JavaScript test suite.
530 return ('--jscheck',)
531 else:
532 # Run the normal testsuite with our Zope testrunner options.
533 # ec2test-remote.py wants the extra options to be after a double-
534 # dash.
535 return ('--', self.test_options)