Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/query.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/query.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/query.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/query.py Sat Nov 15 01:14:46 2014 @@ -1,9 +1,24 @@ +# -*- 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 from trac.web.href import Href from trac.wiki.formatter import LinkFormatter +from trac.wiki.tests import formatter import unittest import difflib @@ -257,12 +272,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 +289,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') @@ -503,6 +582,31 @@ ORDER BY COALESCE(t.id,0)=0,t.id""") self.assertEqual('\xef\xbb\xbfcol1\r\n"value, needs escaped"\r\n', content) + def test_csv_obfuscation(self): + class NoEmailView(MockPerm): + def has_permission(self, action, realm_or_resource=None, id=False, + version=False): + return action != 'EMAIL_VIEW' + __contains__ = has_permission + + query = Mock(get_columns=lambda: ['owner', 'reporter', 'cc'], + execute=lambda r: [{'id': 1, + 'owner': 'j...@example.org', + 'reporter': 'f...@example.org', + 'cc': 'c...@example.org, cc2'}], + time_fields=['time', 'changetime']) + req = Mock(href=self.env.href, perm=NoEmailView()) + content, mimetype = QueryModule(self.env).export_csv(req, query) + self.assertEqual(u'\uFEFFowner,reporter,cc\r\n' + u'joe@â¦,foo@â¦,"cc1@â¦, cc2"\r\n', + content.decode('utf-8')) + req = Mock(href=self.env.href, perm=MockPerm()) + content, mimetype = QueryModule(self.env).export_csv(req, query) + self.assertEqual( + u'\uFEFFowner,reporter,cc\r\n' + u'j...@example.org,f...@example.org,"c...@example.org, cc2"\r\n', + content.decode('utf-8')) + def test_template_data(self): req = Mock(href=self.env.href, perm=MockPerm(), authname='anonymous', tz=None, locale=None) @@ -576,12 +680,267 @@ class TicketQueryMacroTestCase(unittest. dict(col='status|summary', max='0', order='id'), 'list') +QUERY_TEST_CASES = u""" +============================== TicketQuery(format=progress) +[[TicketQuery(format=progress)]] +------------------------------ +<p> +</p><div class="trac-progress"> + + <table xmlns="http://www.w3.org/1999/xhtml" class="progress"> + <tr> + <td class="closed" style="width: 33%"> + <a href="/query?status=closed&group=resolution&max=0&order=time" title="1/3 closed"></a> + </td><td class="open" style="width: 67%"> + <a href="/query?status=assigned&status=new&status=accepted&status=reopened&max=0&order=id" title="2/3 active"></a> + </td> + </tr> + </table> + + <p class="percent">33%</p> + + <p class="legend"> + <span class="first interval"> + <a href="/query?max=0&order=id">Total number of tickets: 3</a> + </span> + <span class="interval"> + - <a href="/query?status=closed&group=resolution&max=0&order=time">closed: 1</a> + </span><span class="interval"> + - <a href="/query?status=assigned&status=new&status=accepted&status=reopened&max=0&order=id">active: 2</a> + </span> + </p> +</div><p> +</p> +------------------------------ +============================== TicketQuery(reporter=santa, format=progress) +[[TicketQuery(reporter=santa, format=progress)]] +------------------------------ +<p> +</p><div class="trac-progress"> + + <table xmlns="http://www.w3.org/1999/xhtml" class="progress"> + <tr> + <td class="closed" style="display: none"> + <a href="/query?status=closed&reporter=santa&group=resolution&max=0&order=time" title="0/1 closed"></a> + </td><td class="open" style="width: 100%"> + <a href="/query?status=assigned&status=new&status=accepted&status=reopened&reporter=santa&max=0&order=id" title="1/1 active"></a> + </td> + </tr> + </table> + + <p class="percent">0%</p> + + <p class="legend"> + <span class="first interval"> + <a href="/query?reporter=santa&max=0&order=id">Total number of tickets: 1</a> + </span> + <span class="interval"> + - <a href="/query?status=closed&reporter=santa&group=resolution&max=0&order=time">closed: 0</a> + </span><span class="interval"> + - <a href="/query?status=assigned&status=new&status=accepted&status=reopened&reporter=santa&max=0&order=id">active: 1</a> + </span> + </p> +</div><p> +</p> +------------------------------ +============================== TicketQuery(reporter=santa&or&owner=santa, format=progress) +[[TicketQuery(reporter=santa&or&owner=santa, format=progress)]] +------------------------------ +<p> +</p><div class="trac-progress"> + + <table xmlns="http://www.w3.org/1999/xhtml" class="progress"> + <tr> + <td class="closed" style="width: 50%"> + <a href="/query?status=closed&reporter=santa&or&owner=santa&status=closed&group=resolution&max=0&order=time" title="1/2 closed"></a> + </td><td class="open" style="width: 50%"> + <a href="/query?status=assigned&status=new&status=accepted&status=reopened&reporter=santa&or&owner=santa&status=assigned&status=new&status=accepted&status=reopened&max=0&order=id" title="1/2 active"></a> + </td> + </tr> + </table> + + <p class="percent">50%</p> + + <p class="legend"> + <span class="first interval"> + <a href="/query?reporter=santa&or&owner=santa&max=0&order=id">Total number of tickets: 2</a> + </span> + <span class="interval"> + - <a href="/query?status=closed&reporter=santa&or&owner=santa&status=closed&group=resolution&max=0&order=time">closed: 1</a> + </span><span class="interval"> + - <a href="/query?status=assigned&status=new&status=accepted&status=reopened&reporter=santa&or&owner=santa&status=assigned&status=new&status=accepted&status=reopened&max=0&order=id">active: 1</a> + </span> + </p> +</div><p> +</p> +------------------------------ +============================== TicketQuery(format=progress, group=project) +[[TicketQuery(format=progress, group=project)]] +------------------------------ +<p> +</p><div class="trac-groupprogress"> + <table xmlns="http://www.w3.org/1999/xhtml" summary="Ticket completion status for each project"> + <tr> + <th scope="row"> + <i><a href="/query?project=&max=0&order=id">(none)</a></i> + + + </th> + <td> + + + <table class="progress" style="width: 40%"> + <tr> + <td class="closed" style="display: none"> + <a href="/query?project=&status=closed&group=resolution&max=0&order=time" title="0/1 closed"></a> + </td><td class="open" style="width: 100%"> + <a href="/query?project=&status=assigned&status=new&status=accepted&status=reopened&max=0&order=id" title="1/1 active"></a> + </td> + </tr> + </table> + + <p class="percent">0 / 1</p> + + + + </td> + </tr><tr> + <th scope="row"> + + + <a href="/query?project=xmas&max=0&order=id">xmas</a> + </th> + <td> + + + <table class="progress" style="width: 80%"> + <tr> + <td class="closed" style="width: 50%"> + <a href="/query?project=xmas&status=closed&group=resolution&max=0&order=time" title="1/2 closed"></a> + </td><td class="open" style="width: 50%"> + <a href="/query?project=xmas&status=assigned&status=new&status=accepted&status=reopened&max=0&order=id" title="1/2 active"></a> + </td> + </tr> + </table> + + <p class="percent">1 / 2</p> + + + + </td> + </tr> + </table> +</div><p> +</p> +------------------------------ +============================== TicketQuery(reporter=santa, format=progress, group=project) +[[TicketQuery(reporter=santa, format=progress, group=project)]] +------------------------------ +<p> +</p><div class="trac-groupprogress"> + <table xmlns="http://www.w3.org/1999/xhtml" summary="Ticket completion status for each project"> + <tr> + <th scope="row"> + + + <a href="/query?project=xmas&reporter=santa&max=0&order=id">xmas</a> + </th> + <td> + + + <table class="progress" style="width: 80%"> + <tr> + <td class="closed" style="display: none"> + <a href="/query?project=xmas&status=closed&reporter=santa&group=resolution&max=0&order=time" title="0/1 closed"></a> + </td><td class="open" style="width: 100%"> + <a href="/query?project=xmas&status=assigned&status=new&status=accepted&status=reopened&reporter=santa&max=0&order=id" title="1/1 active"></a> + </td> + </tr> + </table> + + <p class="percent">0 / 1</p> + + + + </td> + </tr> + </table> +</div><p> +</p> +------------------------------ +============================== TicketQuery(reporter=santa&or&owner=santa, format=progress, group=project) +[[TicketQuery(reporter=santa&or&owner=santa, format=progress, group=project)]] +------------------------------ +<p> +</p><div class="trac-groupprogress"> + <table xmlns="http://www.w3.org/1999/xhtml" summary="Ticket completion status for each project"> + <tr> + <th scope="row"> + + + <a href="/query?project=xmas&reporter=santa&or&owner=santa&project=xmas&max=0&order=id">xmas</a> + </th> + <td> + + + <table class="progress" style="width: 80%"> + <tr> + <td class="closed" style="width: 50%"> + <a href="/query?project=xmas&status=closed&reporter=santa&or&owner=santa&project=xmas&status=closed&group=resolution&max=0&order=time" title="1/2 closed"></a> + </td><td class="open" style="width: 50%"> + <a href="/query?project=xmas&status=assigned&status=new&status=accepted&status=reopened&reporter=santa&or&owner=santa&project=xmas&status=assigned&status=new&status=accepted&status=reopened&max=0&order=id" title="1/2 active"></a> + </td> + </tr> + </table> + + <p class="percent">1 / 2</p> + + + + </td> + </tr> + </table> +</div><p> +</p> +------------------------------ +""" + +def ticket_setup(tc): + tc.env.config.set('ticket-custom', 'project', 'text') + ticket = Ticket(tc.env) + ticket.values.update({'reporter': 'santa', + 'summary': 'This is the summary', + 'status': 'new', + 'project': 'xmas'}) + ticket.insert() + ticket = Ticket(tc.env) + ticket.values.update({'owner': 'elf', + 'summary': 'This is another summary', + 'status': 'assigned'}) + ticket.insert() + ticket = Ticket(tc.env) + ticket.values.update({'owner': 'santa', + 'summary': 'This is th third summary', + 'status': 'closed', + 'project': 'xmas'}) + ticket.insert() + + tc.env.config.set('milestone-groups', 'closed.status', 'closed') + tc.env.config.set('milestone-groups', 'closed.query_args', 'group=resolution,order=time') + tc.env.config.set('milestone-groups', 'closed.overall_completion', 'true') + tc.env.config.set('milestone-groups', 'active.status', '*') + tc.env.config.set('milestone-groups', 'active.css_class', 'open') + +def ticket_teardown(tc): + tc.env.reset_db() 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)) + suite.addTest(formatter.suite(QUERY_TEST_CASES, ticket_setup, __file__, + ticket_teardown)) return suite if __name__ == '__main__':
Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/report.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/report.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/report.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/report.py Sat Nov 15 01:14:46 2014 @@ -1,15 +1,34 @@ # -*- coding: utf-8 -*- +# +# Copyright (C) 2006-2014 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 __future__ import with_statement import doctest +from datetime import datetime, timedelta +import unittest +from StringIO import StringIO + +import trac.tests.compat from trac.db.mysql_backend import MySQLConnection +from trac.ticket.model import Ticket from trac.ticket.report import ReportModule -from trac.test import EnvironmentStub, Mock +from trac.test import EnvironmentStub, Mock, MockPerm +from trac.util.datefmt import utc from trac.web.api import Request, RequestDone +from trac.web.href import Href import trac -import unittest -from StringIO import StringIO class MockMySQLConnection(MySQLConnection): def __init__(self): @@ -99,11 +118,409 @@ class ReportTestCase(unittest.TestCase): 'type=r%C3%A9sum%C3%A9&report=' + str(id), headers_sent['Location']) + def test_quoted_id_with_var(self): + req = Mock(base_path='', chrome={}, args={}, session={}, + abs_href=Href('/'), href=Href('/'), locale='', + perm=MockPerm(), authname=None, tz=None) + db = self.env.get_read_db() + name = """%s"`'%%%?""" + sql = 'SELECT 1 AS %s, $USER AS user' % db.quote(name) + rv = self.report_module.execute_paginated_report(req, db, 1, sql, + {'USER': 'joe'}) + self.assertEqual(5, len(rv), repr(rv)) + cols, results, num_items, missing_args, limit_offset = rv + self.assertEqual([name, 'user'], cols) + self.assertEqual([(1, 'joe')], results) + self.assertEqual([], missing_args) + self.assertEqual(None, limit_offset) + + +class ExecuteReportTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub(default_data=True) + self.req = Mock(base_path='', chrome={}, args={}, session={}, + abs_href=Href('/'), href=Href('/'), locale='', + perm=MockPerm(), authname=None, tz=None) + self.report_module = ReportModule(self.env) + + def tearDown(self): + self.env.reset_db() + + def _insert_ticket(self, when=None, **kwargs): + ticket = Ticket(self.env) + for name, value in kwargs.iteritems(): + ticket[name] = value + ticket['status'] = 'new' + ticket.insert(when=when) + return ticket + + def _save_ticket(self, ticket, author=None, comment=None, when=None, + **kwargs): + if when is None: + when = ticket['changetime'] + timedelta(microseconds=1) + for name, value in kwargs.iteritems(): + ticket[name] = value + return ticket.save_changes(author=author, comment=comment, when=when) + + def _execute_report(self, id, args=None): + mod = self.report_module + req = self.req + with self.env.db_query as db: + title, description, sql = mod.get_report(id) + return mod.execute_paginated_report(req, db, id, sql, args or {}) + + def _generate_tickets(self, columns, data, attrs): + with self.env.db_transaction as db: + tickets = [] + when = datetime(2014, 1, 1, 0, 0, 0, 0, utc) + for idx, line in enumerate(data.splitlines()): + line = line.strip() + if not line or line.startswith('#'): + continue + values = line.split() + assert len(columns) == len(values), 'Line %d' % (idx + 1) + summary = ' '.join(values) + values = map(lambda v: None if v == 'None' else v, values) + d = attrs.copy() + d['summary'] = summary + d.update(zip(columns, values)) + + status = None + if 'status' in d: + status = d.pop('status') + ticket = self._insert_ticket(when=when, status='new', **d) + if status != 'new': + self._save_ticket(ticket, status=status, + when=when + timedelta(microseconds=1)) + tickets.append(ticket) + when += timedelta(seconds=1) + return tickets + + REPORT_1_DATA = """\ + # status priority + new minor + new major + new critical + closed minor + closed major + closed critical""" + + def test_report_1_active_tickets(self): + attrs = dict(reporter='joe', component='component1', version='1.0', + milestone='milestone1', type='defect', owner='joe') + self._generate_tickets(('status', 'priority'), self.REPORT_1_DATA, + attrs) + + rv = self._execute_report(1) + cols, results, num_items, missing_args, limit_offset = rv + + idx_summary = cols.index('summary') + self.assertEqual(['new critical', + 'new major', + 'new minor'], + [r[idx_summary] for r in results]) + idx_color = cols.index('__color__') + self.assertEqual(set(('2', '3', '4')), + set(r[idx_color] for r in results)) + + REPORT_2_DATA = """\ + # status version priority + new 2.0 minor + new 2.0 critical + new 1.0 minor + new 1.0 critical + new None minor + new None critical + closed 2.0 minor + closed 2.0 critical + closed 1.0 minor + closed 1.0 critical + closed None minor + closed None critical""" + + def test_report_2_active_tickets_by_version(self): + attrs = dict(reporter='joe', component='component1', + milestone='milestone1', type='defect', owner='joe') + self._generate_tickets(('status', 'version', 'priority'), + self.REPORT_2_DATA, attrs) + + rv = self._execute_report(2) + cols, results, num_items, missing_args, limit_offset = rv + + idx_summary = cols.index('summary') + self.assertEqual(['new 1.0 critical', + 'new 1.0 minor', + 'new 2.0 critical', + 'new 2.0 minor', + 'new None critical', + 'new None minor'], + [r[idx_summary] for r in results]) + idx_color = cols.index('__color__') + self.assertEqual(set(('2', '4')), + set(r[idx_color] for r in results)) + idx_group = cols.index('__group__') + self.assertEqual(set(('1.0', '2.0', None)), + set(r[idx_group] for r in results)) + + REPORT_3_DATA = """\ + # status milestone priority + new milestone3 minor + new milestone3 major + new milestone1 minor + new milestone1 major + new None minor + new None major + closed milestone3 minor + closed milestone3 major + closed milestone1 minor + closed milestone1 major + closed None minor + closed None major""" + + def test_report_3_active_tickets_by_milestone(self): + attrs = dict(reporter='joe', component='component1', version='1.0', + type='defect', owner='joe') + self._generate_tickets(('status', 'milestone', 'priority'), + self.REPORT_3_DATA, attrs) + + rv = self._execute_report(3) + cols, results, num_items, missing_args, limit_offset = rv + + idx_summary = cols.index('summary') + self.assertEqual(['new milestone1 major', + 'new milestone1 minor', + 'new milestone3 major', + 'new milestone3 minor', + 'new None major', + 'new None minor'], + [r[idx_summary] for r in results]) + idx_color = cols.index('__color__') + self.assertEqual(set(('3', '4')), + set(r[idx_color] for r in results)) + idx_group = cols.index('__group__') + self.assertEqual(set(('Milestone milestone1', 'Milestone milestone3', + None)), + set(r[idx_group] for r in results)) + + REPORT_4_DATA = """\ + # status owner priority + new john trivial + new john blocker + new jack trivial + new jack blocker + new foo trivial + new foo blocker + accepted john trivial + accepted john blocker + accepted jack trivial + accepted jack blocker + accepted foo trivial + accepted foo blocker + closed john trivial + closed john blocker + closed jack trivial + closed jack blocker + closed foo trivial + closed foo blocker""" + + def _test_active_tickets_by_owner(self, report_id, full_description=False): + attrs = dict(reporter='joe', component='component1', + milestone='milestone1', version='1.0', type='defect') + self._generate_tickets(('status', 'owner', 'priority'), + self.REPORT_4_DATA, attrs) + + rv = self._execute_report(report_id) + cols, results, num_items, missing_args, limit_offset = rv + + idx_summary = cols.index('summary') + self.assertEqual(['accepted foo blocker', + 'accepted foo trivial', + 'accepted jack blocker', + 'accepted jack trivial', + 'accepted john blocker', + 'accepted john trivial'], + [r[idx_summary] for r in results]) + idx_color = cols.index('__color__') + self.assertEqual(set(('1', '5')), + set(r[idx_color] for r in results)) + idx_group = cols.index('__group__') + self.assertEqual(set(('jack', 'john', 'foo')), + set(r[idx_group] for r in results)) + if full_description: + self.assertNotIn('_description', cols) + self.assertIn('_description_', cols) + else: + self.assertNotIn('_description_', cols) + self.assertIn('_description', cols) + + def test_report_4_active_tickets_by_owner(self): + self._test_active_tickets_by_owner(4, full_description=False) + + def test_report_5_active_tickets_by_owner_full_description(self): + self._test_active_tickets_by_owner(5, full_description=True) + + REPORT_6_DATA = """\ + # status milestone priority owner + new milestone4 trivial john + new milestone4 critical jack + new milestone2 trivial jack + new milestone2 critical john + new None trivial john + new None critical jack + closed milestone4 trivial jack + closed milestone4 critical john + closed milestone2 trivial john + closed milestone2 critical jack + closed None trivial jack + closed None critical john""" + + def test_report_6_all_tickets_by_milestone(self): + attrs = dict(reporter='joe', component='component1', version='1.0', + type='defect') + self._generate_tickets(('status', 'milestone', 'priority', 'owner'), + self.REPORT_6_DATA, attrs) + + rv = self._execute_report(6, {'USER': 'john'}) + cols, results, num_items, missing_args, limit_offset = rv + + idx_summary = cols.index('summary') + self.assertEqual(['new milestone4 critical jack', + 'new milestone4 trivial john', + 'closed milestone4 critical john', + 'closed milestone4 trivial jack', + 'new milestone2 critical john', + 'new milestone2 trivial jack', + 'closed milestone2 critical jack', + 'closed milestone2 trivial john', + 'new None critical jack', + 'new None trivial john', + 'closed None critical john', + 'closed None trivial jack'], + [r[idx_summary] for r in results]) + idx_style = cols.index('__style__') + self.assertEqual('color: #777; background: #ddd; border-color: #ccc;', + results[2][idx_style]) # closed and owned + self.assertEqual('color: #777; background: #ddd; border-color: #ccc;', + results[3][idx_style]) # closed and not owned + self.assertEqual('font-weight: bold', + results[1][idx_style]) # not closed and owned + self.assertEqual(None, + results[0][idx_style]) # not closed and not owned + idx_color = cols.index('__color__') + self.assertEqual(set(('2', '5')), + set(r[idx_color] for r in results)) + idx_group = cols.index('__group__') + self.assertEqual(set(('milestone2', 'milestone4', None)), + set(r[idx_group] for r in results)) + + REPORT_7_DATA = """\ + # status owner reporter priority + accepted john foo minor + accepted john foo critical + accepted foo foo major + new john foo minor + new john foo blocker + new foo foo major + closed john foo major + closed foo foo major + new foo foo major + new foo john trivial + new foo john major + closed foo foo major + closed foo john major + new foo bar major + new bar foo major""" + + def test_report_7_my_tickets(self): + attrs = dict(component='component1', milestone='milestone1', + version='1.0', type='defect') + tickets = self._generate_tickets( + ('status', 'owner', 'reporter', 'priority'), self.REPORT_7_DATA, + attrs) + + rv = self._execute_report(7, {'USER': 'john'}) + cols, results, num_items, missing_args, limit_offset = rv + + idx_summary = cols.index('summary') + self.assertEqual(['accepted john foo critical', + 'accepted john foo minor', + 'new john foo blocker', + 'new john foo minor', + 'new foo john major', + 'new foo john trivial'], + [r[idx_summary] for r in results]) + idx_group = cols.index('__group__') + self.assertEqual(set(('Accepted', 'Owned', 'Reported')), + set(r[idx_group] for r in results)) + + self._save_ticket(tickets[-1], author='john', comment='commented') + rv = self._execute_report(7, {'USER': 'john'}) + cols, results, num_items, missing_args, limit_offset = rv + + self.assertEqual(7, len(results)) + self.assertEqual('new bar foo major', results[-1][idx_summary]) + self.assertEqual(set(('Accepted', 'Owned', 'Reported', 'Commented')), + set(r[idx_group] for r in results)) + + rv = self._execute_report(7, {'USER': 'blah <b...@example.org>'}) + cols, results, num_items, missing_args, limit_offset = rv + self.assertEqual(0, len(results)) + + self._save_ticket(tickets[-1], author='blah <b...@example.org>', + comment='from anonymous') + rv = self._execute_report(7, {'USER': 'blah <b...@example.org>'}) + cols, results, num_items, missing_args, limit_offset = rv + self.assertEqual(1, len(results)) + self.assertEqual('new bar foo major', results[0][idx_summary]) + self.assertEqual('Commented', results[0][idx_group]) + + REPORT_8_DATA = """\ + # status owner priority + new foo minor + new foo critical + new john minor + new john critical + closed john major + closed foo major""" + + def test_report_8_active_tickets_mine_first(self): + attrs = dict(component='component1', milestone='milestone1', + version='1.0', type='defect') + tickets = self._generate_tickets(('status', 'owner', 'priority'), + self.REPORT_8_DATA, attrs) + + rv = self._execute_report(8, {'USER': 'john'}) + cols, results, num_items, missing_args, limit_offset = rv + + idx_summary = cols.index('summary') + self.assertEqual(['new john critical', + 'new john minor', + 'new foo critical', + 'new foo minor'], + [r[idx_summary] for r in results]) + idx_group = cols.index('__group__') + self.assertEqual('My Tickets', results[1][idx_group]) + self.assertEqual('Active Tickets', results[2][idx_group]) + + rv = self._execute_report(8, {'USER': 'anonymous'}) + cols, results, num_items, missing_args, limit_offset = rv + + self.assertEqual(['new foo critical', + 'new john critical', + 'new foo minor', + 'new john minor'], + [r[idx_summary] for r in results]) + idx_group = cols.index('__group__') + self.assertEqual(['Active Tickets'], + sorted(set(r[idx_group] for r in results))) + def suite(): suite = unittest.TestSuite() suite.addTest(doctest.DocTestSuite(trac.ticket.report)) - suite.addTest(unittest.makeSuite(ReportTestCase, 'test')) + suite.addTest(unittest.makeSuite(ReportTestCase)) + suite.addTest(unittest.makeSuite(ExecuteReportTestCase)) return suite if __name__ == '__main__': Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/roadmap.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/roadmap.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/roadmap.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/roadmap.py Sat Nov 15 01:14:46 2014 @@ -1,62 +1,78 @@ -from trac.test import EnvironmentStub -from trac.ticket.roadmap import * -from trac.core import ComponentManager +# -*- 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 trac.core import ComponentManager +from trac.test import EnvironmentStub, Mock, MockPerm +from trac.tests.contentgen import random_sentence +from trac.ticket.roadmap import * +from trac.web.tests.api import RequestHandlerPermissionsTestCaseBase + + class TicketGroupStatsTestCase(unittest.TestCase): def setUp(self): 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,43 +112,116 @@ 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') + + +class MilestoneModuleTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub() + self.req = Mock(href=self.env.href, perm=MockPerm()) + self.mmodule = MilestoneModule(self.env) + self.terms = ['MilestoneAlpha', 'MilestoneBeta', 'MilestoneGamma'] + for term in self.terms + [' '.join(self.terms)]: + m = Milestone(self.env) + m.name = term + m.due = datetime.now(utc) + m.description = random_sentence() + m.insert() + + def tearDown(self): + self.env.reset_db() + + def test_get_search_filters(self): + filters = self.mmodule.get_search_filters(self.req) + filters = list(filters) + self.assertEqual(1, len(filters)) + self.assertEqual(2, len(filters[0])) + self.assertEqual('milestone', filters[0][0]) + self.assertEqual('Milestones', filters[0][1]) + + def test_get_search_results_milestone_not_in_filters(self): + results = self.mmodule.get_search_results(self.req, self.terms, []) + self.assertEqual([], list(results)) + + def test_get_search_results_matches_all_terms(self): + milestone = Milestone(self.env, ' '.join(self.terms)) + results = self.mmodule.get_search_results(self.req, self.terms, + ['milestone']) + results = list(results) + self.assertEqual(1, len(results)) + self.assertEqual(5, len(results[0])) + self.assertEqual('/trac.cgi/milestone/' + + milestone.name.replace(' ', '%20'), + results[0][0]) + self.assertEqual('Milestone ' + milestone.name, results[0][1]) + self.assertEqual(milestone.due, results[0][2]) + self.assertEqual('', results[0][3]) + self.assertEqual(milestone.description, results[0][4]) + + +class MilestoneModulePermissionsTestCase(RequestHandlerPermissionsTestCaseBase): + + def setUp(self): + super(MilestoneModulePermissionsTestCase, self).setUp(MilestoneModule) + + def test_milestone_notfound_with_milestone_create(self): + self.grant_perm('anonymous', 'MILESTONE_VIEW') + self.grant_perm('anonymous', 'MILESTONE_CREATE') + + req = self.create_request(path_info='/milestone/milestone5') + res = self.process_request(req) + + self.assertEqual('milestone_edit.html', res[0]) + self.assertEqual('milestone5', res[1]['milestone'].name) + self.assertEqual("Milestone milestone5 does not exist. You can" + " create it here.", req.chrome['notices'][0]) + + def test_milestone_notfound_without_milestone_create(self): + self.grant_perm('anonymous', 'MILESTONE_VIEW') + + req = self.create_request(path_info='/milestone/milestone5') + + self.assertRaises(ResourceNotFound, self.process_request, req) def in_tlist(ticket, list): return len([t for t in list if t['id'] == ticket.id]) > 0 + 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)) + suite.addTest(unittest.makeSuite(MilestoneModuleTestCase)) + suite.addTest(unittest.makeSuite(MilestoneModulePermissionsTestCase)) return suite + if __name__ == '__main__': unittest.main(defaultTest='suite') Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/wikisyntax.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/wikisyntax.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/wikisyntax.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/tests/wikisyntax.py Sat Nov 15 01:14:46 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 @@ -146,6 +169,14 @@ REPORT_TEST_CASES = u""" </p> ------------------------------ &#1; &#23; +============================== report link with non-digits +report:blah +------------------------------ +<p> +<a class="missing report" title="report does not exist">report:blah</a> +</p> +------------------------------ +<a class="missing report" title="report does not exist">report:blah</a> ============================== InterTrac for reports trac:report:1 [trac:report:1 Trac r1] @@ -175,7 +206,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 +223,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 +240,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() @@ -299,7 +355,7 @@ New tickets: </p><div><dl class="wiki co New tickets: [[TicketQuery(status=new, format=count)]] ------------------------------ <p> -New tickets: <span class="query_count" title="1 tickets for which status=new&max=0&order=id">1</span> +New tickets: <span class="query_count" title="1 ticket for which status=new&max=0&order=id">1</span> </p> ------------------------------ ============================== TicketQuery macro: one result, compact form @@ -309,6 +365,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 +423,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 +518,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 +547,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/branches/trac-1.0.2-integration/trac/trac/ticket/web_ui.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/web_ui.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/web_ui.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/ticket/web_ui.py Sat Nov 15 01:14:46 2014 @@ -38,20 +38,18 @@ from trac.ticket.api import TicketSystem from trac.ticket.model import Milestone, Ticket, group_milestones from trac.ticket.notification import TicketNotifyEmail from trac.timeline.api import ITimelineEventProvider -from trac.util import as_bool, as_int, get_reporter_id +from trac.util import as_bool, as_int, get_reporter_id, lazy 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 + exception_to_unicode, empty, obfuscate_email_address, shorten_line ) from trac.util.presentation import separated -from trac.util.translation import _, tag_, tagn_, N_, gettext, ngettext +from trac.util.translation import _, tag_, tagn_, N_, ngettext from trac.versioncontrol.diff import get_diff_options, diff_blocks -from trac.web import ( - IRequestHandler, RequestDone, arg_list_to_args, parse_arg_list -) +from trac.web import IRequestHandler, arg_list_to_args, parse_arg_list from trac.web.chrome import ( Chrome, INavigationContributor, ITemplateProvider, add_ctxtnav, add_link, add_notice, add_script, add_script_data, @@ -77,12 +75,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', @@ -123,11 +123,11 @@ class TicketModule(Component): return getattr(TicketSystem(self.env), name) raise AttributeError("TicketModule has no attribute '%s'" % name) - @property + @lazy def must_preserve_newlines(self): preserve_newlines = self.preserve_newlines if preserve_newlines == 'default': - preserve_newlines = self.env.get_version(initial=True) >= 21 # 0.11 + preserve_newlines = self.env.database_initial_version >= 21 # 0.11 return as_bool(preserve_newlines) # IContentConverter methods @@ -296,7 +296,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 +307,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': @@ -1130,7 +1130,7 @@ class TicketModule(Component): for f in fields: name = f['name'] value = ticket.values.get(name, '') - if name in ('cc', 'reporter'): + if name in ('cc', 'owner', 'reporter'): value = Chrome(self.env).format_emails(context, value, ' ') elif name in ticket.time_fields: value = format_datetime(value, '%Y-%m-%d %H:%M:%S', @@ -1241,8 +1241,9 @@ class TicketModule(Component): value = ticket[name] if value: if value not in field['options']: - add_warning(req, '"%s" is not a valid value for ' - 'the %s field.' % (value, name)) + add_warning(req, _('"%(value)s" is not a valid value ' + 'for the %(name)s field.', + value=value, name=name)) valid = False elif not field.get('optional', False): add_warning(req, _("field %(name)s must be set", @@ -1263,6 +1264,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 @@ -1278,9 +1286,9 @@ class TicketModule(Component): for field, message in manipulator.validate_ticket(req, ticket): valid = False if field: - add_warning(req, _("The ticket field '%(field)s' is " - "invalid: %(message)s", - field=field, message=message)) + add_warning(req, tag_("The ticket field '%(field)s'" + " is invalid: %(message)s", + field=field, message=message)) else: add_warning(req, message) return valid @@ -1295,9 +1303,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 +1349,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 +1409,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 +1421,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/branches/trac-1.0.2-integration/trac/trac/timeline/__init__.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/__init__.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/__init__.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/__init__.py Sat Nov 15 01:14:46 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/branches/trac-1.0.2-integration/trac/trac/timeline/api.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/api.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/api.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/api.py Sat Nov 15 01:14:46 2014 @@ -69,5 +69,3 @@ class ITimelineEventProvider(Interface): the 'url' :param event: the event tuple, as returned by `get_timeline_events` """ - - Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.html URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.html?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.html (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.html Sat Nov 15 01:14:46 2014 @@ -1,3 +1,13 @@ +<!--! Copyright (C) 2006-2014 Edgewall Software + + 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.com/license.html. + + 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/. +--> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.rss URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.rss?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.rss (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/templates/timeline.rss Sat Nov 15 01:14:46 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/branches/trac-1.0.2-integration/trac/trac/timeline/tests/__init__.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/tests/__init__.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/tests/__init__.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/tests/__init__.py Sat Nov 15 01:14:46 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/branches/trac-1.0.2-integration/trac/trac/timeline/tests/functional.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/tests/functional.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/tests/functional.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/tests/functional.py Sat Nov 15 01:14:46 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 Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/web_ui.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/web_ui.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/web_ui.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/timeline/web_ui.py Sat Nov 15 01:14:46 2014 @@ -326,11 +326,11 @@ class TimelineModule(Component): elif len(time) >= 2: precision = 'hours' try: - return self.get_timeline_link(formatter.req, - parse_date(path, utc), - label, precision, query, fragment) + dt = parse_date(path, utc, locale='iso8601', hint='iso8601') + return self.get_timeline_link(formatter.req, dt, label, + precision, query, fragment) except TracError, e: - return tag.a(label, title=to_unicode(e.message), + return tag.a(label, title=to_unicode(e), class_='timeline missing') yield ('timeline', link_resolver) @@ -395,7 +395,7 @@ class TimelineModule(Component): name=tag.tt(ep.__class__.__name__), kinds=', '.join('"%s"' % ep_kinds[f] for f in current_filters & ep_filters)), - tag.b(exception_to_unicode(exc)), class_='message'), + tag.strong(exception_to_unicode(exc)), class_='message'), tag.p(tag_("You may want to see the %(other_events)s from the " "Timeline or notify your Trac administrator about the " "error (detailed information was written to the log).", Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db10.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db10.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db10.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db10.py Sat Nov 15 01:14:46 2014 @@ -1,3 +1,16 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2005-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.com/license.html. +# +# 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/. + sql = [ #-- Make the node_change table contain more information, and force a resync """DROP TABLE revision;""", @@ -19,6 +32,7 @@ sql = [ );""" ] + def do_upgrade(env, ver, cursor): for s in sql: cursor.execute(s) Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db11.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db11.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db11.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db11.py Sat Nov 15 01:14:46 2014 @@ -1,3 +1,16 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2005-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.com/license.html. +# +# 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/. + sql = [ #-- Remove empty values from the milestone list """DELETE FROM milestone WHERE COALESCE(name,'')='';""", Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db12.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db12.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db12.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db12.py Sat Nov 15 01:14:46 2014 @@ -1,3 +1,16 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2005-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.com/license.html. +# +# 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/. + sql = [ #-- Some anonymous session might have been left over """DELETE FROM session WHERE username='anonymous';""", Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db13.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db13.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db13.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db13.py Sat Nov 15 01:14:46 2014 @@ -1,3 +1,16 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2005-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.com/license.html. +# +# 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/. + sql = [ #-- Add ticket_type to 'ticket', remove the unused 'url' column """CREATE TEMPORARY TABLE ticket_old AS SELECT * FROM ticket;""", Modified: bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db14.py URL: http://svn.apache.org/viewvc/bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db14.py?rev=1639823&r1=1639822&r2=1639823&view=diff ============================================================================== --- bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db14.py (original) +++ bloodhound/branches/trac-1.0.2-integration/trac/trac/upgrades/db14.py Sat Nov 15 01:14:46 2014 @@ -1,3 +1,16 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2005-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.com/license.html. +# +# 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/. + sql = [ """CREATE TEMPORARY TABLE node_change_old AS SELECT * FROM node_change;""", """DROP TABLE node_change;""",