1#!/usr/bin/env python
2
3"""
4Run the test suite and send the result as an email message.
5
6The code for sending of the directory is copied from
7http://docs.python.org/library/email-examples.html.
8"""
9
10import os
11import sys
12import shutil
13import smtplib
14# For guessing MIME type based on file name extension
15import mimetypes
16
17from optparse import OptionParser
18
19from email import encoders
20from email.message import Message
21from email.mime.audio import MIMEAudio
22from email.mime.base import MIMEBase
23from email.mime.image import MIMEImage
24from email.mime.multipart import MIMEMultipart
25from email.mime.text import MIMEText
26
27def runTestsuite(testDir, sessDir, envs = None):
28    """Run the testsuite and return a (summary, output) tuple."""
29    os.chdir(testDir)
30
31    for env in envs:
32        list = env.split('=')
33        var = list[0].strip()
34        val = list[1].strip()
35        print var + "=" + val
36        os.environ[var] = val
37
38    import shlex, subprocess
39
40    command_line = "./dotest.py -w -s %s" % sessDir
41    # Apply correct tokenization for subprocess.Popen().
42    args = shlex.split(command_line)
43
44    # Use subprocess module to spawn a new process.
45    process = subprocess.Popen(args,
46                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
47    # Wait for subprocess to terminate.
48    stdout, stderr = process.communicate()
49
50    # This will be used as the subject line of our email about this test.
51    cmd = "%s %s" % (' '.join(envs) if envs else "", command_line)
52
53    return (cmd, stderr)
54
55
56COMMASPACE = ', '
57
58def main():
59    parser = OptionParser(usage="""\
60Run lldb test suite and send the results as a MIME message.
61
62Usage: %prog [options]
63
64Unless the -o option is given, the email is sent by forwarding to the specified
65SMTP server, which then does the normal delivery process.
66""")
67    parser.add_option('-d', '--directory',
68                      type='string', action='store',
69                      dest='testDir',
70                      help="""The LLDB test directory directly under the top dir.
71                      Otherwise use the current directory.""")
72    #
73    # This is similar to TestBase.getRunSpec(self) from lldbtest.py.
74    #
75    parser.add_option('-e', '--environment',
76                      type='string', action='append', metavar='ENVIRONMENT',
77                      default=[], dest='environments',
78                      help="""The environment setting as prefix to the test driver.
79                      Example: -e 'CC=clang' -e 'ARCH=x86_64'""")
80    parser.add_option('-m', '--mailserver',
81                      type='string', action='store', metavar='MAILSERVER',
82                      dest='mailserver',
83                      help="""The outgoing SMTP server.""")
84    parser.add_option('-o', '--output',
85                      type='string', action='store', metavar='FILE',
86                      help="""Print the composed message to FILE instead of
87                      sending the message to the SMTP server.""")
88    parser.add_option('-s', '--sender',
89                      type='string', action='store', metavar='SENDER',
90                      help='The value of the From: header (required)')
91    parser.add_option('-r', '--recipient',
92                      type='string', action='append', metavar='RECIPIENT',
93                      default=[], dest='recipients',
94                      help='A To: header value (at least one required)')
95    opts, args = parser.parse_args()
96    if not opts.sender or not opts.recipients:
97        parser.print_help()
98        sys.exit(1)
99    testDir = opts.testDir
100    if not testDir:
101        testDir = '.'
102
103    sessDir = 'tmp-lldb-session'
104    if os.path.exists(sessDir):
105        shutil.rmtree(sessDir)
106    #print "environments:", opts.environments
107    summary, output = runTestsuite(testDir, sessDir, opts.environments)
108
109    # Create the enclosing (outer) message
110    outer = MIMEMultipart()
111    outer['Subject'] = summary
112    outer['To'] = COMMASPACE.join(opts.recipients)
113    outer['From'] = opts.sender
114    outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
115
116    # The sessDir contains all the session logs for failed/errored tests.
117    # Attach them all if it exists!
118
119    if not os.path.exists(sessDir):
120        outer.attach(MIMEText(output, 'plain'))
121    else:
122        outer.attach(MIMEText("%s\n%s\n\n" % (output,
123                                              "Session logs of test failures/errors:"),
124                              'plain'))
125
126    for filename in (os.listdir(sessDir) if os.path.exists(sessDir) else []):
127        path = os.path.join(sessDir, filename)
128        if not os.path.isfile(path):
129            continue
130        # Guess the content type based on the file's extension.  Encoding
131        # will be ignored, although we should check for simple things like
132        # gzip'd or compressed files.
133        ctype, encoding = mimetypes.guess_type(path)
134        if ctype is None or encoding is not None:
135            # No guess could be made, or the file is encoded (compressed), so
136            # use a generic bag-of-bits type.
137            ctype = 'application/octet-stream'
138        maintype, subtype = ctype.split('/', 1)
139        if maintype == 'text':
140            fp = open(path)
141            # Note: we should handle calculating the charset
142            msg = MIMEText(fp.read(), _subtype=subtype)
143            fp.close()
144        elif maintype == 'image':
145            fp = open(path, 'rb')
146            msg = MIMEImage(fp.read(), _subtype=subtype)
147            fp.close()
148        elif maintype == 'audio':
149            fp = open(path, 'rb')
150            msg = MIMEAudio(fp.read(), _subtype=subtype)
151            fp.close()
152        else:
153            fp = open(path, 'rb')
154            msg = MIMEBase(maintype, subtype)
155            msg.set_payload(fp.read())
156            fp.close()
157            # Encode the payload using Base64
158            encoders.encode_base64(msg)
159        # Set the filename parameter
160        msg.add_header('Content-Disposition', 'attachment', filename=filename)
161        outer.attach(msg)
162
163    # Now send or store the message
164    composed = outer.as_string()
165    if opts.output:
166        fp = open(opts.output, 'w')
167        fp.write(composed)
168        fp.close()
169    else:
170        s = smtplib.SMTP(opts.mailserver)
171        s.sendmail(opts.sender, opts.recipients, composed)
172        s.quit()
173
174
175if __name__ == '__main__':
176    main()
177