Author: rjollos
Date: Fri Nov 14 12:10:40 2014
New Revision: 1639617
URL: http://svn.apache.org/r1639617
Log:
Missing file adds from Trac 1.0.2.
Added:
bloodhound/vendor/trac/current/trac/tests/compat.py
bloodhound/vendor/trac/current/trac/ticket/templates/admin_components.html
bloodhound/vendor/trac/current/trac/ticket/templates/admin_enums.html
bloodhound/vendor/trac/current/trac/ticket/templates/admin_milestones.html
bloodhound/vendor/trac/current/trac/ticket/templates/admin_versions.html
bloodhound/vendor/trac/current/trac/timeline/tests/web_ui.py
bloodhound/vendor/trac/current/trac/timeline/tests/wikisyntax.py
bloodhound/vendor/trac/current/trac/util/tests/translation.py
bloodhound/vendor/trac/current/tracopt/ticket/tests/
bloodhound/vendor/trac/current/tracopt/ticket/tests/__init__.py
bloodhound/vendor/trac/current/tracopt/ticket/tests/commit_updater.py
bloodhound/vendor/trac/current/tracopt/versioncontrol/git/tests/git_fs.py
Added: bloodhound/vendor/trac/current/trac/tests/compat.py
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/compat.py?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/tests/compat.py (added)
+++ bloodhound/vendor/trac/current/trac/tests/compat.py Fri Nov 14 12:10:40 2014
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 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/.
+
+"""Some test functions since Python 2.7 to provide backwards-compatibility
+with previous versions of Python from 2.5 onward.
+"""
+
+import os
+import shutil
+import sys
+import unittest
+
+
+if not hasattr(unittest.TestCase, 'assertIs'):
+ def assertIs(self, expr1, expr2, msg=None):
+ if expr1 is not expr2:
+ raise self.failureException(msg or '%r is not %r'
+ % (expr1, expr2))
+ unittest.TestCase.assertIs = assertIs
+
+
+if not hasattr(unittest.TestCase, 'assertIsNot'):
+ def assertIsNot(self, expr1, expr2, msg=None):
+ if expr1 is expr2:
+ raise self.failureException(msg or '%r is %r' % (expr1, expr2))
+ unittest.TestCase.assertIsNot = assertIsNot
+
+
+if not hasattr(unittest.TestCase, 'assertIsNone'):
+ def assertIsNone(self, obj, msg=None):
+ self.assertIs(obj, None, msg)
+ unittest.TestCase.assertIsNone = assertIsNone
+
+
+if not hasattr(unittest.TestCase, 'assertIsNotNone'):
+ def assertIsNotNone(self, obj, msg=None):
+ self.assertIsNot(obj, None, msg)
+ unittest.TestCase.assertIsNotNone = assertIsNotNone
+
+
+if not hasattr(unittest.TestCase, 'assertIn'):
+ def assertIn(self, member, container, msg=None):
+ if member not in container:
+ raise self.failureException(msg or '%r not in %r' %
+ (member, container))
+ unittest.TestCase.assertIn = assertIn
+
+
+if not hasattr(unittest.TestCase, 'assertNotIn'):
+ def assertNotIn(self, member, container, msg=None):
+ if member in container:
+ raise self.failureException(msg or '%r in %r' %
+ (member, container))
+ unittest.TestCase.assertNotIn = assertNotIn
+
+
+if not hasattr(unittest.TestCase, 'assertIsInstance'):
+ def assertIsInstance(self, obj, cls, msg=None):
+ if not isinstance(obj, cls):
+ raise self.failureException(msg or '%r is not an instance of %r' %
+ (obj, cls))
+ unittest.TestCase.assertIsInstance = assertIsInstance
+
+
+if not hasattr(unittest.TestCase, 'assertNotIsInstance'):
+ def assertNotIsInstance(self, obj, cls, msg=None):
+ if isinstance(obj, cls):
+ raise self.failureException(msg or '%r is an instance of %r' %
+ (obj, cls))
+ unittest.TestCase.assertNotIsInstance = assertNotIsInstance
+
+
+def rmtree(path):
+ import errno
+ def onerror(function, path, excinfo):
+ # `os.remove` fails for a readonly file on Windows.
+ # Then, it attempts to be writable and remove.
+ if function != os.remove:
+ raise
+ e = excinfo[1]
+ if isinstance(e, OSError) and e.errno == errno.EACCES:
+ mode = os.stat(path).st_mode
+ os.chmod(path, mode | 0666)
+ function(path)
+ else:
+ raise
+ if os.name == 'nt' and isinstance(path, str):
+ # Use unicode characters in order to allow non-ansi characters
+ # on Windows.
+ path = unicode(path, sys.getfilesystemencoding())
+ shutil.rmtree(path, onerror=onerror)
Added:
bloodhound/vendor/trac/current/trac/ticket/templates/admin_components.html
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/admin_components.html?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/admin_components.html
(added)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/admin_components.html
Fri Nov 14 12:10:40 2014
@@ -0,0 +1,124 @@
+<!--! 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">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:i18n="http://genshi.edgewall.org/i18n">
+ <xi:include href="admin.html" />
+ <head>
+ <title>Components</title>
+ </head>
+
+ <body>
+ <h2>Manage Components <span py:if="view == 'list'"
class="trac-count">(${len(components)})</span></h2>
+
+ <py:def function="owner_field(default_owner='', br_after_label=False)">
+ <div class="field">
+ <label>Owner:<br py:if="br_after_label"/>
+ <py:choose>
+ <select py:when="owners" size="1" id="owner" name="owner">
+ <option py:for="owner in owners"
+ selected="${owner == default_owner or None}"
value="$owner">$owner</option>
+ <option py:if="default_owner and default_owner not in owners"
+ selected="selected"
value="$default_owner">$default_owner</option>
+ </select>
+ <input py:otherwise="" type="text" name="owner"
value="$default_owner" />
+ </py:choose>
+ </label>
+ </div>
+ </py:def>
+
+ <py:choose test="view">
+ <form py:when="'detail'" class="mod" id="modcomp" method="post"
action="">
+ <fieldset>
+ <legend>Modify Component:</legend>
+ <div class="field">
+ <label>Name:<br /><input type="text" name="name"
class="trac-autofocus" value="$component.name" /></label>
+ </div>
+ ${owner_field(component.owner, True)}
+ <div class="field">
+ <fieldset>
+ <label for="description" i18n:msg="">
+ Description: (you may use <a tabindex="42"
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here)
+ </label>
+ <p>
+ <textarea id="description" name="description" class="wikitext
trac-fullwidth trac-resizable"
+ rows="6" cols="60">
+$component.description</textarea>
+ </p>
+ </fieldset>
+ </div>
+ <div class="buttons">
+ <input type="submit" name="save" class="trac-disable-on-submit"
value="${_('Save')}"/>
+ <input type="submit" name="cancel" value="${_('Cancel')}" />
+ </div>
+ </fieldset>
+ </form>
+
+ <py:otherwise>
+ <form class="addnew" id="addcomponent" method="post" action="">
+ <fieldset>
+ <legend>Add Component:</legend>
+ <div class="field">
+ <label>Name: <input type="text" name="name"/></label>
+ </div>
+ ${owner_field()}
+ <div class="buttons">
+ <input type="submit" name="add" class="trac-disable-on-submit"
value="${_('Add')}"/>
+ </div>
+ </fieldset>
+ </form>
+
+ <py:choose>
+ <form py:when="components" id="component_table" method="post"
action="">
+ <table class="listing" id="complist">
+ <thead>
+ <tr><th class="sel"> </th>
+ <th>Name</th><th>Owner</th><th>Default</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr py:for="comp in components">
+ <td class="sel"><input type="checkbox" name="sel"
value="$comp.name" /></td>
+ <td class="name">
+ <a href="${panel_href(comp.name)}">$comp.name</a>
+ </td>
+ <td class="owner">$comp.owner</td>
+ <td class="default">
+ <input type="radio" name="default" value="$comp.name"
+ checked="${comp.name == default or None}" />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="buttons">
+ <input type="submit" name="apply" value="${_('Apply changes')}"
/>
+ <input type="submit" name="remove"
class="trac-disable-on-submit" value="${_('Remove selected items')}"/>
+ </div>
+ <p class="help">
+ You can remove all items from this list to completely hide this
+ field from the user interface.
+ </p>
+ </form>
+
+ <p py:otherwise="" class="help">
+ As long as you don't add any items to the list, this field
+ will remain completely hidden from the user interface.
+ </p>
+ </py:choose>
+ </py:otherwise>
+ </py:choose>
+ </body>
+
+</html>
Added: bloodhound/vendor/trac/current/trac/ticket/templates/admin_enums.html
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/admin_enums.html?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/admin_enums.html
(added)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/admin_enums.html Fri
Nov 14 12:10:40 2014
@@ -0,0 +1,104 @@
+<!--! 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">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:i18n="http://genshi.edgewall.org/i18n"
+ xmlns:py="http://genshi.edgewall.org/">
+ <xi:include href="admin.html" />
+ <head>
+ <title>$label_plural</title>
+ </head>
+
+ <body>
+ <h2>
+ <i18n:msg params="label_plural">Manage $label_plural</i18n:msg>
+ <span py:if="view == 'list'" class="trac-count">(${len(enums)})</span>
+ </h2>
+
+ <py:choose test="view">
+ <form py:when="'detail'" class="mod" id="modenum" method="post"
action="">
+ <fieldset>
+ <legend i18n:msg="label_singular">Modify $label_singular:</legend>
+ <div class="field">
+ <label>Name: <input type="text" name="name" class="trac-autofocus"
value="${enum.name}" /></label>
+ </div>
+ <div class="buttons">
+ <input type="submit" name="save" class="trac-disable-on-submit"
value="${_('Save')}"/>
+ <input type="submit" name="cancel" value="${_('Cancel')}"/>
+ </div>
+ </fieldset>
+ </form>
+
+ <py:otherwise>
+ <form class="addnew" id="addenum" method="post" action="">
+ <fieldset>
+ <legend i18n:msg="label_singular">Add $label_singular:</legend>
+ <div class="field">
+ <label>Name: <input type="text" name="name" id="name"/></label>
+ </div>
+ <div class="buttons">
+ <input type="submit" name="add" class="trac-disable-on-submit"
value="${_('Add')}"/>
+ </div>
+ </fieldset>
+ </form>
+
+ <py:choose>
+ <form py:when="enums" id="enumtable" method="post" action="">
+ <table class="listing" id="enumlist">
+ <thead>
+ <tr><th class="sel"> </th>
+ <th>Name</th><th>Default</th><th>Order</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr py:for="enum in enums">
+ <td><input type="checkbox" name="sel" value="${enum.name}"
/></td>
+ <td><a href="${panel_href(enum.name)}">${enum.name}</a></td>
+ <td class="default">
+ <input type="radio" name="default" value="${enum.name}"
+ checked="${enum.name==default or None}" />
+ </td>
+ <td class="default">
+ <select name="value_${enum.value}">
+ <option py:for="other in enums"
+ selected="${other.value==enum.value or
None}">${other.value}</option>
+ </select>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="buttons">
+ <input type="submit" name="apply" value="${_('Apply changes')}"
/>
+ <input type="submit" name="remove"
class="trac-disable-on-submit" value="${_('Remove selected items')}" />
+ </div>
+ <p class="help">
+ You can remove all items from this list to completely hide this
+ field from the user interface.
+ </p>
+ <p class="help" py:if="type=='priority'" i18n:msg="">
+ <strong>Note:</strong> The order of priorities determines the
+ coloring of entries in the ticket queries and reports.
+ </p>
+ </form>
+
+ <p py:otherwise="" class="help">
+ As long as you don't add any items to the list, this field
+ will remain completely hidden from the user interface.
+ </p>
+ </py:choose>
+ </py:otherwise>
+ </py:choose>
+ </body>
+
+</html>
Added:
bloodhound/vendor/trac/current/trac/ticket/templates/admin_milestones.html
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/admin_milestones.html?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/admin_milestones.html
(added)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/admin_milestones.html
Fri Nov 14 12:10:40 2014
@@ -0,0 +1,166 @@
+<!--! 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">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:i18n="http://genshi.edgewall.org/i18n"
+ py:with="perm = req.perm('admin', 'ticket/milestones');
+ can_create = 'MILESTONE_CREATE' in perm;
+ can_modify = 'MILESTONE_MODIFY' in perm;
+ can_remove = 'MILESTONE_DELETE' in perm">
+ <xi:include href="admin.html" />
+ <head>
+ <title>Milestones</title>
+ <script type="text/javascript"
+ py:if="view == 'detail' and can_modify or
+ view == 'list' and can_create">
+ jQuery(document).ready(function($) {
+ $("#duedate").datetimepicker();
+ $("#completeddate").datetimepicker();
+ });
+ </script>
+ </head>
+
+ <body>
+ <h2>Manage Milestones <span py:if="view == 'list'"
class="trac-count">(${len(milestones)})</span></h2>
+
+ <py:choose test="view">
+ <form py:when="'detail'" class="mod" method="post" id="modifymilestone"
action=""
+ py:with="disabled = 'disabled' if not can_modify else None;
+ readonly = 'readonly' if not can_modify else None">
+ <fieldset>
+ <legend>Modify Milestone:</legend>
+ <div class="field">
+ <label>Name:<br /> <input type="text" name="name"
class="trac-autofocus"
+ value="$milestone.name"
readonly="${readonly}" /></label>
+ </div>
+ <div class="field">
+ <label>Due:<br />
+ <input type="text" id="duedate" name="duedate"
size="${len(datetime_hint)}"
+ value="${milestone.due and
format_datetime(milestone.due)}" readonly="${readonly}"
+ title="${_('Format: %(datehint)s',
datehint=datetime_hint)}"/>
+ <span class="hint" i18n:msg="datehint">Format:
$datetime_hint</span>
+ </label>
+ </div>
+ <div class="field">
+ <label>
+ <input type="checkbox" id="completed" name="completed"
+ checked="${milestone.completed or None}"
disabled="${disabled}"/>
+ Completed:<br />
+ </label>
+ <label>
+ <input type="text" id="completeddate" name="completeddate"
+ size="${len(datetime_hint)}"
+ value="${format_datetime(milestone.completed)}"
readonly="${readonly}"
+ title="${_('Format: %(datehint)s',
datehint=datetime_hint)}" />
+ <span class="hint" i18n:msg="datehint">Format:
$datetime_hint</span>
+ </label>
+ <script type="text/javascript">
+ jQuery(document).ready(function($) {
+ function updateCompletedDate() {
+ $("#completeddate").enable($("#completed").checked());
+ }
+ $("#completed").click(updateCompletedDate);
+ updateCompletedDate();
+ });
+ </script>
+ </div>
+ <div class="field">
+ <fieldset>
+ <label for="description" i18n:msg="">
+ Description: (you may use <a tabindex="42"
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here)
+ </label>
+ <p>
+ <textarea id="description" name="description" class="wikitext
trac-fullwidth trac-resizable"
+ rows="6" cols="60" readonly="${readonly}">
+${milestone.description}</textarea>
+ </p>
+ </fieldset>
+ </div>
+ <div class="buttons">
+ <input type="submit" name="save" value="${_('Save')}"
class="trac-disable-on-submit" disabled="${disabled}"/>
+ <input type="submit" name="cancel" value="${_('Cancel')}"/>
+ </div>
+ </fieldset>
+ </form>
+
+ <py:otherwise>
+ <form class="addnew" id="addmilestone" method="post" action=""
py:if="can_create">
+ <fieldset>
+ <legend>Add Milestone:</legend>
+ <div class="field">
+ <label>Name: <input type="text" name="name" id="name"
size="22"/></label>
+ </div>
+ <div class="field">
+ <label>
+ Due:
+ <input type="text" id="duedate" name="duedate"
size="${len(datetime_hint)}"
+ title="${_('Format: %(datehint)s',
datehint=datetime_hint)}"/>
+ <span class="hint" i18n:msg="datetimehint">Format:
$datetime_hint</span>
+ </label>
+ </div>
+ <div class="buttons">
+ <input type="submit" name="add" class="trac-disable-on-submit"
value="${_('Add')}"/>
+ </div>
+ </fieldset>
+ </form>
+
+ <py:choose>
+ <form id="milestone_table" method="post" action=""
py:when="milestones">
+ <table class="listing" id="millist">
+ <thead>
+ <tr><th class="sel" py:if="can_remove"> </th>
+
<th>Name</th><th>Due</th><th>Completed</th><th>Default</th><th>Tickets</th>
+ </tr>
+ </thead>
+ <tbody><tr py:for="(milestone, ticket_count) in milestones">
+ <td py:if="can_remove">
+ <input type="checkbox" name="sel" value="$milestone.name" />
+ </td>
+ <td>
+ <a href="${panel_href(milestone.name)}">${milestone.name}</a>
+ </td>
+ <td><py:if test="milestone.due">
+ ${format_datetime(milestone.due)}
+ </py:if></td>
+ <td><py:if test="milestone.completed">
+ ${format_datetime(milestone.completed)}
+ </py:if></td>
+ <td class="default">
+ <input type="radio" name="default" value="$milestone.name"
+ checked="${milestone.name==default or None}" />
+ </td>
+ <td class="num">${ticket_count}</td>
+ </tr></tbody>
+ </table>
+ <div class="buttons">
+ <input type="submit" name="apply" value="${_('Apply changes')}"
/>
+ <input type="submit" name="remove"
class="trac-disable-on-submit" value="${_('Remove selected items')}"
py:if="can_remove"/>
+ </div>
+ <p class="help">
+ You can remove all items from this list to completely hide this
+ field from the user interface.
+ </p>
+ </form>
+
+ <p py:otherwise="" class="help">
+ As long as you don't add any items to the list, this field
+ will remain completely hidden from the user interface.
+ </p>
+ </py:choose>
+ </py:otherwise>
+ </py:choose>
+ </body>
+
+</html>
Added: bloodhound/vendor/trac/current/trac/ticket/templates/admin_versions.html
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/admin_versions.html?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/admin_versions.html
(added)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/admin_versions.html
Fri Nov 14 12:10:40 2014
@@ -0,0 +1,127 @@
+<!--! 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">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:i18n="http://genshi.edgewall.org/i18n">
+ <xi:include href="admin.html" />
+ <head>
+ <title>Versions</title>
+ <script type="text/javascript">
+ jQuery(document).ready(function($) {
+ $("#releaseddate").datetimepicker();
+ });
+ </script>
+ </head>
+
+ <body>
+ <h2>Manage Versions <span py:if="view == 'list'"
class="trac-count">(${len(versions)})</span></h2>
+
+ <py:choose test="view">
+ <form py:when="'detail'" class="mod" id="modifyversion" method="post"
action="">
+ <fieldset>
+ <legend>Modify Version:</legend>
+ <div class="field">
+ <label>Name:<br />
+ <input type="text" name="name" class="trac-autofocus"
value="${version.name}" />
+ </label>
+ </div>
+ <div class="field">
+ <label>Released:<br />
+ <input type="text" id="releaseddate" name="time"
size="${len(datetime_hint)}"
+ value="${format_datetime(version.time)}"
+ title="${_('Format: %(datehint)s',
datehint=datetime_hint)}" />
+ <span class="hint" i18n:msg="datehint">Format:
$datetime_hint</span>
+ </label>
+ </div>
+ <div class="field">
+ <fieldset>
+ <label for="description" i18n:msg="">
+ Description: (you may use <a tabindex="42"
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here)
+ </label>
+ <p>
+ <textarea id="description" name="description" class="wikitext
trac-fullwidth trac-resizable" rows="6" cols="60">
+$version.description</textarea>
+ </p>
+ </fieldset>
+ </div>
+ <div class="buttons">
+ <input type="submit" name="save" class="trac-disable-on-submit"
value="${_('Save')}"/>
+ <input type="submit" name="cancel" value="${_('Cancel')}"/>
+ </div>
+ </fieldset>
+ </form>
+
+ <py:otherwise>
+ <form class="addnew" id="addversion" method="post" action="">
+ <fieldset>
+ <legend>Add Version:</legend>
+ <div class="field">
+ <label>Name: <input type="text" name="name" id="name"
size="22"/></label>
+ </div>
+ <div class="field">
+ <label>
+ Released:
+ <input type="text" id="releaseddate" name="time"
size="${len(datetime_hint)}"
+ title="${_('Format: %(datehint)s',
datehint=datetime_hint)}"
+ value="${format_datetime()}"/>
+ <span class="hint" i18n:msg="datehint">Format:
$datetime_hint</span>
+ </label>
+ </div>
+ <div class="buttons">
+ <input type="submit" name="add" class="trac-disable-on-submit"
value="${_('Add')}" />
+ </div>
+ </fieldset>
+ </form>
+
+ <py:choose>
+ <form py:when="versions" id="version_table" method="post" action="">
+ <table class="listing" id="verlist">
+ <thead>
+ <tr><th class="sel"> </th>
+ <th>Name</th><th>Released</th><th>Default</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr py:for="version in versions">
+ <td><input type="checkbox" name="sel"
value="${version.name}"/></td>
+ <td><a
href="${panel_href(version.name)}">${version.name}</a></td>
+ <td>${version.time and format_datetime(version.time)}</td>
+ <td class="default">
+ <input type="radio" name="default" value="${version.name}"
+ checked="${version.name==default or None}" />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="buttons">
+ <input type="submit" name="apply" value="${_('Apply changes')}"
/>
+ <input type="submit" name="remove"
class="trac-disable-on-submit" value="${_('Remove selected items')}" />
+ </div>
+ <p class="help">
+ You can remove all items from this list to completely hide this
+ field from the user interface.
+ </p>
+ </form>
+
+ <p py:otherwise="" class="help">
+ As long as you don't add any items to the list, this field
+ will remain completely hidden from the user interface.
+ </p>
+ </py:choose>
+ </py:otherwise>
+ </py:choose>
+ </body>
+
+</html>
Added: bloodhound/vendor/trac/current/trac/timeline/tests/web_ui.py
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/tests/web_ui.py?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/tests/web_ui.py (added)
+++ bloodhound/vendor/trac/current/trac/timeline/tests/web_ui.py Fri Nov 14
12:10:40 2014
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 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/.
+
+import unittest
+from datetime import datetime, timedelta
+
+from trac.test import EnvironmentStub, Mock, MockPerm, locale_en
+from trac.timeline.web_ui import TimelineModule
+from trac.util.datefmt import (
+ format_date, format_datetime, format_time, pretty_timedelta, utc,
+)
+from trac.util.html import plaintext
+from trac.web.chrome import Chrome
+from trac.web.href import Href
+
+
+class PrettyDateinfoTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.env = EnvironmentStub()
+ self.req = Mock(href=Href('/'), abs_href=Href('http://example.org/'),
+ authname='anonymous', tz=utc, locale=locale_en,
+ lc_time=locale_en, chrome={}, perm=MockPerm(),
+ session={})
+
+ def tearDown(self):
+ self.env.reset_db()
+
+ def _format_chrome(self, d, format, dateonly):
+ data = Chrome(self.env).populate_data(self.req, {})
+ return plaintext(data['pretty_dateinfo'](d, format=format,
+ dateonly=dateonly))
+
+ def _format_timeline(self, d, format, dateonly):
+ data = Chrome(self.env).populate_data(self.req, {})
+ TimelineModule(self.env) \
+ .post_process_request(self.req, 'timeline.html', data, None)
+ return plaintext(data['pretty_dateinfo'](d, format=format,
+ dateonly=dateonly))
+
+ def test_relative(self):
+ t = datetime.now(utc) - timedelta(days=1)
+ label = '%s ago' % pretty_timedelta(t)
+ self.assertEqual(label, self._format_chrome(t, 'relative', False))
+ self.assertEqual(label, self._format_timeline(t, 'relative', False))
+
+ def test_relative_dateonly(self):
+ t = datetime.now(utc) - timedelta(days=1)
+ label = pretty_timedelta(t)
+ self.assertEqual(label, self._format_chrome(t, 'relative', True))
+ self.assertEqual(label, self._format_timeline(t, 'relative', True))
+
+ def test_absolute(self):
+ t = datetime.now(utc) - timedelta(days=1)
+ label = 'on %s at %s' % \
+ (format_date(t, locale=locale_en, tzinfo=utc),
+ format_time(t, locale=locale_en, tzinfo=utc))
+ self.assertEqual(label, self._format_chrome(t, 'absolute', False))
+ self.assertEqual(label, self._format_timeline(t, 'absolute', False))
+
+ def test_absolute_dateonly(self):
+ t = datetime.now(utc) - timedelta(days=1)
+ label = format_datetime(t, locale=locale_en, tzinfo=utc)
+ self.assertEqual(label, self._format_chrome(t, 'absolute', True))
+ self.assertEqual(label, self._format_timeline(t, 'absolute', True))
+
+ def test_absolute_iso8601(self):
+ t = datetime(2014, 1, 28, 2, 30, 44, 0, utc)
+ label = 'at 2014-01-28T02:30:44Z'
+ self.req.lc_time = 'iso8601'
+ self.assertEqual(label, self._format_chrome(t, 'absolute', False))
+ self.assertEqual(label, self._format_timeline(t, 'absolute', False))
+
+ def test_absolute_iso8601_dateonly(self):
+ t = datetime(2014, 1, 28, 2, 30, 44, 0, utc)
+ label = '2014-01-28T02:30:44Z'
+ self.req.lc_time = 'iso8601'
+ self.assertEqual(label, self._format_chrome(t, 'absolute', True))
+ self.assertEqual(label, self._format_timeline(t, 'absolute', True))
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(PrettyDateinfoTestCase))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
Added: bloodhound/vendor/trac/current/trac/timeline/tests/wikisyntax.py
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/tests/wikisyntax.py?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/tests/wikisyntax.py (added)
+++ bloodhound/vendor/trac/current/trac/timeline/tests/wikisyntax.py Fri Nov 14
12:10:40 2014
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 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 time
+import unittest
+
+from trac.timeline.web_ui import TimelineModule
+from trac.wiki.tests import formatter
+
+TIMELINE_TEST_CASES = u"""
+============================== timeline: link resolver
+timeline:2008-01-29
+timeline:2008-01-29T15:48
+timeline:2008-01-29T15:48Z
+timeline:2008-01-29T16:48+01
+timeline:2008-01-0A
+timeline:@datestr_libc@
+------------------------------
+<p>
+<a class="timeline" href="/timeline?from=2008-01-29T00%3A00%3A00Z" title="See
timeline at 2008-01-29T00:00:00Z">timeline:2008-01-29</a>
+<a class="timeline"
href="/timeline?from=2008-01-29T15%3A48%3A00Z&precision=minutes" title="See
timeline at 2008-01-29T15:48:00Z">timeline:2008-01-29T15:48</a>
+<a class="timeline"
href="/timeline?from=2008-01-29T15%3A48%3A00Z&precision=minutes" title="See
timeline at 2008-01-29T15:48:00Z">timeline:2008-01-29T15:48Z</a>
+<a class="timeline"
href="/timeline?from=2008-01-29T15%3A48%3A00Z&precision=seconds" title="See
timeline at 2008-01-29T15:48:00Z">timeline:2008-01-29T16:48+01</a>
+<a class="timeline missing" title=""2008-01-0A" is an invalid date, or
the date format is not known. Try "YYYY-MM-DDThh:mm:ss±hh:mm"
instead.">timeline:2008-01-0A</a>
+<a class="timeline missing" title=""@datestr_libc@" is an invalid
date, or the date format is not known. Try "YYYY-MM-DDThh:mm:ss±hh:mm"
instead.">timeline:@datestr_libc@</a>
+</p>
+------------------------------
+"""
+
+
+def suite():
+ suite = unittest.TestSuite()
+ datestr_libc = time.strftime('%x', (2013, 10, 24, 0, 0, 0, 0, 0, -1))
+ suite.addTest(formatter.suite(TIMELINE_TEST_CASES.replace('@datestr_libc@',
+ datestr_libc),
+ file=__file__))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
Added: bloodhound/vendor/trac/current/trac/util/tests/translation.py
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/tests/translation.py?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/tests/translation.py (added)
+++ bloodhound/vendor/trac/current/trac/util/tests/translation.py Fri Nov 14
12:10:40 2014
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 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 shutil
+import tempfile
+import unittest
+from pkg_resources import resource_exists, resource_filename
+try:
+ import babel
+except ImportError:
+ babel = None
+ locale_identifiers = lambda: ()
+else:
+ try:
+ from babel.localedata import locale_identifiers
+ except ImportError:
+ from babel.localedata import list as locale_identifiers
+
+from trac.test import EnvironmentStub
+from trac.util import translation
+
+
+class TranslationsProxyTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.env = EnvironmentStub()
+ self.env.path = tempfile.mkdtemp(prefix='trac-tempenv-')
+
+ def tearDown(self):
+ translation.deactivate()
+ self.env.reset_db()
+ shutil.rmtree(self.env.path)
+
+ def _get_locale_dir(self):
+ return resource_filename('trac', 'locale')
+
+ def _get_available_locales(self):
+ return sorted(locale
+ for locale in translation.get_available_locales()
+ if resource_exists('trac',
+ 'locale/%s/LC_MESSAGES/messages.mo'
+ % locale))
+
+ def test_activate(self):
+ locales = self._get_available_locales()
+ if locales:
+ translation.activate(locales[0], self.env.path)
+
+ def test_activate_unavailable_locale(self):
+ unavailables = sorted(set(locale_identifiers()) -
+ set(translation.get_available_locales())) or \
+ ('en_US',)
+ locale_dir = self._get_locale_dir()
+ translation.add_domain('catalog1', self.env.path, locale_dir)
+ translation.add_domain('catalog2', self.env.path, locale_dir)
+ translation.activate(unavailables[0], self.env.path)
+
+ def test_activate_with_non_existent_catalogs(self):
+ locales = self._get_available_locales()
+ if locales:
+ locale_dir = self._get_locale_dir()
+ translation.add_domain('catalog1', self.env.path, locale_dir)
+ translation.add_domain('catalog2', self.env.path, locale_dir)
+ translation.activate(locales[0], self.env.path)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TranslationsProxyTestCase))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
Added: bloodhound/vendor/trac/current/tracopt/ticket/tests/__init__.py
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/tracopt/ticket/tests/__init__.py?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/tracopt/ticket/tests/__init__.py (added)
+++ bloodhound/vendor/trac/current/tracopt/ticket/tests/__init__.py Fri Nov 14
12:10:40 2014
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 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 tracopt.ticket.tests import commit_updater
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(commit_updater.suite())
+ return suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
Added: bloodhound/vendor/trac/current/tracopt/ticket/tests/commit_updater.py
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/tracopt/ticket/tests/commit_updater.py?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/tracopt/ticket/tests/commit_updater.py
(added)
+++ bloodhound/vendor/trac/current/tracopt/ticket/tests/commit_updater.py Fri
Nov 14 12:10:40 2014
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 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
+
+from trac.test import EnvironmentStub, Mock
+from trac.tests.contentgen import random_sentence
+from trac.ticket.model import Ticket
+from trac.util.datefmt import utc
+from trac.versioncontrol.api import Repository
+from tracopt.ticket.commit_updater import CommitTicketUpdater
+
+
+class CommitTicketUpdaterTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.env = EnvironmentStub(enable=['trac.*',
+ 'tracopt.ticket.commit_updater.*'])
+ self.env.config.set('ticket', 'commit_ticket_update_check_perms',
False)
+ self.repos = Mock(Repository, 'repos1', {'name': 'repos1', 'id': 1},
+ self.env.log)
+ self.updater = CommitTicketUpdater(self.env)
+
+ def tearDown(self):
+ self.env.reset_db()
+
+ def _make_tickets(self, num):
+ self.tickets = []
+ for i in xrange(0, num):
+ ticket = Ticket(self.env)
+ ticket['reporter'] = 'someone'
+ ticket['summary'] = random_sentence()
+ ticket.insert()
+ self.tickets.append(ticket)
+
+ def test_changeset_added(self):
+ self._make_tickets(1)
+ message = 'This is the first comment. Refs #1.'
+ chgset = Mock(repos=self.repos, rev=1, message=message, author='joe',
+ date=datetime(2001, 1, 1, 1, 1, 1, 0, utc))
+ self.updater.changeset_added(self.repos, chgset)
+ self.assertEqual("""\
+In [changeset:"1/repos1"]:
+{{{
+#!CommitTicketReference repository="repos1" revision="1"
+This is the first comment. Refs #1.
+}}}""", self.tickets[0].get_change(cnum=1)['fields']['comment']['new'])
+
+ def test_changeset_modified(self):
+ self._make_tickets(2)
+ message = 'This is the first comment. Refs #1.'
+ old_chgset = Mock(repos=self.repos, rev=1,
+ message=message, author='joe',
+ date=datetime(2001, 1, 1, 1, 1, 1, 0, utc))
+ message = 'This is the first comment after an edit. Refs #1, #2.'
+ new_chgset = Mock(repos=self.repos, rev=1,
+ message=message, author='joe',
+ date=datetime(2001, 1, 2, 1, 1, 1, 0, utc))
+ self.updater.changeset_added(self.repos, old_chgset)
+ self.updater.changeset_modified(self.repos, new_chgset, old_chgset)
+ self.assertEqual("""\
+In [changeset:"1/repos1"]:
+{{{
+#!CommitTicketReference repository="repos1" revision="1"
+This is the first comment. Refs #1.
+}}}""", self.tickets[0].get_change(cnum=1)['fields']['comment']['new'])
+ self.assertEqual("""\
+In [changeset:"1/repos1"]:
+{{{
+#!CommitTicketReference repository="repos1" revision="1"
+This is the first comment after an edit. Refs #1, #2.
+}}}""", self.tickets[1].get_change(cnum=1)['fields']['comment']['new'])
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(CommitTicketUpdaterTestCase))
+ return suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
Added: bloodhound/vendor/trac/current/tracopt/versioncontrol/git/tests/git_fs.py
URL:
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/tracopt/versioncontrol/git/tests/git_fs.py?rev=1639617&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/tracopt/versioncontrol/git/tests/git_fs.py
(added)
+++ bloodhound/vendor/trac/current/tracopt/versioncontrol/git/tests/git_fs.py
Fri Nov 14 12:10:40 2014
@@ -0,0 +1,521 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 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/.
+
+import os
+import tempfile
+import unittest
+from datetime import datetime, timedelta
+from subprocess import Popen, PIPE
+
+from trac.core import TracError
+from trac.test import EnvironmentStub, Mock, MockPerm, locate
+from trac.tests.compat import rmtree
+from trac.util import create_file
+from trac.util.compat import close_fds
+from trac.util.datefmt import to_timestamp, utc
+from trac.versioncontrol.api import Changeset, DbRepositoryProvider, \
+ NoSuchChangeset, NoSuchNode, \
+ RepositoryManager
+from trac.versioncontrol.web_ui.browser import BrowserModule
+from trac.versioncontrol.web_ui.log import LogModule
+from trac.web.href import Href
+from tracopt.versioncontrol.git.PyGIT import StorageFactory
+from tracopt.versioncontrol.git.git_fs import GitCachedRepository, \
+ GitConnector, GitRepository
+
+
+git_bin = None
+
+
+class GitCommandMixin(object):
+
+ def _git_commit(self, *args, **kwargs):
+ env = kwargs.get('env') or os.environ.copy()
+ if 'date' in kwargs:
+ self._set_committer_date(env, kwargs.pop('date'))
+ args = ('commit',) + args
+ kwargs['env'] = env
+ return self._git(*args, **kwargs)
+
+ def _git(self, *args, **kwargs):
+ args = (git_bin,) + args
+ proc = Popen(args, stdout=PIPE, stderr=PIPE, close_fds=close_fds,
+ cwd=self.repos_path, **kwargs)
+ stdout, stderr = proc.communicate()
+ self.assertEqual(0, proc.returncode,
+ 'git exits with %r, args %r, stdout %r, stderr %r' %
+ (proc.returncode, args, stdout, stderr))
+ return proc
+
+ def _git_date_format(self, dt):
+ if dt.tzinfo is None:
+ dt = dt.replace(tzinfo=utc)
+ offset = dt.utcoffset()
+ secs = offset.days * 3600 * 24 + offset.seconds
+ hours, rem = divmod(abs(secs), 3600)
+ return '%d %c%02d:%02d' % (to_timestamp(dt), '-' if secs < 0 else '+',
+ hours, rem / 60)
+
+ def _set_committer_date(self, env, dt):
+ if not isinstance(dt, basestring):
+ if dt.tzinfo is None:
+ dt = dt.replace(tzinfo=utc)
+ dt = self._git_date_format(dt)
+ env['GIT_COMMITTER_DATE'] = dt
+ env['GIT_AUTHOR_DATE'] = dt
+
+
+class BaseTestCase(unittest.TestCase, GitCommandMixin):
+
+ def setUp(self):
+ self.env = EnvironmentStub()
+ self.repos_path = tempfile.mkdtemp(prefix='trac-gitrepos-')
+ if git_bin:
+ self.env.config.set('git', 'git_bin', git_bin)
+
+ def tearDown(self):
+ self._repomgr.reload_repositories()
+ StorageFactory._clean()
+ self.env.reset_db()
+ if os.path.isdir(self.repos_path):
+ rmtree(self.repos_path)
+
+ @property
+ def _repomgr(self):
+ return RepositoryManager(self.env)
+
+ @property
+ def _dbrepoprov(self):
+ return DbRepositoryProvider(self.env)
+
+ def _add_repository(self, reponame='gitrepos', bare=False):
+ path = self.repos_path \
+ if bare else os.path.join(self.repos_path, '.git')
+ self._dbrepoprov.add_repository(reponame, path, 'git')
+
+ def _git_init(self, data=True, bare=False):
+ if bare:
+ self._git('init', '--bare')
+ else:
+ self._git('init')
+ if not bare and data:
+ self._git('config', 'user.name', 'Joe')
+ self._git('config', 'user.email', '[email protected]')
+ create_file(os.path.join(self.repos_path, '.gitignore'))
+ self._git('add', '.gitignore')
+ self._git_commit('-a', '-m', 'test',
+ date=datetime(2001, 1, 29, 16, 39, 56))
+
+
+class SanityCheckingTestCase(BaseTestCase):
+
+ def test_bare(self):
+ self._git_init(bare=True)
+ self._dbrepoprov.add_repository('gitrepos', self.repos_path, 'git')
+ self._repomgr.get_repository('gitrepos')
+
+ def test_non_bare(self):
+ self._git_init(bare=False)
+ self._dbrepoprov.add_repository('gitrepos.1',
+ os.path.join(self.repos_path, '.git'),
+ 'git')
+ self._repomgr.get_repository('gitrepos.1')
+ self._dbrepoprov.add_repository('gitrepos.2', self.repos_path, 'git')
+ self._repomgr.get_repository('gitrepos.2')
+
+ def test_no_head_file(self):
+ self._git_init(bare=True)
+ os.unlink(os.path.join(self.repos_path, 'HEAD'))
+ self._dbrepoprov.add_repository('gitrepos', self.repos_path, 'git')
+ self.assertRaises(TracError, self._repomgr.get_repository, 'gitrepos')
+
+ def test_no_objects_dir(self):
+ self._git_init(bare=True)
+ rmtree(os.path.join(self.repos_path, 'objects'))
+ self._dbrepoprov.add_repository('gitrepos', self.repos_path, 'git')
+ self.assertRaises(TracError, self._repomgr.get_repository, 'gitrepos')
+
+ def test_no_refs_dir(self):
+ self._git_init(bare=True)
+ rmtree(os.path.join(self.repos_path, 'refs'))
+ self._dbrepoprov.add_repository('gitrepos', self.repos_path, 'git')
+ self.assertRaises(TracError, self._repomgr.get_repository, 'gitrepos')
+
+
+class PersistentCacheTestCase(BaseTestCase):
+
+ def test_persistent(self):
+ self.env.config.set('git', 'persistent_cache', 'enabled')
+ self._git_init()
+ self._add_repository()
+ youngest = self._repository.youngest_rev
+ self._repomgr.reload_repositories() # clear repository cache
+
+ self._commit(datetime(2014, 1, 29, 16, 44, 54, 0, utc))
+ self.assertEqual(youngest, self._repository.youngest_rev)
+ self._repository.sync()
+ self.assertNotEqual(youngest, self._repository.youngest_rev)
+
+ def test_non_persistent(self):
+ self.env.config.set('git', 'persistent_cache', 'disabled')
+ self._git_init()
+ self._add_repository()
+ youngest = self._repository.youngest_rev
+ self._repomgr.reload_repositories() # clear repository cache
+
+ self._commit(datetime(2014, 1, 29, 16, 44, 54, 0, utc))
+ youngest_2 = self._repository.youngest_rev
+ self.assertNotEqual(youngest, youngest_2)
+ self._repository.sync()
+ self.assertNotEqual(youngest, self._repository.youngest_rev)
+ self.assertEqual(youngest_2, self._repository.youngest_rev)
+
+ def _commit(self, date):
+ gitignore = os.path.join(self.repos_path, '.gitignore')
+ create_file(gitignore, date.isoformat())
+ self._git_commit('-a', '-m', date.isoformat(), date=date)
+
+ @property
+ def _repository(self):
+ return self._repomgr.get_repository('gitrepos')
+
+
+class HistoryTimeRangeTestCase(BaseTestCase):
+
+ def test_without_cache(self):
+ self._test_timerange('disabled')
+
+ def test_with_cache(self):
+ self._test_timerange('enabled')
+
+ def _test_timerange(self, cached_repository):
+ self.env.config.set('git', 'cached_repository', cached_repository)
+
+ self._git_init()
+ filename = os.path.join(self.repos_path, '.gitignore')
+ start = datetime(2000, 1, 1, 0, 0, 0, 0, utc)
+ ts = datetime(2014, 2, 5, 15, 24, 6, 0, utc)
+ for idx in xrange(3):
+ create_file(filename, 'commit-%d.txt' % idx)
+ self._git_commit('-a', '-m', 'commit %d' % idx, date=ts)
+ self._add_repository()
+ repos = self._repomgr.get_repository('gitrepos')
+ repos.sync()
+
+ revs = [repos.youngest_rev]
+ while True:
+ parents = repos.parent_revs(revs[-1])
+ if not parents:
+ break
+ revs.extend(parents)
+ self.assertEqual(4, len(revs))
+
+ csets = list(repos.get_changesets(start, ts))
+ self.assertEqual(1, len(csets))
+ self.assertEqual(revs[-1], csets[0].rev) # is oldest rev
+
+ csets = list(repos.get_changesets(start, ts + timedelta(seconds=1)))
+ self.assertEqual(revs, [cset.rev for cset in csets])
+
+
+class GitNormalTestCase(BaseTestCase):
+
+ def _create_req(self, **kwargs):
+ data = dict(args={}, perm=MockPerm(), href=Href('/'), chrome={},
+ authname='trac', tz=utc, get_header=lambda name: None)
+ data.update(kwargs)
+ return Mock(**data)
+
+ def test_get_node(self):
+ self.env.config.set('git', 'persistent_cache', 'false')
+ self.env.config.set('git', 'cached_repository', 'false')
+
+ self._git_init()
+ self._add_repository()
+ repos = self._repomgr.get_repository('gitrepos')
+ rev = repos.youngest_rev
+ self.assertNotEqual(None, rev)
+ self.assertEqual(40, len(rev))
+
+ self.assertEqual(rev, repos.get_node('/').rev)
+ self.assertEqual(rev, repos.get_node('/', rev[:7]).rev)
+ self.assertEqual(rev, repos.get_node('/.gitignore').rev)
+ self.assertEqual(rev, repos.get_node('/.gitignore', rev[:7]).rev)
+
+ self.assertRaises(NoSuchNode, repos.get_node, '/non-existent')
+ self.assertRaises(NoSuchNode, repos.get_node, '/non-existent', rev[:7])
+ self.assertRaises(NoSuchNode, repos.get_node, '/non-existent', rev)
+ self.assertRaises(NoSuchChangeset,
+ repos.get_node, '/', 'invalid-revision')
+ self.assertRaises(NoSuchChangeset,
+ repos.get_node, '/.gitignore', 'invalid-revision')
+ self.assertRaises(NoSuchChangeset,
+ repos.get_node, '/non-existent', 'invalid-revision')
+
+ # git_fs doesn't support non-ANSI strings on Windows
+ if os.name != 'nt':
+ self._git('branch', u'tïckét10605', 'master')
+ repos.sync()
+ self.assertEqual(rev, repos.get_node('/', u'tïckét10605').rev)
+ self.assertEqual(rev, repos.get_node('/.gitignore',
+ u'tïckét10605').rev)
+
+ def _test_on_empty_repos(self, cached_repository):
+ self.env.config.set('git', 'persistent_cache', 'false')
+ self.env.config.set('git', 'cached_repository', cached_repository)
+
+ self._git_init(data=False, bare=True)
+ self._add_repository(bare=True)
+ repos = self._repomgr.get_repository('gitrepos')
+ repos.sync()
+ youngest_rev = repos.youngest_rev
+ self.assertEqual(None, youngest_rev)
+ self.assertEqual(None, repos.oldest_rev)
+ self.assertEqual(None, repos.normalize_rev(''))
+ self.assertEqual(None, repos.normalize_rev(None))
+
+ node = repos.get_node('/', youngest_rev)
+ self.assertEqual([], list(node.get_entries()))
+ self.assertEqual([], list(node.get_history()))
+ self.assertRaises(NoSuchNode, repos.get_node, '/path', youngest_rev)
+
+ req = self._create_req(path_info='/browser/gitrepos')
+ browser_mod = BrowserModule(self.env)
+ self.assertTrue(browser_mod.match_request(req))
+ rv = browser_mod.process_request(req)
+ self.assertEqual('browser.html', rv[0])
+ self.assertEqual(None, rv[1]['rev'])
+
+ req = self._create_req(path_info='/log/gitrepos')
+ log_mod = LogModule(self.env)
+ self.assertTrue(log_mod.match_request(req))
+ rv = log_mod.process_request(req)
+ self.assertEqual('revisionlog.html', rv[0])
+ self.assertEqual([], rv[1]['items'])
+
+ def test_on_empty_and_cached_repos(self):
+ self._test_on_empty_repos('true')
+
+ def test_on_empty_and_non_cached_repos(self):
+ self._test_on_empty_repos('false')
+
+
+class GitRepositoryTestCase(BaseTestCase):
+
+ cached_repository = 'disabled'
+
+ def setUp(self):
+ BaseTestCase.setUp(self)
+ self.env.config.set('git', 'cached_repository', self.cached_repository)
+
+ def _create_merge_commit(self):
+ for idx, branch in enumerate(('alpha', 'beta')):
+ self._git('checkout', '-b', branch, 'master')
+ for n in xrange(2):
+ filename = 'file-%s-%d.txt' % (branch, n)
+ create_file(os.path.join(self.repos_path, filename))
+ self._git('add', filename)
+ self._git_commit('-a', '-m', filename,
+ date=datetime(2014, 2, 2, 17, 12,
+ n * 2 + idx))
+ self._git('checkout', 'alpha')
+ self._git('merge', '-m', 'Merge branch "beta" to "alpha"', 'beta')
+
+ def test_repository_instance(self):
+ self._git_init()
+ self._add_repository('gitrepos')
+ self.assertEqual(GitRepository,
+ type(self._repomgr.get_repository('gitrepos')))
+
+ def test_reset_head(self):
+ self._git_init()
+ create_file(os.path.join(self.repos_path, 'file.txt'), 'text')
+ self._git('add', 'file.txt')
+ self._git_commit('-a', '-m', 'test',
+ date=datetime(2014, 2, 2, 17, 12, 18))
+ self._add_repository('gitrepos')
+ repos = self._repomgr.get_repository('gitrepos')
+ repos.sync()
+ youngest_rev = repos.youngest_rev
+ entries = list(repos.get_node('').get_history())
+ self.assertEqual(2, len(entries))
+ self.assertEqual('', entries[0][0])
+ self.assertEqual(Changeset.EDIT, entries[0][2])
+ self.assertEqual('', entries[1][0])
+ self.assertEqual(Changeset.ADD, entries[1][2])
+
+ self._git('reset', '--hard', 'HEAD~')
+ repos.sync()
+ new_entries = list(repos.get_node('').get_history())
+ self.assertEqual(1, len(new_entries))
+ self.assertEqual(new_entries[0], entries[1])
+ self.assertNotEqual(youngest_rev, repos.youngest_rev)
+
+ def test_tags(self):
+ self._git_init()
+ self._add_repository('gitrepos')
+ repos = self._repomgr.get_repository('gitrepos')
+ repos.sync()
+ self.assertEqual(['master'], self._get_quickjump_names(repos))
+ self._git('tag', 'v1.0', 'master') # add tag
+ repos.sync()
+ self.assertEqual(['master', 'v1.0'], self._get_quickjump_names(repos))
+ self._git('tag', '-d', 'v1.0') # delete tag
+ repos.sync()
+ self.assertEqual(['master'], self._get_quickjump_names(repos))
+
+ def test_branchs(self):
+ self._git_init()
+ self._add_repository('gitrepos')
+ repos = self._repomgr.get_repository('gitrepos')
+ repos.sync()
+ self.assertEqual(['master'], self._get_quickjump_names(repos))
+ self._git('branch', 'alpha', 'master') # add branch
+ repos.sync()
+ self.assertEqual(['alpha', 'master'], self._get_quickjump_names(repos))
+ self._git('branch', '-m', 'alpha', 'beta') # rename branch
+ repos.sync()
+ self.assertEqual(['beta', 'master'], self._get_quickjump_names(repos))
+ self._git('branch', '-D', 'beta') # delete branch
+ repos.sync()
+ self.assertEqual(['master'], self._get_quickjump_names(repos))
+
+ def test_parent_child_revs(self):
+ self._git_init()
+ self._git('branch', 'initial')
+ self._create_merge_commit()
+ self._git('branch', 'latest')
+
+ self._add_repository('gitrepos')
+ repos = self._repomgr.get_repository('gitrepos')
+ repos.sync()
+
+ rev = repos.normalize_rev('initial')
+ children = repos.child_revs(rev)
+ self.assertEqual(2, len(children), 'child_revs: %r' % children)
+ parents = repos.parent_revs(rev)
+ self.assertEqual(0, len(parents), 'parent_revs: %r' % parents)
+ self.assertEqual(1, len(repos.child_revs(children[0])))
+ self.assertEqual(1, len(repos.child_revs(children[1])))
+
+ rev = repos.normalize_rev('latest')
+ children = repos.child_revs(rev)
+ self.assertEqual(0, len(children), 'child_revs: %r' % children)
+ parents = repos.parent_revs(rev)
+ self.assertEqual(2, len(parents), 'parent_revs: %r' % parents)
+ self.assertEqual(1, len(repos.parent_revs(parents[0])))
+ self.assertEqual(1, len(repos.parent_revs(parents[1])))
+
+ def _get_quickjump_names(self, repos):
+ return sorted(name for type, name, path, rev
+ in repos.get_quickjump_entries('HEAD'))
+
+
+class GitCachedRepositoryTestCase(GitRepositoryTestCase):
+
+ cached_repository = 'enabled'
+
+ def test_repository_instance(self):
+ self._git_init()
+ self._add_repository('gitrepos')
+ self.assertEqual(GitCachedRepository,
+ type(self._repomgr.get_repository('gitrepos')))
+
+ def test_sync(self):
+ self._git_init()
+ for idx in xrange(3):
+ filename = 'file%d.txt' % idx
+ create_file(os.path.join(self.repos_path, filename))
+ self._git('add', filename)
+ self._git_commit('-a', '-m', filename,
+ date=datetime(2014, 2, 2, 17, 12, idx))
+ self._add_repository('gitrepos')
+ repos = self._repomgr.get_repository('gitrepos')
+ revs = [entry[1] for entry in repos.repos.get_node('').get_history()]
+ revs.reverse()
+ revs2 = []
+ def feedback(rev):
+ revs2.append(rev)
+ repos.sync(feedback=feedback)
+ self.assertEqual(revs, revs2)
+ self.assertEqual(4, len(revs2))
+
+ revs2 = []
+ def feedback_1(rev):
+ revs2.append(rev)
+ if len(revs2) == 2:
+ raise StopSync
+ def feedback_2(rev):
+ revs2.append(rev)
+ try:
+ repos.sync(feedback=feedback_1, clean=True)
+ except StopSync:
+ self.assertEqual(revs[:2], revs2)
+ repos.sync(feedback=feedback_2) # restart sync
+ self.assertEqual(revs, revs2)
+
+ def test_sync_merge(self):
+ self._git_init()
+ self._create_merge_commit()
+
+ self._add_repository('gitrepos')
+ repos = self._repomgr.get_repository('gitrepos')
+ youngest_rev = repos.repos.youngest_rev
+ oldest_rev = repos.repos.oldest_rev
+
+ revs = []
+ def feedback(rev):
+ revs.append(rev)
+ repos.sync(feedback=feedback)
+ self.assertEqual(6, len(revs))
+ self.assertEqual(youngest_rev, revs[-1])
+ self.assertEqual(oldest_rev, revs[0])
+
+ revs2 = []
+ def feedback_1(rev):
+ revs2.append(rev)
+ if len(revs2) == 3:
+ raise StopSync
+ def feedback_2(rev):
+ revs2.append(rev)
+ try:
+ repos.sync(feedback=feedback_1, clean=True)
+ except StopSync:
+ self.assertEqual(revs[:3], revs2)
+ repos.sync(feedback=feedback_2) # restart sync
+ self.assertEqual(revs, revs2)
+
+
+class StopSync(Exception):
+ pass
+
+
+def suite():
+ global git_bin
+ suite = unittest.TestSuite()
+ git_bin = locate('git')
+ if git_bin:
+ suite.addTest(unittest.makeSuite(SanityCheckingTestCase))
+ suite.addTest(unittest.makeSuite(PersistentCacheTestCase))
+ suite.addTest(unittest.makeSuite(HistoryTimeRangeTestCase))
+ suite.addTest(unittest.makeSuite(GitNormalTestCase))
+ suite.addTest(unittest.makeSuite(GitRepositoryTestCase))
+ suite.addTest(unittest.makeSuite(GitCachedRepositoryTestCase))
+ else:
+ print("SKIP: tracopt/versioncontrol/git/tests/git_fs.py (git cli "
+ "binary, 'git', not found)")
+ return suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')