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&amp;group=resolution&amp;max=0&amp;order=time" 
title="1/3 closed"></a>
+      </td><td class="open" style="width: 67%">
+        <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;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&amp;order=id">Total number of tickets: 3</a>
+    </span>
+    <span class="interval">
+      - <a 
href="/query?status=closed&amp;group=resolution&amp;max=0&amp;order=time">closed:
 1</a>
+    </span><span class="interval">
+      - <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;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&amp;reporter=santa&amp;group=resolution&amp;max=0&amp;order=time"
 title="0/1 closed"></a>
+      </td><td class="open" style="width: 100%">
+        <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;max=0&amp;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&amp;max=0&amp;order=id">Total number of 
tickets: 1</a>
+    </span>
+    <span class="interval">
+      - <a 
href="/query?status=closed&amp;reporter=santa&amp;group=resolution&amp;max=0&amp;order=time">closed:
 0</a>
+    </span><span class="interval">
+      - <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;max=0&amp;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&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="1/2 closed"></a>
+      </td><td class="open" style="width: 50%">
+        <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;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&amp;or&amp;owner=santa&amp;max=0&amp;order=id">Total
 number of tickets: 2</a>
+    </span>
+    <span class="interval">
+      - <a 
href="/query?status=closed&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time">closed:
 1</a>
+    </span><span class="interval">
+      - <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;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=&amp;max=0&amp;order=id">(none)</a></i>
+
+
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 40%">
+    <tr>
+      <td class="closed" style="display: none">
+        <a 
href="/query?project=&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="0/1 closed"></a>
+      </td><td class="open" style="width: 100%">
+        <a 
href="/query?project=&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;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&amp;max=0&amp;order=id">xmas</a>
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 80%">
+    <tr>
+      <td class="closed" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="1/2 closed"></a>
+      </td><td class="open" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;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&amp;reporter=santa&amp;max=0&amp;order=id">xmas</a>
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 80%">
+    <tr>
+      <td class="closed" style="display: none">
+        <a 
href="/query?project=xmas&amp;status=closed&amp;reporter=santa&amp;group=resolution&amp;max=0&amp;order=time"
 title="0/1 closed"></a>
+      </td><td class="open" style="width: 100%">
+        <a 
href="/query?project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;max=0&amp;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&amp;reporter=santa&amp;or&amp;owner=santa&amp;project=xmas&amp;max=0&amp;order=id">xmas</a>
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 80%">
+    <tr>
+      <td class="closed" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=closed&amp;reporter=santa&amp;or&amp;owner=santa&amp;project=xmas&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="1/2 closed"></a>
+      </td><td class="open" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;or&amp;owner=santa&amp;project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;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>
 ------------------------------
 &amp;#1; &amp;#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&amp;max=0&amp;order=id">1</span>
+New tickets: <span class="query_count" title="1 ticket for which 
status=new&amp;max=0&amp;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;""",


Reply via email to