You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by jo...@apache.org on 2012/10/26 22:56:57 UTC

[11/15] git commit: [#3883] Completely decouple SCM tests from Allura's main package.

[#3883] Completely decouple SCM tests from Allura's main package.

Signed-off-by: Peter Hartmann <ma...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/0231935d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/0231935d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/0231935d

Branch: refs/heads/master
Commit: 0231935db01966c61e8f7756546ebf8d51dd6d73
Parents: 07df372
Author: Peter Hartmann <ma...@gmail.com>
Authored: Mon Sep 24 00:07:33 2012 +0200
Committer: Cory Johns <jo...@geek.net>
Committed: Fri Oct 26 20:28:38 2012 +0000

----------------------------------------------------------------------
 Allura/allura/tests/decorators.py                  |    1 -
 Allura/allura/tests/functional/test_auth.py        |   12 -
 Allura/allura/tests/model/test_repo.py             |  480 ---------------
 Allura/allura/tests/test_tasks.py                  |    2 -
 ForgeHg/forgehg/tests/__init__.py                  |    6 +
 ForgeHg/forgehg/tests/functional/test_auth.py      |   18 +
 .../forgehg/tests/functional/test_controllers.py   |    6 +-
 ForgeHg/forgehg/tests/model/test_repository.py     |    9 +-
 ForgeHg/forgehg/tests/test_hg_app.py               |    5 +-
 ForgeHg/forgehg/tests/test_tasks.py                |    5 +-
 ForgeSVN/forgesvn/tests/model/test_repository.py   |  449 ++++++++++++++-
 ForgeSVN/forgesvn/tests/test_tasks.py              |    9 +
 12 files changed, 495 insertions(+), 507 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/Allura/allura/tests/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/decorators.py b/Allura/allura/tests/decorators.py
index 4c3ce53..0222bdb 100644
--- a/Allura/allura/tests/decorators.py
+++ b/Allura/allura/tests/decorators.py
@@ -50,7 +50,6 @@ with_discussion = with_tool('test', 'Discussion', 'discussion')
 with_link = with_tool('test', 'Link', 'link')
 with_tracker = with_tool('test', 'Tickets', 'bugs')
 with_wiki = with_tool('test', 'Wiki', 'wiki')
-with_hg = with_tool('test', 'Hg', 'src-hg', 'Mercurial', type='hg')
 with_url = with_tool('test', 'ShortUrl', 'url')
 
 class raises(object):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/Allura/allura/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index c84239f..7f7e9b2 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -291,15 +291,3 @@ class TestAuth(TestController):
     def test_default_lookup(self):
         # Make sure that default _lookup() throws 404
         self.app.get('/auth/foobar', status=404)
-
-class TestUserPermissions(TestController):
-    allow = dict(allow_read=True, allow_write=True, allow_create=True)
-    read = dict(allow_read=True, allow_write=False, allow_create=False)
-    disallow = dict(allow_read=False, allow_write=False, allow_create=False)
-
-    @td.with_hg
-    def test_list_repos(self):
-        r = self.app.get('/auth/repo_permissions', params=dict(username='test-admin'), status=200)
-        assert_equal(json.loads(r.body), {"allow_write": [
-            '/hg/test/src-hg',
-        ]})

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/Allura/allura/tests/model/test_repo.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/model/test_repo.py b/Allura/allura/tests/model/test_repo.py
index 93d1cb3..90eaac1 100644
--- a/Allura/allura/tests/model/test_repo.py
+++ b/Allura/allura/tests/model/test_repo.py
@@ -1,488 +1,8 @@
-import os
-import unittest
-from itertools import count
-from datetime import datetime
-
-import mock
 from nose.tools import assert_equal
 from pylons import c
-import tg
-import ming
-from ming.base import Object
-from ming.orm import session, ThreadLocalORMSession
 
 from alluratest.controller import setup_basic_test, setup_global_objects
 from allura import model as M
-from allura.lib import helpers as h
-import allura.tasks
-
-class _Test(unittest.TestCase):
-    idgen = ( 'obj_%d' % i for i in count())
-
-    def _make_tree(self, object_id, **kwargs):
-        t, isnew = M.repo.Tree.upsert(object_id)
-        repo = getattr(self, 'repo', None)
-        t.repo = repo
-        for k,v in kwargs.iteritems():
-            if isinstance(v, basestring):
-                obj = M.repo.Blob(
-                    t, k, self.idgen.next())
-                t.blob_ids.append(Object(
-                        name=k, id=obj._id))
-            else:
-                obj = self._make_tree(self.idgen.next(), **v)
-                t.tree_ids.append(Object(
-                        name=k, id=obj._id))
-        session(t).flush()
-        return t
-
-    def _make_commit(self, object_id, **tree_parts):
-        ci, isnew = M.repo.Commit.upsert(object_id)
-        if isnew:
-            ci.committed.email=c.user.email_addresses[0]
-            ci.authored.email=c.user.email_addresses[0]
-            dt = datetime.utcnow()
-            # BSON datetime resolution is to 1 millisecond, not 1 microsecond
-            # like Python. Round this now so it'll match the value that's
-            # pulled from MongoDB in the tests.
-            ci.authored.date = dt.replace(microsecond=dt.microsecond/1000 * 1000)
-            ci.message='summary\n\nddescription'
-            ci.set_context(self.repo)
-            ci.tree_id = 't_' + object_id
-            ci.tree = self._make_tree(ci.tree_id, **tree_parts)
-        return ci, isnew
-
-    def _make_log(self, ci):
-        session(ci).flush(ci)
-        rb = M.repo_refresh.CommitRunBuilder([ci._id])
-        rb.run()
-        rb.cleanup()
-
-    def setUp(self):
-        setup_basic_test()
-        setup_global_objects()
-        ThreadLocalORMSession.flush_all()
-        ThreadLocalORMSession.close_all()
-        self.prefix = tg.config.get('scm.repos.root', '/')
-
-class _TestWithRepo(_Test):
-    def setUp(self):
-        super(_TestWithRepo, self).setUp()
-        h.set_context('test', neighborhood='Projects')
-        c.project.install_app('svn', 'test1')
-        h.set_context('test', 'test1', neighborhood='Projects')
-        self.repo = M.Repository(name='test1', tool='svn')
-        self.repo._impl = mock.Mock(spec=M.RepositoryImplementation())
-        self.repo._impl.shorthand_for_commit = M.RepositoryImplementation.shorthand_for_commit
-        self.repo._impl.url_for_commit = (
-            lambda *a, **kw: M.RepositoryImplementation.url_for_commit(
-                self.repo._impl, *a, **kw))
-        self.repo._impl.log = lambda *a,**kw:(['foo'], [])
-        self.repo._impl._repo = self.repo
-        self.repo._impl.all_commit_ids = lambda *a,**kw: []
-        ThreadLocalORMSession.flush_all()
-        # ThreadLocalORMSession.close_all()
-
-class _TestWithRepoAndCommit(_TestWithRepo):
-    def setUp(self):
-        super(_TestWithRepoAndCommit, self).setUp()
-        self.ci, isnew = self._make_commit('foo')
-        ThreadLocalORMSession.flush_all()
-        # ThreadLocalORMSession.close_all()
-
-class TestRepo(_TestWithRepo):
-
-    def test_create(self):
-        assert self.repo.fs_path == os.path.join(self.prefix, 'svn/p/test/')
-        assert self.repo.url_path == '/p/test/'
-        assert self.repo.full_fs_path == os.path.join(self.prefix, 'svn/p/test/test1')
-
-    def test_passthrough(self):
-        argless = ['init']
-        for fn in argless:
-            getattr(self.repo, fn)()
-            getattr(self.repo._impl, fn).assert_called_with()
-        unary = [ 'commit', 'open_blob' ]
-        for fn in unary:
-            getattr(self.repo, fn)('foo')
-            getattr(self.repo._impl, fn).assert_called_with('foo')
-
-    def test_shorthand_for_commit(self):
-        self.assertEqual(
-            self.repo.shorthand_for_commit('a'*40),
-            '[aaaaaa]')
-
-    def test_url_for_commit(self):
-        self.assertEqual(
-            self.repo.url_for_commit('a'*40),
-            '/p/test/test1/ci/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/')
-
-    def test_init_as_clone(self):
-        self.repo.init_as_clone('srcpath', 'srcname', 'srcurl')
-        assert self.repo.upstream_repo.name == 'srcname'
-        assert self.repo.upstream_repo.url == 'srcurl'
-        assert self.repo._impl.clone_from.called_with('srcpath')
-
-    @mock.patch.object(M.repo.CommitRunDoc.m, 'get')
-    def test_log(self, crd):
-        head = mock.Mock(name='commit_head', _id=3)
-        commits = dict([(i, mock.Mock(name='commit_%s'%i, _id=i)) for i in range(3)])
-        commits[3] = head
-        head.query.get = lambda _id: commits[_id]
-        self.repo._impl.commit = mock.Mock(return_value=head)
-        crd.return_value = mock.Mock(commit_ids=[3, 2, 1, 0], commit_times=[4, 3, 2, 1], parent_commit_ids=[])
-        log = self.repo.log()
-        assert_equal([c._id for c in log], [3, 2, 1, 0])
-
-    def test_count_revisions(self):
-        ci = mock.Mock()
-        self.repo.count_revisions = mock.Mock(return_value=42)
-        self.repo._impl.commit = mock.Mock(return_value=ci)
-        assert self.repo.count() == 42
-
-    def test_latest(self):
-        ci = mock.Mock()
-        self.repo._impl.commit = mock.Mock(return_value=ci)
-        assert self.repo.latest() is ci
-
-    def test_index(self):
-        i = self.repo.index()
-        assert i['type_s'] == 'Repository', i
-        assert i['name_s'] == 'test1', i
-
-    def test_scm_host_url(self):
-        assert (
-            self.repo.clone_url('rw', 'nobody')
-            == 'svn+ssh://nobody@localhost:8022/scm-repo/p/test/test1/'),\
-            self.repo.clone_url('rw', 'nobody')
-        assert (
-            self.repo.clone_url('https', 'nobody')
-            == 'https://nobody@localhost:8022/scm-repo/p/test/test1/'),\
-            self.repo.clone_url('https', 'nobody')
-
-    def test_merge_request(self):
-        M.MergeRequest.upsert(app_config_id=c.app.config._id, status='open')
-        M.MergeRequest.upsert(app_config_id=c.app.config._id, status='closed')
-        session(M.MergeRequest).flush()
-        session(M.MergeRequest).clear()
-        assert self.repo.merge_requests_by_statuses('open').count() == 1
-        assert self.repo.merge_requests_by_statuses('closed').count() == 1
-        assert self.repo.merge_requests_by_statuses('open', 'closed').count() == 2
-
-    def test_guess_type(self):
-        assert self.repo.guess_type('foo.txt') == ('text/plain', None)
-        assert self.repo.guess_type('foo.gbaer') == ('application/octet-stream', None)
-        assert self.repo.guess_type('foo.html') == ('text/html', None)
-        assert self.repo.guess_type('.gitignore') == ('text/plain', None)
-
-    def test_refresh(self):
-        ci = mock.Mock()
-        ci.authored.name = 'Test Committer'
-        ci.author_url = '/u/test-committer/'
-        self.repo._impl.commit = mock.Mock(return_value=ci)
-        self.repo._impl.new_commits = mock.Mock(return_value=['foo%d' % i for i in range(100) ])
-        self.repo._impl.all_commit_ids = mock.Mock(return_value=['foo%d' % i for i in range(100) ])
-        self.repo.symbolics_for_commit = mock.Mock(return_value=[['master', 'branch'], []])
-        self.repo.count_revisions=mock.Mock(return_value=100)
-        def refresh_commit_info(oid, seen, lazy=False):
-            M.repo.CommitDoc(dict(
-                    authored=dict(
-                        name='Test Committer',
-                        email='test@test.com'),
-                    _id=oid)).m.insert()
-        def set_heads():
-            self.repo.heads = [ ming.base.Object(name='head', object_id='foo0', count=100) ]
-        self.repo._impl.refresh_commit_info = refresh_commit_info
-        self.repo._impl.refresh_heads = mock.Mock(side_effect=set_heads)
-        self.repo.shorthand_for_commit = lambda oid: '[' + str(oid) + ']'
-        self.repo.url_for_commit = lambda oid: '/ci/' + str(oid) + '/'
-
-        with mock.patch('allura.model.repo_refresh.g.post_event') as post_event:
-            self.repo.refresh()
-            post_event.assert_called_with(
-                    'repo_refreshed', commit_number=100, new=True)
-            # refresh second time - all commits are seen
-            self.repo.refresh()
-            post_event.assert_called_with(
-                    'repo_refreshed', commit_number=0, new=False)
-
-        with mock.patch('allura.model.repository.Repository.refresh') as refresh:
-            allura.tasks.repo_tasks.refresh.post()
-            with mock.patch('allura.model.repository.Repository.unknown_commit_ids') as unknown_commit_ids:
-                unknown_commit_ids.return_value = [1,2,3]
-                #there are new commits so refresh has to be called once
-                #and new refresh should be queued
-                M.MonQTask.run_ready()
-                refresh.assert_called_once_with()
-            q = {
-                'task_name': 'allura.tasks.repo_tasks.refresh',
-                'state': 'ready',
-            }
-            assert M.MonQTask.query.find(q).count() == 1
-            refresh.reset_mock()
-            #adding second task and checking if refresh is fired once
-            allura.tasks.repo_tasks.refresh.post()
-            with mock.patch('allura.lib.mail_util.SMTPClient.sendmail') as sendmail:
-                M.MonQTask.run_ready()
-            refresh.assert_called_once_with()
-
-        ThreadLocalORMSession.flush_all()
-        notifications = M.Notification.query.find().all()
-        for n in notifications:
-            if '100 new commits' in n.subject:
-                assert "master,branch:  by Test Committer http://localhost/#" in n.text
-                break
-        else:
-            assert False, 'Did not find notification'
-        assert M.Feed.query.find(dict(
-            title='New commit',
-            author_name='Test Committer')).count()
-
-    def test_refresh_private(self):
-        ci = mock.Mock()
-        self.repo._impl.commit = mock.Mock(return_value=ci)
-        self.repo._impl.new_commits = mock.Mock(return_value=['foo%d' % i for i in range(100) ])
-        self.repo.count_revisions=mock.Mock(return_value=100)
-        def set_heads():
-            self.repo.heads = [ ming.base.Object(name='head', object_id='foo0', count=100) ]
-        self.repo._impl.refresh_heads = mock.Mock(side_effect=set_heads)
-
-        # make unreadable by *anonymous, so additional notification logic executes
-        self.repo.acl = []
-        c.project.acl = []
-
-        self.repo.refresh()
-
-    def test_push_upstream_context(self):
-        self.repo.init_as_clone('srcpath', '/p/test/svn/', '/p/test/svn/')
-        old_app_instance = M.Project.app_instance
-        try:
-            M.Project.app_instance = mock.Mock(return_value=ming.base.Object(
-                    config=ming.base.Object(_id=None)))
-            with self.repo.push_upstream_context():
-                assert c.project.shortname == 'test'
-        finally:
-            M.Project.app_instance = old_app_instance
-
-    def test_pending_upstream_merges(self):
-        self.repo.init_as_clone('srcpath', '/p/test/svn/', '/p/test/svn/')
-        old_app_instance = M.Project.app_instance
-        try:
-            M.Project.app_instance = mock.Mock(return_value=ming.base.Object(
-                    config=ming.base.Object(_id=None)))
-            self.repo.pending_upstream_merges()
-        finally:
-            M.Project.app_instance = old_app_instance
-
-class TestMergeRequest(_TestWithRepoAndCommit):
-
-    def setUp(self):
-        super(TestMergeRequest, self).setUp()
-        c.project.install_app('svn', 'test2')
-        h.set_context('test', 'test2', neighborhood='Projects')
-        self.repo2 = M.Repository(name='test2', tool='svn')
-        self.repo2._impl = mock.Mock(spec=M.RepositoryImplementation())
-        self.repo2._impl.log = lambda *a,**kw:(['foo'], [])
-        self.repo2._impl._repo = self.repo2
-        self.repo2.init_as_clone('/p/test/', 'test1', '/p/test/test1/')
-        ThreadLocalORMSession.flush_all()
-        ThreadLocalORMSession.close_all()
-
-    def test_upsert(self):
-        h.set_context('test', 'test1', neighborhood='Projects')
-        mr = M.MergeRequest.upsert(
-            downstream=ming.base.Object(
-                project_id=c.project._id,
-                mount_point='test2',
-                commit_id='foo'),
-            target_branch='foobranch',
-            summary='summary',
-            description='description')
-        u = M.User.by_username('test-admin')
-        assert mr.creator == u
-        assert mr.creator_name == u.get_pref('display_name')
-        assert mr.creator_url == u.url()
-        assert mr.downstream_url == '/p/test/test2/'
-        assert mr.downstream_repo_url == 'http://svn.localhost/p/test/test2/'
-        assert mr.commits == [ self._make_commit('foo')[0] ]
-
-class TestRepoObject(_TestWithRepoAndCommit):
-
-    def test_upsert(self):
-        obj0, isnew0 = M.repo.Tree.upsert('foo1')
-        obj1, isnew1 = M.repo.Tree.upsert('foo1')
-        assert obj0 is obj1
-        assert isnew0 and not isnew1
-
-    def test_set_last_commit(self):
-        obj, isnew = M.repo.Tree.upsert('foo1')
-        M.repo_refresh.set_last_commit(
-            self.repo._id, '/', 'fakefile', obj._id,
-            M.repo_refresh.get_commit_info(self.ci))
-
-    def test_get_last_commit(self):
-        obj, isnew = M.repo.Tree.upsert('foo1')
-        lc0 = M.repo_refresh.set_last_commit(
-            self.repo._id, '/', 'fakefile', obj._id,
-            M.repo_refresh.get_commit_info(self.ci))
-
-        lc1 = M.repo.LastCommitDoc.m.get(object_id=obj._id)
-        assert lc0 == lc1
-
-    def test_get_last_commit_missing(self):
-        obj, isnew = M.repo.Tree.upsert('foo1')
-        lc1 = M.repo.LastCommitDoc.m.get(object_id=obj._id)
-        assert lc1 is None
-
-    def test_artifact_methods(self):
-        assert self.ci.index_id() == 'allura/model/repo/Commit#foo', self.ci.index_id()
-        assert self.ci.primary() is self.ci, self.ci.primary()
-
-
-class TestCommit(_TestWithRepo):
-
-    def setUp(self):
-        super(TestCommit, self).setUp()
-        self.ci, isnew = self._make_commit(
-            'foo',
-            a=dict(
-                a=dict(
-                    a='',
-                    b='',),
-                b=''))
-        self.tree = self.ci.tree
-        impl = M.RepositoryImplementation()
-        impl._repo = self.repo
-        self.repo._impl.shorthand_for_commit = impl.shorthand_for_commit
-        self.repo._impl.url_for_commit = impl.url_for_commit
-
-    def test_upsert(self):
-        obj0, isnew0 = M.repo.Commit.upsert('foo')
-        obj1, isnew1 = M.repo.Commit.upsert('foo')
-        assert obj0 is obj1
-        assert not isnew1
-        u = M.User.by_username('test-admin')
-        assert self.ci.author_url == u.url()
-        assert self.ci.committer_url == u.url()
-        assert self.ci.tree is self.tree
-        assert self.ci.summary == 'summary'
-        assert self.ci.shorthand_id() == '[foo]'
-        assert self.ci.url() == '/p/test/test1/ci/foo/'
-
-    def test_get_path(self):
-        b = self.ci.get_path('a/a/a')
-        assert isinstance(b, M.repo.Blob)
-        x = self.ci.get_path('a/a')
-        assert isinstance(x, M.repo.Tree)
-
-    def test_count_revisions(self):
-        rb = M.repo_refresh.CommitRunBuilder(['foo'])
-        rb.run()
-        rb.cleanup()
-        assert self.repo.count_revisions(self.ci) == 1
-
-    def test_compute_diffs(self):
-        self.repo._impl.commit = mock.Mock(return_value=self.ci)
-        M.repo_refresh.refresh_commit_trees(self.ci, {})
-        M.repo_refresh.compute_diffs(self.repo._id, {}, self.ci)
-        # self.ci.compute_diffs()
-        assert self.ci.diffs.added == [ 'a' ]
-        assert (self.ci.diffs.copied
-                == self.ci.diffs.changed
-                == self.ci.diffs.removed
-                == [])
-        ci, isnew = self._make_commit('bar')
-        ci.parent_ids = [ 'foo' ]
-        self._make_log(ci)
-        M.repo_refresh.refresh_commit_trees(ci, {})
-        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
-        assert ci.diffs.removed == [ 'a' ]
-        assert (ci.diffs.copied
-                == ci.diffs.changed
-                == ci.diffs.added
-                == [])
-        ci, isnew = self._make_commit(
-            'baz',
-            b=dict(
-                a=dict(
-                    a='',
-                    b='',),
-                b=''))
-        ci.parent_ids = [ 'foo' ]
-        self._make_log(ci)
-        M.repo_refresh.refresh_commit_trees(ci, {})
-        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
-        assert ci.diffs.added == [ 'b' ]
-        assert ci.diffs.removed == [ 'a' ]
-        assert (ci.diffs.copied
-                == ci.diffs.changed
-                == [])
-
-    def test_diffs_file_renames(self):
-        def open_blob(blob):
-            blobs = {
-                u'a': u'Leia',
-                u'/b/a/a': u'Darth Vader',
-                u'/b/a/b': u'Luke Skywalker',
-                u'/b/b': u'Death Star will destroy you',
-                u'/b/c': u'Luke Skywalker',  # moved from /b/a/b
-                u'/b/a/z': u'Death Star will destroy you\nALL',  # moved from /b/b and modified
-            }
-            from cStringIO import StringIO
-            return StringIO(blobs[blob.path()])
-        self.repo._impl.open_blob = open_blob
-
-        self.repo._impl.commit = mock.Mock(return_value=self.ci)
-        M.repo_refresh.refresh_commit_trees(self.ci, {})
-        M.repo_refresh.compute_diffs(self.repo._id, {}, self.ci)
-        assert self.ci.diffs.added == ['a']
-        assert (self.ci.diffs.copied
-                == self.ci.diffs.changed
-                == self.ci.diffs.removed
-                == [])
-
-        ci, isnew = self._make_commit(
-            'bar',
-            b=dict(
-                a=dict(
-                    a='',
-                    b='',),
-                b=''))
-        ci.parent_ids = ['foo']
-        self._make_log(ci)
-        M.repo_refresh.refresh_commit_trees(ci, {})
-        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
-        assert ci.diffs.added == ['b']
-        assert ci.diffs.removed == ['a']
-        assert (ci.diffs.copied
-                == ci.diffs.changed
-                == [])
-
-        ci, isnew = self._make_commit(
-            'baz',
-            b=dict(
-                a=dict(
-                    z=''),
-                c=''))
-        ci.parent_ids = ['bar']
-        self._make_log(ci)
-        M.repo_refresh.refresh_commit_trees(ci, {})
-        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
-        assert ci.diffs.added == ci.diffs.changed == []
-        assert ci.diffs.removed == ['b/a/a']
-        # see mock for open_blob
-        assert len(ci.diffs.copied) == 2
-        assert ci.diffs.copied[0]['old'] == 'b/a/b'
-        assert ci.diffs.copied[0]['new'] == 'b/c'
-        assert ci.diffs.copied[0]['ratio'] == 1
-        assert ci.diffs.copied[0]['diff'] == ''
-        assert ci.diffs.copied[1]['old'] == 'b/b'
-        assert ci.diffs.copied[1]['new'] == 'b/a/z'
-        assert ci.diffs.copied[1]['ratio'] < 1
-        assert '+++' in ci.diffs.copied[1]['diff']
-
-    def test_context(self):
-        self.ci.context()
 
 class TestGitLikeTree(object):
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/Allura/allura/tests/test_tasks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_tasks.py b/Allura/allura/tests/test_tasks.py
index 05b246b..edb82f1 100644
--- a/Allura/allura/tests/test_tasks.py
+++ b/Allura/allura/tests/test_tasks.py
@@ -1,7 +1,6 @@
 # -*- coding: utf-8 -*-
 import operator
 import sys
-import shutil
 import unittest
 from base64 import b64encode
 
@@ -24,7 +23,6 @@ from allura.tasks import event_tasks
 from allura.tasks import index_tasks
 from allura.tasks import mail_tasks
 from allura.tasks import notification_tasks
-from allura.tasks import repo_tasks
 from allura.tests import decorators as td
 from allura.lib.decorators import event_handler, task
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeHg/forgehg/tests/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeHg/forgehg/tests/__init__.py b/ForgeHg/forgehg/tests/__init__.py
index e69de29..79b30ee 100644
--- a/ForgeHg/forgehg/tests/__init__.py
+++ b/ForgeHg/forgehg/tests/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+
+## Make our own Mercurial tool test decorator
+from allura.tests.decorators import with_tool
+
+with_hg = with_tool('test', 'Hg', 'src-hg', 'Mercurial', type='hg')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeHg/forgehg/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/ForgeHg/forgehg/tests/functional/test_auth.py b/ForgeHg/forgehg/tests/functional/test_auth.py
new file mode 100644
index 0000000..b172e37
--- /dev/null
+++ b/ForgeHg/forgehg/tests/functional/test_auth.py
@@ -0,0 +1,18 @@
+import json
+from datadiff.tools import assert_equal
+
+from allura.tests import TestController
+
+from forgehg.tests import with_hg
+
+class TestUserPermissions(TestController):
+    allow = dict(allow_read=True, allow_write=True, allow_create=True)
+    read = dict(allow_read=True, allow_write=False, allow_create=False)
+    disallow = dict(allow_read=False, allow_write=False, allow_create=False)
+
+    @with_hg
+    def test_list_repos(self):
+        r = self.app.get('/auth/repo_permissions', params=dict(username='test-admin'), status=200)
+        assert_equal(json.loads(r.body), {"allow_write": [
+            '/hg/test/src-hg',
+        ]})

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeHg/forgehg/tests/functional/test_controllers.py
----------------------------------------------------------------------
diff --git a/ForgeHg/forgehg/tests/functional/test_controllers.py b/ForgeHg/forgehg/tests/functional/test_controllers.py
index 3ef2e97..2298b1b 100644
--- a/ForgeHg/forgehg/tests/functional/test_controllers.py
+++ b/ForgeHg/forgehg/tests/functional/test_controllers.py
@@ -9,10 +9,10 @@ from ming.orm import ThreadLocalORMSession
 from datadiff.tools import assert_equal
 
 from allura.lib import helpers as h
-from allura.tests import decorators as td
 from allura import model as M
 from alluratest.controller import TestController
 
+from forgehg.tests import with_hg
 
 class TestRootController(TestController):
 
@@ -20,7 +20,7 @@ class TestRootController(TestController):
         TestController.setUp(self)
         self.setup_with_tools()
 
-    @td.with_hg
+    @with_hg
     def setup_with_tools(self):
         h.set_context('test', 'src-hg', neighborhood='Projects')
         repo_dir = pkg_resources.resource_filename(
@@ -180,7 +180,7 @@ class TestLogPagination(TestController):
         TestController.setUp(self)
         self.setup_with_tools()
 
-    @td.with_hg
+    @with_hg
     def setup_with_tools(self):
         h.set_context('test', 'src-hg', neighborhood='Projects')
         repo_dir = pkg_resources.resource_filename(

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeHg/forgehg/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeHg/forgehg/tests/model/test_repository.py b/ForgeHg/forgehg/tests/model/test_repository.py
index 1193dcb..6f546d5 100644
--- a/ForgeHg/forgehg/tests/model/test_repository.py
+++ b/ForgeHg/forgehg/tests/model/test_repository.py
@@ -10,10 +10,11 @@ from ming.orm import ThreadLocalORMSession
 
 from alluratest.controller import setup_basic_test, setup_global_objects
 from allura.lib import helpers as h
-from allura.tests import decorators as td
 from allura.tests.model.test_repo import RepoImplTestBase
 from allura import model as M
+
 from forgehg import model as HM
+from forgehg.tests import with_hg
 
 class TestNewRepo(unittest.TestCase):
 
@@ -21,7 +22,7 @@ class TestNewRepo(unittest.TestCase):
         setup_basic_test()
         self.setup_with_tools()
 
-    @td.with_hg
+    @with_hg
     def setup_with_tools(self):
         setup_global_objects()
         h.set_context('test', 'src-hg', neighborhood='Projects')
@@ -85,7 +86,7 @@ class TestHgRepo(unittest.TestCase, RepoImplTestBase):
         setup_basic_test()
         self.setup_with_tools()
 
-    @td.with_hg
+    @with_hg
     def setup_with_tools(self):
         setup_global_objects()
         h.set_context('test', 'src-hg', neighborhood='Projects')
@@ -208,7 +209,7 @@ class TestHgCommit(unittest.TestCase):
         setup_basic_test()
         self.setup_with_tools()
 
-    @td.with_hg
+    @with_hg
     def setup_with_tools(self):
         setup_global_objects()
         h.set_context('test', 'src-hg', neighborhood='Projects')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeHg/forgehg/tests/test_hg_app.py
----------------------------------------------------------------------
diff --git a/ForgeHg/forgehg/tests/test_hg_app.py b/ForgeHg/forgehg/tests/test_hg_app.py
index db09b09..c3ee795 100644
--- a/ForgeHg/forgehg/tests/test_hg_app.py
+++ b/ForgeHg/forgehg/tests/test_hg_app.py
@@ -6,7 +6,8 @@ from ming.orm import ThreadLocalORMSession
 
 from alluratest.controller import setup_basic_test, setup_global_objects
 from allura.lib import helpers as h
-from allura.tests import decorators as td
+
+from forgehg.tests import with_hg
 
 class TestHgApp(unittest.TestCase):
 
@@ -14,7 +15,7 @@ class TestHgApp(unittest.TestCase):
         setup_basic_test()
         self.setup_with_tools()
 
-    @td.with_hg
+    @with_hg
     def setup_with_tools(self):
         setup_global_objects()
         h.set_context('test', 'src-hg', neighborhood='Projects')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeHg/forgehg/tests/test_tasks.py
----------------------------------------------------------------------
diff --git a/ForgeHg/forgehg/tests/test_tasks.py b/ForgeHg/forgehg/tests/test_tasks.py
index b330dd6..eef1916 100644
--- a/ForgeHg/forgehg/tests/test_tasks.py
+++ b/ForgeHg/forgehg/tests/test_tasks.py
@@ -5,7 +5,8 @@ from ming.orm import ThreadLocalORMSession
 from alluratest.controller import setup_basic_test, setup_global_objects
 from allura.lib import helpers as h
 from allura.tasks import repo_tasks
-from allura.tests import decorators as td
+
+from forgehg.tests import with_hg
 
 class TestHgReactors(unittest.TestCase):
 
@@ -13,7 +14,7 @@ class TestHgReactors(unittest.TestCase):
         setup_basic_test()
         self.setup_with_tools()
 
-    @td.with_hg
+    @with_hg
     def setup_with_tools(self):
         setup_global_objects()
         h.set_context('test', 'src-hg', neighborhood='Projects')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeSVN/forgesvn/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py b/ForgeSVN/forgesvn/tests/model/test_repository.py
index 790c31e..488e441 100644
--- a/ForgeSVN/forgesvn/tests/model/test_repository.py
+++ b/ForgeSVN/forgesvn/tests/model/test_repository.py
@@ -2,9 +2,15 @@ import os
 import shutil
 import unittest
 import pkg_resources
+from itertools import count
+from datetime import datetime
 
+from pylons import c
 import mock
-from ming.orm import ThreadLocalORMSession
+import tg
+import ming
+from ming.base import Object
+from ming.orm import session, ThreadLocalORMSession
 
 from alluratest.controller import setup_basic_test, setup_global_objects
 from allura import model as M
@@ -278,3 +284,444 @@ class TestSVNRev(unittest.TestCase):
                  +self.rev.diffs.copied)
         for d in diffs:
             print d
+
+class _Test(unittest.TestCase):
+    idgen = ( 'obj_%d' % i for i in count())
+
+    def _make_tree(self, object_id, **kwargs):
+        t, isnew = M.repo.Tree.upsert(object_id)
+        repo = getattr(self, 'repo', None)
+        t.repo = repo
+        for k,v in kwargs.iteritems():
+            if isinstance(v, basestring):
+                obj = M.repo.Blob(
+                    t, k, self.idgen.next())
+                t.blob_ids.append(Object(
+                        name=k, id=obj._id))
+            else:
+                obj = self._make_tree(self.idgen.next(), **v)
+                t.tree_ids.append(Object(
+                        name=k, id=obj._id))
+        session(t).flush()
+        return t
+
+    def _make_commit(self, object_id, **tree_parts):
+        ci, isnew = M.repo.Commit.upsert(object_id)
+        if isnew:
+            ci.committed.email=c.user.email_addresses[0]
+            ci.authored.email=c.user.email_addresses[0]
+            dt = datetime.utcnow()
+            # BSON datetime resolution is to 1 millisecond, not 1 microsecond
+            # like Python. Round this now so it'll match the value that's
+            # pulled from MongoDB in the tests.
+            ci.authored.date = dt.replace(microsecond=dt.microsecond/1000 * 1000)
+            ci.message='summary\n\nddescription'
+            ci.set_context(self.repo)
+            ci.tree_id = 't_' + object_id
+            ci.tree = self._make_tree(ci.tree_id, **tree_parts)
+        return ci, isnew
+
+    def _make_log(self, ci):
+        session(ci).flush(ci)
+        rb = M.repo_refresh.CommitRunBuilder([ci._id])
+        rb.run()
+        rb.cleanup()
+
+    def setUp(self):
+        setup_basic_test()
+        setup_global_objects()
+        ThreadLocalORMSession.flush_all()
+        ThreadLocalORMSession.close_all()
+        self.prefix = tg.config.get('scm.repos.root', '/')
+        
+class _TestWithRepo(_Test):
+    def setUp(self):
+        super(_TestWithRepo, self).setUp()
+        h.set_context('test', neighborhood='Projects')
+        c.project.install_app('svn', 'test1')
+        h.set_context('test', 'test1', neighborhood='Projects')
+        self.repo = M.Repository(name='test1', tool='svn')
+        self.repo._impl = mock.Mock(spec=M.RepositoryImplementation())
+        self.repo._impl.shorthand_for_commit = M.RepositoryImplementation.shorthand_for_commit
+        self.repo._impl.url_for_commit = (
+            lambda *a, **kw: M.RepositoryImplementation.url_for_commit(
+                self.repo._impl, *a, **kw))
+        self.repo._impl.log = lambda *a,**kw:(['foo'], [])
+        self.repo._impl._repo = self.repo
+        self.repo._impl.all_commit_ids = lambda *a,**kw: []
+        ThreadLocalORMSession.flush_all()
+        # ThreadLocalORMSession.close_all()
+
+class _TestWithRepoAndCommit(_TestWithRepo):
+    def setUp(self):
+        super(_TestWithRepoAndCommit, self).setUp()
+        self.ci, isnew = self._make_commit('foo')
+        ThreadLocalORMSession.flush_all()
+        # ThreadLocalORMSession.close_all()
+
+class TestRepo(_TestWithRepo):
+
+    def test_create(self):
+        assert self.repo.fs_path == os.path.join(self.prefix, 'svn/p/test/')
+        assert self.repo.url_path == '/p/test/'
+        assert self.repo.full_fs_path == os.path.join(self.prefix, 'svn/p/test/test1')
+
+    def test_passthrough(self):
+        argless = ['init']
+        for fn in argless:
+            getattr(self.repo, fn)()
+            getattr(self.repo._impl, fn).assert_called_with()
+        unary = [ 'commit', 'open_blob' ]
+        for fn in unary:
+            getattr(self.repo, fn)('foo')
+            getattr(self.repo._impl, fn).assert_called_with('foo')
+
+    def test_shorthand_for_commit(self):
+        self.assertEqual(
+            self.repo.shorthand_for_commit('a'*40),
+            '[aaaaaa]')
+
+    def test_url_for_commit(self):
+        self.assertEqual(
+            self.repo.url_for_commit('a'*40),
+            '/p/test/test1/ci/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/')
+
+    def test_init_as_clone(self):
+        self.repo.init_as_clone('srcpath', 'srcname', 'srcurl')
+        assert self.repo.upstream_repo.name == 'srcname'
+        assert self.repo.upstream_repo.url == 'srcurl'
+        assert self.repo._impl.clone_from.called_with('srcpath')
+
+    def test_log(self):
+        ci = mock.Mock()
+        ci.log = mock.Mock(return_value=[1,2,3])
+        self.repo._impl.commit = mock.Mock(return_value=ci)
+        assert self.repo.log() == [1,2,3], self.repo.log()
+
+    def test_count_revisions(self):
+        ci = mock.Mock()
+        ci.count_revisions = mock.Mock(return_value=42)
+        self.repo._impl.commit = mock.Mock(return_value=ci)
+        assert self.repo.count() == 42
+
+    def test_latest(self):
+        ci = mock.Mock()
+        self.repo._impl.commit = mock.Mock(return_value=ci)
+        assert self.repo.latest() is ci
+
+    def test_index(self):
+        i = self.repo.index()
+        assert i['type_s'] == 'Repository', i
+        assert i['name_s'] == 'test1', i
+
+    def test_scm_host_url(self):
+        assert (
+            self.repo.clone_url('rw', 'nobody')
+            == 'svn+ssh://nobody@localhost:8022/scm-repo/p/test/test1/'),\
+            self.repo.clone_url('rw', 'nobody')
+        assert (
+            self.repo.clone_url('https', 'nobody')
+            == 'https://nobody@localhost:8022/scm-repo/p/test/test1/'),\
+            self.repo.clone_url('https', 'nobody')
+
+    def test_merge_request(self):
+        M.MergeRequest.upsert(app_config_id=c.app.config._id, status='open')
+        M.MergeRequest.upsert(app_config_id=c.app.config._id, status='closed')
+        session(M.MergeRequest).flush()
+        session(M.MergeRequest).clear()
+        assert self.repo.merge_requests_by_statuses('open').count() == 1
+        assert self.repo.merge_requests_by_statuses('closed').count() == 1
+        assert self.repo.merge_requests_by_statuses('open', 'closed').count() == 2
+
+    def test_guess_type(self):
+        assert self.repo.guess_type('foo.txt') == ('text/plain', None)
+        assert self.repo.guess_type('foo.gbaer') == ('application/octet-stream', None)
+        assert self.repo.guess_type('foo.html') == ('text/html', None)
+        assert self.repo.guess_type('.gitignore') == ('text/plain', None)
+
+    def test_refresh(self):
+        ci = mock.Mock()
+        ci.count_revisions=mock.Mock(return_value=100)
+        ci.authored.name = 'Test Committer'
+        ci.author_url = '/u/test-committer/'
+        self.repo._impl.commit = mock.Mock(return_value=ci)
+        self.repo._impl.new_commits = mock.Mock(return_value=['foo%d' % i for i in range(100) ])
+        self.repo._impl.all_commit_ids = mock.Mock(return_value=['foo%d' % i for i in range(100) ])
+        self.repo.symbolics_for_commit = mock.Mock(return_value=[['master', 'branch'], []])
+        def refresh_commit_info(oid, seen, lazy=False):
+            M.repo.CommitDoc(dict(
+                    authored=dict(
+                        name='Test Committer',
+                        email='test@test.com'),
+                    _id=oid)).m.insert()
+        def set_heads():
+            self.repo.heads = [ ming.base.Object(name='head', object_id='foo0', count=100) ]
+        self.repo._impl.refresh_commit_info = refresh_commit_info
+        self.repo._impl.refresh_heads = mock.Mock(side_effect=set_heads)
+        self.repo.shorthand_for_commit = lambda oid: '[' + str(oid) + ']'
+        self.repo.url_for_commit = lambda oid: '/ci/' + str(oid) + '/'
+        self.repo.refresh()
+        ThreadLocalORMSession.flush_all()
+        notifications = M.Notification.query.find().all()
+        for n in notifications:
+            if '100 new commits' in n.subject:
+                assert "master,branch:  by Test Committer http://localhost/#" in n.text
+                break
+        else:
+            assert False, 'Did not find notification'
+        assert M.Feed.query.find(dict(
+            title='New commit',
+            author_name='Test Committer')).count()
+
+    def test_refresh_private(self):
+        ci = mock.Mock()
+        ci.count_revisions=mock.Mock(return_value=100)
+        self.repo._impl.commit = mock.Mock(return_value=ci)
+        self.repo._impl.new_commits = mock.Mock(return_value=['foo%d' % i for i in range(100) ])
+        def set_heads():
+            self.repo.heads = [ ming.base.Object(name='head', object_id='foo0', count=100) ]
+        self.repo._impl.refresh_heads = mock.Mock(side_effect=set_heads)
+
+        # make unreadable by *anonymous, so additional notification logic executes
+        self.repo.acl = []
+        c.project.acl = []
+
+        self.repo.refresh()
+
+    def test_push_upstream_context(self):
+        self.repo.init_as_clone('srcpath', '/p/test/svn/', '/p/test/svn/')
+        old_app_instance = M.Project.app_instance
+        try:
+            M.Project.app_instance = mock.Mock(return_value=ming.base.Object(
+                    config=ming.base.Object(_id=None)))
+            with self.repo.push_upstream_context():
+                assert c.project.shortname == 'test'
+        finally:
+            M.Project.app_instance = old_app_instance
+
+    def test_pending_upstream_merges(self):
+        self.repo.init_as_clone('srcpath', '/p/test/svn/', '/p/test/svn/')
+        old_app_instance = M.Project.app_instance
+        try:
+            M.Project.app_instance = mock.Mock(return_value=ming.base.Object(
+                    config=ming.base.Object(_id=None)))
+            self.repo.pending_upstream_merges()
+        finally:
+            M.Project.app_instance = old_app_instance
+
+class TestMergeRequest(_TestWithRepoAndCommit):
+
+    def setUp(self):
+        super(TestMergeRequest, self).setUp()
+        c.project.install_app('svn', 'test2')
+        h.set_context('test', 'test2', neighborhood='Projects')
+        self.repo2 = M.Repository(name='test2', tool='svn')
+        self.repo2._impl = mock.Mock(spec=M.RepositoryImplementation())
+        self.repo2._impl.log = lambda *a,**kw:(['foo'], [])
+        self.repo2._impl._repo = self.repo2
+        self.repo2.init_as_clone('/p/test/', 'test1', '/p/test/test1/')
+        ThreadLocalORMSession.flush_all()
+        ThreadLocalORMSession.close_all()
+
+    def test_upsert(self):
+        h.set_context('test', 'test1', neighborhood='Projects')
+        mr = M.MergeRequest.upsert(
+            downstream=ming.base.Object(
+                project_id=c.project._id,
+                mount_point='test2',
+                commit_id='foo'),
+            target_branch='foobranch',
+            summary='summary',
+            description='description')
+        u = M.User.by_username('test-admin')
+        assert mr.creator == u
+        assert mr.creator_name == u.get_pref('display_name')
+        assert mr.creator_url == u.url()
+        assert mr.downstream_url == '/p/test/test2/'
+        assert mr.downstream_repo_url == 'http://svn.localhost/p/test/test2/'
+        assert mr.commits == [ self._make_commit('foo')[0] ]
+
+class TestRepoObject(_TestWithRepoAndCommit):
+
+    def test_upsert(self):
+        obj0, isnew0 = M.repo.Tree.upsert('foo1')
+        obj1, isnew1 = M.repo.Tree.upsert('foo1')
+        assert obj0 is obj1
+        assert isnew0 and not isnew1
+
+    def test_set_last_commit(self):
+        obj, isnew = M.repo.Tree.upsert('foo1')
+        M.repo_refresh.set_last_commit(
+            self.repo._id, '/', 'fakefile', obj._id,
+            M.repo_refresh.get_commit_info(self.ci))
+
+    def test_get_last_commit(self):
+        obj, isnew = M.repo.Tree.upsert('foo1')
+        lc0 = M.repo_refresh.set_last_commit(
+            self.repo._id, '/', 'fakefile', obj._id,
+            M.repo_refresh.get_commit_info(self.ci))
+
+        lc1 = M.repo.LastCommitDoc.m.get(object_id=obj._id)
+        assert lc0 == lc1
+
+    def test_get_last_commit_missing(self):
+        obj, isnew = M.repo.Tree.upsert('foo1')
+        lc1 = M.repo.LastCommitDoc.m.get(object_id=obj._id)
+        assert lc1 is None
+
+    def test_artifact_methods(self):
+        assert self.ci.index_id() == 'allura/model/repo/Commit#foo', self.ci.index_id()
+        assert self.ci.primary() is self.ci, self.ci.primary()
+
+
+class TestCommit(_TestWithRepo):
+
+    def setUp(self):
+        super(TestCommit, self).setUp()
+        self.ci, isnew = self._make_commit(
+            'foo',
+            a=dict(
+                a=dict(
+                    a='',
+                    b='',),
+                b=''))
+        self.tree = self.ci.tree
+        impl = M.RepositoryImplementation()
+        impl._repo = self.repo
+        self.repo._impl.shorthand_for_commit = impl.shorthand_for_commit
+        self.repo._impl.url_for_commit = impl.url_for_commit
+
+    def test_upsert(self):
+        obj0, isnew0 = M.repo.Commit.upsert('foo')
+        obj1, isnew1 = M.repo.Commit.upsert('foo')
+        assert obj0 is obj1
+        assert not isnew1
+        u = M.User.by_username('test-admin')
+        assert self.ci.author_url == u.url()
+        assert self.ci.committer_url == u.url()
+        assert self.ci.tree is self.tree
+        assert self.ci.summary == 'summary'
+        assert self.ci.shorthand_id() == '[foo]'
+        assert self.ci.url() == '/p/test/test1/ci/foo/'
+
+    def test_get_path(self):
+        b = self.ci.get_path('a/a/a')
+        assert isinstance(b, M.repo.Blob)
+        x = self.ci.get_path('a/a')
+        assert isinstance(x, M.repo.Tree)
+
+    def test_log(self):
+        rb = M.repo_refresh.CommitRunBuilder(['foo'])
+        rb.run()
+        rb.cleanup()
+        commits = self.ci.log(0, 100)
+        assert commits[0]._id == 'foo'
+
+    def test_count_revisions(self):
+        rb = M.repo_refresh.CommitRunBuilder(['foo'])
+        rb.run()
+        rb.cleanup()
+        assert self.ci.count_revisions() == 1
+
+    def test_compute_diffs(self):
+        self.repo._impl.commit = mock.Mock(return_value=self.ci)
+        M.repo_refresh.refresh_commit_trees(self.ci, {})
+        M.repo_refresh.compute_diffs(self.repo._id, {}, self.ci)
+        # self.ci.compute_diffs()
+        assert self.ci.diffs.added == [ 'a' ]
+        assert (self.ci.diffs.copied
+                == self.ci.diffs.changed
+                == self.ci.diffs.removed
+                == [])
+        ci, isnew = self._make_commit('bar')
+        ci.parent_ids = [ 'foo' ]
+        self._make_log(ci)
+        M.repo_refresh.refresh_commit_trees(ci, {})
+        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
+        assert ci.diffs.removed == [ 'a' ]
+        assert (ci.diffs.copied
+                == ci.diffs.changed
+                == ci.diffs.added
+                == [])
+        ci, isnew = self._make_commit(
+            'baz',
+            b=dict(
+                a=dict(
+                    a='',
+                    b='',),
+                b=''))
+        ci.parent_ids = [ 'foo' ]
+        self._make_log(ci)
+        M.repo_refresh.refresh_commit_trees(ci, {})
+        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
+        assert ci.diffs.added == [ 'b' ]
+        assert ci.diffs.removed == [ 'a' ]
+        assert (ci.diffs.copied
+                == ci.diffs.changed
+                == [])
+
+    def test_diffs_file_renames(self):
+        def open_blob(blob):
+            blobs = {
+                u'a': u'Leia',
+                u'/b/a/a': u'Darth Vader',
+                u'/b/a/b': u'Luke Skywalker',
+                u'/b/b': u'Death Star will destroy you',
+                u'/b/c': u'Luke Skywalker',  # moved from /b/a/b
+                u'/b/a/z': u'Death Star will destroy you\nALL',  # moved from /b/b and modified
+            }
+            from cStringIO import StringIO
+            return StringIO(blobs[blob.path()])
+        self.repo._impl.open_blob = open_blob
+
+        self.repo._impl.commit = mock.Mock(return_value=self.ci)
+        M.repo_refresh.refresh_commit_trees(self.ci, {})
+        M.repo_refresh.compute_diffs(self.repo._id, {}, self.ci)
+        assert self.ci.diffs.added == ['a']
+        assert (self.ci.diffs.copied
+                == self.ci.diffs.changed
+                == self.ci.diffs.removed
+                == [])
+
+        ci, isnew = self._make_commit(
+            'bar',
+            b=dict(
+                a=dict(
+                    a='',
+                    b='',),
+                b=''))
+        ci.parent_ids = ['foo']
+        self._make_log(ci)
+        M.repo_refresh.refresh_commit_trees(ci, {})
+        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
+        assert ci.diffs.added == ['b']
+        assert ci.diffs.removed == ['a']
+        assert (ci.diffs.copied
+                == ci.diffs.changed
+                == [])
+
+        ci, isnew = self._make_commit(
+            'baz',
+            b=dict(
+                a=dict(
+                    z=''),
+                c=''))
+        ci.parent_ids = ['bar']
+        self._make_log(ci)
+        M.repo_refresh.refresh_commit_trees(ci, {})
+        M.repo_refresh.compute_diffs(self.repo._id, {}, ci)
+        assert ci.diffs.added == ci.diffs.changed == []
+        assert ci.diffs.removed == ['b/a/a']
+        # see mock for open_blob
+        assert len(ci.diffs.copied) == 2
+        assert ci.diffs.copied[0]['old'] == 'b/a/b'
+        assert ci.diffs.copied[0]['new'] == 'b/c'
+        assert ci.diffs.copied[0]['ratio'] == 1
+        assert ci.diffs.copied[0]['diff'] == ''
+        assert ci.diffs.copied[1]['old'] == 'b/b'
+        assert ci.diffs.copied[1]['new'] == 'b/a/z'
+        assert ci.diffs.copied[1]['ratio'] < 1
+        assert '+++' in ci.diffs.copied[1]['diff']
+
+    def test_context(self):
+        self.ci.context()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/0231935d/ForgeSVN/forgesvn/tests/test_tasks.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/tests/test_tasks.py b/ForgeSVN/forgesvn/tests/test_tasks.py
index c3980e9..c3173a4 100644
--- a/ForgeSVN/forgesvn/tests/test_tasks.py
+++ b/ForgeSVN/forgesvn/tests/test_tasks.py
@@ -2,9 +2,11 @@
 import shutil
 import unittest
 
+import tg
 import mock
 from pylons import c
 from ming.orm import ThreadLocalORMSession
+from paste.deploy.converters import asbool
 
 from alluratest.controller import setup_basic_test, setup_global_objects
 
@@ -19,6 +21,13 @@ class TestRepoTasks(unittest.TestCase):
     def setUp(self):
         setup_basic_test()
         self.setup_with_tools()
+        if asbool(tg.config.get('smtp.mock')):
+            self.smtp_mock = mock.patch('allura.lib.mail_util.smtplib.SMTP')
+            self.smtp_mock.start()
+        
+    def tearDown(self):
+        if asbool(tg.config.get('smtp.mock')):
+            self.smtp_mock.stop()
 
     @with_svn
     def setup_with_tools(self):