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', 'j...@example.com') + 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')