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