ghc-dup: d24c5e8a11204e433b3ab0e114689da476fec5ae
1: #
2: # (c) Simon Marlow 2002
3: #
4:
5: # This allows us to use the "with X:" syntax with python 2.5:
6: from __future__ import with_statement
7:
8: import sys
9: import os
10: import errno
11: import string
12: import re
13: import traceback
14: import copy
15: import glob
16: import types
17:
18: have_subprocess = False
19: try:
20: import subprocess
21: have_subprocess = True
22: except:
23: print "Warning: subprocess not found, will fall back to spawnv"
24:
25: from string import join
26: from testglobals import *
27: from testutil import *
28:
29: if config.use_threads:
30: import threading
31: import thread
32:
33: # Options valid for all the tests in the current "directory". After
34: # each test, we reset the options to these. To change the options for
35: # multiple tests, the function setTestOpts() below can be used to alter
36: # these options.
37: global thisdir_testopts
38: thisdir_testopts = TestOptions()
39:
40: def getThisDirTestOpts():
41: return thisdir_testopts
42:
43: # Options valid for the current test only (these get reset to
44: # testdir_testopts after each test).
45:
46: global testopts_local
47: if config.use_threads:
48: testopts_local = threading.local()
49: else:
50: class TestOpts_Local:
51: pass
52: testopts_local = TestOpts_Local()
53:
54: def getTestOpts():
55: return testopts_local.x
56:
57: def setLocalTestOpts(opts):
58: global testopts_local
59: testopts_local.x=opts
60:
61: # This can be called at the top of a file of tests, to set default test options
62: # for the following tests.
63: def setTestOpts( f ):
64: f( thisdir_testopts );
65:
66: # -----------------------------------------------------------------------------
67: # Canned setup functions for common cases. eg. for a test you might say
68: #
69: # test('test001', normal, compile, [''])
70: #
71: # to run it without any options, but change it to
72: #
73: # test('test001', expect_fail, compile, [''])
74: #
75: # to expect failure for this test.
76:
77: def normal( opts ):
78: return;
79:
80: def skip( opts ):
81: opts.skip = 1
82:
83: def expect_fail( opts ):
84: opts.expect = 'fail';
85:
86: def reqlib( lib ):
87: return lambda opts, l=lib: _reqlib (opts, l )
88:
89: # Cache the results of looking to see if we have a library or not.
90: # This makes quite a difference, especially on Windows.
91: have_lib = {}
92:
93: def _reqlib( opts, lib ):
94: if have_lib.has_key(lib):
95: got_it = have_lib[lib]
96: else:
97: if have_subprocess:
98: # By preference we use subprocess, as the alternative uses
99: # /dev/null which mingw doesn't have.
100: p = subprocess.Popen([config.ghc_pkg, '--no-user-package-db', 'describe', lib],
101: stdout=subprocess.PIPE,
102: stderr=subprocess.PIPE)
103: # read from stdout and stderr to avoid blocking due to
104: # buffers filling
105: p.communicate()
106: r = p.wait()
107: else:
108: r = os.system(config.ghc_pkg + ' describe ' + lib
109: + ' > /dev/null 2> /dev/null')
110: got_it = r == 0
111: have_lib[lib] = got_it
112:
113: if not got_it:
114: opts.expect = 'missing-lib'
115:
116: def req_profiling( opts ):
117: if not config.have_profiling:
118: opts.expect = 'fail'
119:
120: def req_shared_libs( opts ):
121: if not config.have_shared_libs:
122: opts.expect = 'fail'
123:
124: def req_interp( opts ):
125: if not config.have_interp:
126: opts.expect = 'fail'
127:
128: def req_smp( opts ):
129: if not config.have_smp:
130: opts.expect = 'fail'
131:
132: def expect_broken( bug ):
133: return lambda opts, b=bug: _expect_broken (opts, b )
134:
135: def _expect_broken( opts, bug ):
136: opts.expect = 'fail';
137:
138: def ignore_output( opts ):
139: opts.ignore_output = 1
140:
141: def no_stdin( opts ):
142: opts.no_stdin = 1
143:
144: def combined_output( opts ):
145: opts.combined_output = True
146:
147: # -----
148:
149: def expect_fail_for( ways ):
150: return lambda opts, w=ways: _expect_fail_for( opts, w )
151:
152: def _expect_fail_for( opts, ways ):
153: opts.expect_fail_for = ways
154:
155: def expect_broken_for( bug, ways ):
156: return lambda opts, b=bug, w=ways: _expect_broken_for( opts, b, w )
157:
158: def _expect_broken_for( opts, bug, ways ):
159: opts.expect_fail_for = ways
160:
161: # -----
162:
163: def omit_ways( ways ):
164: return lambda opts, w=ways: _omit_ways( opts, w )
165:
166: def _omit_ways( opts, ways ):
167: opts.omit_ways = ways
168:
169: # -----
170:
171: def only_ways( ways ):
172: return lambda opts, w=ways: _only_ways( opts, w )
173:
174: def _only_ways( opts, ways ):
175: opts.only_ways = ways
176:
177: # -----
178:
179: def extra_ways( ways ):
180: return lambda opts, w=ways: _extra_ways( opts, w )
181:
182: def _extra_ways( opts, ways ):
183: opts.extra_ways = ways
184:
185: # -----
186:
187: def omit_compiler_types( compiler_types ):
188: return lambda opts, c=compiler_types: _omit_compiler_types(opts, c)
189:
190: def _omit_compiler_types( opts, compiler_types ):
191: if config.compiler_type in compiler_types:
192: opts.skip = 1
193:
194: # -----
195:
196: def only_compiler_types( compiler_types ):
197: return lambda opts, c=compiler_types: _only_compiler_types(opts, c)
198:
199: def _only_compiler_types( opts, compiler_types ):
200: if config.compiler_type not in compiler_types:
201: opts.skip = 1
202:
203: # -----
204:
205: def set_stdin( file ):
206: return lambda opts, f=file: _set_stdin(opts, f);
207:
208: def _set_stdin( opts, f ):
209: opts.stdin = f
210:
211: # -----
212:
213: def exit_code( val ):
214: return lambda opts, v=val: _exit_code(opts, v);
215:
216: def _exit_code( opts, v ):
217: opts.exit_code = v
218:
219: # -----
220:
221: def extra_run_opts( val ):
222: return lambda opts, v=val: _extra_run_opts(opts, v);
223:
224: def _extra_run_opts( opts, v ):
225: opts.extra_run_opts = v
226:
227: # -----
228:
229: def extra_hc_opts( val ):
230: return lambda opts, v=val: _extra_hc_opts(opts, v);
231:
232: def _extra_hc_opts( opts, v ):
233: opts.extra_hc_opts = v
234:
235: # -----
236:
237: def extra_clean( files ):
238: return lambda opts, v=files: _extra_clean(opts, v);
239:
240: def _extra_clean( opts, v ):
241: opts.clean_files = v
242:
243: # -----
244:
245: def stats_num_field( field, min, max ):
246: return lambda opts, f=field, x=min, y=max: _stats_num_field(opts, f, x, y);
247:
248: def _stats_num_field( opts, f, x, y ):
249: # copy the dictionary, as the config gets shared between all tests
250: opts.stats_num_fields = opts.stats_num_fields.copy()
251: opts.stats_num_fields[f] = (x, y)
252:
253: def compiler_stats_num_field( field, min, max ):
254: return lambda opts, f=field, x=min, y=max: _compiler_stats_num_field(opts, f, x, y);
255:
256: def _compiler_stats_num_field( opts, f, x, y ):
257: # copy the dictionary, as the config gets shared between all tests
258: opts.compiler_stats_num_fields = opts.compiler_stats_num_fields.copy()
259: opts.compiler_stats_num_fields[f] = (x, y)
260:
261: # -----
262:
263: def stats_range_field( field, min, max ):
264: return lambda opts, f=field, x=min, y=max: _stats_range_field(opts, f, x, y);
265:
266: def _stats_range_field( opts, f, x, y ):
267: # copy the dictionary, as the config gets shared between all tests
268: opts.stats_range_fields = opts.stats_range_fields.copy()
269: opts.stats_range_fields[f] = (x, y)
270:
271: def compiler_stats_range_field( field, min, max ):
272: return lambda opts, f=field, x=min, y=max: _compiler_stats_range_field(opts, f, x, y);
273:
274: def _compiler_stats_range_field( opts, f, x, y ):
275: # copy the dictionary, as the config gets shared between all tests
276: opts.compiler_stats_range_fields = opts.compiler_stats_range_fields.copy()
277: opts.compiler_stats_range_fields[f] = (x, y)
278:
279: # -----
280:
281: def skip_if_no_ghci(opts):
282: if not ('ghci' in config.run_ways):
283: opts.skip = 1
284:
285: # ----
286:
287: def skip_if_fast(opts):
288: if config.fast:
289: opts.skip = 1
290:
291: # -----
292:
293: def if_platform( plat, f ):
294: if config.platform == plat:
295: return f
296: else:
297: return normal
298:
299: def if_not_platform( plat, f ):
300: if config.platform != plat:
301: return f
302: else:
303: return normal
304:
305: def if_os( os, f ):
306: if config.os == os:
307: return f
308: else:
309: return normal
310:
311: def unless_os( os, f ):
312: if config.os == os:
313: return normal
314: else:
315: return f
316:
317: def if_arch( arch, f ):
318: if config.arch == arch:
319: return f
320: else:
321: return normal
322:
323: def if_wordsize( ws, f ):
324: if config.wordsize == str(ws):
325: return f
326: else:
327: return normal
328:
329: def if_msys( f ):
330: if config.msys:
331: return f
332: else:
333: return normal
334:
335: def if_cygwin( f ):
336: if config.cygwin:
337: return f
338: else:
339: return normal
340:
341: # ---
342:
343: def if_in_tree_compiler( f ):
344: if config.in_tree_compiler:
345: return f
346: else:
347: return normal
348:
349: def unless_in_tree_compiler( f ):
350: if config.in_tree_compiler:
351: return normal
352: else:
353: return f
354:
355: def if_compiler_type( compiler, f ):
356: if config.compiler_type == compiler:
357: return f
358: else:
359: return normal
360:
361: def if_compiler_profiled( f ):
362: if config.compiler_profiled:
363: return f
364: else:
365: return normal
366:
367: def unless_compiler_profiled( f ):
368: if config.compiler_profiled:
369: return normal
370: else:
371: return f
372:
373: def if_compiler_lt( compiler, version, f ):
374: if config.compiler_type == compiler and \
375: version_lt(config.compiler_version, version):
376: return f
377: else:
378: return normal
379:
380: def if_compiler_le( compiler, version, f ):
381: if config.compiler_type == compiler and \
382: version_le(config.compiler_version, version):
383: return f
384: else:
385: return normal
386:
387: def if_compiler_gt( compiler, version, f ):
388: if config.compiler_type == compiler and \
389: version_gt(config.compiler_version, version):
390: return f
391: else:
392: return normal
393:
394: def if_compiler_ge( compiler, version, f ):
395: if config.compiler_type == compiler and \
396: version_ge(config.compiler_version, version):
397: return f
398: else:
399: return normal
400:
401: def namebase( nb ):
402: return lambda opts, nb=nb: _namebase(opts, nb)
403:
404: def _namebase( opts, nb ):
405: opts.with_namebase = nb
406:
407: # ---
408:
409: def if_tag( tag, f ):
410: if tag in config.compiler_tags:
411: return f
412: else:
413: return normal
414:
415: def unless_tag( tag, f ):
416: if not (tag in config.compiler_tags):
417: return f
418: else:
419: return normal
420:
421: # ---
422: def alone(opts):
423: opts.alone = True
424:
425: # ---
426: def literate( opts ):
427: opts.literate = 1;
428:
429: def c_src( opts ):
430: opts.c_src = 1;
431:
432: def objc_src( opts ):
433: opts.objc_src = 1;
434:
435: def objcpp_src( opts ):
436: opts.objcpp_src = 1;
437:
438: # ----
439:
440: def pre_cmd( cmd ):
441: return lambda opts, c=cmd: _pre_cmd(opts, cmd)
442:
443: def _pre_cmd( opts, cmd ):
444: opts.pre_cmd = cmd
445:
446: # ----
447:
448: def clean_cmd( cmd ):
449: return lambda opts, c=cmd: _clean_cmd(opts, cmd)
450:
451: def _clean_cmd( opts, cmd ):
452: opts.clean_cmd = cmd
453:
454: # ----
455:
456: def cmd_prefix( prefix ):
457: return lambda opts, p=prefix: _cmd_prefix(opts, prefix)
458:
459: def _cmd_prefix( opts, prefix ):
460: opts.cmd_wrapper = lambda cmd, p=prefix: p + ' ' + cmd;
461:
462: # ----
463:
464: def cmd_wrapper( fun ):
465: return lambda opts, f=fun: _cmd_wrapper(opts, fun)
466:
467: def _cmd_wrapper( opts, fun ):
468: opts.cmd_wrapper = fun
469:
470: # ----
471:
472: def compile_cmd_prefix( prefix ):
473: return lambda opts, p=prefix: _compile_cmd_prefix(opts, prefix)
474:
475: def _compile_cmd_prefix( opts, prefix ):
476: opts.compile_cmd_prefix = prefix
477:
478: # ----
479:
480: def normalise_slashes( opts ):
481: opts.extra_normaliser = normalise_slashes_
482:
483: def normalise_exe( opts ):
484: opts.extra_normaliser = normalise_exe_
485:
486: def normalise_fun( fun ):
487: return lambda opts, f=fun: _normalise_fun(opts, f)
488:
489: def _normalise_fun( opts, f ):
490: opts.extra_normaliser = f
491:
492: def normalise_errmsg_fun( fun ):
493: return lambda opts, f=fun: _normalise_errmsg_fun(opts, f)
494:
495: def _normalise_errmsg_fun( opts, f ):
496: opts.extra_errmsg_normaliser = f
497:
498: def two_normalisers(f, g):
499: return lambda x, f=f, g=g: f(g(x))
500:
501: # ----
502: # Function for composing two opt-fns together
503:
504: def composes( fs ):
505: return reduce(lambda f, g: compose(f, g), fs)
506:
507: def compose( f, g ):
508: return lambda opts, f=f, g=g: _compose(opts,f,g)
509:
510: def _compose( opts, f, g ):
511: f(opts)
512: g(opts)
513:
514: # -----------------------------------------------------------------------------
515: # The current directory of tests
516:
517: def newTestDir( dir ):
518: global thisdir_testopts
519: # reset the options for this test directory
520: thisdir_testopts = copy.copy(default_testopts)
521: thisdir_testopts.testdir = dir
522: thisdir_testopts.compiler_always_flags = config.compiler_always_flags
523:
524: # -----------------------------------------------------------------------------
525: # Actually doing tests
526:
527: allTests = []
528: allTestNames = set([])
529:
530: def runTest (opts, name, setup, func, args):
531: n = 1
532:
533: if type(setup) is types.ListType:
534: setup = composes(setup)
535:
536: setup(opts)
537:
538: if opts.alone:
539: n = config.threads
540:
541: ok = 0
542:
543: if config.use_threads:
544: t.thread_pool.acquire()
545: try:
546: while config.threads<(t.running_threads+n):
547: t.thread_pool.wait()
548: t.running_threads = t.running_threads+n
549: ok=1
550: t.thread_pool.release()
551: thread.start_new_thread(test_common_thread, (n, name, opts, func, args))
552: except:
553: if not ok:
554: t.thread_pool.release()
555: else:
556: test_common_work (name, opts, func, args)
557:
558: # name :: String
559: # setup :: TestOpts -> IO ()
560: def test (name, setup, func, args):
561: global allTests
562: global allTestNames
563: if name in allTestNames:
564: framework_fail(name, 'duplicate', 'There are multiple tests with this name')
565: myTestOpts = copy.copy(thisdir_testopts)
566: allTests += [lambda : runTest(myTestOpts, name, setup, func, args)]
567: allTestNames.add(name)
568:
569: if config.use_threads:
570: def test_common_thread(n, name, opts, func, args):
571: t.lock.acquire()
572: try:
573: test_common_work(name,opts,func,args)
574: finally:
575: t.lock.release()
576: t.thread_pool.acquire()
577: t.running_threads = t.running_threads - n
578: t.thread_pool.notify()
579: t.thread_pool.release()
580:
581: def get_package_cache_timestamp():
582: if config.package_conf_cache_file == '':
583: return 0.0
584: else:
585: try:
586: return os.stat(config.package_conf_cache_file).st_mtime
587: except:
588: return 0.0
589:
590:
591: def test_common_work (name, opts, func, args):
592: try:
593: t.total_tests = t.total_tests+1
594: setLocalTestOpts(opts)
595:
596: package_conf_cache_file_start_timestamp = get_package_cache_timestamp()
597:
598: # All the ways we might run this test
599: if func == compile or func == multimod_compile:
600: all_ways = config.compile_ways
601: elif func == compile_and_run or func == multimod_compile_and_run:
602: all_ways = config.run_ways
603: elif func == ghci_script:
604: if 'ghci' in config.run_ways:
605: all_ways = ['ghci']
606: else:
607: all_ways = []
608: else:
609: all_ways = ['normal']
610:
611: # A test itself can request extra ways by setting opts.extra_ways
612: all_ways = all_ways + filter(lambda way: way not in all_ways,
613: opts.extra_ways)
614:
615: t.total_test_cases = t.total_test_cases + len(all_ways)
616:
617: ok_way = lambda way: \
618: not getTestOpts().skip \
619: and (config.only == [] or name in config.only) \
620: and (getTestOpts().only_ways == None or way in getTestOpts().only_ways) \
621: and (config.cmdline_ways == [] or way in config.cmdline_ways) \
622: and way not in getTestOpts().omit_ways
623:
624: # Which ways we are asked to skip
625: do_ways = filter (ok_way,all_ways)
626:
627: # In fast mode, we skip all but one way
628: if config.fast and len(do_ways) > 0:
629: do_ways = [do_ways[0]]
630:
631: if not config.clean_only:
632: # Run the required tests...
633: for way in do_ways:
634: do_test (name, way, func, args)
635:
636: for way in all_ways:
637: if way not in do_ways:
638: skiptest (name,way)
639:
640: if getTestOpts().cleanup != '' and (config.clean_only or do_ways != []):
641: clean(map (lambda suff: name + suff,
642: ['', '.exe', '.exe.manifest', '.genscript',
643: '.stderr.normalised', '.stdout.normalised',
644: '.run.stderr', '.run.stdout',
645: '.run.stderr.normalised', '.run.stdout.normalised',
646: '.comp.stderr', '.comp.stdout',
647: '.comp.stderr.normalised', '.comp.stdout.normalised',
648: '.interp.stderr', '.interp.stdout',
649: '.interp.stderr.normalised', '.interp.stdout.normalised',
650: '.stats', '.comp.stats',
651: '.hi', '.o', '.prof', '.exe.prof', '.hc',
652: '_stub.h', '_stub.c', '_stub.o',
653: '.hp', '.exe.hp', '.ps', '.aux', '.hcr', '.eventlog']))
654:
655: if func == multi_compile or func == multi_compile_fail:
656: extra_mods = args[1]
657: clean(map (lambda (f,x): replace_suffix(f, 'o'), extra_mods))
658: clean(map (lambda (f,x): replace_suffix(f, 'hi'), extra_mods))
659:
660: clean(getTestOpts().clean_files)
661:
662: try:
663: cleanCmd = getTestOpts().clean_cmd
664: if cleanCmd != None:
665: result = runCmdFor(name, 'cd ' + getTestOpts().testdir + ' && ' + cleanCmd)
666: if result != 0:
667: framework_fail(name, 'cleaning', 'clean-command failed: ' + str(result))
668: except e:
669: framework_fail(name, 'cleaning', 'clean-command exception')
670:
671: package_conf_cache_file_end_timestamp = get_package_cache_timestamp();
672:
673: if package_conf_cache_file_start_timestamp != package_conf_cache_file_end_timestamp:
674: framework_fail(name, 'whole-test', 'Package cache timestamps do not match: ' + str(package_conf_cache_file_start_timestamp) + ' ' + str(package_conf_cache_file_end_timestamp))
675:
676: try:
677: for f in files_written[name]:
678: if os.path.exists(f):
679: try:
680: if not f in files_written_not_removed[name]:
681: files_written_not_removed[name].append(f)
682: except:
683: files_written_not_removed[name] = [f]
684: except:
685: pass
686: except Exception, e:
687: framework_fail(name, 'runTest', 'Unhandled exception: ' + str(e))
688:
689: def clean(strs):
690: for str in strs:
691: for name in glob.glob(in_testdir(str)):
692: clean_full_path(name)
693:
694: def clean_full_path(name):
695: try:
696: # Remove files...
697: os.remove(name)
698: except OSError, e1:
699: try:
700: # ... and empty directories
701: os.rmdir(name)
702: except OSError, e2:
703: # We don't want to fail here, but we do want to know
704: # what went wrong, so print out the exceptions.
705: # ENOENT isn't a problem, though, as we clean files
706: # that don't necessarily exist.
707: if e1.errno != errno.ENOENT:
708: print e1
709: if e2.errno != errno.ENOENT:
710: print e2
711:
712: def do_test(name, way, func, args):
713: full_name = name + '(' + way + ')'
714:
715: try:
716: print '=====>', full_name, t.total_tests, 'of', len(allTests), \
717: str([t.n_unexpected_passes, \
718: t.n_unexpected_failures, \
719: t.n_framework_failures])
720:
721: if config.use_threads:
722: t.lock.release()
723:
724: try:
725: preCmd = getTestOpts().pre_cmd
726: if preCmd != None:
727: result = runCmdFor(name, 'cd ' + getTestOpts().testdir + ' && ' + preCmd)
728: if result != 0:
729: framework_fail(name, way, 'pre-command failed: ' + str(result))
730: except e:
731: framework_fail(name, way, 'pre-command exception')
732:
733: try:
734: result = apply(func, [name,way] + args)
735: finally:
736: if config.use_threads:
737: t.lock.acquire()
738:
739: if getTestOpts().expect != 'pass' and \
740: getTestOpts().expect != 'fail' and \
741: getTestOpts().expect != 'missing-lib':
742: framework_fail(name, way, 'bad expected ' + getTestOpts().expect)
743:
744: try:
745: passFail = result['passFail']
746: except:
747: passFail = 'No passFail found'
748:
749: if passFail == 'pass':
750: if getTestOpts().expect == 'pass' \
751: and way not in getTestOpts().expect_fail_for:
752: t.n_expected_passes = t.n_expected_passes + 1
753: if name in t.expected_passes:
754: t.expected_passes[name].append(way)
755: else:
756: t.expected_passes[name] = [way]
757: else:
758: print '*** unexpected pass for', full_name
759: t.n_unexpected_passes = t.n_unexpected_passes + 1
760: addPassingTestInfo(t.unexpected_passes, getTestOpts().testdir, name, way)
761: elif passFail == 'fail':
762: if getTestOpts().expect == 'pass' \
763: and way not in getTestOpts().expect_fail_for:
764: print '*** unexpected failure for', full_name
765: t.n_unexpected_failures = t.n_unexpected_failures + 1
766: reason = result['reason']
767: addFailingTestInfo(t.unexpected_failures, getTestOpts().testdir, name, reason, way)
768: else:
769: if getTestOpts().expect == 'missing-lib':
770: t.n_missing_libs = t.n_missing_libs + 1
771: if name in t.missing_libs:
772: t.missing_libs[name].append(way)
773: else:
774: t.missing_libs[name] = [way]
775: else:
776: t.n_expected_failures = t.n_expected_failures + 1
777: if name in t.expected_failures:
778: t.expected_failures[name].append(way)
779: else:
780: t.expected_failures[name] = [way]
781: else:
782: framework_fail(name, way, 'bad result ' + passFail)
783: except:
784: framework_fail(name, way, 'do_test exception')
785: traceback.print_exc()
786:
787: def addPassingTestInfo (testInfos, directory, name, way):
788: directory = re.sub('^\\.[/\\\\]', '', directory)
789:
790: if not directory in testInfos:
791: testInfos[directory] = {}
792:
793: if not name in testInfos[directory]:
794: testInfos[directory][name] = []
795:
796: testInfos[directory][name].append(way)
797:
798: def addFailingTestInfo (testInfos, directory, name, reason, way):
799: directory = re.sub('^\\.[/\\\\]', '', directory)
800:
801: if not directory in testInfos:
802: testInfos[directory] = {}
803:
804: if not name in testInfos[directory]:
805: testInfos[directory][name] = {}
806:
807: if not reason in testInfos[directory][name]:
808: testInfos[directory][name][reason] = []
809:
810: testInfos[directory][name][reason].append(way)
811:
812: def skiptest (name, way):
813: # print 'Skipping test \"', name, '\"'
814: t.n_tests_skipped = t.n_tests_skipped + 1
815: if name in t.tests_skipped:
816: t.tests_skipped[name].append(way)
817: else:
818: t.tests_skipped[name] = [way]
819:
820: def framework_fail( name, way, reason ):
821: full_name = name + '(' + way + ')'
822: print '*** framework failure for', full_name, reason, ':'
823: t.n_framework_failures = t.n_framework_failures + 1
824: if name in t.framework_failures:
825: t.framework_failures[name].append(way)
826: else:
827: t.framework_failures[name] = [way]
828:
829: def badResult(result):
830: try:
831: if result['passFail'] == 'pass':
832: return False
833: return True
834: except:
835: return True
836:
837: def passed():
838: return {'passFail': 'pass'}
839:
840: def failBecause(reason):
841: return {'passFail': 'fail', 'reason': reason}
842:
843: # -----------------------------------------------------------------------------
844: # Generic command tests
845:
846: # A generic command test is expected to run and exit successfully.
847: #
848: # The expected exit code can be changed via exit_code() as normal, and
849: # the expected stdout/stderr are stored in <testname>.stdout and
850: # <testname>.stderr. The output of the command can be ignored
851: # altogether by using run_command_ignore_output instead of
852: # run_command.
853:
854: def run_command( name, way, cmd ):
855: return simple_run( name, '', cmd, '' )
856:
857: # -----------------------------------------------------------------------------
858: # GHCi tests
859:
860: def ghci_script( name, way, script ):
861: # filter out -fforce-recomp from compiler_always_flags, because we're
862: # actually testing the recompilation behaviour in the GHCi tests.
863: flags = filter(lambda f: f != '-fforce-recomp', getTestOpts().compiler_always_flags)
864: flags.append(getTestOpts().extra_hc_opts)
865:
866: # We pass HC and HC_OPTS as environment variables, so that the
867: # script can invoke the correct compiler by using ':! $HC $HC_OPTS'
868: cmd = "HC='" + config.compiler + "' " + \
869: "HC_OPTS='" + join(flags,' ') + "' " + \
870: "'" + config.compiler + "'" + \
871: ' --interactive -v0 -ignore-dot-ghci ' + \
872: join(flags,' ')
873:
874: getTestOpts().stdin = script
875: return simple_run( name, way, cmd, getTestOpts().extra_run_opts )
876:
877: # -----------------------------------------------------------------------------
878: # Compile-only tests
879:
880: def compile( name, way, extra_hc_opts ):
881: return do_compile( name, way, 0, '', [], extra_hc_opts )
882:
883: def compile_fail( name, way, extra_hc_opts ):
884: return do_compile( name, way, 1, '', [], extra_hc_opts )
885:
886: def multimod_compile( name, way, top_mod, extra_hc_opts ):
887: return do_compile( name, way, 0, top_mod, [], extra_hc_opts )
888:
889: def multimod_compile_fail( name, way, top_mod, extra_hc_opts ):
890: return do_compile( name, way, 1, top_mod, [], extra_hc_opts )
891:
892: def multi_compile( name, way, top_mod, extra_mods, extra_hc_opts ):
893: return do_compile( name, way, 0, top_mod, extra_mods, extra_hc_opts)
894:
895: def multi_compile_fail( name, way, top_mod, extra_mods, extra_hc_opts ):
896: return do_compile( name, way, 1, top_mod, extra_mods, extra_hc_opts)
897:
898: def do_compile( name, way, should_fail, top_mod, extra_mods, extra_hc_opts ):
899: # print 'Compile only, extra args = ', extra_hc_opts
900: pretest_cleanup(name)
901:
902: result = extras_build( way, extra_mods, extra_hc_opts )
903: if badResult(result):
904: return result
905: extra_hc_opts = result['hc_opts']
906:
907: force = 0
908: if extra_mods:
909: force = 1
910: result = simple_build( name, way, extra_hc_opts, should_fail, top_mod, 0, 1, force)
911:
912: if badResult(result):
913: return result
914:
915: # the actual stderr should always match the expected, regardless
916: # of whether we expected the compilation to fail or not (successful
917: # compilations may generate warnings).
918:
919: if getTestOpts().with_namebase == None:
920: namebase = name
921: else:
922: namebase = getTestOpts().with_namebase
923:
924: (platform_specific, expected_stderr_file) = platform_wordsize_qualify(namebase, 'stderr', way)
925: actual_stderr_file = qualify(name, 'comp.stderr')
926:
927: if not compare_outputs('stderr', \
928: two_normalisers(two_normalisers(getTestOpts().extra_errmsg_normaliser, normalise_errmsg), normalise_whitespace), \
929: expected_stderr_file, actual_stderr_file):
930: return failBecause('stderr mismatch')
931:
932: # no problems found, this test passed
933: return passed()
934:
935: # -----------------------------------------------------------------------------
936: # Compile-and-run tests
937:
938: def compile_and_run__( name, way, top_mod, extra_mods, extra_hc_opts ):
939: # print 'Compile and run, extra args = ', extra_hc_opts
940: pretest_cleanup(name)
941:
942: result = extras_build( way, extra_mods, extra_hc_opts )
943: if badResult(result):
944: return result
945: extra_hc_opts = result['hc_opts']
946:
947: if way == 'ghci': # interpreted...
948: return interpreter_run( name, way, extra_hc_opts, 0, top_mod )
949: elif way == 'extcore' or way == 'optextcore' :
950: return extcore_run( name, way, extra_hc_opts, 0, top_mod )
951: else: # compiled...
952: force = 0
953: if extra_mods:
954: force = 1
955:
956: result = simple_build( name, way, extra_hc_opts, 0, top_mod, 1, 1, force)
957: if badResult(result):
958: return result
959:
960: cmd = './' + name;
961:
962: # we don't check the compiler's stderr for a compile-and-run test
963: return simple_run( name, way, cmd, getTestOpts().extra_run_opts )
964:
965: def compile_and_run( name, way, extra_hc_opts ):
966: return compile_and_run__( name, way, '', [], extra_hc_opts)
967:
968: def multimod_compile_and_run( name, way, top_mod, extra_hc_opts ):
969: return compile_and_run__( name, way, top_mod, [], extra_hc_opts)
970:
971: def multi_compile_and_run( name, way, top_mod, extra_mods, extra_hc_opts ):
972: return compile_and_run__( name, way, top_mod, extra_mods, extra_hc_opts)
973:
974: def stats( name, way, stats_file ):
975: opts = getTestOpts()
976: return checkStats(stats_file, opts.stats_range_fields
977: , opts.stats_num_fields)
978:
979: # -----------------------------------------------------------------------------
980: # Check -t stats info
981:
982: def checkStats(stats_file, range_fields, num_fields):
983: result = passed()
984: if len(num_fields) + len(range_fields) > 0:
985: f = open(in_testdir(stats_file))
986: contents = f.read()
987: f.close()
988:
989: for (field, (expected, dev)) in range_fields.items():
990: m = re.search('\("' + field + '", "([0-9]+)"\)', contents)
991: if m == None:
992: print 'Failed to find field: ', field
993: result = failBecause('no such stats field')
994: val = int(m.group(1))
995:
996: min = expected * ((100 - float(dev))/100);
997: max = expected * ((100 + float(dev))/100);
998:
999: if val < min:
1000: print field, val, 'is more than ' + repr(dev) + '%'
1001: print 'less than the exepected value', expected
1002: print 'If this is because you have improved GHC, please'
1003: print 'update the test so that GHC doesn\'t regress again'
1004: result = failBecause('stat too good')
1005: if val > max:
1006: print field, val, 'is more than ' + repr(dev) + '% greater than the expected value,', expected, max
1007: result = failBecause('stat not good enough')
1008:
1009: # ToDo: remove all uses of this, and delete it
1010: for (field, (min, max)) in num_fields.items():
1011: m = re.search('\("' + field + '", "([0-9]+)"\)', contents)
1012: if m == None:
1013: print 'Failed to find field: ', field
1014: result = failBecause('no such stats field')
1015: val = int(m.group(1))
1016:
1017: if val < min:
1018: print field, val, 'is less than minimum allowed', min
1019: print 'If this is because you have improved GHC, please'
1020: print 'update the test so that GHC doesn\'t regress again'
1021: result = failBecause('stat too good')
1022: if val > max:
1023: print field, val, 'is more than maximum allowed', max
1024: result = failBecause('stat not good enough')
1025:
1026: return result
1027:
1028: # -----------------------------------------------------------------------------
1029: # Build a single-module program
1030:
1031: def extras_build( way, extra_mods, extra_hc_opts ):
1032: for modopts in extra_mods:
1033: mod, opts = modopts
1034: result = simple_build( mod, way, opts + ' ' + extra_hc_opts, 0, '', 0, 0, 0)
1035: if not (mod.endswith('.hs') or mod.endswith('.lhs')):
1036: extra_hc_opts += ' ' + replace_suffix(mod, 'o')
1037: if badResult(result):
1038: return result
1039:
1040: return {'passFail' : 'pass', 'hc_opts' : extra_hc_opts}
1041:
1042:
1043: def simple_build( name, way, extra_hc_opts, should_fail, top_mod, link, addsuf, noforce ):
1044: opts = getTestOpts()
1045: errname = add_suffix(name, 'comp.stderr')
1046: rm_no_fail( qualify(errname, '') )
1047:
1048: if top_mod != '':
1049: srcname = top_mod
1050: rm_no_fail( qualify(name, '') )
1051: base, suf = os.path.splitext(top_mod)
1052: rm_no_fail( qualify(base, '') )
1053: rm_no_fail( qualify(base, 'exe') )
1054: elif addsuf:
1055: srcname = add_hs_lhs_suffix(name)
1056: rm_no_fail( qualify(name, '') )
1057: else:
1058: srcname = name
1059: rm_no_fail( qualify(name, 'o') )
1060:
1061: rm_no_fail( qualify(replace_suffix(srcname, "o"), '') )
1062:
1063: to_do = ''
1064: if top_mod != '':
1065: to_do = '--make '
1066: if link:
1067: to_do = to_do + '-o ' + name
1068: elif link:
1069: to_do = '-o ' + name
1070: elif opts.compile_to_hc:
1071: to_do = '-C'
1072: else:
1073: to_do = '-c' # just compile
1074:
1075: stats_file = name + '.comp.stats'
1076: if len(opts.compiler_stats_num_fields) + len(opts.compiler_stats_range_fields) > 0:
1077: extra_hc_opts += ' +RTS -V0 -t' + stats_file + ' --machine-readable -RTS'
1078:
1079: # Required by GHC 7.3+, harmless for earlier versions:
1080: if (getTestOpts().c_src or
1081: getTestOpts().objc_src or
1082: getTestOpts().objcpp_src):
1083: extra_hc_opts += ' -no-hs-main '
1084:
1085: if getTestOpts().compile_cmd_prefix == '':
1086: cmd_prefix = ''
1087: else:
1088: cmd_prefix = getTestOpts().compile_cmd_prefix + ' '
1089:
1090: comp_flags = getTestOpts().compiler_always_flags
1091: if noforce:
1092: comp_flags = filter(lambda f: f != '-fforce-recomp', comp_flags)
1093:
1094: cmd = 'cd ' + getTestOpts().testdir + " && " + cmd_prefix + "'" \
1095: + config.compiler + "' " \
1096: + join(comp_flags,' ') + ' ' \
1097: + to_do + ' ' + srcname + ' ' \
1098: + join(config.way_flags[way],' ') + ' ' \
1099: + extra_hc_opts + ' ' \
1100: + opts.extra_hc_opts + ' ' \
1101: + '>' + errname + ' 2>&1'
1102:
1103: result = runCmdFor(name, cmd)
1104:
1105: if result != 0 and not should_fail:
1106: actual_stderr = qualify(name, 'comp.stderr')
1107: if_verbose(1,'Compile failed (status ' + `result` + ') errors were:')
1108: if_verbose_dump(1,actual_stderr)
1109:
1110: # ToDo: if the sub-shell was killed by ^C, then exit
1111:
1112: statsResult = checkStats(stats_file, opts.compiler_stats_range_fields
1113: , opts.compiler_stats_num_fields)
1114:
1115: if badResult(statsResult):
1116: return statsResult
1117:
1118: if should_fail:
1119: if result == 0:
1120: return failBecause('exit code 0')
1121: else:
1122: if result != 0:
1123: return failBecause('exit code non-0')
1124:
1125: return passed()
1126:
1127: # -----------------------------------------------------------------------------
1128: # Run a program and check its output
1129: #
1130: # If testname.stdin exists, route input from that, else
1131: # from /dev/null. Route output to testname.run.stdout and
1132: # testname.run.stderr. Returns the exit code of the run.
1133:
1134: def simple_run( name, way, prog, args ):
1135: opts = getTestOpts()
1136:
1137: # figure out what to use for stdin
1138: if opts.stdin != '':
1139: use_stdin = opts.stdin
1140: else:
1141: stdin_file = add_suffix(name, 'stdin')
1142: if os.path.exists(in_testdir(stdin_file)):
1143: use_stdin = stdin_file
1144: else:
1145: use_stdin = '/dev/null'
1146:
1147: run_stdout = add_suffix(name,'run.stdout')
1148: run_stderr = add_suffix(name,'run.stderr')
1149:
1150: rm_no_fail(qualify(name,'run.stdout'))
1151: rm_no_fail(qualify(name,'run.stderr'))
1152: rm_no_fail(qualify(name, 'hp'))
1153: rm_no_fail(qualify(name,'ps'))
1154: rm_no_fail(qualify(name, 'prof'))
1155:
1156: my_rts_flags = rts_flags(way)
1157:
1158: stats_file = name + '.stats'
1159: if len(opts.stats_range_fields) + len(opts.stats_num_fields) > 0:
1160: args += ' +RTS -V0 -t' + stats_file + ' --machine-readable -RTS'
1161:
1162: if opts.no_stdin:
1163: stdin_comes_from = ''
1164: else:
1165: stdin_comes_from = ' <' + use_stdin
1166:
1167: if opts.combined_output:
1168: redirection = ' >' + run_stdout \
1169: + ' 2>&1'
1170: else:
1171: redirection = ' >' + run_stdout \
1172: + ' 2>' + run_stderr
1173:
1174: cmd = prog + ' ' + args + ' ' \
1175: + my_rts_flags + ' ' \
1176: + stdin_comes_from \
1177: + redirection
1178:
1179: if getTestOpts().cmd_wrapper != None:
1180: cmd = getTestOpts().cmd_wrapper(cmd);
1181:
1182: cmd = 'cd ' + getTestOpts().testdir + ' && ' + cmd
1183:
1184: # run the command
1185: result = runCmdFor(name, cmd)
1186:
1187: exit_code = result >> 8
1188: signal = result & 0xff
1189:
1190: # check the exit code
1191: if exit_code != opts.exit_code:
1192: print 'Wrong exit code (expected', opts.exit_code, ', actual', exit_code, ')'
1193: dump_stdout(name)
1194: dump_stderr(name)
1195: return failBecause('bad exit code')
1196:
1197: check_hp = my_rts_flags.find("-h") != -1
1198: check_prof = my_rts_flags.find("-p") != -1
1199:
1200: if not opts.ignore_output:
1201: bad_stderr = not opts.combined_output and not check_stderr_ok(name, way)
1202: bad_stdout = not check_stdout_ok(name, way)
1203: if bad_stderr:
1204: return failBecause('bad stderr')
1205: if bad_stdout:
1206: return failBecause('bad stdout')
1207: # exit_code > 127 probably indicates a crash, so don't try to run hp2ps.
1208: if check_hp and (exit_code <= 127 or exit_code == 251) and not check_hp_ok(name):
1209: return failBecause('bad heap profile')
1210: if check_prof and not check_prof_ok(name, way):
1211: return failBecause('bad profile')
1212:
1213: return checkStats(stats_file, opts.stats_range_fields
1214: , opts.stats_num_fields)
1215:
1216: def rts_flags(way):
1217: if (way == ''):
1218: return ''
1219: else:
1220: args = config.way_rts_flags[way]
1221:
1222: if args == []:
1223: return ''
1224: else:
1225: return '+RTS ' + join(args,' ') + ' -RTS'
1226:
1227: # -----------------------------------------------------------------------------
1228: # Run a program in the interpreter and check its output
1229:
1230: def interpreter_run( name, way, extra_hc_opts, compile_only, top_mod ):
1231: outname = add_suffix(name, 'interp.stdout')
1232: errname = add_suffix(name, 'interp.stderr')
1233: rm_no_fail(outname)
1234: rm_no_fail(errname)
1235: rm_no_fail(name)
1236:
1237: if (top_mod == ''):
1238: srcname = add_hs_lhs_suffix(name)
1239: else:
1240: srcname = top_mod
1241:
1242: scriptname = add_suffix(name, 'genscript')
1243: qscriptname = in_testdir(scriptname)
1244: rm_no_fail(qscriptname)
1245:
1246: delimiter = '===== program output begins here\n'
1247:
1248: script = open(qscriptname, 'w')
1249: if not compile_only:
1250: # set the prog name and command-line args to match the compiled
1251: # environment.
1252: script.write(':set prog ' + name + '\n')
1253: script.write(':set args ' + getTestOpts().extra_run_opts + '\n')
1254: # Add marker lines to the stdout and stderr output files, so we
1255: # can separate GHCi's output from the program's.
1256: script.write(':! echo ' + delimiter)
1257: script.write(':! echo 1>&2 ' + delimiter)
1258: # Set stdout to be line-buffered to match the compiled environment.
1259: script.write('System.IO.hSetBuffering System.IO.stdout System.IO.LineBuffering\n')
1260: # wrapping in GHC.TopHandler.runIO ensures we get the same output
1261: # in the event of an exception as for the compiled program.
1262: script.write('GHC.TopHandler.runIOFastExit Main.main Prelude.>> Prelude.return ()\n')
1263: script.close()
1264:
1265: # figure out what to use for stdin
1266: if getTestOpts().stdin != '':
1267: stdin_file = in_testdir(getTestOpts().stdin)
1268: else:
1269: stdin_file = qualify(name, 'stdin')
1270:
1271: if os.path.exists(stdin_file):
1272: stdin = open(stdin_file, 'r')
1273: os.system('cat ' + stdin_file + ' >>' + qscriptname)
1274:
1275: script.close()
1276:
1277: cmd = "'" + config.compiler + "' " \
1278: + join(getTestOpts().compiler_always_flags,' ') + ' ' \
1279: + srcname + ' ' \
1280: + join(config.way_flags[way],' ') + ' ' \
1281: + extra_hc_opts + ' ' \
1282: + getTestOpts().extra_hc_opts + ' ' \
1283: + '<' + scriptname + ' 1>' + outname + ' 2>' + errname
1284:
1285: if getTestOpts().cmd_wrapper != None:
1286: cmd = getTestOpts().cmd_wrapper(cmd);
1287:
1288: cmd = 'cd ' + getTestOpts().testdir + " && " + cmd
1289:
1290: result = runCmdFor(name, cmd)
1291:
1292: exit_code = result >> 8
1293: signal = result & 0xff
1294:
1295: # split the stdout into compilation/program output
1296: split_file(in_testdir(outname), delimiter,
1297: qualify(name, 'comp.stdout'),
1298: qualify(name, 'run.stdout'))
1299: split_file(in_testdir(errname), delimiter,
1300: qualify(name, 'comp.stderr'),
1301: qualify(name, 'run.stderr'))
1302:
1303: # check the exit code
1304: if exit_code != getTestOpts().exit_code:
1305: print 'Wrong exit code (expected', getTestOpts().exit_code, ', actual', exit_code, ')'
1306: dump_stdout(name)
1307: dump_stderr(name)
1308: return failBecause('bad exit code')
1309:
1310: # ToDo: if the sub-shell was killed by ^C, then exit
1311:
1312: if getTestOpts().ignore_output or (check_stderr_ok(name, way) and
1313: check_stdout_ok(name, way )):
1314: return passed()
1315: else:
1316: return failBecause('bad stdout or stderr')
1317:
1318:
1319: def split_file(in_fn, delimiter, out1_fn, out2_fn):
1320: infile = open(in_fn)
1321: out1 = open(out1_fn, 'w')
1322: out2 = open(out2_fn, 'w')
1323:
1324: line = infile.readline()
1325: line = re.sub('\r', '', line) # ignore Windows EOL
1326: while (re.sub('^\s*','',line) != delimiter and line != ''):
1327: out1.write(line)
1328: line = infile.readline()
1329: line = re.sub('\r', '', line)
1330: out1.close()
1331:
1332: line = infile.readline()
1333: while (line != ''):
1334: out2.write(line)
1335: line = infile.readline()
1336: out2.close()
1337:
1338: # -----------------------------------------------------------------------------
1339: # Generate External Core for the given program, then compile the resulting Core
1340: # and compare its output to the expected output
1341:
1342: def extcore_run( name, way, extra_hc_opts, compile_only, top_mod ):
1343:
1344: depsfilename = qualify(name, 'deps')
1345: errname = add_suffix(name, 'comp.stderr')
1346: qerrname = qualify(errname,'')
1347:
1348: hcname = qualify(name, 'hc')
1349: oname = qualify(name, 'o')
1350:
1351: rm_no_fail( qerrname )
1352: rm_no_fail( qualify(name, '') )
1353:
1354: if (top_mod == ''):
1355: srcname = add_hs_lhs_suffix(name)
1356: else:
1357: srcname = top_mod
1358:
1359: qcorefilename = qualify(name, 'hcr')
1360: corefilename = add_suffix(name, 'hcr')
1361: rm_no_fail(qcorefilename)
1362:
1363: # Generate External Core
1364:
1365: if (top_mod == ''):
1366: to_do = ' ' + srcname + ' '
1367: else:
1368: to_do = ' --make ' + top_mod + ' '
1369:
1370: cmd = 'cd ' + getTestOpts().testdir + " && '" \
1371: + config.compiler + "' " \
1372: + join(getTestOpts().compiler_always_flags,' ') + ' ' \
1373: + join(config.way_flags[way],' ') + ' ' \
1374: + extra_hc_opts + ' ' \
1375: + getTestOpts().extra_hc_opts \
1376: + to_do \
1377: + '>' + errname + ' 2>&1'
1378: result = runCmdFor(name, cmd)
1379:
1380: exit_code = result >> 8
1381:
1382: if exit_code != 0:
1383: if_verbose(1,'Compiling to External Core failed (status ' + `result` + ') errors were:')
1384: if_verbose_dump(1,qerrname)
1385: return failBecause('ext core exit code non-0')
1386:
1387: # Compile the resulting files -- if there's more than one module, we need to read the output
1388: # of the previous compilation in order to find the dependencies
1389: if (top_mod == ''):
1390: to_compile = corefilename
1391: else:
1392: result = runCmdFor(name, 'grep Compiling ' + qerrname + ' | awk \'{print $4}\' > ' + depsfilename)
1393: deps = open(depsfilename).read()
1394: deplist = string.replace(deps, '\n',' ');
1395: deplist2 = string.replace(deplist,'.lhs,', '.hcr');
1396: to_compile = string.replace(deplist2,'.hs,', '.hcr');
1397:
1398: flags = join(filter(lambda f: f != '-fext-core',config.way_flags[way]),' ')
1399:
1400: cmd = 'cd ' + getTestOpts().testdir + " && '" \
1401: + config.compiler + "' " \
1402: + join(getTestOpts().compiler_always_flags,' ') + ' ' \
1403: + to_compile + ' ' \
1404: + extra_hc_opts + ' ' \
1405: + getTestOpts().extra_hc_opts + ' ' \
1406: + flags \
1407: + ' -fglasgow-exts -o ' + name \
1408: + '>' + errname + ' 2>&1'
1409:
1410: result = runCmdFor(name, cmd)
1411: exit_code = result >> 8
1412:
1413: if exit_code != 0:
1414: if_verbose(1,'Compiling External Core file(s) failed (status ' + `result` + ') errors were:')
1415: if_verbose_dump(1,qerrname)
1416: return failBecause('ext core exit code non-0')
1417:
1418: # Clean up
1419: rm_no_fail ( oname )
1420: rm_no_fail ( hcname )
1421: rm_no_fail ( qcorefilename )
1422: rm_no_fail ( depsfilename )
1423:
1424: return simple_run ( name, way, './'+name, getTestOpts().extra_run_opts )
1425:
1426: # -----------------------------------------------------------------------------
1427: # Utils
1428:
1429: def check_stdout_ok( name, way ):
1430: if getTestOpts().with_namebase == None:
1431: namebase = name
1432: else:
1433: namebase = getTestOpts().with_namebase
1434:
1435: actual_stdout_file = qualify(name, 'run.stdout')
1436: (platform_specific, expected_stdout_file) = platform_wordsize_qualify(namebase, 'stdout', way)
1437:
1438: def norm(str):
1439: if platform_specific:
1440: return str
1441: else:
1442: return normalise_output(str)
1443:
1444: return compare_outputs('stdout', \
1445: two_normalisers(norm, getTestOpts().extra_normaliser), \
1446: expected_stdout_file, actual_stdout_file)
1447:
1448: def dump_stdout( name ):
1449: print 'Stdout:'
1450: print read_no_crs(qualify(name, 'run.stdout'))
1451:
1452: def check_stderr_ok( name, way ):
1453: if getTestOpts().with_namebase == None:
1454: namebase = name
1455: else:
1456: namebase = getTestOpts().with_namebase
1457:
1458: actual_stderr_file = qualify(name, 'run.stderr')
1459: (platform_specific, expected_stderr_file) = platform_wordsize_qualify(namebase, 'stderr', way)
1460:
1461: def norm(str):
1462: if platform_specific:
1463: return str
1464: else:
1465: return normalise_errmsg(str)
1466:
1467: return compare_outputs('stderr', \
1468: two_normalisers(norm, getTestOpts().extra_normaliser), \
1469: expected_stderr_file, actual_stderr_file)
1470:
1471: def dump_stderr( name ):
1472: print "Stderr:"
1473: print read_no_crs(qualify(name, 'run.stderr'))
1474:
1475: def read_no_crs(file):
1476: str = ''
1477: try:
1478: h = open(file)
1479: str = h.read()
1480: h.close
1481: except:
1482: # On Windows, if the program fails very early, it seems the
1483: # files stdout/stderr are redirected to may not get created
1484: pass
1485: return re.sub('\r', '', str)
1486:
1487: def write_file(file, str):
1488: h = open(file, 'w')
1489: h.write(str)
1490: h.close
1491:
1492: def check_hp_ok(name):
1493:
1494: # do not qualify for hp2ps because we should be in the right directory
1495: hp2psCmd = "cd " + getTestOpts().testdir + " && '" + config.hp2ps + "' " + name
1496:
1497: hp2psResult = runCmdExitCode(hp2psCmd)
1498:
1499: actual_ps_file = qualify(name, 'ps')
1500:
1501: if(hp2psResult == 0):
1502: if (os.path.exists(actual_ps_file)):
1503: if gs_working:
1504: gsResult = runCmdExitCode(genGSCmd(actual_ps_file))
1505: if (gsResult == 0):
1506: return (True)
1507: else:
1508: print "hp2ps output for " + name + "is not valid PostScript"
1509: else: return (True) # assume postscript is valid without ghostscript
1510: else:
1511: print "hp2ps did not generate PostScript for " + name
1512: return (False)
1513: else:
1514: print "hp2ps error when processing heap profile for " + name
1515: return(False)
1516:
1517: def check_prof_ok(name, way):
1518:
1519: prof_file = qualify(name,'prof')
1520:
1521: if not os.path.exists(prof_file):
1522: print prof_file + " does not exist"
1523: return(False)
1524:
1525: if os.path.getsize(qualify(name,'prof')) == 0:
1526: print prof_file + " is empty"
1527: return(False)
1528:
1529: if getTestOpts().with_namebase == None:
1530: namebase = name
1531: else:
1532: namebase = getTestOpts().with_namebase
1533:
1534: (platform_specific, expected_prof_file) = \
1535: platform_wordsize_qualify(namebase, 'prof.sample', way)
1536:
1537: # sample prof file is not required
1538: if not os.path.exists(expected_prof_file):
1539: return True
1540: else:
1541: return compare_outputs('prof', \
1542: two_normalisers(normalise_whitespace,normalise_prof), \
1543: expected_prof_file, prof_file)
1544:
1545: # Compare expected output to actual output, and optionally accept the
1546: # new output. Returns true if output matched or was accepted, false
1547: # otherwise.
1548: def compare_outputs( kind, normaliser, expected_file, actual_file ):
1549: if os.path.exists(expected_file):
1550: expected_raw = read_no_crs(expected_file)
1551: # print "norm:", normaliser(expected_raw)
1552: expected_str = normaliser(expected_raw)
1553: expected_file_for_diff = expected_file
1554: else:
1555: expected_str = ''
1556: expected_file_for_diff = '/dev/null'
1557:
1558: actual_raw = read_no_crs(actual_file)
1559: actual_str = normaliser(actual_raw)
1560:
1561: if expected_str == actual_str:
1562: return 1
1563: else:
1564: print 'Actual ' + kind + ' output differs from expected:'
1565:
1566: if expected_file_for_diff == '/dev/null':
1567: expected_normalised_file = '/dev/null'
1568: else:
1569: expected_normalised_file = expected_file + ".normalised"
1570: write_file(expected_normalised_file, expected_str)
1571:
1572: actual_normalised_file = actual_file + ".normalised"
1573: write_file(actual_normalised_file, actual_str)
1574:
1575: # Ignore whitespace when diffing. We should only get to this
1576: # point if there are non-whitespace differences
1577: #
1578: # Note we are diffing the *actual* output, not the normalised
1579: # output. The normalised output may have whitespace squashed
1580: # (including newlines) so the diff would be hard to read.
1581: # This does mean that the diff might contain changes that
1582: # would be normalised away.
1583: r = os.system( 'diff -uw ' + expected_file_for_diff + \
1584: ' ' + actual_file )
1585:
1586: # If for some reason there were no non-whitespace differences,
1587: # then do a full diff
1588: if r == 0:
1589: r = os.system( 'diff -u ' + expected_file_for_diff + \
1590: ' ' + actual_file )
1591:
1592: if config.accept:
1593: print 'Accepting new output.'
1594: write_file(expected_file, actual_raw)
1595: return 1
1596: else:
1597: return 0
1598:
1599:
1600: def normalise_whitespace( str ):
1601: # Merge contiguous whitespace characters into a single space.
1602: str = re.sub('[ \t\n]+', ' ', str)
1603: return str
1604:
1605: def normalise_errmsg( str ):
1606: # If somefile ends in ".exe" or ".exe:", zap ".exe" (for Windows)
1607: # the colon is there because it appears in error messages; this
1608: # hacky solution is used in place of more sophisticated filename
1609: # mangling
1610: str = re.sub('([^\\s])\\.exe', '\\1', str)
1611: # normalise slashes, minimise Windows/Unix filename differences
1612: str = re.sub('\\\\', '/', str)
1613: # The inplace ghc's are called ghc-stage[123] to avoid filename
1614: # collisions, so we need to normalise that to just "ghc"
1615: str = re.sub('ghc-stage[123]', 'ghc', str)
1616: # We sometimes see the name of the integer-gmp package on stderr,
1617: # but this can change (either the implementation name or the
1618: # version number), so we canonicalise it here
1619: str = re.sub('integer-[a-z]+', 'integer-impl', str)
1620: return str
1621:
1622: # normalise a .prof file, so that we can reasonably compare it against
1623: # a sample. This doesn't compare any of the actual profiling data,
1624: # only the shape of the profile and the number of entries.
1625: def normalise_prof (str):
1626: # strip everything up to the line beginning "COST CENTRE"
1627: str = re.sub('^(.*\n)*COST CENTRE[^\n]*\n','',str)
1628:
1629: # strip results for CAFs, these tend to change unpredictably
1630: str = re.sub('[ \t]*(CAF|IDLE).*\n','',str)
1631:
1632: # XXX Ignore Main.main. Sometimes this appears under CAF, and
1633: # sometimes under MAIN.
1634: str = re.sub('[ \t]*main[ \t]+Main.*\n','',str)
1635:
1636: # We have somthing like this:
1637:
1638: # MAIN MAIN 101 0 0.0 0.0 100.0 100.0
1639: # k Main 204 1 0.0 0.0 0.0 0.0
1640: # foo Main 205 1 0.0 0.0 0.0 0.0
1641: # foo.bar Main 207 1 0.0 0.0 0.0 0.0
1642:
1643: # then we remove all the specific profiling data, leaving only the
1644: # cost centre name, module, and entries, to end up with this:
1645:
1646: # MAIN MAIN 0
1647: # k Main 1
1648: # foo Main 1
1649: # foo.bar Main 1
1650:
1651: str = re.sub('\n([ \t]*[^ \t]+)([ \t]+[^ \t]+)([ \t]+\\d+)([ \t]+\\d+)[ \t]+([\\d\\.]+)[ \t]+([\\d\\.]+)[ \t]+([\\d\\.]+)[ \t]+([\\d\\.]+)','\n\\1 \\2 \\4',str)
1652: return str
1653:
1654: def normalise_slashes_( str ):
1655: str = re.sub('\\\\', '/', str)
1656: return str
1657:
1658: def normalise_exe_( str ):
1659: str = re.sub('\.exe', '', str)
1660: return str
1661:
1662: def normalise_output( str ):
1663: # Remove a .exe extension (for Windows)
1664: # This can occur in error messages generated by the program.
1665: str = re.sub('([^\\s])\\.exe', '\\1', str)
1666: return str
1667:
1668: def if_verbose( n, str ):
1669: if config.verbose >= n:
1670: print str
1671:
1672: def if_verbose_dump( n, f ):
1673: if config.verbose >= n:
1674: try:
1675: print open(f).read()
1676: except:
1677: print ''
1678:
1679: def rawSystem(cmd_and_args):
1680: # We prefer subprocess.call to os.spawnv as the latter
1681: # seems to send its arguments through a shell or something
1682: # with the Windows (non-cygwin) python. An argument "a b c"
1683: # turns into three arguments ["a", "b", "c"].
1684:
1685: # However, subprocess is new in python 2.4, so fall back to
1686: # using spawnv if we don't have it
1687:
1688: if have_subprocess:
1689: return subprocess.call(cmd_and_args)
1690: else:
1691: return os.spawnv(os.P_WAIT, cmd_and_args[0], cmd_and_args)
1692:
1693: # cmd is a complex command in Bourne-shell syntax
1694: # e.g (cd . && 'c:/users/simonpj/darcs/HEAD/compiler/stage1/ghc-inplace' ...etc)
1695: # Hence it must ultimately be run by a Bourne shell
1696: #
1697: # Mostly it invokes the command wrapped in 'timeout' thus
1698: # timeout 300 'cd . && ...blah blah'
1699: # so it's timeout's job to invoke the Bourne shell
1700: #
1701: # But watch out for the case when there is no timeout program!
1702: # Then, when using the native Python, os.system will invoke the cmd shell
1703:
1704: def runCmd( cmd ):
1705: if_verbose( 1, cmd )
1706: r = 0
1707: if config.os == 'mingw32':
1708: # On MinGW, we will always have timeout
1709: assert config.timeout_prog!=''
1710:
1711: if config.timeout_prog != '':
1712: r = rawSystem([config.timeout_prog, str(config.timeout), cmd])
1713: else:
1714: r = os.system(cmd)
1715: return r << 8
1716:
1717: def runCmdFor( name, cmd ):
1718: if_verbose( 1, cmd )
1719: r = 0
1720: if config.os == 'mingw32':
1721: # On MinGW, we will always have timeout
1722: assert config.timeout_prog!=''
1723:
1724: if config.timeout_prog != '':
1725: if config.check_files_written:
1726: fn = name + ".strace"
1727: r = rawSystem(["strace", "-o", fn, "-fF", "-e", "creat,open,chdir,clone,vfork",
1728: config.timeout_prog, str(config.timeout),
1729: cmd])
1730: addTestFilesWritten(name, fn)
1731: rm_no_fail(fn)
1732: else:
1733: r = rawSystem([config.timeout_prog, str(config.timeout), cmd])
1734: else:
1735: r = os.system(cmd)
1736: return r << 8
1737:
1738: def runCmdExitCode( cmd ):
1739: return (runCmd(cmd) >> 8);
1740:
1741:
1742: # -----------------------------------------------------------------------------
1743: # checking for files being written to by multiple tests
1744:
1745: re_strace_call_end = '(\) += ([0-9]+|-1 E.*)| <unfinished ...>)$'
1746: re_strace_unavailable = re.compile('^\) += \? <unavailable>$')
1747: re_strace_pid = re.compile('^([0-9]+) +(.*)')
1748: re_strace_clone = re.compile('^(clone\(|<... clone resumed> ).*\) = ([0-9]+)$')
1749: re_strace_clone_unfinished = re.compile('^clone\( <unfinished \.\.\.>$')
1750: re_strace_vfork = re.compile('^(vfork\(\)|<\.\.\. vfork resumed> \)) += ([0-9]+)$')
1751: re_strace_vfork_unfinished = re.compile('^vfork\( <unfinished \.\.\.>$')
1752: re_strace_chdir = re.compile('^chdir\("([^"]*)"(\) += 0| <unfinished ...>)$')
1753: re_strace_chdir_resumed = re.compile('^<\.\.\. chdir resumed> \) += 0$')
1754: re_strace_open = re.compile('^open\("([^"]*)", ([A-Z_|]*)(, [0-9]+)?' + re_strace_call_end)
1755: re_strace_open_resumed = re.compile('^<... open resumed> ' + re_strace_call_end)
1756: re_strace_ignore_sigchild = re.compile('^--- SIGCHLD \(Child exited\) @ 0 \(0\) ---$')
1757: re_strace_ignore_sigvtalarm = re.compile('^--- SIGVTALRM \(Virtual timer expired\) @ 0 \(0\) ---$')
1758: re_strace_ignore_sigint = re.compile('^--- SIGINT \(Interrupt\) @ 0 \(0\) ---$')
1759: re_strace_ignore_sigfpe = re.compile('^--- SIGFPE \(Floating point exception\) @ 0 \(0\) ---$')
1760: re_strace_ignore_sigsegv = re.compile('^--- SIGSEGV \(Segmentation fault\) @ 0 \(0\) ---$')
1761: re_strace_ignore_sigpipe = re.compile('^--- SIGPIPE \(Broken pipe\) @ 0 \(0\) ---$')
1762:
1763: # Files that are read or written but shouldn't be:
1764: # * ghci_history shouldn't be read or written by tests
1765: # * things under package.conf.d shouldn't be written by tests
1766: bad_file_usages = {}
1767:
1768: # Mapping from tests to the list of files that they write
1769: files_written = {}
1770:
1771: # Mapping from tests to the list of files that they write but don't clean
1772: files_written_not_removed = {}
1773:
1774: def add_bad_file_usage(name, file):
1775: try:
1776: if not file in bad_file_usages[name]:
1777: bad_file_usages[name].append(file)
1778: except:
1779: bad_file_usages[name] = [file]
1780:
1781: def mkPath(curdir, path):
1782: # Given the current full directory is 'curdir', what is the full
1783: # path to 'path'?
1784: return os.path.realpath(os.path.join(curdir, path))
1785:
1786: def addTestFilesWritten(name, fn):
1787: if config.use_threads:
1788: with t.lockFilesWritten:
1789: addTestFilesWrittenHelper(name, fn)
1790: else:
1791: addTestFilesWrittenHelper(name, fn)
1792:
1793: def addTestFilesWrittenHelper(name, fn):
1794: started = False
1795: working_directories = {}
1796:
1797: with open(fn, 'r') as f:
1798: for line in f:
1799: m_pid = re_strace_pid.match(line)
1800: if m_pid:
1801: pid = m_pid.group(1)
1802: content = m_pid.group(2)
1803: elif re_strace_unavailable.match(line):
1804: next
1805: else:
1806: framework_fail(name, 'strace', "Can't find pid in strace line: " + line)
1807:
1808: m_open = re_strace_open.match(content)
1809: m_chdir = re_strace_chdir.match(content)
1810: m_clone = re_strace_clone.match(content)
1811: m_vfork = re_strace_vfork.match(content)
1812:
1813: if not started:
1814: working_directories[pid] = os.getcwd()
1815: started = True
1816:
1817: if m_open:
1818: file = m_open.group(1)
1819: file = mkPath(working_directories[pid], file)
1820: if file.endswith("ghci_history"):
1821: add_bad_file_usage(name, file)
1822: elif not file in ['/dev/tty', '/dev/null'] and not file.startswith("/tmp/ghc"):
1823: flags = m_open.group(2).split('|')
1824: if 'O_WRONLY' in flags or 'O_RDWR' in flags:
1825: if re.match('package\.conf\.d', file):
1826: add_bad_file_usage(name, file)
1827: else:
1828: try:
1829: if not file in files_written[name]:
1830: files_written[name].append(file)
1831: except:
1832: files_written[name] = [file]
1833: elif 'O_RDONLY' in flags:
1834: pass
1835: else:
1836: framework_fail(name, 'strace', "Can't understand flags in open strace line: " + line)
1837: elif m_chdir:
1838: # We optimistically assume that unfinished chdir's are going to succeed
1839: dir = m_chdir.group(1)
1840: working_directories[pid] = mkPath(working_directories[pid], dir)
1841: elif m_clone:
1842: working_directories[m_clone.group(2)] = working_directories[pid]
1843: elif m_vfork:
1844: working_directories[m_vfork.group(2)] = working_directories[pid]
1845: elif re_strace_open_resumed.match(content):
1846: pass
1847: elif re_strace_chdir_resumed.match(content):
1848: pass
1849: elif re_strace_vfork_unfinished.match(content):
1850: pass
1851: elif re_strace_clone_unfinished.match(content):
1852: pass
1853: elif re_strace_ignore_sigchild.match(content):
1854: pass
1855: elif re_strace_ignore_sigvtalarm.match(content):
1856: pass
1857: elif re_strace_ignore_sigint.match(content):
1858: pass
1859: elif re_strace_ignore_sigfpe.match(content):
1860: pass
1861: elif re_strace_ignore_sigsegv.match(content):
1862: pass
1863: elif re_strace_ignore_sigpipe.match(content):
1864: pass
1865: else:
1866: framework_fail(name, 'strace', "Can't understand strace line: " + line)
1867:
1868: def checkForFilesWrittenProblems(file):
1869: foundProblem = False
1870:
1871: files_written_inverted = {}
1872: for t in files_written.keys():
1873: for f in files_written[t]:
1874: try:
1875: files_written_inverted[f].append(t)
1876: except:
1877: files_written_inverted[f] = [t]
1878:
1879: for f in files_written_inverted.keys():
1880: if len(files_written_inverted[f]) > 1:
1881: if not foundProblem:
1882: foundProblem = True
1883: file.write("\n")
1884: file.write("\nSome files are written by multiple tests:\n")
1885: file.write(" " + f + " (" + str(files_written_inverted[f]) + ")\n")
1886: if foundProblem:
1887: file.write("\n")
1888:
1889: # -----
1890:
1891: if len(files_written_not_removed) > 0:
1892: file.write("\n")
1893: file.write("\nSome files written but not removed:\n")
1894: tests = files_written_not_removed.keys()
1895: tests.sort()
1896: for t in tests:
1897: for f in files_written_not_removed[t]:
1898: file.write(" " + t + ": " + f + "\n")
1899: file.write("\n")
1900:
1901: # -----
1902:
1903: if len(bad_file_usages) > 0:
1904: file.write("\n")
1905: file.write("\nSome bad file usages:\n")
1906: tests = bad_file_usages.keys()
1907: tests.sort()
1908: for t in tests:
1909: for f in bad_file_usages[t]:
1910: file.write(" " + t + ": " + f + "\n")
1911: file.write("\n")
1912:
1913: # -----------------------------------------------------------------------------
1914: # checking if ghostscript is available for checking the output of hp2ps
1915:
1916: def genGSCmd(psfile):
1917: return (config.gs + ' -dNODISPLAY -dBATCH -dQUIET -dNOPAUSE ' + psfile);
1918:
1919: def gsNotWorking():
1920: global gs_working
1921: print "GhostScript not available for hp2ps tests"
1922:
1923: global gs_working
1924: gs_working = 0
1925: if config.have_profiling:
1926: if config.gs != '':
1927: resultGood = runCmdExitCode(genGSCmd(config.confdir + '/good.ps'));
1928: if resultGood == 0:
1929: resultBad = runCmdExitCode(genGSCmd(config.confdir + '/bad.ps'));
1930: if resultBad != 0:
1931: print "GhostScript available for hp2ps tests"
1932: gs_working = 1;
1933: else:
1934: gsNotWorking();
1935: else:
1936: gsNotWorking();
1937: else:
1938: gsNotWorking();
1939:
1940: def rm_no_fail( file ):
1941: try:
1942: os.remove( file )
1943: finally:
1944: return
1945:
1946: def add_suffix( name, suffix ):
1947: if suffix == '':
1948: return name
1949: else:
1950: return name + '.' + suffix
1951:
1952: def add_hs_lhs_suffix(name):
1953: if getTestOpts().c_src:
1954: return add_suffix(name, 'c')
1955: elif getTestOpts().objc_src:
1956: return add_suffix(name, 'm')
1957: elif getTestOpts().objcpp_src:
1958: return add_suffix(name, 'mm')
1959: elif getTestOpts().literate:
1960: return add_suffix(name, 'lhs')
1961: else:
1962: return add_suffix(name, 'hs')
1963:
1964: def replace_suffix( name, suffix ):
1965: base, suf = os.path.splitext(name)
1966: return base + '.' + suffix
1967:
1968: def in_testdir( name ):
1969: return (getTestOpts().testdir + '/' + name)
1970:
1971: def qualify( name, suff ):
1972: return in_testdir(add_suffix(name, suff))
1973:
1974:
1975: # Finding the sample output. The filename is of the form
1976: #
1977: # <test>.stdout[-<compiler>][-<version>][-ws-<wordsize>][-<platform>]
1978: #
1979: # and we pick the most specific version available. The <version> is
1980: # the major version of the compiler (e.g. 6.8.2 would be "6.8"). For
1981: # more fine-grained control use if_compiler_lt().
1982: #
1983: def platform_wordsize_qualify( name, suff, way ):
1984:
1985: basepath = qualify(name, suff)
1986:
1987: paths = [(platformSpecific, basepath + comp + vers + ws + plat + w)
1988: for (platformSpecific, plat) in [(1, '-' + config.platform),
1989: (1, '-' + config.os),
1990: (0, '')]
1991: for ws in ['-ws-' + config.wordsize, '']
1992: for comp in ['-' + config.compiler_type, '']
1993: for vers in ['-' + config.compiler_maj_version, '']
1994: for w in ['-' + way, '']]
1995:
1996: dir = glob.glob(basepath + '*')
1997: dir = map (lambda d: normalise_slashes_(d), dir)
1998:
1999: for (platformSpecific, f) in paths:
2000: if f in dir:
2001: return (platformSpecific,f)
2002:
2003: return (0, basepath)
2004:
2005: # Clean up prior to the test, so that we can't spuriously conclude
2006: # that it passed on the basis of old run outputs.
2007: def pretest_cleanup(name):
2008: rm_no_fail(qualify(name,'comp.stderr'))
2009: rm_no_fail(qualify(name,'run.stderr'))
2010: rm_no_fail(qualify(name,'run.stdout'))
2011: rm_no_fail(qualify(name,'tix')) # remove the old tix file
2012: # simple_build zaps the following:
2013: # rm_nofail(qualify("o"))
2014: # rm_nofail(qualify(""))
2015: # not interested in the return code
2016:
2017: # -----------------------------------------------------------------------------
2018: # Return a list of all the files ending in '.T' below the directory dir.
2019:
2020: def findTFiles(roots):
2021: return concat(map(findTFiles_,roots))
2022:
2023: def findTFiles_(path):
2024: if os.path.isdir(path):
2025: paths = map(lambda x, p=path: p + '/' + x, os.listdir(path))
2026: return findTFiles(paths)
2027: elif path[-2:] == '.T':
2028: return [path]
2029: else:
2030: return []
2031:
2032: # -----------------------------------------------------------------------------
2033: # Output a test summary to the specified file object
2034:
2035: def summary(t, file):
2036:
2037: file.write('\n')
2038: file.write('OVERALL SUMMARY for test run started at ' \
2039: + t.start_time + '\n'\
2040: + string.rjust(`t.total_tests`, 8) \
2041: + ' total tests, which gave rise to\n' \
2042: + string.rjust(`t.total_test_cases`, 8) \
2043: + ' test cases, of which\n' \
2044: + string.rjust(`t.n_framework_failures`, 8) \
2045: + ' caused framework failures\n' \
2046: + string.rjust(`t.n_tests_skipped`, 8)
2047: + ' were skipped\n\n' \
2048: + string.rjust(`t.n_expected_passes`, 8)
2049: + ' expected passes\n' \
2050: + string.rjust(`t.n_missing_libs`, 8)
2051: + ' had missing libraries\n' \
2052: + string.rjust(`t.n_expected_failures`, 8) \
2053: + ' expected failures\n' \
2054: + string.rjust(`t.n_unexpected_passes`, 8) \
2055: + ' unexpected passes\n'
2056: + string.rjust(`t.n_unexpected_failures`, 8) \
2057: + ' unexpected failures\n'
2058: + '\n')
2059:
2060: if t.n_unexpected_passes > 0:
2061: file.write('Unexpected passes:\n')
2062: printPassingTestInfosSummary(file, t.unexpected_passes)
2063:
2064: if t.n_unexpected_failures > 0:
2065: file.write('Unexpected failures:\n')
2066: printFailingTestInfosSummary(file, t.unexpected_failures)
2067:
2068: if config.check_files_written:
2069: checkForFilesWrittenProblems(file)
2070:
2071: def printPassingTestInfosSummary(file, testInfos):
2072: directories = testInfos.keys()
2073: directories.sort()
2074: maxDirLen = max(map ((lambda x : len(x)), directories))
2075: for directory in directories:
2076: tests = testInfos[directory].keys()
2077: tests.sort()
2078: for test in tests:
2079: file.write(' ' + directory.ljust(maxDirLen + 2) + test + \
2080: ' (' + join(testInfos[directory][test],',') + ')\n')
2081: file.write('\n')
2082:
2083: def printFailingTestInfosSummary(file, testInfos):
2084: directories = testInfos.keys()
2085: directories.sort()
2086: maxDirLen = max(map ((lambda x : len(x)), directories))
2087: for directory in directories:
2088: tests = testInfos[directory].keys()
2089: tests.sort()
2090: for test in tests:
2091: reasons = testInfos[directory][test].keys()
2092: for reason in reasons:
2093: file.write(' ' + directory.ljust(maxDirLen + 2) + test + \
2094: ' [' + reason + ']' + \
2095: ' (' + join(testInfos[directory][test][reason],',') + ')\n')
2096: file.write('\n')
2097:
2098: def getStdout(cmd):
2099: if have_subprocess:
2100: p = subprocess.Popen(cmd,
2101: stdout=subprocess.PIPE,
2102: stderr=subprocess.PIPE)
2103: (stdout, stderr) = p.communicate()
2104: r = p.wait()
2105: if r != 0:
2106: raise Exception("Command failed: " + str(cmd))
2107: if stderr != '':
2108: raise Exception("stderr from command: " + str(cmd))
2109: return stdout
2110: else:
2111: raise Exception("Need subprocess to get stdout, but don't have it")
2112:
Generated by git2html.