~louis/ubuntu/trusty/clamav/lp799623_fix_logrotate

« back to all changes in this revision

Viewing changes to libclamav/c++/llvm/utils/lit/lit/TestRunner.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2010-03-12 11:30:04 UTC
  • mfrom: (0.41.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100312113004-b0fop4bkycszdd0z
Tags: 0.96~rc1+dfsg-0ubuntu1
* New upstream RC - FFE (LP: #537636):
  - Add OfficialDatabaseOnly option to clamav-base.postinst.in
  - Add LocalSocketGroup option to clamav-base.postinst.in
  - Add LocalSocketMode option to clamav-base.postinst.in
  - Add CrossFilesystems option to clamav-base.postinst.in
  - Add ClamukoScannerCount option to clamav-base.postinst.in
  - Add BytecodeSecurity opiton to clamav-base.postinst.in
  - Add DetectionStatsHostID option to clamav-freshclam.postinst.in
  - Add Bytecode option to clamav-freshclam.postinst.in
  - Add MilterSocketGroup option to clamav-milter.postinst.in
  - Add MilterSocketMode option to clamav-milter.postinst.in
  - Add ReportHostname option to clamav-milter.postinst.in
  - Bump libclamav SO version to 6.1.0 in libclamav6.install
  - Drop clamdmon from clamav.examples (no longer shipped by upstream)
  - Drop libclamav.a from libclamav-dev.install (not built by upstream)
  - Update SO version for lintian override for libclamav6
  - Add new Bytecode Testing Tool, usr/bin/clambc, to clamav.install
  - Add build-depends on python and python-setuptools for new test suite
  - Update debian/copyright for the embedded copy of llvm (using the system
    llvm is not currently feasible)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import os, signal, subprocess, sys
 
2
import StringIO
 
3
 
 
4
import ShUtil
 
5
import Test
 
6
import Util
 
7
 
 
8
import platform
 
9
import tempfile
 
10
 
 
11
class InternalShellError(Exception):
 
12
    def __init__(self, command, message):
 
13
        self.command = command
 
14
        self.message = message
 
15
 
 
16
# Don't use close_fds on Windows.
 
17
kUseCloseFDs = platform.system() != 'Windows'
 
18
 
 
19
# Use temporary files to replace /dev/null on Windows.
 
20
kAvoidDevNull = platform.system() == 'Windows'
 
21
 
 
22
def executeCommand(command, cwd=None, env=None):
 
23
    p = subprocess.Popen(command, cwd=cwd,
 
24
                         stdin=subprocess.PIPE,
 
25
                         stdout=subprocess.PIPE,
 
26
                         stderr=subprocess.PIPE,
 
27
                         env=env)
 
28
    out,err = p.communicate()
 
29
    exitCode = p.wait()
 
30
 
 
31
    # Detect Ctrl-C in subprocess.
 
32
    if exitCode == -signal.SIGINT:
 
33
        raise KeyboardInterrupt
 
34
 
 
35
    return out, err, exitCode
 
36
 
 
37
def executeShCmd(cmd, cfg, cwd, results):
 
38
    if isinstance(cmd, ShUtil.Seq):
 
39
        if cmd.op == ';':
 
40
            res = executeShCmd(cmd.lhs, cfg, cwd, results)
 
41
            return executeShCmd(cmd.rhs, cfg, cwd, results)
 
42
 
 
43
        if cmd.op == '&':
 
44
            raise NotImplementedError,"unsupported test command: '&'"
 
45
 
 
46
        if cmd.op == '||':
 
47
            res = executeShCmd(cmd.lhs, cfg, cwd, results)
 
48
            if res != 0:
 
49
                res = executeShCmd(cmd.rhs, cfg, cwd, results)
 
50
            return res
 
51
        if cmd.op == '&&':
 
52
            res = executeShCmd(cmd.lhs, cfg, cwd, results)
 
53
            if res is None:
 
54
                return res
 
55
 
 
56
            if res == 0:
 
57
                res = executeShCmd(cmd.rhs, cfg, cwd, results)
 
58
            return res
 
59
 
 
60
        raise ValueError,'Unknown shell command: %r' % cmd.op
 
61
 
 
62
    assert isinstance(cmd, ShUtil.Pipeline)
 
63
    procs = []
 
64
    input = subprocess.PIPE
 
65
    stderrTempFiles = []
 
66
    # To avoid deadlock, we use a single stderr stream for piped
 
67
    # output. This is null until we have seen some output using
 
68
    # stderr.
 
69
    for i,j in enumerate(cmd.commands):
 
70
        # Apply the redirections, we use (N,) as a sentinal to indicate stdin,
 
71
        # stdout, stderr for N equal to 0, 1, or 2 respectively. Redirects to or
 
72
        # from a file are represented with a list [file, mode, file-object]
 
73
        # where file-object is initially None.
 
74
        redirects = [(0,), (1,), (2,)]
 
75
        for r in j.redirects:
 
76
            if r[0] == ('>',2):
 
77
                redirects[2] = [r[1], 'w', None]
 
78
            elif r[0] == ('>>',2):
 
79
                redirects[2] = [r[1], 'a', None]
 
80
            elif r[0] == ('>&',2) and r[1] in '012':
 
81
                redirects[2] = redirects[int(r[1])]
 
82
            elif r[0] == ('>&',) or r[0] == ('&>',):
 
83
                redirects[1] = redirects[2] = [r[1], 'w', None]
 
84
            elif r[0] == ('>',):
 
85
                redirects[1] = [r[1], 'w', None]
 
86
            elif r[0] == ('>>',):
 
87
                redirects[1] = [r[1], 'a', None]
 
88
            elif r[0] == ('<',):
 
89
                redirects[0] = [r[1], 'r', None]
 
90
            else:
 
91
                raise NotImplementedError,"Unsupported redirect: %r" % (r,)
 
92
 
 
93
        # Map from the final redirections to something subprocess can handle.
 
94
        final_redirects = []
 
95
        for index,r in enumerate(redirects):
 
96
            if r == (0,):
 
97
                result = input
 
98
            elif r == (1,):
 
99
                if index == 0:
 
100
                    raise NotImplementedError,"Unsupported redirect for stdin"
 
101
                elif index == 1:
 
102
                    result = subprocess.PIPE
 
103
                else:
 
104
                    result = subprocess.STDOUT
 
105
            elif r == (2,):
 
106
                if index != 2:
 
107
                    raise NotImplementedError,"Unsupported redirect on stdout"
 
108
                result = subprocess.PIPE
 
109
            else:
 
110
                if r[2] is None:
 
111
                    if kAvoidDevNull and r[0] == '/dev/null':
 
112
                        r[2] = tempfile.TemporaryFile(mode=r[1])
 
113
                    else:
 
114
                        r[2] = open(r[0], r[1])
 
115
                    # Workaround a Win32 and/or subprocess bug when appending.
 
116
                    if r[1] == 'a':
 
117
                        r[2].seek(0, 2)
 
118
                result = r[2]
 
119
            final_redirects.append(result)
 
120
 
 
121
        stdin, stdout, stderr = final_redirects
 
122
 
 
123
        # If stderr wants to come from stdout, but stdout isn't a pipe, then put
 
124
        # stderr on a pipe and treat it as stdout.
 
125
        if (stderr == subprocess.STDOUT and stdout != subprocess.PIPE):
 
126
            stderr = subprocess.PIPE
 
127
            stderrIsStdout = True
 
128
        else:
 
129
            stderrIsStdout = False
 
130
 
 
131
            # Don't allow stderr on a PIPE except for the last
 
132
            # process, this could deadlock.
 
133
            #
 
134
            # FIXME: This is slow, but so is deadlock.
 
135
            if stderr == subprocess.PIPE and j != cmd.commands[-1]:
 
136
                stderr = tempfile.TemporaryFile(mode='w+b')
 
137
                stderrTempFiles.append((i, stderr))
 
138
 
 
139
        # Resolve the executable path ourselves.
 
140
        args = list(j.args)
 
141
        args[0] = Util.which(args[0], cfg.environment['PATH'])
 
142
        if not args[0]:
 
143
            raise InternalShellError(j, '%r: command not found' % j.args[0])
 
144
 
 
145
        procs.append(subprocess.Popen(args, cwd=cwd,
 
146
                                      stdin = stdin,
 
147
                                      stdout = stdout,
 
148
                                      stderr = stderr,
 
149
                                      env = cfg.environment,
 
150
                                      close_fds = kUseCloseFDs))
 
151
 
 
152
        # Immediately close stdin for any process taking stdin from us.
 
153
        if stdin == subprocess.PIPE:
 
154
            procs[-1].stdin.close()
 
155
            procs[-1].stdin = None
 
156
 
 
157
        # Update the current stdin source.
 
158
        if stdout == subprocess.PIPE:
 
159
            input = procs[-1].stdout
 
160
        elif stderrIsStdout:
 
161
            input = procs[-1].stderr
 
162
        else:
 
163
            input = subprocess.PIPE
 
164
 
 
165
    # FIXME: There is probably still deadlock potential here. Yawn.
 
166
    procData = [None] * len(procs)
 
167
    procData[-1] = procs[-1].communicate()
 
168
 
 
169
    for i in range(len(procs) - 1):
 
170
        if procs[i].stdout is not None:
 
171
            out = procs[i].stdout.read()
 
172
        else:
 
173
            out = ''
 
174
        if procs[i].stderr is not None:
 
175
            err = procs[i].stderr.read()
 
176
        else:
 
177
            err = ''
 
178
        procData[i] = (out,err)
 
179
        
 
180
    # Read stderr out of the temp files.
 
181
    for i,f in stderrTempFiles:
 
182
        f.seek(0, 0)
 
183
        procData[i] = (procData[i][0], f.read())
 
184
 
 
185
    exitCode = None
 
186
    for i,(out,err) in enumerate(procData):
 
187
        res = procs[i].wait()
 
188
        # Detect Ctrl-C in subprocess.
 
189
        if res == -signal.SIGINT:
 
190
            raise KeyboardInterrupt
 
191
 
 
192
        results.append((cmd.commands[i], out, err, res))
 
193
        if cmd.pipe_err:
 
194
            # Python treats the exit code as a signed char.
 
195
            if res < 0:
 
196
                exitCode = min(exitCode, res)
 
197
            else:
 
198
                exitCode = max(exitCode, res)
 
199
        else:
 
200
            exitCode = res
 
201
 
 
202
    if cmd.negate:
 
203
        exitCode = not exitCode
 
204
 
 
205
    return exitCode
 
206
 
 
207
def executeScriptInternal(test, litConfig, tmpBase, commands, cwd):
 
208
    ln = ' &&\n'.join(commands)
 
209
    try:
 
210
        cmd = ShUtil.ShParser(ln, litConfig.isWindows).parse()
 
211
    except:
 
212
        return (Test.FAIL, "shell parser error on: %r" % ln)
 
213
 
 
214
    results = []
 
215
    try:
 
216
        exitCode = executeShCmd(cmd, test.config, cwd, results)
 
217
    except InternalShellError,e:
 
218
        out = ''
 
219
        err = e.message
 
220
        exitCode = 255
 
221
 
 
222
    out = err = ''
 
223
    for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
 
224
        out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
 
225
        out += 'Command %d Result: %r\n' % (i, res)
 
226
        out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
 
227
        out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
 
228
 
 
229
    return out, err, exitCode
 
230
 
 
231
def executeTclScriptInternal(test, litConfig, tmpBase, commands, cwd):
 
232
    import TclUtil
 
233
    cmds = []
 
234
    for ln in commands:
 
235
        # Given the unfortunate way LLVM's test are written, the line gets
 
236
        # backslash substitution done twice.
 
237
        ln = TclUtil.TclLexer(ln).lex_unquoted(process_all = True)
 
238
 
 
239
        try:
 
240
            tokens = list(TclUtil.TclLexer(ln).lex())
 
241
        except:
 
242
            return (Test.FAIL, "Tcl lexer error on: %r" % ln)
 
243
 
 
244
        # Validate there are no control tokens.
 
245
        for t in tokens:
 
246
            if not isinstance(t, str):
 
247
                return (Test.FAIL,
 
248
                        "Invalid test line: %r containing %r" % (ln, t))
 
249
 
 
250
        try:
 
251
            cmds.append(TclUtil.TclExecCommand(tokens).parse_pipeline())
 
252
        except:
 
253
            return (Test.FAIL, "Tcl 'exec' parse error on: %r" % ln)
 
254
 
 
255
    cmd = cmds[0]
 
256
    for c in cmds[1:]:
 
257
        cmd = ShUtil.Seq(cmd, '&&', c)
 
258
 
 
259
    # FIXME: This is lame, we shouldn't need bash. See PR5240.
 
260
    bashPath = litConfig.getBashPath()
 
261
    if litConfig.useTclAsSh and bashPath:
 
262
        script = tmpBase + '.script'
 
263
 
 
264
        # Write script file
 
265
        f = open(script,'w')
 
266
        print >>f, 'set -o pipefail'
 
267
        cmd.toShell(f, pipefail = True)
 
268
        f.close()
 
269
 
 
270
        if 0:
 
271
            print >>sys.stdout, cmd
 
272
            print >>sys.stdout, open(script).read()
 
273
            print >>sys.stdout
 
274
            return '', '', 0
 
275
 
 
276
        command = [litConfig.getBashPath(), script]
 
277
        out,err,exitCode = executeCommand(command, cwd=cwd,
 
278
                                          env=test.config.environment)
 
279
 
 
280
        # Tcl commands fail on standard error output.
 
281
        if err:
 
282
            exitCode = 1
 
283
            out = 'Command has output on stderr!\n\n' + out
 
284
 
 
285
        return out,err,exitCode
 
286
    else:
 
287
        results = []
 
288
        try:
 
289
            exitCode = executeShCmd(cmd, test.config, cwd, results)
 
290
        except InternalShellError,e:
 
291
            results.append((e.command, '', e.message + '\n', 255))
 
292
            exitCode = 255
 
293
 
 
294
    out = err = ''
 
295
 
 
296
    # Tcl commands fail on standard error output.
 
297
    if [True for _,_,err,res in results if err]:
 
298
        exitCode = 1
 
299
        out += 'Command has output on stderr!\n\n'
 
300
 
 
301
    for i,(cmd, cmd_out, cmd_err, res) in enumerate(results):
 
302
        out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
 
303
        out += 'Command %d Result: %r\n' % (i, res)
 
304
        out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
 
305
        out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
 
306
 
 
307
    return out, err, exitCode
 
308
 
 
309
def executeScript(test, litConfig, tmpBase, commands, cwd):
 
310
    script = tmpBase + '.script'
 
311
    if litConfig.isWindows:
 
312
        script += '.bat'
 
313
 
 
314
    # Write script file
 
315
    f = open(script,'w')
 
316
    if litConfig.isWindows:
 
317
        f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands))
 
318
    else:
 
319
        f.write(' &&\n'.join(commands))
 
320
    f.write('\n')
 
321
    f.close()
 
322
 
 
323
    if litConfig.isWindows:
 
324
        command = ['cmd','/c', script]
 
325
    else:
 
326
        command = ['/bin/sh', script]
 
327
        if litConfig.useValgrind:
 
328
            # FIXME: Running valgrind on sh is overkill. We probably could just
 
329
            # run on clang with no real loss.
 
330
            valgrindArgs = ['valgrind', '-q',
 
331
                            '--tool=memcheck', '--trace-children=yes',
 
332
                            '--error-exitcode=123']
 
333
            valgrindArgs.extend(litConfig.valgrindArgs)
 
334
 
 
335
            command = valgrindArgs + command
 
336
 
 
337
    return executeCommand(command, cwd=cwd, env=test.config.environment)
 
338
 
 
339
def isExpectedFail(xfails, xtargets, target_triple):
 
340
    # Check if any xfail matches this target.
 
341
    for item in xfails:
 
342
        if item == '*' or item in target_triple:
 
343
            break
 
344
    else:
 
345
        return False
 
346
 
 
347
    # If so, see if it is expected to pass on this target.
 
348
    #
 
349
    # FIXME: Rename XTARGET to something that makes sense, like XPASS.
 
350
    for item in xtargets:
 
351
        if item == '*' or item in target_triple:
 
352
            return False
 
353
 
 
354
    return True
 
355
 
 
356
import re
 
357
 
 
358
def parseIntegratedTestScript(test):
 
359
    """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test
 
360
    script and extract the lines to 'RUN' as well as 'XFAIL' and 'XTARGET'
 
361
    information. The RUN lines also will have variable substitution performed.
 
362
    """
 
363
 
 
364
    # Get the temporary location, this is always relative to the test suite
 
365
    # root, not test source root.
 
366
    #
 
367
    # FIXME: This should not be here?
 
368
    sourcepath = test.getSourcePath()
 
369
    execpath = test.getExecPath()
 
370
    execdir,execbase = os.path.split(execpath)
 
371
    tmpBase = os.path.join(execdir, 'Output', execbase)
 
372
    if test.index is not None:
 
373
        tmpBase += '_%d' % test.index
 
374
 
 
375
    # We use #_MARKER_# to hide %% while we do the other substitutions.
 
376
    substitutions = [('%%', '#_MARKER_#')]
 
377
    substitutions.extend(test.config.substitutions)
 
378
    substitutions.extend([('%s', sourcepath),
 
379
                          ('%S', os.path.dirname(sourcepath)),
 
380
                          ('%p', os.path.dirname(sourcepath)),
 
381
                          ('%t', tmpBase + '.tmp'),
 
382
                          # FIXME: Remove this once we kill DejaGNU.
 
383
                          ('%abs_tmp', tmpBase + '.tmp'),
 
384
                          ('#_MARKER_#', '%')])
 
385
 
 
386
    # Collect the test lines from the script.
 
387
    script = []
 
388
    xfails = []
 
389
    xtargets = []
 
390
    ignoredAny = False
 
391
    for ln in open(sourcepath):
 
392
        conditional = re.search('IF\((.+?)\((.+?)\)\):', ln)
 
393
        if conditional:
 
394
            ln = ln[conditional.end():]
 
395
            condition = conditional.group(1)
 
396
            value = conditional.group(2)
 
397
 
 
398
            # Actually test the condition.
 
399
            if condition not in test.config.conditions:
 
400
                return (Test.UNRESOLVED, "unknown condition '"+condition+"'")
 
401
            if not test.config.conditions[condition](value):
 
402
                ignoredAny = True
 
403
                continue
 
404
 
 
405
        if 'RUN:' in ln:
 
406
            # Isolate the command to run.
 
407
            index = ln.index('RUN:')
 
408
            ln = ln[index+4:]
 
409
 
 
410
            # Trim trailing whitespace.
 
411
            ln = ln.rstrip()
 
412
 
 
413
            # Collapse lines with trailing '\\'.
 
414
            if script and script[-1][-1] == '\\':
 
415
                script[-1] = script[-1][:-1] + ln
 
416
            else:
 
417
                script.append(ln)
 
418
        elif 'XFAIL:' in ln:
 
419
            items = ln[ln.index('XFAIL:') + 6:].split(',')
 
420
            xfails.extend([s.strip() for s in items])
 
421
        elif 'XTARGET:' in ln:
 
422
            items = ln[ln.index('XTARGET:') + 8:].split(',')
 
423
            xtargets.extend([s.strip() for s in items])
 
424
        elif 'END.' in ln:
 
425
            # Check for END. lines.
 
426
            if ln[ln.index('END.'):].strip() == 'END.':
 
427
                break
 
428
 
 
429
    # Apply substitutions to the script.
 
430
    def processLine(ln):
 
431
        # Apply substitutions
 
432
        for a,b in substitutions:
 
433
            ln = ln.replace(a,b)
 
434
 
 
435
        # Strip the trailing newline and any extra whitespace.
 
436
        return ln.strip()
 
437
    script = map(processLine, script)
 
438
 
 
439
    # Verify the script contains a run line.
 
440
    if not script:
 
441
        if ignoredAny:
 
442
            return (Test.UNSUPPORTED, "Test has only ignored run lines")
 
443
        return (Test.UNRESOLVED, "Test has no run line!")
 
444
 
 
445
    if script[-1][-1] == '\\':
 
446
        return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')")
 
447
 
 
448
    isXFail = isExpectedFail(xfails, xtargets, test.suite.config.target_triple)
 
449
    return script,isXFail,tmpBase,execdir
 
450
 
 
451
def formatTestOutput(status, out, err, exitCode, script):
 
452
    output = StringIO.StringIO()
 
453
    print >>output, "Script:"
 
454
    print >>output, "--"
 
455
    print >>output, '\n'.join(script)
 
456
    print >>output, "--"
 
457
    print >>output, "Exit Code: %r" % exitCode
 
458
    print >>output, "Command Output (stdout):"
 
459
    print >>output, "--"
 
460
    output.write(out)
 
461
    print >>output, "--"
 
462
    print >>output, "Command Output (stderr):"
 
463
    print >>output, "--"
 
464
    output.write(err)
 
465
    print >>output, "--"
 
466
    return (status, output.getvalue())
 
467
 
 
468
def executeTclTest(test, litConfig):
 
469
    if test.config.unsupported:
 
470
        return (Test.UNSUPPORTED, 'Test is unsupported')
 
471
 
 
472
    res = parseIntegratedTestScript(test)
 
473
    if len(res) == 2:
 
474
        return res
 
475
 
 
476
    script, isXFail, tmpBase, execdir = res
 
477
 
 
478
    if litConfig.noExecute:
 
479
        return (Test.PASS, '')
 
480
 
 
481
    # Create the output directory if it does not already exist.
 
482
    Util.mkdir_p(os.path.dirname(tmpBase))
 
483
 
 
484
    res = executeTclScriptInternal(test, litConfig, tmpBase, script, execdir)
 
485
    if len(res) == 2:
 
486
        return res
 
487
 
 
488
    out,err,exitCode = res
 
489
    if isXFail:
 
490
        ok = exitCode != 0
 
491
        status = (Test.XPASS, Test.XFAIL)[ok]
 
492
    else:
 
493
        ok = exitCode == 0
 
494
        status = (Test.FAIL, Test.PASS)[ok]
 
495
 
 
496
    if ok:
 
497
        return (status,'')
 
498
 
 
499
    return formatTestOutput(status, out, err, exitCode, script)
 
500
 
 
501
def executeShTest(test, litConfig, useExternalSh):
 
502
    if test.config.unsupported:
 
503
        return (Test.UNSUPPORTED, 'Test is unsupported')
 
504
 
 
505
    res = parseIntegratedTestScript(test)
 
506
    if len(res) == 2:
 
507
        return res
 
508
 
 
509
    script, isXFail, tmpBase, execdir = res
 
510
 
 
511
    if litConfig.noExecute:
 
512
        return (Test.PASS, '')
 
513
 
 
514
    # Create the output directory if it does not already exist.
 
515
    Util.mkdir_p(os.path.dirname(tmpBase))
 
516
 
 
517
    if useExternalSh:
 
518
        res = executeScript(test, litConfig, tmpBase, script, execdir)
 
519
    else:
 
520
        res = executeScriptInternal(test, litConfig, tmpBase, script, execdir)
 
521
    if len(res) == 2:
 
522
        return res
 
523
 
 
524
    out,err,exitCode = res
 
525
    if isXFail:
 
526
        ok = exitCode != 0
 
527
        status = (Test.XPASS, Test.XFAIL)[ok]
 
528
    else:
 
529
        ok = exitCode == 0
 
530
        status = (Test.FAIL, Test.PASS)[ok]
 
531
 
 
532
    if ok:
 
533
        return (status,'')
 
534
 
 
535
    return formatTestOutput(status, out, err, exitCode, script)