class MboxGeneratedHeaderTest(TestCase): fixtures = ['default_states', 'default_events'] def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.user = create_user() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, delegate=self.user, content='') self.patch.save() def testPatchworkIdHeader(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'X-Patchwork-Id: %d' % self.patch.id) def testPatchworkDelegateHeader(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'X-Patchwork-Delegate: %s' % self.user.email)
class MboxPassThroughHeaderTest(TestCase): """ Test that we see 'Cc' and 'To' headers passed through from original message to mbox view """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.cc_header = 'Cc: CC Person <*****@*****.**>' self.to_header = 'To: To Person <*****@*****.**>' self.patch = Patch(project = defaults.project, msgid = 'p1', name = 'testpatch', submitter = self.person, content = '') def testCCHeader(self): self.patch.headers = self.cc_header + '\n' self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, self.cc_header) def testToHeader(self): self.patch.headers = self.to_header + '\n' self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, self.to_header)
def _insert_patch(self): patch = Patch(project=defaults.project, submitter=defaults.patch_author_person, msgid=defaults.patch_name, content=defaults.patch) patch.save() return patch
def setUp(self): defaults.project.save() self.user = create_maintainer(defaults.project) self.client.login(username=self.user.username, password=self.user.username) self.properties_form_id = 'patchform-properties' self.url = reverse('patchwork.views.patch.list', args=[defaults.project.linkname]) self.base_data = { 'action': 'Update', 'project': str(defaults.project.id), 'form': 'patchlistform', 'archived': '*', 'delegate': '*', 'state': '*' } self.patches = [] for name in ['patch one', 'patch two', 'patch three']: patch = Patch(project=defaults.project, msgid=name, name=name, content='', submitter=Person.objects.get(user=self.user)) patch.save() self.patches.append(patch)
class MboxPatchResponseTest(TestCase): fixtures = ['default_states'] """ Test that the mbox view appends the Acked-by from a patch comment """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, diff='', content='comment 1 text\nAcked-by: 1\n') self.patch.save() comment = Comment(submission=self.patch, msgid='p2', submitter=self.person, content='comment 2 text\nAcked-by: 2\n') comment.save() def testPatchResponse(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'Acked-by: 1\nAcked-by: 2\n')
class MboxPatchSplitResponseTest(TestCase): """ Test that the mbox view appends the Acked-by from a patch comment, and places it before an '---' update line. """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project = defaults.project, msgid = 'p1', name = 'testpatch', submitter = self.person, content = '') self.patch.save() comment = Comment(patch = self.patch, msgid = 'p1', submitter = self.person, content = 'comment 1 text\nAcked-by: 1\n---\nupdate\n') comment.save() comment = Comment(patch = self.patch, msgid = 'p2', submitter = self.person, content = 'comment 2 text\nAcked-by: 2\n') comment.save() def testPatchResponse(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'Acked-by: 1\nAcked-by: 2\n')
class UTF8HeaderPatchViewTest(UTF8PatchViewTest): fixtures = ['default_states', 'default_events'] patch_filename = '0002-utf-8.patch' patch_encoding = 'utf-8' patch_author_name = u'P\xe4tch Author' def setUp(self): defaults.project.save() self.patch_author = Person(name=self.patch_author_name, email=defaults.patch_author_person.email) self.patch_author.save() self.patch_content = read_patch(self.patch_filename, encoding=self.patch_encoding) self.patch = Patch(project=defaults.project, msgid='x', name=defaults.patch_name, submitter=self.patch_author, content=self.patch_content) self.patch.save() self.client = Client() def tearDown(self): self.patch.delete() self.patch_author.delete() defaults.project.delete()
class MboxCommentPostcriptUnchangedTest(TestCase): """ Test that the mbox view doesn't change the postscript part of a mail. There where always a missing blank right after the postscript delimiter '---' and an additional newline right before. """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project = defaults.project, msgid = 'p1', name = 'testpatch', submitter = self.person, content = '') self.patch.save() self.txt = 'some comment\n---\n some/file | 1 +\n' comment = Comment(patch = self.patch, msgid = 'p1', submitter = self.person, content = self.txt) comment.save() def testCommentUnchanged(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, self.txt) self.txt += "\n" self.assertNotContains(response, self.txt)
class MboxDateHeaderTest(TestCase): """ Test that the date provided in the patch mail view is correct """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project = defaults.project, msgid = 'p1', name = 'testpatch', submitter = self.person, content = '') self.patch.save() def testDateHeader(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) mail = email.message_from_string(response.content) mail_date = dateutil.parser.parse(mail['Date']) # patch dates are all in UTC patch_date = self.patch.date.replace(tzinfo=dateutil.tz.tzutc(), microsecond=0) self.assertEqual(mail_date, patch_date) def testSuppliedDateHeader(self): hour_offset = 3 tz = dateutil.tz.tzoffset(None, hour_offset * 60 * 60) date = datetime.datetime.utcnow() - datetime.timedelta(days = 1) date = date.replace(tzinfo=tz, microsecond=0) self.patch.headers = 'Date: %s\n' % date.strftime("%a, %d %b %Y %T %z") self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) mail = email.message_from_string(response.content) mail_date = dateutil.parser.parse(mail['Date']) self.assertEqual(mail_date, date)
class MboxPatchSplitResponseTest(TestCase): fixtures = ['default_states', 'default_events'] """ Test that the mbox view appends the Acked-by from a patch comment, and places it before an '---' update line. """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') self.patch.save() comment = Comment(patch=self.patch, msgid='p1', submitter=self.person, content='comment 1 text\nAcked-by: 1\n---\nupdate\n') comment.save() comment = Comment(patch=self.patch, msgid='p2', submitter=self.person, content='comment 2 text\nAcked-by: 2\n') comment.save() def testPatchResponse(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'Acked-by: 1\nAcked-by: 2\n')
class MboxCommentPostcriptUnchangedTest(TestCase): fixtures = ['default_states', 'default_events'] """ Test that the mbox view doesn't change the postscript part of a mail. There where always a missing blank right after the postscript delimiter '---' and an additional newline right before. """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') self.patch.save() self.txt = 'some comment\n---\n some/file | 1 +\n' comment = Comment(patch=self.patch, msgid='p1', submitter=self.person, content=self.txt) comment.save() def testCommentUnchanged(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, self.txt) self.txt += "\n" self.assertNotContains(response, self.txt)
class MboxGeneratedHeaderTest(TestCase): fixtures = ['default_states'] def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.user = create_user() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, delegate=self.user, content='') self.patch.save() def testPatchworkIdHeader(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'X-Patchwork-Id: %d' % self.patch.id) def testPatchworkDelegateHeader(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'X-Patchwork-Delegate: %s' % self.user.email)
def _insert_patch(self): patch = Patch(project=defaults.project, submitter=defaults.patch_author_person, msgid=make_msgid(), content=defaults.patch, date=self.last_patch_ts) self.last_patch_ts += datetime.timedelta(0, 1) patch.save() return patch
def setUp(self): defaults.project.save() for (name, email, date) in self.patchmeta: patch_name = 'testpatch' + name person = Person(name = name, email = email) person.save() patch = Patch(project = defaults.project, msgid = patch_name, submitter = person, content = '', date = date) patch.save()
def testList(self): defaults.project.save() defaults.patch_author_person.save() patch = Patch(project = defaults.project, submitter = defaults.patch_author_person, msgid = defaults.patch_name, content = defaults.patch) patch.save() patches = self.rpc.patch_list() self.assertEqual(len(patches), 1) self.assertEqual(patches[0]['id'], patch.id)
def testList(self): defaults.project.save() defaults.patch_author_person.save() patch = Patch(project=defaults.project, submitter=defaults.patch_author_person, msgid=defaults.patch_name, content=defaults.patch) patch.save() patches = self.rpc.patch_list() self.assertEqual(len(patches), 1) self.assertEqual(patches[0]['id'], patch.id)
def setUp(self): defaults.project.save() for (name, email, date) in self.patchmeta: patch_name = 'testpatch' + name person = Person(name=name, email=email) person.save() patch = Patch(project=defaults.project, msgid=patch_name, submitter=person, content='', date=date) patch.save()
def create_patches(count=1): """Create 'count' unique patches.""" defaults.project.save() defaults.patch_author_person.save() patches = [] for i in range(0, count): patch = Patch(project=defaults.project, submitter=defaults.patch_author_person, msgid=make_msgid(), name='testpatch%d' % (i + 1), diff=defaults.patch) patch.save() patches.append(patch) return patches
def testPatchSubmitterExpiry(self): defaults.project.save() defaults.patch_author_person.save() # someone submits a patch... patch = Patch(project=defaults.project, msgid='*****@*****.**', name='test patch', submitter=defaults.patch_author_person, content=defaults.patch) patch.save() # ... then starts registration... date = ((datetime.datetime.now() - EmailConfirmation.validity) - datetime.timedelta(hours=1)) userid = 'test-user' user = User.objects.create_user(userid, defaults.patch_author_person.email, userid) user.is_active = False user.date_joined = user.last_login = date user.save() self.assertEqual(user.email, patch.submitter.email) conf = EmailConfirmation(type='registration', user=user, email=user.email) conf.date = date conf.save() # ... which expires do_expiry() # we should see no matching user self.assertFalse( User.objects.filter(email=patch.submitter.email).exists()) # but the patch and person should still be present self.assertTrue( Person.objects.filter(pk=defaults.patch_author_person.pk).exists()) self.assertTrue(Patch.objects.filter(pk=patch.pk).exists()) # and there should be no user associated with the person self.assertEqual( Person.objects.get(pk=defaults.patch_author_person.pk).user, None)
def setUp(self, patch_count=3): patch_names = ['testpatch%d' % (i) for i in range(1, patch_count+1)] self.user = create_user() self.client.login(username = self.user.username, password = self.user.username) defaults.project.save() self.bundle = Bundle(owner = self.user, project = defaults.project, name = 'testbundle') self.bundle.save() self.patches = [] for patch_name in patch_names: patch = Patch(project = defaults.project, msgid = patch_name, name = patch_name, submitter = Person.objects.get(user = self.user), content = '') patch.save() self.patches.append(patch)
def setUp(self): defaults.project.save() self.user = create_maintainer(defaults.project) self.client.login(username = self.user.username, password = self.user.username) self.properties_form_id = 'patchform-properties' self.url = reverse( 'patchwork.views.patch.list', args = [defaults.project.linkname]) self.base_data = { 'action': 'Update', 'project': str(defaults.project.id), 'form': 'patchlistform', 'archived': '*', 'delegate': '*', 'state': '*'} self.patches = [] for name in ['patch one', 'patch two', 'patch three']: patch = Patch(project = defaults.project, msgid = name, name = name, content = '', submitter = Person.objects.get(user = self.user)) patch.save() self.patches.append(patch)
def testPatchSubmitterExpiry(self): defaults.project.save() defaults.patch_author_person.save() # someone submits a patch... patch = Patch(project=defaults.project, msgid='*****@*****.**', name='test patch', submitter=defaults.patch_author_person, content=defaults.patch) patch.save() # ... then starts registration... date = ((datetime.datetime.now() - EmailConfirmation.validity) - datetime.timedelta(hours=1)) userid = 'test-user' user = User.objects.create_user( userid, defaults.patch_author_person.email, userid) user.is_active = False user.date_joined = user.last_login = date user.save() self.assertEqual(user.email, patch.submitter.email) conf = EmailConfirmation(type='registration', user=user, email=user.email) conf.date = date conf.save() # ... which expires do_expiry() # we should see no matching user self.assertFalse(User.objects.filter(email=patch.submitter.email) .exists()) # but the patch and person should still be present self.assertTrue(Person.objects.filter( pk=defaults.patch_author_person.pk).exists()) self.assertTrue(Patch.objects.filter(pk=patch.pk).exists()) # and there should be no user associated with the person self.assertEqual(Person.objects.get( pk=defaults.patch_author_person.pk).user, None)
def testPullRequestEvent(self): submitter = Person() submitter.save() patch = Patch(project=self.project, pull_url="git://foo.bar master", submitter=submitter) patch.save() # n events for n series, +1 for the pull request above events = self.get_json('/projects/%(project_id)s/events/') self.assertEqual(events['count'], self.n_series + 1) prs = filter(lambda r: r['name'] == 'pull-request-new', events['results']) prs = list(prs) self.assertEqual(len(prs), 1) self.assertEqual(prs[0]['patch'], patch.id) self.assertEqual(prs[0]['parameters']['pull_url'], patch.pull_url)
def create_patch(**kwargs): """Create 'Patch' object.""" num = Patch.objects.count() values = { 'submitter': create_person(), 'delegate': None, 'project': create_project(), 'msgid': make_msgid(), 'name': 'testpatch%d' % num, 'headers': '', 'content': '', 'diff': SAMPLE_DIFF, } values.update(kwargs) patch = Patch(**values) patch.save() return patch
class MboxAuthorship(TestCase): fixtures = ['default_states', 'default_events'] """ Test that the From in mbox is the same as in the original header """ def setUp(self): defaults.project.save() self.original_author = "Orignal Author <*****@*****.**>" self.person = defaults.patch_author_person self.person.name = "Changed Name" self.person.email = "*****@*****.**" self.person.save() def testWithChangedPersonName(self): self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='', headers='From: {}'.format(self.original_author)) self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, 'From: {}'.format(self.original_author)) self.assertNotContains(response, self.person.name) self.assertNotContains(response, self.person.email) def testWithoutTheHeaderFallback(self): """ this should fallback to the Person """ self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='', headers='') self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains( response, 'From: {} <{}>'.format(self.person.name, self.person.email))
class MboxBrokenFromHeaderTest(TestCase): """ Test that a person with characters outside ASCII in his name do produce correct From header. As RFC 2822 state we must retain the <*****@*****.**> format for the mail while the name part may be coded in some ways. """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.name = u'©ool guŷ' self.person.save() self.patch = Patch(project = defaults.project, msgid = 'p1', name = 'testpatch', submitter = self.person, content = '') def testFromHeader(self): self.patch.save() from_email = '<' + self.person.email + '>' response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, from_email)
class UTF8PatchViewTest(TestCase): fixtures = ['default_states', 'default_events'] patch_filename = '0002-utf-8.patch' patch_encoding = 'utf-8' def setUp(self): defaults.project.save() defaults.patch_author_person.save() self.patch_content = read_patch(self.patch_filename, encoding=self.patch_encoding) self.patch = Patch(project=defaults.project, msgid='x', name=defaults.patch_name, submitter=defaults.patch_author_person, content=self.patch_content) self.patch.save() self.client = Client() def testPatchView(self): response = self.client.get('/patch/%d/' % self.patch.id) self.assertContains(response, self.patch.name) def testMboxView(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertEqual(response.status_code, 200) self.assertTrue( self.patch.content in response.content.decode(self.patch_encoding)) def testRawView(self): response = self.client.get('/patch/%d/raw/' % self.patch.id) self.assertEqual(response.status_code, 200) self.assertEqual(response.content.decode(self.patch_encoding), self.patch.content) def tearDown(self): self.patch.delete() defaults.patch_author_person.delete() defaults.project.delete()
class MboxPatchworkLink(TestCase): fixtures = ['default_states', 'default_events'] """ Test that the mbox view appends the patch link """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') self.patch.save() def testPatchResponse(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id, {'link': 'Patchwork'}) m = re.search(r'^Patchwork:.*/%d/$' % self.patch.pk, response.content, re.M) self.assertTrue(m)
class UTF8HeaderPatchViewTest(UTF8PatchViewTest): patch_filename = '0002-utf-8.patch' patch_encoding = 'utf-8' patch_author_name = u'P\xe4tch Author' def setUp(self): defaults.project.save() self.patch_author = Person(name = self.patch_author_name, email = defaults.patch_author_person.email) self.patch_author.save() self.patch_content = read_patch(self.patch_filename, encoding = self.patch_encoding) self.patch = Patch(project = defaults.project, msgid = 'x', name = defaults.patch_name, submitter = self.patch_author, content = self.patch_content) self.patch.save() self.client = Client() def tearDown(self): self.patch.delete() self.patch_author.delete() defaults.project.delete()
class UTF8PatchViewTest(TestCase): fixtures = ['default_states', 'default_events'] patch_filename = '0002-utf-8.patch' patch_encoding = 'utf-8' def setUp(self): defaults.project.save() defaults.patch_author_person.save() self.patch_content = read_patch(self.patch_filename, encoding = self.patch_encoding) self.patch = Patch(project = defaults.project, msgid = 'x', name = defaults.patch_name, submitter = defaults.patch_author_person, content = self.patch_content) self.patch.save() self.client = Client() def testPatchView(self): response = self.client.get('/patch/%d/' % self.patch.id) self.assertContains(response, self.patch.name) def testMboxView(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertEquals(response.status_code, 200) self.assertTrue(self.patch.content in \ response.content.decode(self.patch_encoding)) def testRawView(self): response = self.client.get('/patch/%d/raw/' % self.patch.id) self.assertEquals(response.status_code, 200) self.assertEquals(response.content.decode(self.patch_encoding), self.patch.content) def tearDown(self): self.patch.delete() defaults.patch_author_person.delete() defaults.project.delete()
class MboxDateHeaderTest(TestCase): fixtures = ['default_states', 'default_events'] """ Test that the date provided in the patch mail view is correct """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') self.patch.save() def testDateHeader(self): response = self.client.get('/patch/%d/mbox/' % self.patch.id) mail = email.message_from_string(response.content) mail_date = dateutil.parser.parse(mail['Date']) # patch dates are all in UTC patch_date = self.patch.date.replace(tzinfo=dateutil.tz.tzutc(), microsecond=0) self.assertEqual(mail_date, patch_date) def testSuppliedDateHeader(self): hour_offset = 3 tz = dateutil.tz.tzoffset(None, hour_offset * 60 * 60) date = datetime.datetime.utcnow() - datetime.timedelta(days=1) date = date.replace(tzinfo=tz, microsecond=0) self.patch.headers = 'Date: %s\n' % date.strftime("%a, %d %b %Y %T %z") self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) mail = email.message_from_string(response.content) mail_date = dateutil.parser.parse(mail['Date']) self.assertEqual(mail_date, date)
class MboxBrokenFromHeaderTest(TestCase): fixtures = ['default_states', 'default_events'] """ Test that a person with characters outside ASCII in his name do produce correct From header. As RFC 2822 state we must retain the <*****@*****.**> format for the mail while the name part may be coded in some ways. """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.name = u'©ool guŷ' self.person.save() self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') def testFromHeader(self): self.patch.save() from_email = '<' + self.person.email + '>' response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, from_email)
class MboxPassThroughHeaderTest(TestCase): fixtures = ['default_states', 'default_events'] """ Test that we see 'Cc' and 'To' headers passed through from original message to mbox view """ def setUp(self): defaults.project.save() self.person = defaults.patch_author_person self.person.save() self.cc_header = 'Cc: CC Person <*****@*****.**>' self.to_header = 'To: To Person <*****@*****.**>' self.date_header = 'Date: Fri, 7 Jun 2013 15:42:54 +1000' self.patch = Patch(project=defaults.project, msgid='p1', name='testpatch', submitter=self.person, content='') def testCCHeader(self): self.patch.headers = self.cc_header + '\n' self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, self.cc_header) def testToHeader(self): self.patch.headers = self.to_header + '\n' self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, self.to_header) def testDateHeader(self): self.patch.headers = self.date_header + '\n' self.patch.save() response = self.client.get('/patch/%d/mbox/' % self.patch.id) self.assertContains(response, self.date_header)
class PatchNotificationEmailTest(TestCase): def setUp(self): self.project = defaults.project self.project.send_notifications = True self.project.save() self.submitter = defaults.patch_author_person self.submitter.save() self.patch = Patch(project = self.project, msgid = 'testpatch', name = 'testpatch', content = '', submitter = self.submitter) self.patch.save() def tearDown(self): self.patch.delete() self.submitter.delete() self.project.delete() def _expireNotifications(self, **kwargs): timestamp = datetime.datetime.now() - \ datetime.timedelta(minutes = settings.NOTIFICATION_DELAY_MINUTES + 1) qs = PatchChangeNotification.objects.all() if kwargs: qs = qs.filter(**kwargs) qs.update(last_modified = timestamp) def testNoNotifications(self): self.assertEquals(send_notifications(), []) def testNoReadyNotifications(self): """ We shouldn't see immediate notifications""" PatchChangeNotification(patch = self.patch, orig_state = self.patch.state).save() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 0) def testNotifications(self): PatchChangeNotification(patch = self.patch, orig_state = self.patch.state).save() self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertEquals(msg.to, [self.submitter.email]) self.assertTrue(self.patch.get_absolute_url() in msg.body) def testNotificationEscaping(self): self.patch.name = 'Patch name with " character' self.patch.save() PatchChangeNotification(patch = self.patch, orig_state = self.patch.state).save() self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertEquals(msg.to, [self.submitter.email]) self.assertFalse('"' in msg.body) def testNotificationOptout(self): """ensure opt-out addresses don't get notifications""" PatchChangeNotification(patch = self.patch, orig_state = self.patch.state).save() self._expireNotifications() EmailOptout(email = self.submitter.email).save() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 0) def testNotificationMerge(self): patches = [self.patch, Patch(project = self.project, msgid = 'testpatch-2', name = 'testpatch 2', content = '', submitter = self.submitter)] for patch in patches: patch.save() PatchChangeNotification(patch = patch, orig_state = patch.state).save() self.assertEquals(PatchChangeNotification.objects.count(), len(patches)) self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertTrue(patches[0].get_absolute_url() in msg.body) self.assertTrue(patches[1].get_absolute_url() in msg.body) def testUnexpiredNotificationMerge(self): """Test that when there are multiple pending notifications, with at least one within the notification delay, that other notifications are held""" patches = [self.patch, Patch(project = self.project, msgid = 'testpatch-2', name = 'testpatch 2', content = '', submitter = self.submitter)] for patch in patches: patch.save() PatchChangeNotification(patch = patch, orig_state = patch.state).save() self.assertEquals(PatchChangeNotification.objects.count(), len(patches)) self._expireNotifications() # update one notification, to bring it out of the notification delay patches[0].state = State.objects.exclude(pk = patches[0].state.pk)[0] patches[0].save() # the updated notification should prevent the other from being sent errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 0) # expire the updated notification self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertTrue(patches[0].get_absolute_url() in msg.body) self.assertTrue(patches[1].get_absolute_url() in msg.body)
class PatchNotificationEmailTest(TestCase): fixtures = ['default_states', 'default_events'] def setUp(self): self.project = defaults.project self.project.send_notifications = True self.project.save() self.submitter = defaults.patch_author_person self.submitter.save() self.patch = Patch(project=self.project, msgid='testpatch', name='testpatch', content='', submitter=self.submitter) self.patch.save() def tearDown(self): self.patch.delete() self.submitter.delete() self.project.delete() def _expireNotifications(self, **kwargs): timestamp = datetime.datetime.now() - \ datetime.timedelta(minutes = settings.NOTIFICATION_DELAY_MINUTES + 1) qs = PatchChangeNotification.objects.all() if kwargs: qs = qs.filter(**kwargs) qs.update(last_modified=timestamp) def testNoNotifications(self): self.assertEquals(send_notifications(), []) def testNoReadyNotifications(self): """ We shouldn't see immediate notifications""" PatchChangeNotification(patch=self.patch, orig_state=self.patch.state).save() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 0) def testNotifications(self): PatchChangeNotification(patch=self.patch, orig_state=self.patch.state).save() self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertEquals(msg.to, [self.submitter.email]) self.assertTrue(self.patch.get_absolute_url() in msg.body) def testNotificationEscaping(self): self.patch.name = 'Patch name with " character' self.patch.save() PatchChangeNotification(patch=self.patch, orig_state=self.patch.state).save() self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertEquals(msg.to, [self.submitter.email]) self.assertFalse('"' in msg.body) def testNotificationOptout(self): """ensure opt-out addresses don't get notifications""" PatchChangeNotification(patch=self.patch, orig_state=self.patch.state).save() self._expireNotifications() EmailOptout(email=self.submitter.email).save() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 0) def testNotificationMerge(self): patches = [ self.patch, Patch(project=self.project, msgid='testpatch-2', name='testpatch 2', content='', submitter=self.submitter) ] for patch in patches: patch.save() PatchChangeNotification(patch=patch, orig_state=patch.state).save() self.assertEquals(PatchChangeNotification.objects.count(), len(patches)) self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertTrue(patches[0].get_absolute_url() in msg.body) self.assertTrue(patches[1].get_absolute_url() in msg.body) def testUnexpiredNotificationMerge(self): """Test that when there are multiple pending notifications, with at least one within the notification delay, that other notifications are held""" patches = [ self.patch, Patch(project=self.project, msgid='testpatch-2', name='testpatch 2', content='', submitter=self.submitter) ] for patch in patches: patch.save() PatchChangeNotification(patch=patch, orig_state=patch.state).save() self.assertEquals(PatchChangeNotification.objects.count(), len(patches)) self._expireNotifications() # update one notification, to bring it out of the notification delay patches[0].state = State.objects.exclude(pk=patches[0].state.pk)[0] patches[0].save() # the updated notification should prevent the other from being sent errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 0) # expire the updated notification self._expireNotifications() errors = send_notifications() self.assertEquals(errors, []) self.assertEquals(len(mail.outbox), 1) msg = mail.outbox[0] self.assertTrue(patches[0].get_absolute_url() in msg.body) self.assertTrue(patches[1].get_absolute_url() in msg.body)
class PatchNotificationModelTest(TestCase): fixtures = ['default_states', 'default_events'] """Tests for the creation & update of the PatchChangeNotification model""" def setUp(self): self.project = defaults.project self.project.send_notifications = True self.project.save() self.submitter = defaults.patch_author_person self.submitter.save() self.patch = Patch(project=self.project, msgid='testpatch', name='testpatch', content='', submitter=self.submitter) def tearDown(self): self.patch.delete() self.submitter.delete() self.project.delete() def testPatchCreation(self): """Ensure we don't get a notification on create""" self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0) def testPatchUninterestingChange(self): """Ensure we don't get a notification for "uninteresting" changes""" self.patch.save() self.patch.archived = True self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0) def testPatchChange(self): """Ensure we get a notification for interesting patch changes""" self.patch.save() oldstate = self.patch.state state = State.objects.exclude(pk=oldstate.pk)[0] self.patch.state = state self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) notification = PatchChangeNotification.objects.all()[0] self.assertEqual(notification.patch, self.patch) self.assertEqual(notification.orig_state, oldstate) def testNotificationCancelled(self): """Ensure we cancel notifications that are no longer valid""" self.patch.save() oldstate = self.patch.state state = State.objects.exclude(pk=oldstate.pk)[0] self.patch.state = state self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) self.patch.state = oldstate self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0) def testNotificationUpdated(self): """Ensure we update notifications when the patch has a second change, but keep the original patch details""" self.patch.save() oldstate = self.patch.state newstates = State.objects.exclude(pk=oldstate.pk)[:2] self.patch.state = newstates[0] self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) notification = PatchChangeNotification.objects.all()[0] self.assertEqual(notification.orig_state, oldstate) orig_timestamp = notification.last_modified self.patch.state = newstates[1] self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) notification = PatchChangeNotification.objects.all()[0] self.assertEqual(notification.orig_state, oldstate) self.assertTrue(notification.last_modified >= orig_timestamp) def testProjectNotificationsDisabled(self): """Ensure we don't see notifications created when a project is configured not to send them""" self.project.send_notifications = False self.project.save() self.patch.save() oldstate = self.patch.state state = State.objects.exclude(pk=oldstate.pk)[0] self.patch.state = state self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0)
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = clean_header(mail.get('X-Patchwork-Hint', '')) if hint and hint.lower() == 'ignore': logger.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: logger.error('Failed to find a project for email') return # parse metadata msgid = clean_header(mail.get('Message-Id')) if not msgid: raise ValueError("Broken 'Message-Id' header") msgid = msgid[:255] author = find_author(mail) subject = mail.get('Subject') name, prefixes = clean_subject(subject, [project.linkname]) is_comment = subject_check(subject) x, n = parse_series_marker(prefixes) version = parse_version(name, prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) # parse content if not is_comment: diff, message = find_patch_content(mail) else: diff, message = find_comment_content(mail) if not (diff or message): return # nothing to work with pull_url = parse_pull_request(message) # build objects if not is_comment and (diff or pull_url): # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate_by_header(mail) if not delegate and diff: filenames = find_filenames(diff) delegate = find_delegate_by_filename(project, filenames) # if we don't have a series marker, we will never have an existing # series to match against. series = None if n: series = find_series(project, mail) else: x = n = 1 # We will create a new series if: # - there is no existing series to assign this patch to, or # - there is an existing series, but it already has a patch with this # number in it if not series or ( SeriesPatch.objects.filter(series=series, number=x).count()): series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # NOTE(stephenfin) We must save references for series. We # do this to handle the case where a later patch is # received first. Without storing references, it would not # be possible to identify the relationship between patches # as the earlier patch does not reference the later one. for ref in refs + [msgid]: ref = ref[:255] # we don't want duplicates try: # we could have a ref to a previous series. (For # example, a series sent in reply to another # series.) That should not create a series ref # for this series, so check for the msg-id only, # not the msg-id/series pair. SeriesReference.objects.get(msgid=ref, series__project=project) except SeriesReference.DoesNotExist: SeriesReference.objects.create(series=series, msgid=ref) patch = Patch( msgid=msgid, project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() logger.debug('Patch saved') # add to a series if we have found one, and we have a numbered # patch. Don't add unnumbered patches (for example diffs sent # in reply, or just messages with random refs/in-reply-tos) if series and x: series.add_patch(patch, x) return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not is_comment: if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True except CoverLetter.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: is_cover_letter = True if is_cover_letter: author.save() # we don't use 'find_series' here as a cover letter will # always be the first item in a thread, thus the references # could only point to a different series or unrelated # message try: series = SeriesReference.objects.get( msgid=msgid, series__project=project).series except SeriesReference.DoesNotExist: series = None if not series: series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # we don't save the in-reply-to or references fields # for a cover letter, as they can't refer to the same # series SeriesReference.objects.get_or_create(series=series, msgid=msgid) cover_letter = CoverLetter( msgid=msgid, project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message) cover_letter.save() logger.debug('Cover letter saved') series.add_cover_letter(cover_letter) return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return author.save() comment = Comment( submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() logger.debug('Comment saved') return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = mail.get('X-Patchwork-Hint', '').lower() if hint == 'ignore': logger.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: logger.error('Failed to find a project for email') return # parse content diff, message = find_content(project, mail) if not (diff or message): return # nothing to work with msgid = mail.get('Message-Id').strip() author = find_author(mail) subject = mail.get('Subject') name, prefixes = clean_subject(subject, [project.linkname]) is_comment = subject_check(subject) x, n = parse_series_marker(prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) pull_url = parse_pull_request(message) # build objects if not is_comment and (diff or pull_url): # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate(mail) if not delegate and diff: filenames = find_filenames(diff) delegate = auto_delegate(project, filenames) patch = Patch(msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() logger.debug('Patch saved') return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not is_comment: if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True except CoverLetter.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: is_cover_letter = True if is_cover_letter: author.save() cover_letter = CoverLetter(msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message) cover_letter.save() logger.debug('Cover letter saved') return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return if is_comment and diff: message += diff author.save() comment = Comment(submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() logger.debug('Comment saved') return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = clean_header(mail.get('X-Patchwork-Hint', '')) if hint and hint.lower() == 'ignore': logger.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: logger.error('Failed to find a project for email') return # parse metadata msgid = clean_header(mail.get('Message-Id')) if not msgid: raise ValueError("Broken 'Message-Id' header") msgid = msgid[:255] author = find_author(mail) subject = mail.get('Subject') name, prefixes = clean_subject(subject, [project.linkname]) is_comment = subject_check(subject) x, n = parse_series_marker(prefixes) version = parse_version(name, prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) # parse content if not is_comment: diff, message = find_patch_content(mail) else: diff, message = find_comment_content(mail) if not (diff or message): return # nothing to work with pull_url = parse_pull_request(message) # build objects if not is_comment and (diff or pull_url): # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate_by_header(mail) if not delegate and diff: filenames = find_filenames(diff) delegate = find_delegate_by_filename(project, filenames) # if we don't have a series marker, we will never have an existing # series to match against. series = None if n: series = find_series(project, mail) else: x = n = 1 # We will create a new series if: # - there is no existing series to assign this patch to, or # - there is an existing series, but it already has a patch with this # number in it if not series or (SeriesPatch.objects.filter(series=series, number=x).count()): series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # NOTE(stephenfin) We must save references for series. We # do this to handle the case where a later patch is # received first. Without storing references, it would not # be possible to identify the relationship between patches # as the earlier patch does not reference the later one. for ref in refs + [msgid]: ref = ref[:255] # we don't want duplicates try: # we could have a ref to a previous series. (For # example, a series sent in reply to another # series.) That should not create a series ref # for this series, so check for the msg-id only, # not the msg-id/series pair. SeriesReference.objects.get(msgid=ref, series__project=project) except SeriesReference.DoesNotExist: SeriesReference.objects.create(series=series, msgid=ref) patch = Patch(msgid=msgid, project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() logger.debug('Patch saved') # add to a series if we have found one, and we have a numbered # patch. Don't add unnumbered patches (for example diffs sent # in reply, or just messages with random refs/in-reply-tos) if series and x: series.add_patch(patch, x) return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not is_comment: if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # if no match, this is a new cover letter is_cover_letter = True except CoverLetter.MultipleObjectsReturned: # if multiple cover letters are found, just ignore pass else: is_cover_letter = True if is_cover_letter: author.save() # we don't use 'find_series' here as a cover letter will # always be the first item in a thread, thus the references # could only point to a different series or unrelated # message try: series = SeriesReference.objects.get( msgid=msgid, series__project=project).series except SeriesReference.DoesNotExist: series = None if not series: series = Series(project=project, date=date, submitter=author, version=version, total=n) series.save() # we don't save the in-reply-to or references fields # for a cover letter, as they can't refer to the same # series SeriesReference.objects.get_or_create(series=series, msgid=msgid) cover_letter = CoverLetter(msgid=msgid, project=project, name=name[:255], date=date, headers=headers, submitter=author, content=message) cover_letter.save() logger.debug('Cover letter saved') series.add_cover_letter(cover_letter) return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return author.save() comment = Comment(submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() logger.debug('Comment saved') return comment
def parse_mail(mail, list_id=None): """Parse a mail and add to the database. Args: mail (`mbox.Mail`): Mail to parse and add. list_id (str): Mailing list ID Returns: None """ # some basic sanity checks if 'From' not in mail: raise ValueError("Missing 'From' header") if 'Subject' not in mail: raise ValueError("Missing 'Subject' header") if 'Message-Id' not in mail: raise ValueError("Missing 'Message-Id' header") hint = mail.get('X-Patchwork-Hint', '').lower() if hint == 'ignore': LOGGER.debug("Ignoring email due to 'ignore' hint") return if list_id: project = find_project_by_id(list_id) else: project = find_project_by_header(mail) if project is None: LOGGER.error('Failed to find a project for email') return # parse content diff, message = find_content(project, mail) if not (diff or message): return # nothing to work with msgid = mail.get('Message-Id').strip() author = find_author(mail) name, prefixes = clean_subject(mail.get('Subject'), [project.linkname]) x, n = parse_series_marker(prefixes) refs = find_references(mail) date = find_date(mail) headers = find_headers(mail) pull_url = find_pull_request(message) # build objects if diff or pull_url: # patches or pull requests # we delay the saving until we know we have a patch. author.save() delegate = find_delegate(mail) if not delegate and diff: filenames = patch_get_filenames(diff) delegate = auto_delegate(project, filenames) patch = Patch( msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message, diff=diff, pull_url=pull_url, delegate=delegate, state=find_state(mail)) patch.save() LOGGER.debug('Patch saved') return patch elif x == 0: # (potential) cover letters # if refs are empty, it's implicitly a cover letter. If not, # however, we need to see if a match already exists and, if # not, assume that it is indeed a new cover letter is_cover_letter = False if not refs == []: try: CoverLetter.objects.all().get(name=name) except CoverLetter.DoesNotExist: # no match => new cover is_cover_letter = True else: is_cover_letter = True if is_cover_letter: author.save() cover_letter = CoverLetter( msgid=msgid, project=project, name=name, date=date, headers=headers, submitter=author, content=message) cover_letter.save() LOGGER.debug('Cover letter saved') return cover_letter # comments # we only save comments if we have the parent email submission = find_submission_for_comment(project, refs) if not submission: return author.save() comment = Comment( submission=submission, msgid=msgid, date=date, headers=headers, submitter=author, content=message) comment.save() LOGGER.debug('Comment saved') return comment
class PatchTagsTest(TransactionTestCase): ACK = 1 REVIEW = 2 TEST = 3 fixtures = ['default_tags', 'default_states', 'default_events'] def assertTagsEqual(self, patch, acks, reviews, tests): patch = Patch.objects.get(pk=patch.pk) def count(name): try: return patch.patchtag_set.get(tag__name=name).count except PatchTag.DoesNotExist: return 0 counts = ( count(name='Acked-by'), count(name='Reviewed-by'), count(name='Tested-by'), ) self.assertEqual(counts, (acks, reviews, tests)) def create_tag(self, tagtype=None): tags = { self.ACK: 'Acked', self.REVIEW: 'Reviewed', self.TEST: 'Tested' } if tagtype not in tags: return '' return '%s-by: %s\n' % (tags[tagtype], self.tagger) def create_tag_comment(self, patch, tagtype=None): comment = Comment(patch=patch, msgid=str(datetime.datetime.now()), submitter=defaults.patch_author_person, content=self.create_tag(tagtype)) comment.save() return comment def setUp(self): settings.DEBUG = True project = Project(linkname='test-project', name='Test Project', use_tags=True) project.save() defaults.patch_author_person.save() self.patch = Patch(project=project, msgid='x', name=defaults.patch_name, submitter=defaults.patch_author_person, content='') self.patch.save() self.tagger = 'Test Tagger <*****@*****.**>' def tearDown(self): self.patch.delete() def testNoComments(self): self.assertTagsEqual(self.patch, 0, 0, 0) def testNoTagComment(self): self.create_tag_comment(self.patch, None) self.assertTagsEqual(self.patch, 0, 0, 0) def testSingleComment(self): self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) def testMultipleComments(self): self.create_tag_comment(self.patch, self.ACK) self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 2, 0, 0) def testMultipleCommentTypes(self): self.create_tag_comment(self.patch, self.ACK) self.create_tag_comment(self.patch, self.REVIEW) self.create_tag_comment(self.patch, self.TEST) self.assertTagsEqual(self.patch, 1, 1, 1) def testCommentAdd(self): self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 2, 0, 0) def testCommentUpdate(self): comment = self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) comment.content += self.create_tag(self.ACK) comment.save() self.assertTagsEqual(self.patch, 2, 0, 0) def testCommentDelete(self): comment = self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) comment.delete() self.assertTagsEqual(self.patch, 0, 0, 0) def testSingleCommentMultipleTags(self): comment = self.create_tag_comment(self.patch, self.ACK) comment.content += self.create_tag(self.REVIEW) comment.save() self.assertTagsEqual(self.patch, 1, 1, 0) def testMultipleCommentsMultipleTags(self): c1 = self.create_tag_comment(self.patch, self.ACK) c1.content += self.create_tag(self.REVIEW) c1.save() self.assertTagsEqual(self.patch, 1, 1, 0)
class PatchNotificationModelTest(TestCase): """Tests for the creation & update of the PatchChangeNotification model""" def setUp(self): self.project = defaults.project self.project.send_notifications = True self.project.save() self.submitter = defaults.patch_author_person self.submitter.save() self.patch = Patch(project = self.project, msgid = 'testpatch', name = 'testpatch', content = '', submitter = self.submitter) def tearDown(self): self.patch.delete() self.submitter.delete() self.project.delete() def testPatchCreation(self): """Ensure we don't get a notification on create""" self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0) def testPatchUninterestingChange(self): """Ensure we don't get a notification for "uninteresting" changes""" self.patch.save() self.patch.archived = True self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0) def testPatchChange(self): """Ensure we get a notification for interesting patch changes""" self.patch.save() oldstate = self.patch.state state = State.objects.exclude(pk = oldstate.pk)[0] self.patch.state = state self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) notification = PatchChangeNotification.objects.all()[0] self.assertEqual(notification.patch, self.patch) self.assertEqual(notification.orig_state, oldstate) def testNotificationCancelled(self): """Ensure we cancel notifications that are no longer valid""" self.patch.save() oldstate = self.patch.state state = State.objects.exclude(pk = oldstate.pk)[0] self.patch.state = state self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) self.patch.state = oldstate self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0) def testNotificationUpdated(self): """Ensure we update notifications when the patch has a second change, but keep the original patch details""" self.patch.save() oldstate = self.patch.state newstates = State.objects.exclude(pk = oldstate.pk)[:2] self.patch.state = newstates[0] self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) notification = PatchChangeNotification.objects.all()[0] self.assertEqual(notification.orig_state, oldstate) orig_timestamp = notification.last_modified self.patch.state = newstates[1] self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 1) notification = PatchChangeNotification.objects.all()[0] self.assertEqual(notification.orig_state, oldstate) self.assertTrue(notification.last_modified > orig_timestamp) def testProjectNotificationsDisabled(self): """Ensure we don't see notifications created when a project is configured not to send them""" self.project.send_notifications = False self.project.save() self.patch.save() oldstate = self.patch.state state = State.objects.exclude(pk = oldstate.pk)[0] self.patch.state = state self.patch.save() self.assertEqual(PatchChangeNotification.objects.count(), 0)
class PatchTagsTest(TransactionTestCase): ACK = 1 REVIEW = 2 TEST = 3 fixtures = ['default_tags', 'default_states'] def assertTagsEqual(self, patch, acks, reviews, tests): patch = Patch.objects.get(pk=patch.pk) def count(name): try: return patch.patchtag_set.get(tag__name=name).count except PatchTag.DoesNotExist: return 0 counts = ( count(name='Acked-by'), count(name='Reviewed-by'), count(name='Tested-by'), ) self.assertEqual(counts, (acks, reviews, tests)) def create_tag(self, tagtype = None): tags = { self.ACK: 'Acked', self.REVIEW: 'Reviewed', self.TEST: 'Tested' } if tagtype not in tags: return '' return '%s-by: %s\n' % (tags[tagtype], self.tagger) def create_tag_comment(self, patch, tagtype = None): comment = Comment(patch=patch, msgid=str(datetime.datetime.now()), submitter=defaults.patch_author_person, content=self.create_tag(tagtype)) comment.save() return comment def setUp(self): settings.DEBUG = True project = Project(linkname='test-project', name='Test Project', use_tags=True) project.save() defaults.patch_author_person.save() self.patch = Patch(project=project, msgid='x', name=defaults.patch_name, submitter=defaults.patch_author_person, content='') self.patch.save() self.tagger = 'Test Tagger <*****@*****.**>' def tearDown(self): self.patch.delete() def testNoComments(self): self.assertTagsEqual(self.patch, 0, 0, 0) def testNoTagComment(self): self.create_tag_comment(self.patch, None) self.assertTagsEqual(self.patch, 0, 0, 0) def testSingleComment(self): self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) def testMultipleComments(self): self.create_tag_comment(self.patch, self.ACK) self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 2, 0, 0) def testMultipleCommentTypes(self): self.create_tag_comment(self.patch, self.ACK) self.create_tag_comment(self.patch, self.REVIEW) self.create_tag_comment(self.patch, self.TEST) self.assertTagsEqual(self.patch, 1, 1, 1) def testCommentAdd(self): self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 2, 0, 0) def testCommentUpdate(self): comment = self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) comment.content += self.create_tag(self.ACK) comment.save() self.assertTagsEqual(self.patch, 2, 0, 0) def testCommentDelete(self): comment = self.create_tag_comment(self.patch, self.ACK) self.assertTagsEqual(self.patch, 1, 0, 0) comment.delete() self.assertTagsEqual(self.patch, 0, 0, 0) def testSingleCommentMultipleTags(self): comment = self.create_tag_comment(self.patch, self.ACK) comment.content += self.create_tag(self.REVIEW) comment.save() self.assertTagsEqual(self.patch, 1, 1, 0) def testMultipleCommentsMultipleTags(self): c1 = self.create_tag_comment(self.patch, self.ACK) c1.content += self.create_tag(self.REVIEW) c1.save() self.assertTagsEqual(self.patch, 1, 1, 0)
class PatchChecksTest(TransactionTestCase): fixtures = ['default_tags', 'default_states'] def setUp(self): project = defaults.project defaults.project.save() defaults.patch_author_person.save() self.patch = Patch(project=project, msgid='x', name=defaults.patch_name, submitter=defaults.patch_author_person, diff='') self.patch.save() self.user = create_user() def create_check(self, **kwargs): check_values = { 'patch': self.patch, 'user': self.user, 'date': dt.now(), 'state': Check.STATE_SUCCESS, 'target_url': 'http://example.com/', 'description': '', 'context': 'intel/jenkins-ci', } for key in check_values: if key in kwargs: check_values[key] = kwargs[key] check = Check(**check_values) check.save() return check def assertCheckEqual(self, patch, check_state): self.assertEqual(self.patch.combined_check_state, check_state) def assertChecksEqual(self, patch, checks=None): if not checks: checks = [] self.assertEqual(len(self.patch.checks), len(checks)) self.assertEqual( sorted(self.patch.checks, key=lambda check: check.id), sorted(checks, key=lambda check: check.id)) def assertCheckCountEqual(self, patch, total, state_counts=None): if not state_counts: state_counts = {} counts = self.patch.check_count self.assertEqual(self.patch.check_set.count(), total) for state in state_counts: self.assertEqual(counts[state], state_counts[state]) # also check the ones we didn't explicitly state for state, _ in Check.STATE_CHOICES: if state not in state_counts: self.assertEqual(counts[state], 0) def tearDown(self): self.patch.delete() def test_checks__no_checks(self): self.assertChecksEqual(self.patch, []) def test_checks__single_check(self): check = self.create_check() self.assertChecksEqual(self.patch, [check]) def test_checks__multiple_checks(self): check_a = self.create_check() check_b = self.create_check(context='new-context/test1') self.assertChecksEqual(self.patch, [check_a, check_b]) def test_checks__duplicate_checks(self): self.create_check(date=(dt.now() - timedelta(days=1))) check = self.create_check() # this isn't a realistic scenario (dates shouldn't be set by user so # they will always increment), but it's useful to verify the removal # of older duplicates by the function self.create_check(date=(dt.now() - timedelta(days=2))) self.assertChecksEqual(self.patch, [check]) def test_check_count__no_checks(self): self.assertCheckCountEqual(self.patch, 0) def test_check_count__single_check(self): self.create_check() self.assertCheckCountEqual(self.patch, 1, {Check.STATE_SUCCESS: 1}) def test_check_count__multiple_checks(self): self.create_check(date=(dt.now() - timedelta(days=1))) self.create_check(context='new/test1') self.assertCheckCountEqual(self.patch, 2, {Check.STATE_SUCCESS: 2}) def test_check_count__duplicate_check_same_state(self): self.create_check(date=(dt.now() - timedelta(days=1))) self.assertCheckCountEqual(self.patch, 1, {Check.STATE_SUCCESS: 1}) self.create_check() self.assertCheckCountEqual(self.patch, 2, {Check.STATE_SUCCESS: 1}) def test_check_count__duplicate_check_new_state(self): self.create_check(date=(dt.now() - timedelta(days=1))) self.assertCheckCountEqual(self.patch, 1, {Check.STATE_SUCCESS: 1}) self.create_check(state=Check.STATE_FAIL) self.assertCheckCountEqual(self.patch, 2, {Check.STATE_FAIL: 1}) def test_check__no_checks(self): self.assertCheckEqual(self.patch, Check.STATE_PENDING) def test_check__single_check(self): self.create_check() self.assertCheckEqual(self.patch, Check.STATE_SUCCESS) def test_check__failure_check(self): self.create_check() self.create_check(context='new/test1', state=Check.STATE_FAIL) self.assertCheckEqual(self.patch, Check.STATE_FAIL) def test_check__warning_check(self): self.create_check() self.create_check(context='new/test1', state=Check.STATE_WARNING) self.assertCheckEqual(self.patch, Check.STATE_WARNING) def test_check__success_check(self): self.create_check() self.create_check(context='new/test1') self.assertCheckEqual(self.patch, Check.STATE_SUCCESS)