You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by rj...@apache.org on 2014/02/13 06:08:08 UTC
svn commit: r1567849 [11/17] - in /bloodhound/vendor/trac: 1.0-stable/
current/ current/contrib/ current/contrib/cgi-bin/
current/contrib/workflow/ current/doc/ current/doc/utils/
current/sample-plugins/ current/sample-plugins/permissions/ current/samp...
Modified: bloodhound/vendor/trac/current/trac/ticket/tests/notification.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/notification.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/notification.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/notification.py Thu Feb 13 05:08:02 2014
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2005-2009 Edgewall Software
+# Copyright (C) 2005-2013 Edgewall Software
# Copyright (C) 2005-2006 Emmanuel Blot <em...@free.fr>
# All rights reserved.
#
@@ -16,21 +16,22 @@
# (lsmithson@open-networks.co.uk) extensible Python SMTP Server
#
-from trac.util.datefmt import utc
-from trac.ticket.model import Ticket
-from trac.ticket.notification import TicketNotifyEmail
-from trac.test import EnvironmentStub, Mock, MockPerm
-from trac.tests.notification import SMTPThreadedServer, parse_smtp_message, \
- smtp_address
-
import base64
-from datetime import datetime
import os
import quopri
import re
import unittest
+from datetime import datetime
+
+from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.tests import compat
+from trac.tests.notification import SMTP_TEST_PORT, SMTPThreadedServer,\
+ parse_smtp_message, smtp_address
+from trac.ticket.model import Ticket
+from trac.ticket.notification import TicketNotifyEmail
+from trac.ticket.web_ui import TicketModule
+from trac.util.datefmt import utc
-SMTP_TEST_PORT = 7000 + os.getpid() % 1000
MAXBODYWIDTH = 76
notifysuite = None
@@ -48,7 +49,7 @@ class NotificationTestCase(unittest.Test
'joe.user@example.net, joe.bar@example.net')
self.env.config.set('notification', 'use_public_cc', 'true')
self.env.config.set('notification', 'smtp_port', str(SMTP_TEST_PORT))
- self.env.config.set('notification', 'smtp_server','localhost')
+ self.env.config.set('notification', 'smtp_server', 'localhost')
self.req = Mock(href=self.env.href, abs_href=self.env.abs_href, tz=utc,
perm=MockPerm())
@@ -61,9 +62,9 @@ class NotificationTestCase(unittest.Test
"""To/Cc recipients"""
ticket = Ticket(self.env)
ticket['reporter'] = '"Joe User" < joe.user@example.org >'
- ticket['owner'] = 'joe.user@example.net'
- ticket['cc'] = 'joe.user@example.com, joe.bar@example.org, ' \
- 'joe.bar@example.net'
+ ticket['owner'] = 'joe.user@example.net'
+ ticket['cc'] = 'joe.user@example.com, joe.bar@example.org, ' \
+ 'joe.bar@example.net'
ticket['summary'] = 'Foo'
ticket.insert()
tn = TicketNotifyEmail(self.env)
@@ -72,17 +73,17 @@ class NotificationTestCase(unittest.Test
# checks there is no duplicate in the recipient list
rcpts = []
for r in recipients:
- self.failIf(r in rcpts)
+ self.assertNotIn(r, rcpts)
rcpts.append(r)
# checks that all cc recipients have been notified
cc_list = self.env.config.get('notification', 'smtp_always_cc')
cc_list = "%s, %s" % (cc_list, ticket['cc'])
for r in cc_list.replace(',', ' ').split():
- self.failIf(r not in recipients)
+ self.assertIn(r, recipients)
# checks that owner has been notified
- self.failIf(smtp_address(ticket['owner']) not in recipients)
+ self.assertIn(smtp_address(ticket['owner']), recipients)
# checks that reporter has been notified
- self.failIf(smtp_address(ticket['reporter']) not in recipients)
+ self.assertIn(smtp_address(ticket['reporter']), recipients)
def test_no_recipient(self):
"""No recipient case"""
@@ -97,9 +98,9 @@ class NotificationTestCase(unittest.Test
recipients = notifysuite.smtpd.get_recipients()
message = notifysuite.smtpd.get_message()
# checks that no message has been sent
- self.failIf(recipients)
- self.failIf(sender)
- self.failIf(message)
+ self.assertEqual([], recipients)
+ self.assertIsNone(sender)
+ self.assertIsNone(message)
def test_cc_only(self):
"""Notification w/o explicit recipients but Cc: (#3101)"""
@@ -112,30 +113,30 @@ class NotificationTestCase(unittest.Test
# checks that all cc recipients have been notified
cc_list = self.env.config.get('notification', 'smtp_always_cc')
for r in cc_list.replace(',', ' ').split():
- self.failIf(r not in recipients)
+ self.assertIn(r, recipients)
def test_structure(self):
"""Basic SMTP message structure (headers, body)"""
ticket = Ticket(self.env)
ticket['reporter'] = '"Joe User" <jo...@example.org>'
- ticket['owner'] = 'joe.user@example.net'
- ticket['cc'] = 'joe.user@example.com, joe.bar@example.org, ' \
- 'joe.bar@example.net'
+ ticket['owner'] = 'joe.user@example.net'
+ ticket['cc'] = 'joe.user@example.com, joe.bar@example.org, ' \
+ 'joe.bar@example.net'
ticket['summary'] = 'This is a summary'
ticket.insert()
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# checks for header existence
- self.failIf(not headers)
- # checks for body existance
- self.failIf(not body)
+ self.assertTrue(headers)
+ # checks for body existence
+ self.assertTrue(body)
# checks for expected headers
- self.failIf('Date' not in headers)
- self.failIf('Subject' not in headers)
- self.failIf('Message-ID' not in headers)
- self.failIf('From' not in headers)
+ self.assertIn('Date', headers)
+ self.assertIn('Subject', headers)
+ self.assertIn('Message-ID', headers)
+ self.assertIn('From', headers)
def test_date(self):
"""Date format compliance (RFC822)
@@ -148,7 +149,7 @@ class NotificationTestCase(unittest.Test
date_re = re.compile(date_str)
# python time module does not detect incorrect time values
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
- months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', \
+ months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
tz = ['UT', 'GMT', 'EST', 'EDT', 'CST', 'CDT', 'MST', 'MDT',
'PST', 'PDT']
@@ -159,17 +160,17 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
- self.failIf('Date' not in headers)
+ headers, body = parse_smtp_message(message)
+ self.assertIn('Date', headers)
mo = date_re.match(headers['Date'])
- self.failIf(not mo)
+ self.assertTrue(mo)
if mo.group('day'):
- self.failIf(mo.group('day') not in days)
- self.failIf(int(mo.group('dm')) not in range(1, 32))
- self.failIf(mo.group('month') not in months)
- self.failIf(int(mo.group('hour')) not in range(0, 24))
+ self.assertIn(mo.group('day'), days)
+ self.assertIn(int(mo.group('dm')), range(1, 32))
+ self.assertIn(mo.group('month'), months)
+ self.assertIn(int(mo.group('hour')), range(0, 24))
if mo.group('tz'):
- self.failIf(mo.group('tz') not in tz)
+ self.assertIn(mo.group('tz'), tz)
def test_bcc_privacy(self):
"""Visibility of recipients"""
@@ -186,15 +187,15 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
if public:
# Msg should have a To list
- self.failIf('To' not in headers)
+ self.assertIn('To', headers)
# Extract the list of 'To' recipients from the message
to = [rcpt.strip() for rcpt in headers['To'].split(',')]
else:
# Msg should not have a To list
- self.failIf('To' in headers)
+ self.assertNotIn('To', headers)
# Extract the list of 'To' recipients from the message
to = []
# Extract the list of 'Cc' recipients from the message
@@ -207,18 +208,18 @@ class NotificationTestCase(unittest.Test
for rcpt in cclist:
# Each recipient of the 'Cc' list should appear
# in the 'Cc' header
- self.failIf(rcpt not in cc)
+ self.assertIn(rcpt, cc)
# Check the message has actually been sent to the recipients
- self.failIf(rcpt not in rcptlist)
+ self.assertIn(rcpt, rcptlist)
# Build the list of the expected 'Bcc' recipients
bccrcpt = self.env.config.get('notification', 'smtp_always_bcc')
bcclist = [bccr.strip() for bccr in bccrcpt.split(',')]
for rcpt in bcclist:
# Check none of the 'Bcc' recipients appears
# in the 'To' header
- self.failIf(rcpt in to)
+ self.assertNotIn(rcpt, to)
# Check the message has actually been sent to the recipients
- self.failIf(rcpt not in rcptlist)
+ self.assertIn(rcpt, rcptlist)
run_bcc_feature(True)
run_bcc_feature(False)
@@ -238,26 +239,26 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# Msg should not have a 'To' header
if not enabled:
- self.failIf('To' in headers)
+ self.assertNotIn('To', headers)
else:
tolist = [addr.strip() for addr in headers['To'].split(',')]
# Msg should have a 'Cc' field
- self.failIf('Cc' not in headers)
+ self.assertIn('Cc', headers)
cclist = [addr.strip() for addr in headers['Cc'].split(',')]
if enabled:
# Msg should be delivered to the reporter
- self.failIf(ticket['reporter'] not in tolist)
+ self.assertIn(ticket['reporter'], tolist)
else:
# Msg should not be delivered to joeuser
- self.failIf(ticket['reporter'] in cclist)
+ self.assertNotIn(ticket['reporter'], cclist)
# Msg should still be delivered to the always_cc list
- self.failIf(self.env.config.get('notification',
- 'smtp_always_cc') not in cclist)
+ self.assertIn(self.env.config.get('notification',
+ 'smtp_always_cc'), cclist)
# Validate with and without the short addr option enabled
- for enable in [False, True]:
+ for enable in False, True:
_test_short_login(enable)
def test_default_domain(self):
@@ -282,21 +283,21 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# Msg should always have a 'Cc' field
- self.failIf('Cc' not in headers)
+ self.assertIn('Cc', headers)
cclist = [addr.strip() for addr in headers['Cc'].split(',')]
- self.failIf('joewithdom@example.com' not in cclist)
- self.failIf('joe.bar@example.net' not in cclist)
+ self.assertIn('joewithdom@example.com', cclist)
+ self.assertIn('joe.bar@example.net', cclist)
if not enabled:
- self.failIf(len(cclist) != 2)
- self.failIf('joenodom' in cclist)
+ self.assertEqual(2, len(cclist))
+ self.assertNotIn('joenodom', cclist)
else:
- self.failIf(len(cclist) != 3)
- self.failIf('joenodom@example.org' not in cclist)
+ self.assertEqual(3, len(cclist))
+ self.assertIn('joenodom@example.org', cclist)
# Validate with and without a default domain
- for enable in [False, True]:
+ for enable in False, True:
_test_default_domain(enable)
def test_email_map(self):
@@ -317,15 +318,15 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# Msg should always have a 'To' field
- self.failIf('To' not in headers)
+ self.assertIn('To', headers)
tolist = [addr.strip() for addr in headers['To'].split(',')]
# 'To' list should have been resolved to the real email address
- self.failIf('user-joe@example.com' not in tolist)
- self.failIf('user-jim@example.com' not in tolist)
- self.failIf('joeuser' in tolist)
- self.failIf('jim@domain' in tolist)
+ self.assertIn('user-joe@example.com', tolist)
+ self.assertIn('user-jim@example.com', tolist)
+ self.assertNotIn('joeuser', tolist)
+ self.assertNotIn('jim@domain', tolist)
def test_from_author(self):
"""Using the reporter or change author as the notification sender"""
@@ -346,7 +347,7 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
self.assertEqual('"Joe User" <us...@example.com>', headers['From'])
# Ticket change uses the change author
ticket['summary'] = 'Modified summary'
@@ -354,7 +355,7 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=False, modtime=ticket['changetime'])
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
self.assertEqual('"Jim User" <us...@example.com>', headers['From'])
# Known author without name uses e-mail address only
ticket['summary'] = 'Final summary'
@@ -362,7 +363,7 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=False, modtime=ticket['changetime'])
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
self.assertEqual('user-noname@example.com', headers['From'])
# Known author without e-mail uses smtp_from and smtp_from_name
ticket['summary'] = 'Other summary'
@@ -370,7 +371,7 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=False, modtime=ticket['changetime'])
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
self.assertEqual('"My Trac" <tr...@example.com>', headers['From'])
# Unknown author with name and e-mail address
ticket['summary'] = 'Some summary'
@@ -378,7 +379,7 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=False, modtime=ticket['changetime'])
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
self.assertEqual('"Test User" <te...@example.com>', headers['From'])
# Unknown author with e-mail address only
ticket['summary'] = 'Some summary'
@@ -386,7 +387,7 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=False, modtime=ticket['changetime'])
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
self.assertEqual('test@example.com', headers['From'])
# Unknown author uses smtp_from and smtp_from_name
ticket['summary'] = 'Better summary'
@@ -394,7 +395,7 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=False, modtime=ticket['changetime'])
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
self.assertEqual('"My Trac" <tr...@example.com>', headers['From'])
def test_ignore_domains(self):
@@ -412,16 +413,16 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# Msg should always have a 'To' field
- self.failIf('To' not in headers)
+ self.assertIn('To', headers)
tolist = [addr.strip() for addr in headers['To'].split(',')]
# 'To' list should not contain addresses with non-SMTP domains
- self.failIf('kerberos@example.com' in tolist)
- self.failIf('kerberos@example.org' in tolist)
+ self.assertNotIn('kerberos@example.com', tolist)
+ self.assertNotIn('kerberos@example.org', tolist)
# 'To' list should have been resolved to the actual email address
- self.failIf('kerb@example.net' not in tolist)
- self.failIf(len(tolist) != 1)
+ self.assertIn('kerb@example.net', tolist)
+ self.assertEqual(1, len(tolist))
def test_admit_domains(self):
"""SMTP domain inclusion"""
@@ -436,16 +437,16 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# Msg should always have a 'To' field
- self.failIf('Cc' not in headers)
+ self.assertIn('Cc', headers)
cclist = [addr.strip() for addr in headers['Cc'].split(',')]
# 'Cc' list should contain addresses with SMTP included domains
- self.failIf('joe.user@localdomain' not in cclist)
- self.failIf('joe.user@server' not in cclist)
+ self.assertIn('joe.user@localdomain', cclist)
+ self.assertIn('joe.user@server', cclist)
# 'Cc' list should not contain non-FQDN domains
- self.failIf('joe.user@unknown' in cclist)
- self.failIf(len(cclist) != 2+2)
+ self.assertNotIn('joe.user@unknown', cclist)
+ self.assertEqual(4, len(cclist))
def test_multiline_header(self):
"""Encoded headers split into multiple lines"""
@@ -458,11 +459,11 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# Discards the project name & ticket number
subject = headers['Subject']
summary = subject[subject.find(':')+2:]
- self.failIf(ticket['summary'] != summary)
+ self.assertEqual(ticket['summary'], summary)
def test_mimebody_b64(self):
"""MIME Base64/utf-8 encoding"""
@@ -472,8 +473,7 @@ class NotificationTestCase(unittest.Test
ticket['summary'] = u'This is a long enough summary to cause Trac ' \
u'to generate a multi-line (2 lines) súmmäry'
ticket.insert()
- self._validate_mimebody((base64, 'base64', 'utf-8'), \
- ticket, True)
+ self._validate_mimebody((base64, 'base64', 'utf-8'), ticket, True)
def test_mimebody_qp(self):
"""MIME QP/utf-8 encoding"""
@@ -493,8 +493,7 @@ class NotificationTestCase(unittest.Test
ticket['reporter'] = 'joe.user'
ticket['summary'] = u'This is a summary'
ticket.insert()
- self._validate_mimebody((None, '7bit', 'utf-8'), \
- ticket, True)
+ self._validate_mimebody((None, '7bit', 'utf-8'), ticket, True)
def test_mimebody_none_8bit(self):
"""MIME None encoding resulting in 8bit"""
@@ -503,8 +502,7 @@ class NotificationTestCase(unittest.Test
ticket['reporter'] = 'joe.user'
ticket['summary'] = u'This is a summary for Jöe Usèr'
ticket.insert()
- self._validate_mimebody((None, '8bit', 'utf-8'), \
- ticket, True)
+ self._validate_mimebody((None, '8bit', 'utf-8'), ticket, True)
def test_md5_digest(self):
"""MD5 digest w/ non-ASCII recipient address (#3491)"""
@@ -518,12 +516,12 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
def test_updater(self):
"""No-self-notification option"""
- def _test_updater(disable):
- if disable:
+ def _test_updater(disabled):
+ if disabled:
self.env.config.set('notification', 'always_notify_updater',
'false')
ticket = Ticket(self.env)
@@ -538,19 +536,19 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=False, modtime=now)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
# checks for header existence
- self.failIf(not headers)
+ self.assertTrue(headers)
# checks for updater in the 'To' recipient list
- self.failIf('To' not in headers)
+ self.assertIn('To', headers)
tolist = [addr.strip() for addr in headers['To'].split(',')]
- if disable:
- self.failIf('joe.bar2@example.com' in tolist)
+ if disabled:
+ self.assertNotIn('joe.bar2@example.com', tolist)
else:
- self.failIf('joe.bar2@example.com' not in tolist)
+ self.assertIn('joe.bar2@example.com', tolist)
# Validate with and without a default domain
- for disable in [False, True]:
+ for disable in False, True:
_test_updater(disable)
def test_updater_only(self):
@@ -573,9 +571,9 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
recipients = notifysuite.smtpd.get_recipients()
- self.failIf(recipients is None)
- self.failIf(len(recipients) != 1)
- self.failIf(recipients[0] != 'joe@example.com')
+ self.assertIsNotNone(recipients)
+ self.assertEqual(1, len(recipients))
+ self.assertEqual(recipients[0], 'joe@example.com')
def test_updater_is_reporter(self):
"""Notification to reporter w/ updater option disabled (#3780)"""
@@ -598,38 +596,38 @@ class NotificationTestCase(unittest.Test
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
recipients = notifysuite.smtpd.get_recipients()
- self.failIf(recipients is None)
- self.failIf(len(recipients) != 1)
- self.failIf(recipients[0] != 'joe@example.org')
+ self.assertIsNotNone(recipients)
+ self.assertEqual(1, len(recipients))
+ self.assertEqual('joe@example.org', recipients[0])
def _validate_mimebody(self, mime, ticket, newtk):
"""Body of a ticket notification message"""
- (mime_decoder, mime_name, mime_charset) = mime
+ mime_decoder, mime_name, mime_charset = mime
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=newtk)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
- self.failIf('MIME-Version' not in headers)
- self.failIf('Content-Type' not in headers)
- self.failIf('Content-Transfer-Encoding' not in headers)
- self.failIf(not re.compile(r"1.\d").match(headers['MIME-Version']))
+ headers, body = parse_smtp_message(message)
+ self.assertIn('MIME-Version', headers)
+ self.assertIn('Content-Type', headers)
+ self.assertIn('Content-Transfer-Encoding', headers)
+ self.assertTrue(re.compile(r"1.\d").match(headers['MIME-Version']))
type_re = re.compile(r'^text/plain;\scharset="([\w\-\d]+)"$')
charset = type_re.match(headers['Content-Type'])
- self.failIf(not charset)
+ self.assertTrue(charset)
charset = charset.group(1)
- self.assertEqual(charset, mime_charset)
+ self.assertEqual(mime_charset, charset)
self.assertEqual(headers['Content-Transfer-Encoding'], mime_name)
# checks the width of each body line
for line in body.splitlines():
- self.failIf(len(line) > MAXBODYWIDTH)
- # attempts to decode the body, following the specified MIME endoding
+ self.assertTrue(len(line) <= MAXBODYWIDTH)
+ # attempts to decode the body, following the specified MIME encoding
# and charset
try:
if mime_decoder:
body = mime_decoder.decodestring(body)
body = unicode(body, charset)
except Exception, e:
- raise AssertionError, e
+ raise AssertionError(e)
# now processes each line of the body
bodylines = body.splitlines()
# body starts with one of more summary lines, first line is prefixed
@@ -637,25 +635,25 @@ class NotificationTestCase(unittest.Test
# finds the banner after the summary
banner_delim_re = re.compile(r'^\-+\+\-+$')
bodyheader = []
- while ( not banner_delim_re.match(bodylines[0]) ):
+ while not banner_delim_re.match(bodylines[0]):
bodyheader.append(bodylines.pop(0))
# summary should be present
- self.failIf(not bodyheader)
+ self.assertTrue(bodyheader)
# banner should not be empty
- self.failIf(not bodylines)
+ self.assertTrue(bodylines)
# extracts the ticket ID from the first line
- (tknum, bodyheader[0]) = bodyheader[0].split(' ', 1)
- self.assertEqual(tknum[0], '#')
+ tknum, bodyheader[0] = bodyheader[0].split(' ', 1)
+ self.assertEqual('#', tknum[0])
try:
tkid = int(tknum[1:-1])
- self.assertEqual(tkid, 1)
+ self.assertEqual(1, tkid)
except ValueError:
- raise AssertionError, "invalid ticket number"
- self.assertEqual(tknum[-1], ':')
+ raise AssertionError("invalid ticket number")
+ self.assertEqual(':', tknum[-1])
summary = ' '.join(bodyheader)
self.assertEqual(summary, ticket['summary'])
# now checks the banner contents
- self.failIf(not banner_delim_re.match(bodylines[0]))
+ self.assertTrue(banner_delim_re.match(bodylines[0]))
banner = True
footer = None
props = {}
@@ -667,11 +665,11 @@ class NotificationTestCase(unittest.Test
if banner:
# parse banner and fill in a property dict
properties = line.split('|')
- self.assertEqual(len(properties), 2)
+ self.assertEqual(2, len(properties))
for prop in properties:
if prop.strip() == '':
continue
- (k, v) = prop.split(':')
+ k, v = prop.split(':')
props[k.strip().lower()] = v.strip()
# detect footer marker (weak detection)
if not footer:
@@ -679,10 +677,10 @@ class NotificationTestCase(unittest.Test
footer = 0
continue
# check footer
- if footer != None:
+ if footer is not None:
footer += 1
# invalid footer detection
- self.failIf(footer > 3)
+ self.assertTrue(footer <= 3)
# check ticket link
if line[:11] == 'Ticket URL:':
ticket_link = self.env.abs_href.ticket(ticket.id)
@@ -693,12 +691,13 @@ class NotificationTestCase(unittest.Test
xlist = ['summary', 'description', 'comment', 'time', 'changetime']
# check banner content (field exists, msg value matches ticket value)
for p in [prop for prop in ticket.values.keys() if prop not in xlist]:
- self.failIf(not props.has_key(p))
+ self.assertIn(p, props)
# Email addresses might be obfuscated
if '@' in ticket[p] and '@' in props[p]:
- self.failIf(props[p].split('@')[0] != ticket[p].split('@')[0])
+ self.assertEqual(props[p].split('@')[0],
+ ticket[p].split('@')[0])
else:
- self.failIf(props[p] != ticket[p])
+ self.assertEqual(props[p], ticket[p])
def test_props_format_ambiwidth_single(self):
self.env.config.set('notification', 'mime_encoding', 'none')
@@ -827,7 +826,7 @@ Resolution: fixed |
Type: defect | Status: new
Priority: major | Milestone: milestone1
Component: Lorem ipsum dolor sit amet, | Version: 2.0
- consectetur adipisicing elit, sed do eiusmod | Keywords:
+ consectetur adipisicing elit, sed do eiusmod |
tempor incididunt ut labore et dolore magna |
aliqua. Ut enim ad minim veniam, quis nostrud |
exercitation ullamco laboris nisi ut aliquip |
@@ -837,7 +836,7 @@ Resolution: fixed |
Excepteur sint occaecat cupidatat non |
proident, sunt in culpa qui officia deserunt |
mollit anim id est laborum. |
-Resolution: fixed |"""
+Resolution: fixed | Keywords:"""
self._validate_props_format(formatted, ticket)
def test_props_format_wrap_leftside_unicode(self):
@@ -865,10 +864,10 @@ Resolution: fixed
Type: defect | Status: new
Priority: major | Milestone: milestone1
Component: Trac 㯠BSD ã©ã¤ã»ã³ã¹ã®ãã¨ã§é
å¸ | Version: 2.0
- ããã¦ãã¾ãã[1:]ãã®ã©ã¤ã»ã³ã¹ã®å
¨æã¯ãé
| Keywords:
+ ããã¦ãã¾ãã[1:]ãã®ã©ã¤ã»ã³ã¹ã®å
¨æã¯ãé
|
å¸ãã¡ã¤ã«ã«å«ã¾ãã¦ãã [3:COPYING] ãã¡ã¤ã« |
ã¨åããã®ã[2:ãªã³ã©ã¤ã³]ã§åç
§ã§ãã¾ãã |
-Resolution: fixed |"""
+Resolution: fixed | Keywords:"""
self._validate_props_format(formatted, ticket)
def test_props_format_wrap_rightside(self):
@@ -893,7 +892,7 @@ Resolution: fixed
u'culpa qui officia deserunt mollit anim id ' \
u'est laborum.'
ticket['component'] = u'component1'
- ticket['version'] = u'2.0'
+ ticket['version'] = u'2.0 Standard and International Edition'
ticket['resolution'] = u'fixed'
ticket['keywords'] = u''
ticket.insert()
@@ -901,8 +900,8 @@ Resolution: fixed
Reporter: anonymous | Owner: somebody
Type: defect | Status: new
Priority: major | Milestone: Lorem ipsum dolor sit amet,
- Component: component1 | consectetur adipisicing elit, sed do eiusmod
-Resolution: fixed | tempor incididunt ut labore et dolore magna
+ | consectetur adipisicing elit, sed do eiusmod
+ | tempor incididunt ut labore et dolore magna
| aliqua. Ut enim ad minim veniam, quis nostrud
| exercitation ullamco laboris nisi ut aliquip ex
| ea commodo consequat. Duis aute irure dolor in
@@ -911,8 +910,9 @@ Resolution: fixed | tempor incid
| occaecat cupidatat non proident, sunt in culpa
| qui officia deserunt mollit anim id est
| laborum.
- | Version: 2.0
- | Keywords:"""
+ Component: component1 | Version: 2.0 Standard and International
+ | Edition
+Resolution: fixed | Keywords:"""
self._validate_props_format(formatted, ticket)
def test_props_format_wrap_rightside_unicode(self):
@@ -937,10 +937,10 @@ Resolution: fixed | tempor incid
Reporter: anonymous | Owner: somebody
Type: defect | Status: new
Priority: major | Milestone: Trac å¨ç»è¿ä¿®æ¹çBSDåè®®ä¸åå¸ã
- Component: component1 | [1:]åè®®çå®æ´ææ¬å¯ä»¥[2:å¨çº¿æ¥ç]ä¹å¯å¨åå¸ç
-Resolution: fixed | ç [3:COPYING] æ件ä¸æ¾å°ã
- | Version: 2.0
- | Keywords:"""
+ | [1:]åè®®çå®æ´ææ¬å¯ä»¥[2:å¨çº¿æ¥ç]ä¹å¯å¨åå¸ç
+ | ç [3:COPYING] æ件ä¸æ¾å°ã
+ Component: component1 | Version: 2.0
+Resolution: fixed | Keywords:"""
self._validate_props_format(formatted, ticket)
def test_props_format_wrap_bothsides(self):
@@ -964,31 +964,33 @@ Resolution: fixed | ç [3:CO
u'occaecat cupidatat non proident, sunt in ' \
u'culpa qui officia deserunt mollit anim id ' \
u'est laborum.'
- ticket['component'] = ticket['milestone']
+ ticket['component'] = (u'Lorem ipsum dolor sit amet, consectetur '
+ u'adipisicing elit, sed do eiusmod tempor '
+ u'incididunt ut labore et dolore magna aliqua.')
ticket['version'] = u'2.0'
ticket['resolution'] = u'fixed'
- ticket['keywords'] = u''
+ ticket['keywords'] = u'Ut enim ad minim veniam, ....'
ticket.insert()
formatted = """\
Reporter: anonymous | Owner: somebody
Type: defect | Status: new
Priority: major | Milestone: Lorem ipsum dolor sit
- Component: Lorem ipsum dolor sit | amet, consectetur adipisicing elit,
- amet, consectetur adipisicing | sed do eiusmod tempor incididunt ut
- elit, sed do eiusmod tempor | labore et dolore magna aliqua. Ut
- incididunt ut labore et dolore | enim ad minim veniam, quis nostrud
- magna aliqua. Ut enim ad minim | exercitation ullamco laboris nisi
- veniam, quis nostrud exercitation | ut aliquip ex ea commodo consequat.
- ullamco laboris nisi ut aliquip | Duis aute irure dolor in
- ex ea commodo consequat. Duis | reprehenderit in voluptate velit
- aute irure dolor in reprehenderit | esse cillum dolore eu fugiat nulla
- in voluptate velit esse cillum | pariatur. Excepteur sint occaecat
- dolore eu fugiat nulla pariatur. | cupidatat non proident, sunt in
- Excepteur sint occaecat cupidatat | culpa qui officia deserunt mollit
- non proident, sunt in culpa qui | anim id est laborum.
- officia deserunt mollit anim id | Version: 2.0
- est laborum. | Keywords:
-Resolution: fixed |"""
+ | amet, consectetur adipisicing elit,
+ | sed do eiusmod tempor incididunt ut
+ | labore et dolore magna aliqua. Ut
+ | enim ad minim veniam, quis nostrud
+ | exercitation ullamco laboris nisi
+ | ut aliquip ex ea commodo consequat.
+ | Duis aute irure dolor in
+ | reprehenderit in voluptate velit
+ | esse cillum dolore eu fugiat nulla
+ Component: Lorem ipsum dolor sit | pariatur. Excepteur sint occaecat
+ amet, consectetur adipisicing | cupidatat non proident, sunt in
+ elit, sed do eiusmod tempor | culpa qui officia deserunt mollit
+ incididunt ut labore et dolore | anim id est laborum.
+ magna aliqua. | Version: 2.0
+Resolution: fixed | Keywords: Ut enim ad minim
+ | veniam, ...."""
self._validate_props_format(formatted, ticket)
def test_props_format_wrap_bothsides_unicode(self):
@@ -1011,7 +1013,7 @@ Resolution: fixed |""
u'ã«å«ã¾ãã¦ãã[3:CÐPYING]ãã¡ã¤' \
u'ã«ã¨åããã®ã[2:ãªã³ã©ã¤ã³]ã§' \
u'åç
§ã§ãã¾ãã'
- ticket['version'] = u'2.0'
+ ticket['version'] = u'2.0 International Edition'
ticket['resolution'] = u'fixed'
ticket['keywords'] = u''
ticket.insert()
@@ -1022,17 +1024,74 @@ Resolution: fixed |""
Component: Trac 㯠BSD ã©ã¤ã»ã³ã¹ | è®®ä¸åå¸ã[1:]åè®®çå®æ´ææ¬å¯ä»¥[2:
ã®ãã¨ã§é
å¸ããã¦ãã¾ãã[1:]ã | å¨çº¿æ¥ç]ä¹å¯å¨åå¸çç [3:COPYING]
ã®ã©ã¤ã»ã³ã¹ã®å
¨æã¯ãâ»é
å¸ãã¡ | æ件ä¸æ¾å°ã
- ã¤ã«ã«å«ã¾ãã¦ãã[3:CÐPYING]ã | Version: 2.0
- ã¡ã¤ã«ã¨åããã®ã[2:ãªã³ã©ã¤ã³] | Keywords:
+ ã¤ã«ã«å«ã¾ãã¦ãã[3:CÐPYING]ã | Version: 2.0 International
+ ã¡ã¤ã«ã¨åããã®ã[2:ãªã³ã©ã¤ã³] | Edition
ã§åç
§ã§ãã¾ãã |
-Resolution: fixed |"""
+Resolution: fixed | Keywords:"""
+ self._validate_props_format(formatted, ticket)
+
+ def test_props_format_wrap_ticket_10283(self):
+ self.env.config.set('notification', 'mime_encoding', 'none')
+ for name, value in (('blockedby', 'text'),
+ ('blockedby.label', 'Blocked by'),
+ ('blockedby.order', '6'),
+ ('blocking', 'text'),
+ ('blocking.label', 'Blocking'),
+ ('blocking.order', '5'),
+ ('deployment', 'text'),
+ ('deployment.label', 'Deployment state'),
+ ('deployment.order', '1'),
+ ('nodes', 'text'),
+ ('nodes.label', 'Related nodes'),
+ ('nodes.order', '3'),
+ ('privacy', 'text'),
+ ('privacy.label', 'Privacy sensitive'),
+ ('privacy.order', '2'),
+ ('sensitive', 'text'),
+ ('sensitive.label', 'Security sensitive'),
+ ('sensitive.order', '4')):
+ self.env.config.set('ticket-custom', name, value)
+
+ ticket = Ticket(self.env)
+ ticket['summary'] = u'This is a summary'
+ ticket['reporter'] = u'anonymous'
+ ticket['owner'] = u'somebody'
+ ticket['type'] = u'defect'
+ ticket['status'] = u'closed'
+ ticket['priority'] = u'normal'
+ ticket['milestone'] = u'iter_01'
+ ticket['component'] = u'XXXXXXXXXXXXXXXXXXXXXXXXXX'
+ ticket['resolution'] = u'fixed'
+ ticket['keywords'] = u''
+ ticket['deployment'] = ''
+ ticket['privacy'] = '0'
+ ticket['nodes'] = 'XXXXXXXXXX'
+ ticket['sensitive'] = '0'
+ ticket['blocking'] = ''
+ ticket['blockedby'] = ''
+ ticket.insert()
+
+ formatted = """\
+ Reporter: anonymous | Owner:
+ | somebody
+ Type: defect | Status:
+ | closed
+ Priority: normal | Milestone:
+ | iter_01
+ Component: XXXXXXXXXXXXXXXXXXXXXXXXXX | Resolution:
+ | fixed
+ Keywords: | Deployment state:
+ Privacy sensitive: 0 | Related nodes:
+ | XXXXXXXXXX
+Security sensitive: 0 | Blocking:
+ Blocked by: |"""
self._validate_props_format(formatted, ticket)
def _validate_props_format(self, expected, ticket):
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
message = notifysuite.smtpd.get_message()
- (headers, body) = parse_smtp_message(message)
+ headers, body = parse_smtp_message(message)
bodylines = body.splitlines()
# Extract ticket properties
delim_re = re.compile(r'^\-+\+\-+$')
@@ -1052,7 +1111,7 @@ Resolution: fixed |""
ticket.insert()
tn = TicketNotifyEmail(self.env)
tn.notify(ticket, newticket=True)
- self.assertNotEqual(None, notifysuite.smtpd.get_message())
+ self.assertIsNotNone(notifysuite.smtpd.get_message())
self.assertEqual('My Summary', ticket['summary'])
self.assertEqual('Some description', ticket['description'])
valid_fieldnames = set([f['name'] for f in ticket.fields])
@@ -1070,7 +1129,6 @@ Resolution: fixed |""
tn.get_message_id('foo')
-
class NotificationTestSuite(unittest.TestSuite):
"""Thin test suite wrapper to start and stop the SMTP test server"""
@@ -1079,18 +1137,19 @@ class NotificationTestSuite(unittest.Tes
unittest.TestSuite.__init__(self)
self.smtpd = SMTPThreadedServer(SMTP_TEST_PORT)
self.smtpd.start()
- self.addTest(unittest.makeSuite(NotificationTestCase, 'test'))
+ self.addTest(unittest.makeSuite(NotificationTestCase))
self.remaining = self.countTestCases()
def tear_down(self):
"""Reset the local SMTP test server"""
self.smtpd.cleanup()
- self.remaining = self.remaining-1
+ self.remaining -= 1
if self.remaining > 0:
return
# stop the SMTP test server when all tests have been completed
self.smtpd.stop()
+
def suite():
global notifysuite
if not notifysuite:
@@ -1098,4 +1157,4 @@ def suite():
return notifysuite
if __name__ == '__main__':
- unittest.TextTestRunner(verbosity=2).run(suite())
+ unittest.main(defaultTest='suite')
Modified: bloodhound/vendor/trac/current/trac/ticket/tests/query.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/query.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/query.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/query.py Thu Feb 13 05:08:02 2014
@@ -1,4 +1,18 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2004-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
from trac.test import Mock, EnvironmentStub, MockPerm, locale_en
+from trac.ticket.model import Ticket
from trac.ticket.query import Query, QueryModule, TicketQueryMacro
from trac.util.datefmt import utc
from trac.web.chrome import web_context
@@ -257,12 +271,14 @@ ORDER BY COALESCE(t.id,0)=0,t.id""" % {'
sql, args = query.get_sql()
foo = self.env.get_read_db().quote('foo')
self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value,%s.value AS %s
-FROM ticket AS t
- LEFT OUTER JOIN ticket_custom AS %s ON (id=%s.ticket AND %s.name='foo')
+"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value,t.%s AS %s
+FROM (
+ SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,
+ (SELECT c.value FROM ticket_custom c WHERE c.ticket=t.id AND c.name='foo') AS %s
+ FROM ticket AS t) AS t
LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
-WHERE ((COALESCE(%s.value,'')=%%s))
-ORDER BY COALESCE(t.id,0)=0,t.id""" % ((foo,) * 6))
+WHERE ((COALESCE(t.%s,'')=%%s))
+ORDER BY COALESCE(t.id,0)=0,t.id""" % ((foo,) * 4))
self.assertEqual(['something'], args)
tickets = query.execute(self.req)
@@ -272,15 +288,77 @@ ORDER BY COALESCE(t.id,0)=0,t.id""" % ((
sql, args = query.get_sql()
foo = self.env.get_read_db().quote('foo')
self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value,%s.value AS %s
-FROM ticket AS t
- LEFT OUTER JOIN ticket_custom AS %s ON (id=%s.ticket AND %s.name='foo')
+"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value,t.%s AS %s
+FROM (
+ SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,
+ (SELECT c.value FROM ticket_custom c WHERE c.ticket=t.id AND c.name='foo') AS %s
+ FROM ticket AS t) AS t
LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
-ORDER BY COALESCE(%s.value,'')='',%s.value,COALESCE(t.id,0)=0,t.id""" %
- ((foo,) * 7))
+ORDER BY COALESCE(t.%s,'')='',t.%s,COALESCE(t.id,0)=0,t.id""" %
+ ((foo,) * 5))
self.assertEqual([], args)
tickets = query.execute(self.req)
+ def test_constrained_by_id_ranges(self):
+ query = Query.from_string(self.env, 'id=42,44,51-55&order=id')
+ sql, args = query.get_sql()
+ self.assertEqualSQL(sql,
+"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
+FROM ticket AS t
+ LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
+WHERE ((t.id BETWEEN %s AND %s OR t.id IN (42,44)))
+ORDER BY COALESCE(t.id,0)=0,t.id""")
+ self.assertEqual([51, 55], args)
+
+ def test_constrained_by_id_and_custom_field(self):
+ self.env.config.set('ticket-custom', 'foo', 'text')
+ ticket = Ticket(self.env)
+ ticket['reporter'] = 'joe'
+ ticket['summary'] = 'Foo'
+ ticket['foo'] = 'blah'
+ ticket.insert()
+
+ query = Query.from_string(self.env, 'id=%d-42&foo=blah' % ticket.id)
+ tickets = query.execute(self.req)
+ self.assertEqual(1, len(tickets))
+ self.assertEqual(ticket.id, tickets[0]['id'])
+
+ query = Query.from_string(self.env, 'id=%d,42&foo=blah' % ticket.id)
+ tickets = query.execute(self.req)
+ self.assertEqual(1, len(tickets))
+ self.assertEqual(ticket.id, tickets[0]['id'])
+
+ query = Query.from_string(self.env, 'id=%d,42,43-84&foo=blah' %
+ ticket.id)
+ tickets = query.execute(self.req)
+ self.assertEqual(1, len(tickets))
+ self.assertEqual(ticket.id, tickets[0]['id'])
+
+ def test_too_many_custom_fields(self):
+ fields = ['col_%02d' % i for i in xrange(100)]
+ for f in fields:
+ self.env.config.set('ticket-custom', f, 'text')
+
+ ticket = Ticket(self.env)
+ ticket['reporter'] = 'joe'
+ ticket['summary'] = 'Foo'
+ for idx, f in enumerate(fields):
+ ticket[f] = '%d.%s' % (idx, f)
+ ticket.insert()
+
+ string = 'col_00=0.col_00&order=id&col=id&col=reporter&col=summary' + \
+ ''.join('&col=' + f for f in fields)
+ query = Query.from_string(self.env, string)
+ tickets = query.execute(self.req)
+ self.assertEqual(ticket.id, tickets[0]['id'])
+ self.assertEqual('joe', tickets[0]['reporter'])
+ self.assertEqual('Foo', tickets[0]['summary'])
+ self.assertEqual('0.col_00', tickets[0]['col_00'])
+ self.assertEqual('99.col_99', tickets[0]['col_99'])
+
+ query = Query.from_string(self.env, 'col_00=notfound')
+ self.assertEqual([], query.execute(self.req))
+
def test_constrained_by_multiple_owners(self):
query = Query.from_string(self.env, 'owner=someone|someone_else',
order='id')
@@ -579,9 +657,9 @@ class TicketQueryMacroTestCase(unittest.
def suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(QueryTestCase, 'test'))
- suite.addTest(unittest.makeSuite(QueryLinksTestCase, 'test'))
- suite.addTest(unittest.makeSuite(TicketQueryMacroTestCase, 'test'))
+ suite.addTest(unittest.makeSuite(QueryTestCase))
+ suite.addTest(unittest.makeSuite(QueryLinksTestCase))
+ suite.addTest(unittest.makeSuite(TicketQueryMacroTestCase))
return suite
if __name__ == '__main__':
Modified: bloodhound/vendor/trac/current/trac/ticket/tests/report.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/report.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/report.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/report.py Thu Feb 13 05:08:02 2014
@@ -1,4 +1,15 @@
# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
import doctest
@@ -103,7 +114,7 @@ class ReportTestCase(unittest.TestCase):
def suite():
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite(trac.ticket.report))
- suite.addTest(unittest.makeSuite(ReportTestCase, 'test'))
+ suite.addTest(unittest.makeSuite(ReportTestCase))
return suite
if __name__ == '__main__':
Modified: bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py Thu Feb 13 05:08:02 2014
@@ -1,3 +1,16 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
from trac.test import EnvironmentStub
from trac.ticket.roadmap import *
from trac.core import ComponentManager
@@ -10,53 +23,53 @@ class TicketGroupStatsTestCase(unittest.
self.stats = TicketGroupStats('title', 'units')
def test_init(self):
- self.assertEquals('title', self.stats.title, 'title incorrect')
- self.assertEquals('units', self.stats.unit, 'unit incorrect')
- self.assertEquals(0, self.stats.count, 'count not zero')
- self.assertEquals(0, len(self.stats.intervals), 'intervals not empty')
+ self.assertEqual('title', self.stats.title, 'title incorrect')
+ self.assertEqual('units', self.stats.unit, 'unit incorrect')
+ self.assertEqual(0, self.stats.count, 'count not zero')
+ self.assertEqual(0, len(self.stats.intervals), 'intervals not empty')
def test_add_iterval(self):
self.stats.add_interval('intTitle', 3, {'k1': 'v1'}, 'css', 0)
self.stats.refresh_calcs()
- self.assertEquals(3, self.stats.count, 'count not incremented')
+ self.assertEqual(3, self.stats.count, 'count not incremented')
int = self.stats.intervals[0]
- self.assertEquals('intTitle', int['title'], 'title incorrect')
- self.assertEquals(3, int['count'], 'count incorrect')
- self.assertEquals({'k1': 'v1'}, int['qry_args'], 'query args incorrect')
- self.assertEquals('css', int['css_class'], 'css class incorrect')
- self.assertEquals(100, int['percent'], 'percent incorrect')
+ self.assertEqual('intTitle', int['title'], 'title incorrect')
+ self.assertEqual(3, int['count'], 'count incorrect')
+ self.assertEqual({'k1': 'v1'}, int['qry_args'], 'query args incorrect')
+ self.assertEqual('css', int['css_class'], 'css class incorrect')
+ self.assertEqual(100, int['percent'], 'percent incorrect')
self.stats.add_interval('intTitle', 3, {'k1': 'v1'}, 'css', 0)
self.stats.refresh_calcs()
- self.assertEquals(50, int['percent'], 'percent not being updated')
+ self.assertEqual(50, int['percent'], 'percent not being updated')
def test_add_interval_no_prog(self):
self.stats.add_interval('intTitle', 3, {'k1': 'v1'}, 'css', 0)
self.stats.add_interval('intTitle', 5, {'k1': 'v1'}, 'css', 0)
self.stats.refresh_calcs()
interval = self.stats.intervals[1]
- self.assertEquals(0, self.stats.done_count, 'count added for no prog')
- self.assertEquals(0, self.stats.done_percent, 'percent incremented')
+ self.assertEqual(0, self.stats.done_count, 'count added for no prog')
+ self.assertEqual(0, self.stats.done_percent, 'percent incremented')
def test_add_interval_prog(self):
self.stats.add_interval('intTitle', 3, {'k1': 'v1'}, 'css', 0)
self.stats.add_interval('intTitle', 1, {'k1': 'v1'}, 'css', 1)
self.stats.refresh_calcs()
- self.assertEquals(4, self.stats.count, 'count not incremented')
- self.assertEquals(1, self.stats.done_count, 'count not added to prog')
- self.assertEquals(25, self.stats.done_percent, 'done percent not incr')
+ self.assertEqual(4, self.stats.count, 'count not incremented')
+ self.assertEqual(1, self.stats.done_count, 'count not added to prog')
+ self.assertEqual(25, self.stats.done_percent, 'done percent not incr')
def test_add_interval_fudging(self):
self.stats.add_interval('intTitle', 3, {'k1': 'v1'}, 'css', 0)
self.stats.add_interval('intTitle', 5, {'k1': 'v1'}, 'css', 1)
self.stats.refresh_calcs()
- self.assertEquals(8, self.stats.count, 'count not incremented')
- self.assertEquals(5, self.stats.done_count, 'count not added to prog')
- self.assertEquals(62, self.stats.done_percent,
- 'done percnt not fudged downward')
- self.assertEquals(62, self.stats.intervals[1]['percent'],
- 'interval percent not fudged downward')
- self.assertEquals(38, self.stats.intervals[0]['percent'],
- 'interval percent not fudged upward')
+ self.assertEqual(8, self.stats.count, 'count not incremented')
+ self.assertEqual(5, self.stats.done_count, 'count not added to prog')
+ self.assertEqual(62, self.stats.done_percent,
+ 'done percnt not fudged downward')
+ self.assertEqual(62, self.stats.intervals[1]['percent'],
+ 'interval percent not fudged downward')
+ self.assertEqual(38, self.stats.intervals[0]['percent'],
+ 'interval percent not fudged upward')
class DefaultTicketGroupStatsProviderTestCase(unittest.TestCase):
@@ -96,32 +109,32 @@ class DefaultTicketGroupStatsProviderTes
self.env.reset_db()
def test_stats(self):
- self.assertEquals(self.stats.title, 'ticket status', 'title incorrect')
- self.assertEquals(self.stats.unit, 'tickets', 'unit incorrect')
- self.assertEquals(2, len(self.stats.intervals), 'more than 2 intervals')
+ self.assertEqual(self.stats.title, 'ticket status', 'title incorrect')
+ self.assertEqual(self.stats.unit, 'tickets', 'unit incorrect')
+ self.assertEqual(2, len(self.stats.intervals), 'more than 2 intervals')
def test_closed_interval(self):
closed = self.stats.intervals[0]
- self.assertEquals('closed', closed['title'], 'closed title incorrect')
- self.assertEquals('closed', closed['css_class'], 'closed class incorrect')
- self.assertEquals(True, closed['overall_completion'],
- 'closed should contribute to overall completion')
- self.assertEquals({'status': ['closed'], 'group': ['resolution']},
- closed['qry_args'], 'qry_args incorrect')
- self.assertEquals(1, closed['count'], 'closed count incorrect')
- self.assertEquals(33, closed['percent'], 'closed percent incorrect')
+ self.assertEqual('closed', closed['title'], 'closed title incorrect')
+ self.assertEqual('closed', closed['css_class'], 'closed class incorrect')
+ self.assertTrue(closed['overall_completion'],
+ 'closed should contribute to overall completion')
+ self.assertEqual({'status': ['closed'], 'group': ['resolution']},
+ closed['qry_args'], 'qry_args incorrect')
+ self.assertEqual(1, closed['count'], 'closed count incorrect')
+ self.assertEqual(33, closed['percent'], 'closed percent incorrect')
def test_open_interval(self):
open = self.stats.intervals[1]
- self.assertEquals('active', open['title'], 'open title incorrect')
- self.assertEquals('open', open['css_class'], 'open class incorrect')
- self.assertEquals(False, open['overall_completion'],
- "open shouldn't contribute to overall completion")
- self.assertEquals({'status':
- [u'assigned', u'new', u'accepted', u'reopened']},
- open['qry_args'], 'qry_args incorrect')
- self.assertEquals(2, open['count'], 'open count incorrect')
- self.assertEquals(67, open['percent'], 'open percent incorrect')
+ self.assertEqual('active', open['title'], 'open title incorrect')
+ self.assertEqual('open', open['css_class'], 'open class incorrect')
+ self.assertFalse(open['overall_completion'],
+ "open shouldn't contribute to overall completion")
+ self.assertEqual({'status':
+ [u'assigned', u'new', u'accepted', u'reopened']},
+ open['qry_args'], 'qry_args incorrect')
+ self.assertEqual(2, open['count'], 'open count incorrect')
+ self.assertEqual(67, open['percent'], 'open percent incorrect')
def in_tlist(ticket, list):
@@ -129,9 +142,8 @@ def in_tlist(ticket, list):
def suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(TicketGroupStatsTestCase, 'test'))
- suite.addTest(unittest.makeSuite(DefaultTicketGroupStatsProviderTestCase,
- 'test'))
+ suite.addTest(unittest.makeSuite(TicketGroupStatsTestCase))
+ suite.addTest(unittest.makeSuite(DefaultTicketGroupStatsProviderTestCase))
return suite
if __name__ == '__main__':
Modified: bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py Thu Feb 13 05:08:02 2014
@@ -1,11 +1,28 @@
# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
import unittest
+from datetime import datetime, timedelta
-from trac.ticket.model import Ticket
-from trac.ticket.roadmap import Milestone
+from trac.test import locale_en
+from trac.ticket.query import QueryModule
+from trac.ticket.report import ReportModule
+from trac.ticket.roadmap import RoadmapModule
+from trac.ticket.model import Milestone, Ticket
+from trac.util.datefmt import format_datetime, pretty_timedelta, utc
from trac.wiki.tests import formatter
+
TICKET_TEST_CASES = u"""
============================== ticket: link resolver
ticket:1
@@ -109,6 +126,9 @@ trac:#2041
""" # "
def ticket_setup(tc):
+ config = tc.env.config
+ config.set('ticket-custom', 'custom1', 'text')
+ config.save()
ticket = Ticket(tc.env)
ticket.values.update({'reporter': 'santa',
'summary': 'This is the summary',
@@ -116,6 +136,9 @@ def ticket_setup(tc):
ticket.insert()
def ticket_teardown(tc):
+ config = tc.env.config
+ config.remove('ticket-custom', 'custom1')
+ config.save()
tc.env.reset_db()
@@ -127,7 +150,7 @@ REPORT_TEST_CASES = u"""
------------------------------
<p>
<a class="report" href="/report/1">{1}</a>, <a class="report" href="/report/2">{2}</a>
-<a class="report" href="/report/12">{12}</a>, {abc}
+<a class="missing report" title="report does not exist">{12}</a>, {abc}
</p>
------------------------------
============================== escaping the above
@@ -175,7 +198,16 @@ trac:report:1
""" # '
def report_setup(tc):
- pass # TBD
+ def create_report(tc, id):
+ tc.env.db_transaction("""
+ INSERT INTO report (id,title,query,description)
+ VALUES (%s,%s,'SELECT 1','')""", (id, 'Report %s' % id))
+ create_report(tc, 1)
+ create_report(tc, 2)
+
+
+dt_past = datetime.now(utc) - timedelta(days=1)
+dt_future = datetime.now(utc) + timedelta(days=1)
MILESTONE_TEST_CASES = u"""
@@ -183,11 +215,15 @@ MILESTONE_TEST_CASES = u"""
milestone:foo
[milestone:boo Milestone Boo]
[milestone:roo Milestone Roo]
+[milestone:woo Milestone Woo]
+[milestone:zoo Milestone Zoo]
------------------------------
<p>
<a class="missing milestone" href="/milestone/foo" rel="nofollow">milestone:foo</a>
-<a class="milestone" href="/milestone/boo">Milestone Boo</a>
-<a class="closed milestone" href="/milestone/roo">Milestone Roo</a>
+<a class="milestone" href="/milestone/boo" title="No date set">Milestone Boo</a>
+<a class="closed milestone" href="/milestone/roo" title="Completed %(dt_past)s ago (%(datestr_past)s)">Milestone Roo</a>
+<a class="milestone" href="/milestone/woo" title="Due in %(dt_future)s (%(datestr_future)s)">Milestone Woo</a>
+<a class="milestone" href="/milestone/zoo" title="%(dt_past)s late (%(datestr_past)s)">Milestone Zoo</a>
</p>
------------------------------
============================== milestone: link resolver + arguments
@@ -196,23 +232,35 @@ milestone:?action=new
------------------------------
<p>
<a class="missing milestone" href="/milestone/?action=new" rel="nofollow">milestone:?action=new</a>
-<a class="milestone" href="/milestone/boo#KnownIssues">Known Issues for 1.0</a>
+<a class="milestone" href="/milestone/boo#KnownIssues" title="No date set">Known Issues for 1.0</a>
</p>
------------------------------
-""" #"
+""" % {'dt_past': pretty_timedelta(dt_past),
+ 'dt_future': pretty_timedelta(dt_future),
+ 'datestr_past': format_datetime(dt_past, locale=locale_en, tzinfo=utc),
+ 'datestr_future': format_datetime(dt_future, locale=locale_en,
+ tzinfo=utc)} #"
def milestone_setup(tc):
- from datetime import datetime
- from trac.util.datefmt import utc
boo = Milestone(tc.env)
boo.name = 'boo'
boo.completed = boo.due = None
boo.insert()
roo = Milestone(tc.env)
roo.name = 'roo'
- roo.completed = datetime.now(utc)
+ roo.completed = dt_past
roo.due = None
roo.insert()
+ woo = Milestone(tc.env)
+ woo.name = 'woo'
+ woo.completed = None
+ woo.due = dt_future
+ woo.insert()
+ zoo = Milestone(tc.env)
+ zoo.name = 'zoo'
+ zoo.completed = None
+ zoo.due = dt_past
+ zoo.insert()
def milestone_teardown(tc):
tc.env.reset_db()
@@ -309,6 +357,20 @@ New tickets: [[TicketQuery(status=new, f
New tickets: <span><a class="new" href="/ticket/1" title="This is the summary">#1</a></span>
</p>
------------------------------
+============================== TicketQuery macro: duplicated fields
+New tickets: [[TicketQuery(status=new, format=compact, col=summary|status|status)]]
+------------------------------
+<p>
+New tickets: <span><a class="new" href="/ticket/1" title="This is the summary">#1</a></span>
+</p>
+------------------------------
+============================== TicketQuery macro: duplicated custom fields
+New tickets: [[TicketQuery(status=new, format=compact, col=summary|custom1|custom1)]]
+------------------------------
+<p>
+New tickets: <span><a class="new" href="/ticket/1" title="This is the summary">#1</a></span>
+</p>
+------------------------------
"""
QUERY2_TEST_CASES = u"""
@@ -353,25 +415,88 @@ def query2_teardown(tc):
COMMENT_TEST_CASES = u"""
============================== comment: link resolver (deprecated)
-comment:ticket:123:2 (deprecated)
-[comment:ticket:123:2 see above] (deprecated)
-[comment:ticket:123:description see descr] (deprecated)
-------------------------------
-<p>
-<a href="/ticket/123#comment:2" title="Comment 2 for Ticket #123">comment:ticket:123:2</a> (deprecated)
-<a href="/ticket/123#comment:2" title="Comment 2 for Ticket #123">see above</a> (deprecated)
-<a href="/ticket/123#comment:description" title="Comment description for Ticket #123">see descr</a> (deprecated)
+comment:ticket:1:1 (deprecated)
+[comment:ticket:1:1 see above] (deprecated)
+comment:ticket:1:description (deprecated)
+[comment:ticket:1:description see descr] (deprecated)
+comment:ticket:2:1 (deprecated)
+comment:ticket:2:3 (deprecated)
+comment:ticket:3:1 (deprecated)
+comment:tiket:2:1 (deprecated)
+comment:ticket:two:1 (deprecated)
+comment:ticket:2:1a (deprecated)
+comment:ticket:2:one (deprecated)
+comment:ticket:1: (deprecated)
+comment:ticket::2 (deprecated)
+comment:ticket:: (deprecated)
+------------------------------
+<p>
+<a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">comment:ticket:1:1</a> (deprecated)
+<a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">see above</a> (deprecated)
+<a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">comment:ticket:1:description</a> (deprecated)
+<a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">see descr</a> (deprecated)
+<a class="ticket" href="/ticket/2#comment:1" title="Comment 1">comment:ticket:2:1</a> (deprecated)
+<a class="missing ticket" title="ticket comment does not exist">comment:ticket:2:3</a> (deprecated)
+<a class="missing ticket" title="ticket does not exist">comment:ticket:3:1</a> (deprecated)
+comment:tiket:2:1 (deprecated)
+comment:ticket:two:1 (deprecated)
+comment:ticket:2:1a (deprecated)
+comment:ticket:2:one (deprecated)
+comment:ticket:1: (deprecated)
+comment:ticket::2 (deprecated)
+comment:ticket:: (deprecated)
</p>
------------------------------
============================== comment: link resolver
-comment:2:ticket:123
-[comment:2:ticket:123 see above]
-[comment:description:ticket:123 see descr]
-------------------------------
-<p>
-<a href="/ticket/123#comment:2" title="Comment 2 for Ticket #123">comment:2:ticket:123</a>
-<a href="/ticket/123#comment:2" title="Comment 2 for Ticket #123">see above</a>
-<a href="/ticket/123#comment:description" title="Comment description for Ticket #123">see descr</a>
+comment:1
+[comment:1 see above]
+comment:description
+[comment:description see descr]
+comment:
+comment:one
+comment:1a
+------------------------------
+<p>
+<a class="ticket" href="/ticket/2#comment:1" title="Comment 1">comment:1</a>
+<a class="ticket" href="/ticket/2#comment:1" title="Comment 1">see above</a>
+<a class="ticket" href="/ticket/2#comment:description" title="Description">comment:description</a>
+<a class="ticket" href="/ticket/2#comment:description" title="Description">see descr</a>
+comment:
+comment:one
+comment:1a
+</p>
+------------------------------
+============================== comment: link resolver with ticket number
+comment:1:ticket:1
+[comment:1:ticket:1 see above]
+comment:description:ticket:1
+[comment:description:ticket:1 see descr]
+comment:1:ticket:2
+comment:3:ticket:2
+comment:1:ticket:3
+comment:2:tiket:1
+comment:1:ticket:two
+comment:one:ticket:1
+comment:1a:ticket:1
+comment:ticket:1
+comment:2:ticket:
+comment::ticket:
+------------------------------
+<p>
+<a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">comment:1:ticket:1</a>
+<a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">see above</a>
+<a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">comment:description:ticket:1</a>
+<a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">see descr</a>
+<a class="ticket" href="/ticket/2#comment:1" title="Comment 1">comment:1:ticket:2</a>
+<a class="missing ticket" title="ticket comment does not exist">comment:3:ticket:2</a>
+<a class="missing ticket" title="ticket does not exist">comment:1:ticket:3</a>
+comment:2:tiket:1
+comment:1:ticket:two
+comment:one:ticket:1
+comment:1a:ticket:1
+comment:ticket:1
+comment:2:ticket:
+comment::ticket:
</p>
------------------------------
""" # "
@@ -385,6 +510,24 @@ comment:2:ticket:123
# As it's a problem with a temp workaround, I think there's no need
# to fix it for now.
+def comment_setup(tc):
+ ticket1 = Ticket(tc.env)
+ ticket1.values.update({'reporter': 'santa',
+ 'summary': 'This is the summary for ticket 1',
+ 'status': 'new'})
+ ticket1.insert()
+ ticket1.save_changes(comment='This is the comment for ticket 1')
+ ticket2 = Ticket(tc.env)
+ ticket2.values.update({'reporter': 'claws',
+ 'summary': 'This is the summary for ticket 2',
+ 'status': 'closed'})
+ ticket2.insert()
+ ticket2.save_changes(comment='This is the comment for ticket 2')
+
+def comment_teardown(tc):
+ tc.env.reset_db()
+
+
def suite():
suite = unittest.TestSuite()
suite.addTest(formatter.suite(TICKET_TEST_CASES, ticket_setup, __file__,
@@ -396,9 +539,9 @@ def suite():
ticket_teardown))
suite.addTest(formatter.suite(QUERY2_TEST_CASES, query2_setup, __file__,
query2_teardown))
- suite.addTest(formatter.suite(COMMENT_TEST_CASES, file=__file__))
+ suite.addTest(formatter.suite(COMMENT_TEST_CASES, comment_setup, __file__,
+ comment_teardown, ('ticket', 2)))
return suite
if __name__ == '__main__':
unittest.main(defaultTest='suite')
-
Modified: bloodhound/vendor/trac/current/trac/ticket/web_ui.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/web_ui.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/web_ui.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/web_ui.py Thu Feb 13 05:08:02 2014
@@ -42,6 +42,7 @@ from trac.util import as_bool, as_int, g
from trac.util.datefmt import (
format_datetime, from_utimestamp, to_utimestamp, utc
)
+from trac.util.html import to_fragment
from trac.util.text import (
exception_to_unicode, empty, obfuscate_email_address, shorten_line,
to_unicode
@@ -77,12 +78,14 @@ class TicketModule(Component):
open / close operations (''since 0.9'').""")
max_description_size = IntOption('ticket', 'max_description_size', 262144,
- """Don't accept tickets with a too big description.
- (''since 0.11'').""")
+ """Maximum allowed description size in characters.
+ (//since 0.11//).""")
max_comment_size = IntOption('ticket', 'max_comment_size', 262144,
- """Don't accept tickets with a too big comment.
- (''since 0.11.2'')""")
+ """Maximum allowed comment size in characters. (//since 0.11.2//).""")
+
+ max_summary_size = IntOption('ticket', 'max_summary_size', 262144,
+ """Maximum allowed summary size in characters. (//since 1.0.2//).""")
timeline_newticket_formatter = Option('timeline', 'newticket_formatter',
'oneliner',
@@ -296,7 +299,7 @@ class TicketModule(Component):
FROM ticket_change tc
INNER JOIN ticket t ON t.id = tc.ticket
AND tc.time>=%s AND tc.time<=%s
- ORDER BY tc.time
+ ORDER BY tc.time, tc.ticket
""" % (ts_start, ts_stop)):
if not (oldvalue or newvalue):
# ignore empty change corresponding to custom field
@@ -307,7 +310,7 @@ class TicketModule(Component):
ev = produce_event(data, status, fields, comment,
cid)
if ev:
- yield (ev, data[1])
+ yield (ev, data[1])
status, fields, comment, cid = 'edit', {}, '', None
data = (id, t, author, type, summary, None)
if field == 'comment':
@@ -1263,6 +1266,13 @@ class TicketModule(Component):
num=self.max_comment_size))
valid = False
+ # Validate summary length
+ if len(ticket['summary']) > self.max_summary_size:
+ add_warning(req, _("Ticket summary is too long (must be less "
+ "than %(num)s characters)",
+ num=self.max_summary_size))
+ valid = False
+
# Validate comment numbering
try:
# replyto must be 'description' or a number
@@ -1295,9 +1305,9 @@ class TicketModule(Component):
except Exception, e:
self.log.error("Failure sending notification on creation of "
"ticket #%s: %s", ticket.id, exception_to_unicode(e))
- add_warning(req, _("The ticket has been created, but an error "
- "occurred while sending notifications: "
- "%(message)s", message=to_unicode(e)))
+ add_warning(req, tag_("The ticket has been created, but an error "
+ "occurred while sending notifications: "
+ "%(message)s", message=to_fragment(e)))
# Redirect the user to the newly created ticket or add attachment
ticketref=tag.a('#', ticket.id, href=req.href.ticket(ticket.id))
@@ -1341,7 +1351,7 @@ class TicketModule(Component):
add_warning(req, tag_("The %(change)s has been saved, but an "
"error occurred while sending "
"notifications: %(message)s",
- change=change, message=to_unicode(e)))
+ change=change, message=to_fragment(e)))
fragment = ''
# After saving the changes, apply the side-effects.
@@ -1401,6 +1411,9 @@ class TicketModule(Component):
def _query_link(self, req, name, value, text=None):
"""Return a link to /query with the appropriate name and value"""
+ from trac.ticket.query import QueryModule
+ if not self.env.is_component_enabled(QueryModule):
+ return text or value
default_query = self.ticketlink_query.lstrip('?')
args = arg_list_to_args(parse_arg_list(default_query))
args[name] = value
@@ -1410,7 +1423,9 @@ class TicketModule(Component):
def _query_link_words(self, context, name, value):
"""Splits a list of words and makes a query link to each separately"""
- if not isinstance(value, basestring): # None or other non-splitable
+ from trac.ticket.query import QueryModule
+ if not (isinstance(value, basestring) and # None or other non-splitable
+ self.env.is_component_enabled(QueryModule)):
return value
default_query = self.ticketlink_query.startswith('?') and \
self.ticketlink_query[1:] or self.ticketlink_query
Modified: bloodhound/vendor/trac/current/trac/timeline/__init__.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/__init__.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/__init__.py (original)
+++ bloodhound/vendor/trac/current/trac/timeline/__init__.py Thu Feb 13 05:08:02 2014
@@ -1 +1,14 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
from trac.timeline.api import *
Modified: bloodhound/vendor/trac/current/trac/timeline/api.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/api.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/api.py (original)
+++ bloodhound/vendor/trac/current/trac/timeline/api.py Thu Feb 13 05:08:02 2014
@@ -69,5 +69,3 @@ class ITimelineEventProvider(Interface):
the 'url'
:param event: the event tuple, as returned by `get_timeline_events`
"""
-
-
Modified: bloodhound/vendor/trac/current/trac/timeline/templates/timeline.rss
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/templates/timeline.rss?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/templates/timeline.rss (original)
+++ bloodhound/vendor/trac/current/trac/timeline/templates/timeline.rss Thu Feb 13 05:08:02 2014
@@ -6,9 +6,7 @@
<title>${project.name}</title>
<link>${abs_href.timeline()}</link>
<description>Trac Timeline</description>
- <language>${req.locale and \
- '%s-%s' % (req.locale.language, req.locale.territory) or \
- 'en-US'}</language>
+ <language>${str(locale).replace('_', '-') if locale else 'en-US'}</language>
<generator>Trac ${trac.version}</generator>
<image py:if="chrome.logo.src_abs">
<title>${project.name}</title>
Modified: bloodhound/vendor/trac/current/trac/timeline/tests/__init__.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/tests/__init__.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/tests/__init__.py (original)
+++ bloodhound/vendor/trac/current/trac/timeline/tests/__init__.py Thu Feb 13 05:08:02 2014
@@ -1 +1,28 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
+import unittest
+
+from trac.timeline.tests import web_ui
+from trac.timeline.tests import wikisyntax
from trac.timeline.tests.functional import functionalSuite
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(web_ui.suite())
+ suite.addTest(wikisyntax.suite())
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
Modified: bloodhound/vendor/trac/current/trac/timeline/tests/functional.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/tests/functional.py?rev=1567849&r1=1567848&r2=1567849&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/tests/functional.py (original)
+++ bloodhound/vendor/trac/current/trac/timeline/tests/functional.py Thu Feb 13 05:08:02 2014
@@ -1,4 +1,17 @@
-#!/usr/bin/python
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2013 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
from trac.tests.functional import *
@@ -29,8 +42,8 @@ class RegressionTestRev5883(FunctionalTw
def functionalSuite(suite=None):
if not suite:
- import trac.tests.functional.testcases
- suite = trac.tests.functional.testcases.functionalSuite()
+ import trac.tests.functional
+ suite = trac.tests.functional.functionalSuite()
suite.addTest(RegressionTestRev5883())
return suite