1ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao"""Methods for reporting bugs."""
2ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
3ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoimport subprocess, sys, os
4ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
5ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao__all__ = ['ReportFailure', 'BugReport', 'getReporters']
6ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
7ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao#
8ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
9ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass ReportFailure(Exception):
10ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    """Generic exception for failures in bug reporting."""
11ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def __init__(self, value):
12ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        self.value = value
13ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
14ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao# Collect information about a bug.
15ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
16ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass BugReport:
17ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def __init__(self, title, description, files):
18ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        self.title = title
19ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        self.description = description
20ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        self.files = files
21ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
22ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao# Reporter interfaces.
23ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
24ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoimport os
25ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
26ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoimport email, mimetypes, smtplib
27ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaofrom email import encoders
28ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaofrom email.message import Message
29ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaofrom email.mime.base import MIMEBase
30ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaofrom email.mime.multipart import MIMEMultipart
31ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaofrom email.mime.text import MIMEText
32ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
33ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao#===------------------------------------------------------------------------===#
34ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao# ReporterParameter
35ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao#===------------------------------------------------------------------------===#
36ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
37ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass ReporterParameter:
38ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def __init__(self, n):
39ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    self.name = n
40ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def getName(self):
41ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    return self.name
42ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def getValue(self,r,bugtype,getConfigOption):
43ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao     return getConfigOption(r.getName(),self.getName())
44ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def saveConfigValue(self):
45ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    return True
46ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
47ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass TextParameter (ReporterParameter):
48ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def getHTML(self,r,bugtype,getConfigOption):
49ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    return """\
50ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao<tr>
51ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao<td class="form_clabel">%s:</td>
52ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao<td class="form_value"><input type="text" name="%s_%s" value="%s"></td>
53ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao</tr>"""%(self.getName(),r.getName(),self.getName(),self.getValue(r,bugtype,getConfigOption))
54ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
55ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass SelectionParameter (ReporterParameter):
56ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def __init__(self, n, values):
57ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    ReporterParameter.__init__(self,n)
58ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    self.values = values
59ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
60ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def getHTML(self,r,bugtype,getConfigOption):
61ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    default = self.getValue(r,bugtype,getConfigOption)
62ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    return """\
63ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao<tr>
64ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s">
65ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao%s
66ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao</select></td>"""%(self.getName(),r.getName(),self.getName(),'\n'.join(["""\
67ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao<option value="%s"%s>%s</option>"""%(o[0],
68ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao                                     o[0] == default and ' selected="selected"' or '',
69ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao                                     o[1]) for o in self.values]))
70ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
71ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao#===------------------------------------------------------------------------===#
72ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao# Reporters
73ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao#===------------------------------------------------------------------------===#
74ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
75ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass EmailReporter:
76ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def getName(self):
77ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return 'Email'
78ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
79ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def getParameters(self):
80ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return map(lambda x:TextParameter(x),['To', 'From', 'SMTP Server', 'SMTP Port'])
81ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
82ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    # Lifted from python email module examples.
83ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def attachFile(self, outer, path):
84ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # Guess the content type based on the file's extension.  Encoding
85ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # will be ignored, although we should check for simple things like
86ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # gzip'd or compressed files.
87ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        ctype, encoding = mimetypes.guess_type(path)
88ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if ctype is None or encoding is not None:
89ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            # No guess could be made, or the file is encoded (compressed), so
90ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            # use a generic bag-of-bits type.
91ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            ctype = 'application/octet-stream'
92ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        maintype, subtype = ctype.split('/', 1)
93ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if maintype == 'text':
94ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            fp = open(path)
95ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            # Note: we should handle calculating the charset
96ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            msg = MIMEText(fp.read(), _subtype=subtype)
97ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            fp.close()
98ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        else:
99ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            fp = open(path, 'rb')
100ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            msg = MIMEBase(maintype, subtype)
101ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            msg.set_payload(fp.read())
102ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            fp.close()
103ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            # Encode the payload using Base64
104ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            encoders.encode_base64(msg)
105ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # Set the filename parameter
106ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path))
107ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        outer.attach(msg)
108ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
109ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def fileReport(self, report, parameters):
110ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        mainMsg = """\
111ea285162342df160e7860e26528bc7110bc6c0cdShih-wei LiaoBUG REPORT
112ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao---
113ea285162342df160e7860e26528bc7110bc6c0cdShih-wei LiaoTitle: %s
114ea285162342df160e7860e26528bc7110bc6c0cdShih-wei LiaoDescription: %s
115ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao"""%(report.title, report.description)
116ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
117ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if not parameters.get('To'):
118ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure('No "To" address specified.')
119ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if not parameters.get('From'):
120ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure('No "From" address specified.')
121ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
122ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        msg = MIMEMultipart()
123ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        msg['Subject'] = 'BUG REPORT: %s'%(report.title)
124ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # FIXME: Get config parameters
125ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        msg['To'] = parameters.get('To')
126ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        msg['From'] = parameters.get('From')
127ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        msg.preamble = mainMsg
128ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
129ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        msg.attach(MIMEText(mainMsg, _subtype='text/plain'))
130ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        for file in report.files:
131ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            self.attachFile(msg, file)
132ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
133ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        try:
134ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            s = smtplib.SMTP(host=parameters.get('SMTP Server'),
135ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao                             port=parameters.get('SMTP Port'))
136ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            s.sendmail(msg['From'], msg['To'], msg.as_string())
137ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            s.close()
138ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        except:
139ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure('Unable to send message via SMTP.')
140ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
141ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return "Message sent!"
142ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
143ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass BugzillaReporter:
144ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def getName(self):
145ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return 'Bugzilla'
146ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
147ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def getParameters(self):
148ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return map(lambda x:TextParameter(x),['URL','Product'])
149ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
150ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def fileReport(self, report, parameters):
151ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        raise NotImplementedError
152ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
153ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
154ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass RadarClassificationParameter(SelectionParameter):
155ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def __init__(self):
156ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    SelectionParameter.__init__(self,"Classification",
157ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            [['1', 'Security'], ['2', 'Crash/Hang/Data Loss'],
158ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao             ['3', 'Performance'], ['4', 'UI/Usability'],
159ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao             ['6', 'Serious Bug'], ['7', 'Other']])
160ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
161ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def saveConfigValue(self):
162ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    return False
163ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
164ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao  def getValue(self,r,bugtype,getConfigOption):
165ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    if bugtype.find("leak") != -1:
166ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao      return '3'
167ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    elif bugtype.find("dereference") != -1:
168ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao      return '2'
169ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    elif bugtype.find("missing ivar release") != -1:
170ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao      return '3'
171ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    else:
172ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao      return '7'
173ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
174ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaoclass RadarReporter:
175ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    @staticmethod
176ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def isAvailable():
177ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # FIXME: Find this .scpt better
178ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        path = os.path.join(os.path.dirname(__file__),'Resources/GetRadarVersion.scpt')
179ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        try:
180ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao          p = subprocess.Popen(['osascript',path],
181ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
182ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        except:
183ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            return False
184ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        data,err = p.communicate()
185ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        res = p.wait()
186ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # FIXME: Check version? Check for no errors?
187ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return res == 0
188ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
189ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def getName(self):
190ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return 'Radar'
191ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
192ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def getParameters(self):
193ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return [ TextParameter('Component'), TextParameter('Component Version'),
194ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao                 RadarClassificationParameter() ]
195ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
196ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    def fileReport(self, report, parameters):
197ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        component = parameters.get('Component', '')
198ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        componentVersion = parameters.get('Component Version', '')
199ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        classification = parameters.get('Classification', '')
200ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        personID = ""
201ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        diagnosis = ""
202ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        config = ""
203ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
204ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if not component.strip():
205ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            component = 'Bugs found by clang Analyzer'
206ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if not componentVersion.strip():
207ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            componentVersion = 'X'
208ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
209ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        script = os.path.join(os.path.dirname(__file__),'Resources/FileRadar.scpt')
210ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        args = ['osascript', script, component, componentVersion, classification, personID, report.title,
211ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao                report.description, diagnosis, config] + map(os.path.abspath, report.files)
212ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao#        print >>sys.stderr, args
213ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        try:
214ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao          p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
215ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        except:
216ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure("Unable to file radar (AppleScript failure).")
217ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        data, err = p.communicate()
218ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        res = p.wait()
219ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
220ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if res:
221ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure("Unable to file radar (AppleScript failure).")
222ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
223ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        try:
224ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            values = eval(data)
225ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        except:
226ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure("Unable to process radar results.")
227ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
228ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        # We expect (int: bugID, str: message)
229ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if len(values) != 2 or not isinstance(values[0], int):
230ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure("Unable to process radar results.")
231ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
232ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        bugID,message = values
233ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        bugID = int(bugID)
234ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
235ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        if not bugID:
236ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao            raise ReportFailure(message)
237ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
238ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID)
239ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
240ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao###
241ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
242ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liaodef getReporters():
243ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    reporters = []
244ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    if RadarReporter.isAvailable():
245ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao        reporters.append(RadarReporter())
246ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    reporters.append(EmailReporter())
247ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao    return reporters
248ea285162342df160e7860e26528bc7110bc6c0cdShih-wei Liao
249