121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen#!/usr/bin/env python
221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen"""
421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny ChenRun the test suite and send the result as an email message.
521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny ChenThe code for sending of the directory is copied from
721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenhttp://docs.python.org/library/email-examples.html.
821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen"""
921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
1021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenimport os
1121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenimport sys
1221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenimport shutil
1321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenimport smtplib
1421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen# For guessing MIME type based on file name extension
1521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenimport mimetypes
1621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
1721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom optparse import OptionParser
1821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
1921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom email import encoders
2021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom email.message import Message
2121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom email.mime.audio import MIMEAudio
2221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom email.mime.base import MIMEBase
2321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom email.mime.image import MIMEImage
2421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom email.mime.multipart import MIMEMultipart
2521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenfrom email.mime.text import MIMEText
2621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
2721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chendef runTestsuite(testDir, sessDir, envs = None):
2821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    """Run the testsuite and return a (summary, output) tuple."""
2921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    os.chdir(testDir)
3021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
3121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    for env in envs:
3221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        list = env.split('=')
3321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        var = list[0].strip()
3421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        val = list[1].strip()
3521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        print var + "=" + val
3621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        os.environ[var] = val
3721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
3821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    import shlex, subprocess
3921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
4021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    command_line = "./dotest.py -w -s %s" % sessDir
4121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # Apply correct tokenization for subprocess.Popen().
4221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    args = shlex.split(command_line)
4321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
4421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # Use subprocess module to spawn a new process.
4521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    process = subprocess.Popen(args,
4621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
4721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # Wait for subprocess to terminate.
4821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    stdout, stderr = process.communicate()
4921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
5021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # This will be used as the subject line of our email about this test.
5121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    cmd = "%s %s" % (' '.join(envs) if envs else "", command_line)
5221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
5321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    return (cmd, stderr)
5421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
5521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
5621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny ChenCOMMASPACE = ', '
5721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
5821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chendef main():
5921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    parser = OptionParser(usage="""\
6021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny ChenRun lldb test suite and send the results as a MIME message.
6121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
6221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny ChenUsage: %prog [options]
6321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
6421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny ChenUnless the -o option is given, the email is sent by forwarding to the specified
6521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny ChenSMTP server, which then does the normal delivery process.
6621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen""")
6721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    parser.add_option('-d', '--directory',
6821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      type='string', action='store',
6921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      dest='testDir',
7021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      help="""The LLDB test directory directly under the top dir.
7121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      Otherwise use the current directory.""")
7221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    #
7321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # This is similar to TestBase.getRunSpec(self) from lldbtest.py.
7421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    #
7521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    parser.add_option('-e', '--environment',
7621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      type='string', action='append', metavar='ENVIRONMENT',
7721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      default=[], dest='environments',
7821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      help="""The environment setting as prefix to the test driver.
7921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      Example: -e 'CC=clang' -e 'ARCH=x86_64'""")
8021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    parser.add_option('-m', '--mailserver',
8121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      type='string', action='store', metavar='MAILSERVER',
8221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      dest='mailserver',
8321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      help="""The outgoing SMTP server.""")
8421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    parser.add_option('-o', '--output',
8521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      type='string', action='store', metavar='FILE',
8621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      help="""Print the composed message to FILE instead of
8721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      sending the message to the SMTP server.""")
8821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    parser.add_option('-s', '--sender',
8921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      type='string', action='store', metavar='SENDER',
9021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      help='The value of the From: header (required)')
9121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    parser.add_option('-r', '--recipient',
9221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      type='string', action='append', metavar='RECIPIENT',
9321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      default=[], dest='recipients',
9421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                      help='A To: header value (at least one required)')
9521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    opts, args = parser.parse_args()
9621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    if not opts.sender or not opts.recipients:
9721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        parser.print_help()
9821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        sys.exit(1)
9921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    testDir = opts.testDir
10021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    if not testDir:
10121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        testDir = '.'
10221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
10321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    sessDir = 'tmp-lldb-session'
10421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    if os.path.exists(sessDir):
10521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        shutil.rmtree(sessDir)
10621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    #print "environments:", opts.environments
10721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    summary, output = runTestsuite(testDir, sessDir, opts.environments)
10821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
10921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # Create the enclosing (outer) message
11021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    outer = MIMEMultipart()
11121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    outer['Subject'] = summary
11221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    outer['To'] = COMMASPACE.join(opts.recipients)
11321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    outer['From'] = opts.sender
11421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
11521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
11621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # The sessDir contains all the session logs for failed/errored tests.
11721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # Attach them all if it exists!
11821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
11921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    if not os.path.exists(sessDir):
12021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        outer.attach(MIMEText(output, 'plain'))
12121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    else:
12221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        outer.attach(MIMEText("%s\n%s\n\n" % (output,
12321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                                              "Session logs of test failures/errors:"),
12421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen                              'plain'))
12521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
12621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    for filename in (os.listdir(sessDir) if os.path.exists(sessDir) else []):
12721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        path = os.path.join(sessDir, filename)
12821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        if not os.path.isfile(path):
12921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            continue
13021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        # Guess the content type based on the file's extension.  Encoding
13121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        # will be ignored, although we should check for simple things like
13221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        # gzip'd or compressed files.
13321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        ctype, encoding = mimetypes.guess_type(path)
13421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        if ctype is None or encoding is not None:
13521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            # No guess could be made, or the file is encoded (compressed), so
13621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            # use a generic bag-of-bits type.
13721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            ctype = 'application/octet-stream'
13821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        maintype, subtype = ctype.split('/', 1)
13921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        if maintype == 'text':
14021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp = open(path)
14121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            # Note: we should handle calculating the charset
14221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            msg = MIMEText(fp.read(), _subtype=subtype)
14321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp.close()
14421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        elif maintype == 'image':
14521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp = open(path, 'rb')
14621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            msg = MIMEImage(fp.read(), _subtype=subtype)
14721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp.close()
14821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        elif maintype == 'audio':
14921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp = open(path, 'rb')
15021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            msg = MIMEAudio(fp.read(), _subtype=subtype)
15121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp.close()
15221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        else:
15321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp = open(path, 'rb')
15421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            msg = MIMEBase(maintype, subtype)
15521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            msg.set_payload(fp.read())
15621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            fp.close()
15721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            # Encode the payload using Base64
15821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen            encoders.encode_base64(msg)
15921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        # Set the filename parameter
16021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        msg.add_header('Content-Disposition', 'attachment', filename=filename)
16121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        outer.attach(msg)
16221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
16321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    # Now send or store the message
16421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    composed = outer.as_string()
16521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    if opts.output:
16621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        fp = open(opts.output, 'w')
16721d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        fp.write(composed)
16821d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        fp.close()
16921d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    else:
17021d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        s = smtplib.SMTP(opts.mailserver)
17121d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        s.sendmail(opts.sender, opts.recipients, composed)
17221d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen        s.quit()
17321d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
17421d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen
17521d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chenif __name__ == '__main__':
17621d77c3d0da4ff30397e39f4761dd92f62ec50edJohnny Chen    main()
177