Merge lp:~jml/launchpad/subunit-by-default into lp:launchpad
- subunit-by-default
- Merge into devel
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 |
Related bugs: |
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.
Description of the change
Jonathan Lange (jml) wrote : | # |
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.
Gavin Panella (allenap) wrote : | # |
Looks good. I have a couple of questions/
borrow SummaryResult if that's okay, for my very-long-
but-still-going parallelization branch.
> === modified file 'lib/devscripts
> --- lib/devscripts/
> +++ lib/devscripts/
> @@ -106,80 +129,82 @@
> call = self.build_
>
> try:
> + popen = subprocess.Popen(
> + call, bufsize=-1,
> + stdout=
> + cwd=self.test_dir)
> +
> + self._gather_
> +
> + # Grab the testrunner exit status
> + result = popen.wait()
> +
> + if self.pqm_message is not None:
> + subject = self.pqm_
> + if result:
> + # failure
> + summary_file.write(
> + '\n\n**NOT** submitted to PQM:\n%s\n' % (subject,))
> + else:
> + # success
> + conn = bzrlib.
> + conn.send_
> + summary_file.write(
> + '\n\nSUBMITTED TO PQM:\n%s\n' % (subject,))
> + except:
> + summary_
> + traceback.
> + 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=
> - cwd=self.test_dir)
> -
> - self._gather_
> -
> - # Grab the testrunner exit status
> - result = popen.wait()
> -
> - if self.pqm_message is not None:
> - subject = self.pqm_
> - if result:
> - # failure
> - summary_file.write(
> - '\n\n**NOT** submitted to PQM:\n%s\n' %
> - (subject,))
> - else:
> - # success
> - conn = bzrlib.
> - config)
> - conn.send_
> - summary_
> - (subject,))
> - except:
> - summary_
> - traceback.
> - ...
Gavin Panella (allenap) wrote : | # |
Maybe it's worth adding the subunit PPA <https:/
Jonathan Lange (jml) wrote : | # |
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/
> borrow SummaryResult if that's okay, for my very-long-
> 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
>> --- lib/devscripts/
>> +++ lib/devscripts/
>> @@ -106,80 +129,82 @@
>> call = self.build_
>>
>> try:
>> + popen = subprocess.Popen(
>> + call, bufsize=-1,
>> + stdout=
>> + cwd=self.test_dir)
>> +
>> + self._gather_
>> +
>> + # Grab the testrunner exit status
>> + result = popen.wait()
>> +
>> + if self.pqm_message is not None:
>> + subject = self.pqm_
>> + if result:
>> + # failure
>> + summary_file.write(
>> + '\n\n**NOT** submitted to PQM:\n%s\n' % (subject,))
>> + else:
>> + # success
>> + conn = bzrlib.
>> + conn.send_
>> + summary_file.write(
>> + '\n\nSUBMITTED TO PQM:\n%s\n' % (subject,))
>> + except:
>> + summary_
>> + traceback.
>> + 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=
>> - cwd=self.test_dir)
>> -
>> - self._gather_
>> -
>> - # Grab the testrunner exit status
>> - result = popen.wait()
>> -
>> - if self.pqm_message is not None:
>> - subject = self.pqm_
>> - if result:
>> - # failure
>> - summary_file.write(
>> - '\n\n**NOT** submitted to PQM:\n%s\n' %
>> - (subject,))
>> - else:
>> - # success
>> - conn = bzrlib.
>> - config)
>> - conn.send_
>> - summary_
>...
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(
For regular files, it's similar:
fd = open('fred.txt', 'w', 1)
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
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
Gavin Panella (allenap) wrote : | # |
Revs 10207, 10208, 10210 and 10211 all look good.
Preview Diff
1 | === modified file 'lib/devscripts/ec2test/builtins.py' |
2 | --- lib/devscripts/ec2test/builtins.py 2010-03-18 10:38:14 +0000 |
3 | +++ lib/devscripts/ec2test/builtins.py 2010-04-14 13:31:40 +0000 |
4 | @@ -109,6 +109,12 @@ |
5 | 'and/or of this script.')) |
6 | |
7 | |
8 | +attached_option = Option( |
9 | + 'attached', |
10 | + help=("Remain attached, i.e. do not go headless. Implied by --postmortem " |
11 | + "and --file.")) |
12 | + |
13 | + |
14 | def filename_type(filename): |
15 | """An option validator for filenames. |
16 | |
17 | @@ -182,6 +188,10 @@ |
18 | return branches, test_branch |
19 | |
20 | |
21 | + |
22 | +DEFAULT_TEST_OPTIONS = '--subunit -vvv' |
23 | + |
24 | + |
25 | class cmd_test(EC2Command): |
26 | """Run the test suite in ec2.""" |
27 | |
28 | @@ -240,11 +250,7 @@ |
29 | 'consulted; for remote branches "Launchpad PQM ' |
30 | '<launchpad@pqm.canonical.com>" is used by default.')), |
31 | postmortem_option, |
32 | - Option( |
33 | - 'headless', |
34 | - help=('After building the instance and test, run the remote ' |
35 | - 'tests headless. Cannot be used with postmortem ' |
36 | - 'or file.')), |
37 | + attached_option, |
38 | debug_option, |
39 | Option( |
40 | 'open-browser', |
41 | @@ -256,10 +262,10 @@ |
42 | |
43 | def run(self, test_branch=None, branch=None, trunk=False, machine=None, |
44 | instance_type=DEFAULT_INSTANCE_TYPE, |
45 | - file=None, email=None, test_options='-vv', noemail=False, |
46 | - submit_pqm_message=None, pqm_public_location=None, |
47 | + file=None, email=None, test_options=DEFAULT_TEST_OPTIONS, |
48 | + noemail=False, submit_pqm_message=None, pqm_public_location=None, |
49 | pqm_submit_location=None, pqm_email=None, postmortem=False, |
50 | - headless=False, debug=False, open_browser=False, |
51 | + attached=False, debug=False, open_browser=False, |
52 | include_download_cache_changes=False): |
53 | if debug: |
54 | pdb.set_trace() |
55 | @@ -267,10 +273,8 @@ |
56 | branch = [] |
57 | branches, test_branch = _get_branches_and_test_branch( |
58 | trunk, branch, test_branch) |
59 | - if ((postmortem or file) and headless): |
60 | - raise BzrCommandError( |
61 | - 'Headless mode currently does not support postmortem or file ' |
62 | - ' options.') |
63 | + if (postmortem or file): |
64 | + attached = True |
65 | if noemail: |
66 | if email: |
67 | raise BzrCommandError( |
68 | @@ -279,12 +283,13 @@ |
69 | if email == []: |
70 | email = True |
71 | |
72 | - if headless and not (email or submit_pqm_message): |
73 | + if not attached and not (email or submit_pqm_message): |
74 | raise BzrCommandError( |
75 | 'You have specified no way to get the results ' |
76 | 'of your headless test run.') |
77 | |
78 | - if test_options != '-vv' and submit_pqm_message is not None: |
79 | + if (test_options != DEFAULT_TEST_OPTIONS |
80 | + and submit_pqm_message is not None): |
81 | raise BzrCommandError( |
82 | "Submitting to PQM with non-default test options isn't " |
83 | "supported") |
84 | @@ -295,7 +300,7 @@ |
85 | |
86 | runner = EC2TestRunner( |
87 | test_branch, email=email, file=file, |
88 | - test_options=test_options, headless=headless, |
89 | + test_options=test_options, headless=(not attached), |
90 | branches=branches, pqm_message=submit_pqm_message, |
91 | pqm_public_location=pqm_public_location, |
92 | pqm_submit_location=pqm_submit_location, |
93 | @@ -304,7 +309,7 @@ |
94 | instance=instance, launchpad_login=instance._launchpad_login, |
95 | timeout=480) |
96 | |
97 | - instance.set_up_and_run(postmortem, not headless, runner.run_tests) |
98 | + instance.set_up_and_run(postmortem, attached, runner.run_tests) |
99 | |
100 | |
101 | class cmd_land(EC2Command): |
102 | @@ -325,9 +330,7 @@ |
103 | Option( |
104 | 'force', |
105 | help="Land the branch even if the proposal is not approved."), |
106 | - Option( |
107 | - 'attached', |
108 | - help="Remain attached, i.e. do not go headless."), |
109 | + attached_option, |
110 | ] |
111 | |
112 | takes_args = ['merge_proposal?'] |
113 | @@ -337,8 +340,8 @@ |
114 | """Return the command that would need to be run to submit with ec2.""" |
115 | ec2_path = os.path.join(get_launchpad_root(), 'utilities', 'ec2') |
116 | command = [ec2_path, 'test'] |
117 | - if not attached: |
118 | - command.extend(['--headless']) |
119 | + if attached: |
120 | + command.extend(['--attached']) |
121 | command.extend(['--email=%s' % email for email in emails]) |
122 | # 'ec2 test' has a bug where you cannot pass full URLs to branches to |
123 | # the -b option. It has special logic for 'launchpad' branches, so we |
124 | |
125 | === modified file 'lib/devscripts/ec2test/ec2test-remote.py' |
126 | --- lib/devscripts/ec2test/ec2test-remote.py 2010-03-18 11:21:40 +0000 |
127 | +++ lib/devscripts/ec2test/ec2test-remote.py 2010-04-14 13:31:40 +0000 |
128 | @@ -10,12 +10,12 @@ |
129 | import optparse |
130 | import os |
131 | import pickle |
132 | -import re |
133 | import subprocess |
134 | import sys |
135 | import textwrap |
136 | import time |
137 | import traceback |
138 | +import unittest |
139 | |
140 | from xml.sax.saxutils import escape |
141 | |
142 | @@ -26,6 +26,66 @@ |
143 | import bzrlib.smtp_connection |
144 | import bzrlib.workingtree |
145 | |
146 | +import subunit |
147 | + |
148 | + |
149 | +class SummaryResult(unittest.TestResult): |
150 | + """Test result object used to generate the summary.""" |
151 | + |
152 | + double_line = '=' * 70 + '\n' |
153 | + single_line = '-' * 70 + '\n' |
154 | + |
155 | + def __init__(self, output_stream): |
156 | + super(SummaryResult, self).__init__() |
157 | + self.stream = output_stream |
158 | + |
159 | + def printError(self, flavor, test, error): |
160 | + """Print an error to the output stream.""" |
161 | + self.stream.write(self.double_line) |
162 | + self.stream.write('%s: %s\n' % (flavor, test)) |
163 | + self.stream.write(self.single_line) |
164 | + self.stream.write('%s\n' % (error,)) |
165 | + self.stream.flush() |
166 | + |
167 | + def addError(self, test, error): |
168 | + super(SummaryResult, self).addError(test, error) |
169 | + self.printError('ERROR', test, self._exc_info_to_string(error, test)) |
170 | + |
171 | + def addFailure(self, test, error): |
172 | + super(SummaryResult, self).addFailure(test, error) |
173 | + self.printError( |
174 | + 'FAILURE', test, self._exc_info_to_string(error, test)) |
175 | + |
176 | + |
177 | +class FlagFallStream: |
178 | + """Wrapper around a stream that only starts forwarding after a flagfall. |
179 | + """ |
180 | + |
181 | + def __init__(self, stream, flag): |
182 | + """Construct a `FlagFallStream` that wraps 'stream'. |
183 | + |
184 | + :param stream: A stream, a file-like object. |
185 | + :param flag: A string that needs to be written to this stream before |
186 | + we start forwarding the output. |
187 | + """ |
188 | + self._stream = stream |
189 | + self._flag = flag |
190 | + self._flag_fallen = False |
191 | + |
192 | + def write(self, bytes): |
193 | + if self._flag_fallen: |
194 | + self._stream.write(bytes) |
195 | + else: |
196 | + index = bytes.find(self._flag) |
197 | + if index == -1: |
198 | + return |
199 | + else: |
200 | + self._stream.write(bytes[index:]) |
201 | + self._flag_fallen = True |
202 | + |
203 | + def flush(self): |
204 | + self._stream.flush() |
205 | + |
206 | |
207 | class BaseTestRunner: |
208 | |
209 | @@ -35,10 +95,6 @@ |
210 | self.pqm_message = pqm_message |
211 | self.public_branch = public_branch |
212 | self.public_branch_revno = public_branch_revno |
213 | - |
214 | - # Set up the testrunner options. |
215 | - if test_options is None: |
216 | - test_options = '-vv' |
217 | self.test_options = test_options |
218 | |
219 | # Configure paths. |
220 | @@ -106,80 +162,75 @@ |
221 | call = self.build_test_command() |
222 | |
223 | try: |
224 | + popen = subprocess.Popen( |
225 | + call, bufsize=-1, |
226 | + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
227 | + cwd=self.test_dir) |
228 | + |
229 | + self._gather_test_output(popen.stdout, summary_file, out_file) |
230 | + |
231 | + # Grab the testrunner exit status |
232 | + result = popen.wait() |
233 | + |
234 | + if self.pqm_message is not None: |
235 | + subject = self.pqm_message.get('Subject') |
236 | + if result: |
237 | + # failure |
238 | + summary_file.write( |
239 | + '\n\n**NOT** submitted to PQM:\n%s\n' % (subject,)) |
240 | + else: |
241 | + # success |
242 | + conn = bzrlib.smtp_connection.SMTPConnection(config) |
243 | + conn.send_email(self.pqm_message) |
244 | + summary_file.write( |
245 | + '\n\nSUBMITTED TO PQM:\n%s\n' % (subject,)) |
246 | + except: |
247 | + summary_file.write('\n\nERROR IN TESTRUNNER\n\n') |
248 | + traceback.print_exc(file=summary_file) |
249 | + result = 1 |
250 | + raise |
251 | + finally: |
252 | + # It probably isn't safe to close the log files ourselves, |
253 | + # since someone else might try to write to them later. |
254 | try: |
255 | - try: |
256 | - popen = subprocess.Popen( |
257 | - call, bufsize=-1, |
258 | - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
259 | - cwd=self.test_dir) |
260 | - |
261 | - self._gather_test_output(popen, summary_file, out_file) |
262 | - |
263 | - # Grab the testrunner exit status |
264 | - result = popen.wait() |
265 | - |
266 | - if self.pqm_message is not None: |
267 | - subject = self.pqm_message.get('Subject') |
268 | - if result: |
269 | - # failure |
270 | - summary_file.write( |
271 | - '\n\n**NOT** submitted to PQM:\n%s\n' % |
272 | - (subject,)) |
273 | - else: |
274 | - # success |
275 | - conn = bzrlib.smtp_connection.SMTPConnection( |
276 | - config) |
277 | - conn.send_email(self.pqm_message) |
278 | - summary_file.write('\n\nSUBMITTED TO PQM:\n%s\n' % |
279 | - (subject,)) |
280 | - except: |
281 | - summary_file.write('\n\nERROR IN TESTRUNNER\n\n') |
282 | - traceback.print_exc(file=summary_file) |
283 | - result = 1 |
284 | - raise |
285 | - finally: |
286 | - # It probably isn't safe to close the log files ourselves, |
287 | - # since someone else might try to write to them later. |
288 | summary_file.close() |
289 | if self.email is not None: |
290 | subject = 'Test results: %s' % ( |
291 | result and 'FAILURE' or 'SUCCESS') |
292 | summary_file = open(self.logger.summary_filename, 'r') |
293 | + out_file = open(self.logger.out_filename, 'r') |
294 | bzrlib.email_message.EmailMessage.send( |
295 | config, config.username(), self.email, |
296 | - subject, summary_file.read()) |
297 | + subject, summary_file.read(), |
298 | + attachment=out_file.read(), |
299 | + attachment_filename='%s.log' % self.get_nick(), |
300 | + attachment_mime_subtype='subunit') |
301 | summary_file.close() |
302 | - finally: |
303 | - # we do this at the end because this is a trigger to ec2test.py |
304 | - # back at home that it is OK to kill the process and take control |
305 | - # itself, if it wants to. |
306 | - out_file.close() |
307 | - self.logger.close_logs() |
308 | - |
309 | - def _gather_test_output(self, test_process, summary_file, out_file): |
310 | + finally: |
311 | + # we do this at the end because this is a trigger to |
312 | + # ec2test.py back at home that it is OK to kill the process |
313 | + # and take control itself, if it wants to. |
314 | + out_file.close() |
315 | + self.logger.close_logs() |
316 | + |
317 | + def get_nick(self): |
318 | + """Return the nick name of the branch that we are testing.""" |
319 | + return self.public_branch.strip('/').split('/')[-1] |
320 | + |
321 | + def _gather_test_output(self, input_stream, summary_file, out_file): |
322 | """Write the testrunner output to the logs.""" |
323 | # Only write to stdout if we are running as the foreground process. |
324 | echo_to_stdout = not self.daemonized |
325 | - |
326 | - last_line = '' |
327 | - while 1: |
328 | - data = test_process.stdout.read(256) |
329 | - if data: |
330 | - out_file.write(data) |
331 | - out_file.flush() |
332 | - if echo_to_stdout: |
333 | - sys.stdout.write(data) |
334 | - sys.stdout.flush() |
335 | - lines = data.split('\n') |
336 | - lines[0] = last_line + lines[0] |
337 | - last_line = lines.pop() |
338 | - for line in lines: |
339 | - if not self.ignore_line(line): |
340 | - summary_file.write(line + '\n') |
341 | - summary_file.flush() |
342 | - else: |
343 | - summary_file.write(last_line) |
344 | - break |
345 | + result = SummaryResult(summary_file) |
346 | + subunit_server = subunit.TestProtocolServer( |
347 | + result, FlagFallStream(summary_file, 'Running tests.')) |
348 | + for line in input_stream: |
349 | + subunit_server.lineReceived(line) |
350 | + out_file.write(line) |
351 | + out_file.flush() |
352 | + if echo_to_stdout: |
353 | + sys.stdout.write(line) |
354 | + sys.stdout.flush() |
355 | |
356 | |
357 | class TestOnMergeRunner(BaseTestRunner): |
358 | @@ -190,31 +241,6 @@ |
359 | command = ['make', 'check', 'VERBOSITY=' + self.test_options] |
360 | return command |
361 | |
362 | - # Used to filter lines in the summary log. See |
363 | - # `BaseTestRunner.ignore_line()`. |
364 | - ignore_line = re.compile( |
365 | - r'( [\w\.\/\-]+( ?\([\w\.\/\-]+\))?|' |
366 | - r'\s*Running.*|' |
367 | - r'\d{4}\-\d{2}\-\d{2} \d{2}\:\d{2}\:\d{2} INFO.+|' |
368 | - r'\s*Set up .+|' |
369 | - r'\s*Tear down .*|' |
370 | - r' Ran \d+ tests with .+)$').match |
371 | - |
372 | - |
373 | -class JSCheckTestRunner(BaseTestRunner): |
374 | - """Executes the Launchpad JavaScript integration test suite.""" |
375 | - |
376 | - def build_test_command(self): |
377 | - """See BaseTestRunner.build_test_command().""" |
378 | - # We use the xvfb server's convenience script, xvfb-run, to |
379 | - # automagically set the display, start the command, shut down the |
380 | - # display, and return the exit code. (See the xvfb-run man page for |
381 | - # details.) |
382 | - return [ |
383 | - 'xvfb-run', |
384 | - '-s', '-screen 0 1024x768x24', |
385 | - 'make', 'jscheck'] |
386 | - |
387 | |
388 | class WebTestLogger: |
389 | """Logs test output to disk and a simple web page.""" |
390 | @@ -428,9 +454,6 @@ |
391 | '--public-branch-revno', dest='public_branch_revno', |
392 | type="int", default=None, |
393 | help=('The revision number of the public branch being tested.')) |
394 | - parser.add_option( |
395 | - '--jscheck', dest='jscheck', default=False, action='store_true', |
396 | - help=('Run the JavaScript integration test suite.')) |
397 | |
398 | options, args = parser.parse_args() |
399 | |
400 | @@ -442,19 +465,12 @@ |
401 | else: |
402 | pqm_message = None |
403 | |
404 | - if options.jscheck: |
405 | - runner_type = JSCheckTestRunner |
406 | - else: |
407 | - # Use the default testrunner. |
408 | - runner_type = TestOnMergeRunner |
409 | - |
410 | - runner = runner_type( |
411 | + runner = TestOnMergeRunner( |
412 | options.email, |
413 | pqm_message, |
414 | options.public_branch, |
415 | options.public_branch_revno, |
416 | - ' '.join(args) |
417 | - ) |
418 | + ' '.join(args)) |
419 | |
420 | try: |
421 | try: |
422 | |
423 | === modified file 'lib/devscripts/ec2test/testrunner.py' |
424 | --- lib/devscripts/ec2test/testrunner.py 2010-03-31 20:52:55 +0000 |
425 | +++ lib/devscripts/ec2test/testrunner.py 2010-04-14 13:31:40 +0000 |
426 | @@ -109,7 +109,7 @@ |
427 | message = image = None |
428 | _running = False |
429 | |
430 | - def __init__(self, branch, email=False, file=None, test_options='-vv', |
431 | + def __init__(self, branch, email=False, file=None, test_options=None, |
432 | headless=False, branches=(), |
433 | pqm_message=None, pqm_public_location=None, |
434 | pqm_submit_location=None, |
435 | @@ -483,7 +483,7 @@ |
436 | cmd.append('--public-branch-revno=%d' % branch_revno) |
437 | |
438 | # Add any additional options for ec2test-remote.py |
439 | - cmd.extend(self.get_remote_test_options()) |
440 | + cmd.extend(['--', self.test_options]) |
441 | self.log( |
442 | 'Running tests... (output is available on ' |
443 | 'http://%s/)\n' % self._instance.hostname) |
444 | @@ -519,17 +519,3 @@ |
445 | 'Writing abridged test results to %s.\n' % self.file) |
446 | user_connection.sftp.get('/var/www/summary.log', self.file) |
447 | user_connection.close() |
448 | - |
449 | - def get_remote_test_options(self): |
450 | - """Return the test command that will be passed to ec2test-remote.py. |
451 | - |
452 | - Returns a tuple of command-line options and switches. |
453 | - """ |
454 | - if '--jscheck' in self.test_options: |
455 | - # We want to run the JavaScript test suite. |
456 | - return ('--jscheck',) |
457 | - else: |
458 | - # Run the normal testsuite with our Zope testrunner options. |
459 | - # ec2test-remote.py wants the extra options to be after a double- |
460 | - # dash. |
461 | - return ('--', self.test_options) |
= 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.