1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 """Unit Tests for bug hunter.""" 6 7 import logging 8 from optparse import Values 9 import smtplib 10 import sys 11 import unittest 12 13 from bug_hunter import BugHunter 14 from bug_hunter import BugHunterUtils 15 16 try: 17 import atom.data 18 import gdata.data 19 import gdata.projecthosting.client 20 except ImportError: 21 logging.error('gdata-client needs to be installed. Please install\n' 22 'and try again (http://code.google.com/p/gdata-python-client/)') 23 sys.exit(1) 24 25 26 class MockClient(object): 27 """A mock class for gdata.projecthosting.client.ProjectHostingClient. 28 29 Mocking the very simple method invocations for get_issues() and 30 get_comments(). 31 """ 32 33 def _CreateIssues(self, n_issues): 34 feed = gdata.projecthosting.data.IssuesFeed() 35 for i in xrange(n_issues): 36 feed.entry.append(gdata.projecthosting.data.IssueEntry( 37 title=atom.data.Title(text='title'), 38 content=atom.data.Content(text='http://www.content.com'), 39 id=atom.data.Id(text='/' + str(i)), 40 status=gdata.projecthosting.data.Status(text='Unconfirmed'), 41 state=gdata.projecthosting.data.State(text='open'), 42 label=[gdata.projecthosting.data.Label('label1')], 43 author=[atom.data.Author(name=atom.data.Name(text='author'))])) 44 return feed 45 46 def get_issues(self, project_name, query): 47 """Get issues using mock object without calling the issue tracker API. 48 49 Based on query argument, this returns the dummy issues. The number of 50 dummy issues are specified in query.text_query. 51 52 Args: 53 project_name: A string for project name in the issue tracker. 54 query: A query object for querying the issue tracker. 55 56 Returns: 57 A IssuesFeed object that contains a simple test issue. 58 """ 59 n_issues = 1 60 if query.text_query.isdigit(): 61 n_issues = int(query.text_query) 62 return self._CreateIssues(n_issues) 63 64 def get_comments(self, project_name, issue_id): 65 """Get comments using mock object without calling the issue tracker API. 66 67 Args: 68 project_name: A string for project name in the issue tracker. 69 issue_id: Issue_id string. 70 71 Returns: 72 A CommentsFeed object that contains a simple test comment. 73 """ 74 feed = gdata.projecthosting.data.CommentsFeed() 75 feed.entry = [gdata.projecthosting.data.CommentEntry( 76 id=atom.data.Id(text='/0'), 77 content=atom.data.Content(text='http://www.comments.com'), 78 updated=atom.data.Updated(text='Updated'), 79 author=[atom.data.Author(name=atom.data.Name(text='cauthor'))])] 80 return feed 81 82 83 class BugHunterUnitTest(unittest.TestCase): 84 """Unit tests for the Bug Hunter class.""" 85 86 def setUp(self): 87 self._old_client = gdata.projecthosting.client.ProjectHostingClient 88 gdata.projecthosting.client.ProjectHostingClient = MockClient 89 90 def tearDown(self): 91 gdata.projecthosting.client.ProjectHostingClient = self._old_client 92 93 def _GetDefaultOption(self, set_10_days_ago, query='steps'): 94 ops = Values() 95 ops.query = query 96 if set_10_days_ago: 97 ops.interval_value = 10 98 ops.interval_unit = 'days' 99 else: 100 ops.interval_value = None 101 ops.email_entries = ['comments'] 102 ops.project_name = 'chromium' 103 ops.query_title = 'query title' 104 ops.max_comments = None 105 return ops 106 107 def _GetIssue(self, n_issues): 108 issues = [] 109 for i in xrange(n_issues): 110 issues.append({'issue_id': str(i), 'title': 'title', 'author': 'author', 111 'status': 'status', 'state': 'state', 112 'content': 'content', 'comments': [], 113 'labels': [], 'urls': []}) 114 return issues 115 116 def testSetUpEmailSubjectMsg(self): 117 bh = BugHunter(self._GetDefaultOption(False)) 118 subject, content = bh._SetUpEmailSubjectMsg(self._GetIssue(1)) 119 self.assertEquals(subject, 120 'BugHunter found 1 query title bug!') 121 self.assertEquals(content, 122 ('<a href="http://code.google.com/p/chromium/issues/' 123 'list?can=2&colspec=ID+Pri+Mstone+ReleaseBlock+Area+' 124 'Feature+Status+Owner+Summary&cells=tiles&sort=-id&' 125 'q=steps">Used Query</a>: steps<br><br>The number of ' 126 'issues : 1<br><ul><li><a href="http://crbug.com/0">0 ' 127 'title</a> [] </li></ul>')) 128 129 def testSetUpEmailSubjectMsgMultipleIssues(self): 130 bh = BugHunter(self._GetDefaultOption(False)) 131 subject, content = bh._SetUpEmailSubjectMsg(self._GetIssue(2)) 132 self.assertEquals(subject, 133 'BugHunter found 2 query title bugs!') 134 135 def testSetUpEmailSubjectMsgWith10DaysAgoAndAssertSubject(self): 136 bh = BugHunter(self._GetDefaultOption(True)) 137 subject, _ = bh._SetUpEmailSubjectMsg(self._GetIssue(1)) 138 self.assertEquals(subject, 139 ('BugHunter found 1 query title bug in the past 10 ' 140 'days!')) 141 142 def testGetIssuesWithMockClient(self): 143 bh = BugHunter(self._GetDefaultOption(False, 144 query=('dummy'))) 145 expected_issues = [{'issue_id': '0', 'title': 'title', 'author': 'author', 146 'status': 'Unconfirmed', 'state': 'open', 147 'content': 'http://www.content.com', 148 'comments': '', 'labels': ['label1'], 149 'urls': ['http://www.content.com']}] 150 self.assertEquals(expected_issues, bh.GetIssues()) 151 152 153 class MockSmtp(object): 154 """A mock class for SMTP.""" 155 156 def __init__(self, server): 157 pass 158 159 def sendmail(self, sender_email_address, receivers_email_addresses, 160 msg): 161 # TODO(imasaki): Do something here. 162 return True 163 164 def quit(self): 165 pass 166 167 168 class BugHunterUtilsTest(unittest.TestCase): 169 """Unit tests for the Bug Hunter utility.""" 170 171 def testStripHTML(self): 172 self.assertEquals(BugHunterUtils.StripHTML('<p>X</p>'), 'X') 173 174 def testStripHTMLEmpty(self): 175 self.assertEquals(BugHunterUtils.StripHTML(''), '') 176 177 def testSendEmail(self): 178 smtplib.SMTP = MockSmtp 179 self.assertEqual(BugHunterUtils.SendEmail('message', 'sender_email_address', 180 'receivers_email_addresses', 181 'subject'), 182 True) 183