##// END OF EJS Templates
merge with beta
marcink -
r2462:1f9c467e merge codereview
parent child Browse files
Show More
@@ -0,0 +1,56 b''
1 """
2 Unit tests for vcs_ library.
3
4 In order to run tests we need to prepare our environment first. Tests would be
5 run for each engine listed at ``conf.SCM_TESTS`` - keys are aliases from
6 ``vcs.backends.BACKENDS``.
7
8 For each SCM we run tests for, we need some repository. We would use
9 repositories location from system environment variables or test suite defaults
10 - see ``conf`` module for more detail. We simply try to check if repository at
11 certain location exists, if not we would try to fetch them. At ``test_vcs`` or
12 ``test_common`` we run unit tests common for each repository type and for
13 example specific mercurial tests are located at ``test_hg`` module.
14
15 Oh, and tests are run with ``unittest.collector`` wrapped by ``collector``
16 function at ``tests/__init__.py``.
17
18 .. _vcs: http://bitbucket.org/marcinkuzminski/vcs
19 .. _unittest: http://pypi.python.org/pypi/unittest
20
21 """
22 import os
23 from rhodecode.lib import vcs
24 from rhodecode.lib.vcs.utils.compat import unittest
25 from rhodecode.tests import *
26 from utils import VCSTestError, SCMFetcher
27
28
29 def setup_package():
30 """
31 Prepares whole package for tests which mainly means it would try to fetch
32 test repositories or use already existing ones.
33 """
34 fetchers = {
35 'hg': {
36 'alias': 'hg',
37 'test_repo_path': TEST_HG_REPO,
38 'remote_repo': HG_REMOTE_REPO,
39 'clone_cmd': 'hg clone',
40 },
41 'git': {
42 'alias': 'git',
43 'test_repo_path': TEST_GIT_REPO,
44 'remote_repo': GIT_REMOTE_REPO,
45 'clone_cmd': 'git clone --bare',
46 },
47 }
48 try:
49 for scm, fetcher_info in fetchers.items():
50 fetcher = SCMFetcher(**fetcher_info)
51 fetcher.setup()
52 except VCSTestError, err:
53 raise RuntimeError(str(err))
54
55 #start_dir = os.path.abspath(os.path.dirname(__file__))
56 #unittest.defaultTestLoader.discover(start_dir)
@@ -0,0 +1,10 b''
1 [user]
2 name = Foo Bar
3 email = foo.bar@example.com
4
5 [ui]
6 username = Foo Bar foo.bar@example.com
7
8 [universal]
9 foo = bar
10
@@ -0,0 +1,111 b''
1 """
2 Module providing backend independent mixin class. It requires that
3 InMemoryChangeset class is working properly at backend class.
4 """
5 import os
6 from rhodecode.lib import vcs
7 import time
8 import shutil
9 import datetime
10 from rhodecode.lib.vcs.utils.compat import unittest
11
12 from conf import SCM_TESTS, get_new_dir
13
14 from rhodecode.lib.vcs.nodes import FileNode
15
16
17 class BackendTestMixin(object):
18 """
19 This is a backend independent test case class which should be created
20 with ``type`` method.
21
22 It is required to set following attributes at subclass:
23
24 - ``backend_alias``: alias of used backend (see ``vcs.BACKENDS``)
25 - ``repo_path``: path to the repository which would be created for set of
26 tests
27 - ``recreate_repo_per_test``: If set to ``False``, repo would NOT be created
28 before every single test. Defaults to ``True``.
29 """
30 recreate_repo_per_test = True
31
32 @classmethod
33 def get_backend(cls):
34 return vcs.get_backend(cls.backend_alias)
35
36 @classmethod
37 def _get_commits(cls):
38 commits = [
39 {
40 'message': u'Initial commit',
41 'author': u'Joe Doe <joe.doe@example.com>',
42 'date': datetime.datetime(2010, 1, 1, 20),
43 'added': [
44 FileNode('foobar', content='Foobar'),
45 FileNode('foobar2', content='Foobar II'),
46 FileNode('foo/bar/baz', content='baz here!'),
47 ],
48 },
49 {
50 'message': u'Changes...',
51 'author': u'Jane Doe <jane.doe@example.com>',
52 'date': datetime.datetime(2010, 1, 1, 21),
53 'added': [
54 FileNode('some/new.txt', content='news...'),
55 ],
56 'changed': [
57 FileNode('foobar', 'Foobar I'),
58 ],
59 'removed': [],
60 },
61 ]
62 return commits
63
64 @classmethod
65 def setUpClass(cls):
66 Backend = cls.get_backend()
67 cls.backend_class = Backend
68 cls.repo_path = get_new_dir(str(time.time()))
69 cls.repo = Backend(cls.repo_path, create=True)
70 cls.imc = cls.repo.in_memory_changeset
71
72 for commit in cls._get_commits():
73 for node in commit.get('added', []):
74 cls.imc.add(FileNode(node.path, content=node.content))
75 for node in commit.get('changed', []):
76 cls.imc.change(FileNode(node.path, content=node.content))
77 for node in commit.get('removed', []):
78 cls.imc.remove(FileNode(node.path))
79
80 cls.tip = cls.imc.commit(message=unicode(commit['message']),
81 author=unicode(commit['author']),
82 date=commit['date'])
83
84 @classmethod
85 def tearDownClass(cls):
86 if not getattr(cls, 'recreate_repo_per_test', False) and \
87 'VCS_REMOVE_TEST_DIRS' in os.environ:
88 shutil.rmtree(cls.repo_path)
89
90 def setUp(self):
91 if getattr(self, 'recreate_repo_per_test', False):
92 self.__class__.setUpClass()
93
94 def tearDown(self):
95 if getattr(self, 'recreate_repo_per_test', False) and \
96 'VCS_REMOVE_TEST_DIRS' in os.environ:
97 shutil.rmtree(self.repo_path)
98
99
100 # For each backend create test case class
101 for alias in SCM_TESTS:
102 attrs = {
103 'backend_alias': alias,
104 }
105 cls_name = ''.join(('%s base backend test' % alias).title().split())
106 bases = (BackendTestMixin, unittest.TestCase)
107 globals()[cls_name] = type(cls_name, bases, attrs)
108
109
110 if __name__ == '__main__':
111 unittest.main()
@@ -0,0 +1,62 b''
1 """
2 Unit tests configuration module for vcs.
3 """
4
5 import os
6 import time
7 import hashlib
8 import tempfile
9 import datetime
10 from rhodecode.tests import *
11 from utils import get_normalized_path
12 from os.path import join as jn
13
14 TEST_TMP_PATH = TESTS_TMP_PATH
15 #__all__ = (
16 # 'TEST_HG_REPO', 'TEST_GIT_REPO', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO',
17 # 'SCM_TESTS',
18 #)
19 #
20 #SCM_TESTS = ['hg', 'git']
21 #uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
22 #
23 THIS = os.path.abspath(os.path.dirname(__file__))
24 #
25 #GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
26 #
27 #TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', '/tmp')
28 #TEST_GIT_REPO = os.environ.get('VCS_TEST_GIT_REPO',
29 # jn(TEST_TMP_PATH, 'vcs-git'))
30 #TEST_GIT_REPO_CLONE = os.environ.get('VCS_TEST_GIT_REPO_CLONE',
31 # jn(TEST_TMP_PATH, 'vcsgitclone%s' % uniq_suffix))
32 #TEST_GIT_REPO_PULL = os.environ.get('VCS_TEST_GIT_REPO_PULL',
33 # jn(TEST_TMP_PATH, 'vcsgitpull%s' % uniq_suffix))
34 #
35 #HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
36 #TEST_HG_REPO = os.environ.get('VCS_TEST_HG_REPO',
37 # jn(TEST_TMP_PATH, 'vcs-hg'))
38 #TEST_HG_REPO_CLONE = os.environ.get('VCS_TEST_HG_REPO_CLONE',
39 # jn(TEST_TMP_PATH, 'vcshgclone%s' % uniq_suffix))
40 #TEST_HG_REPO_PULL = os.environ.get('VCS_TEST_HG_REPO_PULL',
41 # jn(TEST_TMP_PATH, 'vcshgpull%s' % uniq_suffix))
42 #
43 #TEST_DIR = os.environ.get('VCS_TEST_ROOT', tempfile.gettempdir())
44 #TEST_REPO_PREFIX = 'vcs-test'
45 #
46 #
47 #def get_new_dir(title):
48 # """
49 # Returns always new directory path.
50 # """
51 # name = TEST_REPO_PREFIX
52 # if title:
53 # name = '-'.join((name, title))
54 # hex = hashlib.sha1(str(time.time())).hexdigest()
55 # name = '-'.join((name, hex))
56 # path = os.path.join(TEST_DIR, name)
57 # return get_normalized_path(path)
58
59 PACKAGE_DIR = os.path.abspath(os.path.join(
60 os.path.dirname(__file__), '..'))
61
62 TEST_USER_CONFIG_FILE = jn(THIS, 'aconfig')
@@ -0,0 +1,108 b''
1 from __future__ import with_statement
2
3 import os
4 import tarfile
5 import zipfile
6 import datetime
7 import tempfile
8 import StringIO
9 from base import BackendTestMixin
10 from conf import SCM_TESTS
11 from rhodecode.lib.vcs.exceptions import VCSError
12 from rhodecode.lib.vcs.nodes import FileNode
13 from rhodecode.lib.vcs.utils.compat import unittest
14
15
16 class ArchivesTestCaseMixin(BackendTestMixin):
17
18 @classmethod
19 def _get_commits(cls):
20 start_date = datetime.datetime(2010, 1, 1, 20)
21 for x in xrange(5):
22 yield {
23 'message': 'Commit %d' % x,
24 'author': 'Joe Doe <joe.doe@example.com>',
25 'date': start_date + datetime.timedelta(hours=12 * x),
26 'added': [
27 FileNode('%d/file_%d.txt' % (x, x),
28 content='Foobar %d' % x),
29 ],
30 }
31
32 def test_archive_zip(self):
33 path = tempfile.mkstemp()[1]
34 with open(path, 'wb') as f:
35 self.tip.fill_archive(stream=f, kind='zip', prefix='repo')
36 out = zipfile.ZipFile(path)
37
38 for x in xrange(5):
39 node_path = '%d/file_%d.txt' % (x, x)
40 decompressed = StringIO.StringIO()
41 decompressed.write(out.read('repo/' + node_path))
42 self.assertEqual(
43 decompressed.getvalue(),
44 self.tip.get_node(node_path).content)
45
46 def test_archive_tgz(self):
47 path = tempfile.mkstemp()[1]
48 with open(path, 'wb') as f:
49 self.tip.fill_archive(stream=f, kind='tgz', prefix='repo')
50 outdir = tempfile.mkdtemp()
51
52 outfile = tarfile.open(path, 'r|gz')
53 outfile.extractall(outdir)
54
55 for x in xrange(5):
56 node_path = '%d/file_%d.txt' % (x, x)
57 self.assertEqual(
58 open(os.path.join(outdir, 'repo/' + node_path)).read(),
59 self.tip.get_node(node_path).content)
60
61 def test_archive_tbz2(self):
62 path = tempfile.mkstemp()[1]
63 with open(path, 'w+b') as f:
64 self.tip.fill_archive(stream=f, kind='tbz2', prefix='repo')
65 outdir = tempfile.mkdtemp()
66
67 outfile = tarfile.open(path, 'r|bz2')
68 outfile.extractall(outdir)
69
70 for x in xrange(5):
71 node_path = '%d/file_%d.txt' % (x, x)
72 self.assertEqual(
73 open(os.path.join(outdir, 'repo/' + node_path)).read(),
74 self.tip.get_node(node_path).content)
75
76 def test_archive_default_stream(self):
77 tmppath = tempfile.mkstemp()[1]
78 with open(tmppath, 'w') as stream:
79 self.tip.fill_archive(stream=stream)
80 mystream = StringIO.StringIO()
81 self.tip.fill_archive(stream=mystream)
82 mystream.seek(0)
83 with open(tmppath, 'r') as f:
84 self.assertEqual(f.read(), mystream.read())
85
86 def test_archive_wrong_kind(self):
87 with self.assertRaises(VCSError):
88 self.tip.fill_archive(kind='wrong kind')
89
90 def test_archive_empty_prefix(self):
91 with self.assertRaises(VCSError):
92 self.tip.fill_archive(prefix='')
93
94 def test_archive_prefix_with_leading_slash(self):
95 with self.assertRaises(VCSError):
96 self.tip.fill_archive(prefix='/any')
97
98 # For each backend create test case class
99 for alias in SCM_TESTS:
100 attrs = {
101 'backend_alias': alias,
102 }
103 cls_name = ''.join(('%s archive test' % alias).title().split())
104 bases = (ArchivesTestCaseMixin, unittest.TestCase)
105 globals()[cls_name] = type(cls_name, bases, attrs)
106
107 if __name__ == '__main__':
108 unittest.main()
@@ -0,0 +1,118 b''
1 from __future__ import with_statement
2
3 from rhodecode.lib import vcs
4 import datetime
5 from rhodecode.lib.vcs.utils.compat import unittest
6
7 from base import BackendTestMixin
8 from conf import SCM_TESTS
9
10 from rhodecode.lib.vcs.nodes import FileNode
11
12
13 class BranchesTestCaseMixin(BackendTestMixin):
14
15 @classmethod
16 def _get_commits(cls):
17 commits = [
18 {
19 'message': 'Initial commit',
20 'author': 'Joe Doe <joe.doe@example.com>',
21 'date': datetime.datetime(2010, 1, 1, 20),
22 'added': [
23 FileNode('foobar', content='Foobar'),
24 FileNode('foobar2', content='Foobar II'),
25 FileNode('foo/bar/baz', content='baz here!'),
26 ],
27 },
28 {
29 'message': 'Changes...',
30 'author': 'Jane Doe <jane.doe@example.com>',
31 'date': datetime.datetime(2010, 1, 1, 21),
32 'added': [
33 FileNode('some/new.txt', content='news...'),
34 ],
35 'changed': [
36 FileNode('foobar', 'Foobar I'),
37 ],
38 'removed': [],
39 },
40 ]
41 return commits
42
43 def test_simple(self):
44 tip = self.repo.get_changeset()
45 self.assertEqual(tip.date, datetime.datetime(2010, 1, 1, 21))
46
47 def test_new_branch(self):
48 # This check must not be removed to ensure the 'branches' LazyProperty
49 # gets hit *before* the new 'foobar' branch got created:
50 self.assertFalse('foobar' in self.repo.branches)
51 self.imc.add(vcs.nodes.FileNode('docs/index.txt',
52 content='Documentation\n'))
53 foobar_tip = self.imc.commit(
54 message=u'New branch: foobar',
55 author=u'joe',
56 branch='foobar',
57 )
58 self.assertTrue('foobar' in self.repo.branches)
59 self.assertEqual(foobar_tip.branch, 'foobar')
60
61 def test_new_head(self):
62 tip = self.repo.get_changeset()
63 self.imc.add(vcs.nodes.FileNode('docs/index.txt',
64 content='Documentation\n'))
65 foobar_tip = self.imc.commit(
66 message=u'New branch: foobar',
67 author=u'joe',
68 branch='foobar',
69 parents=[tip],
70 )
71 self.imc.change(vcs.nodes.FileNode('docs/index.txt',
72 content='Documentation\nand more...\n'))
73 newtip = self.imc.commit(
74 message=u'At default branch',
75 author=u'joe',
76 branch=foobar_tip.branch,
77 parents=[foobar_tip],
78 )
79
80 newest_tip = self.imc.commit(
81 message=u'Merged with %s' % foobar_tip.raw_id,
82 author=u'joe',
83 branch=self.backend_class.DEFAULT_BRANCH_NAME,
84 parents=[newtip, foobar_tip],
85 )
86
87 self.assertEqual(newest_tip.branch,
88 self.backend_class.DEFAULT_BRANCH_NAME)
89
90 def test_branch_with_slash_in_name(self):
91 self.imc.add(vcs.nodes.FileNode('extrafile', content='Some data\n'))
92 self.imc.commit(u'Branch with a slash!', author=u'joe',
93 branch='issue/123')
94 self.assertTrue('issue/123' in self.repo.branches)
95
96 def test_branch_with_slash_in_name_and_similar_without(self):
97 self.imc.add(vcs.nodes.FileNode('extrafile', content='Some data\n'))
98 self.imc.commit(u'Branch with a slash!', author=u'joe',
99 branch='issue/123')
100 self.imc.add(vcs.nodes.FileNode('extrafile II', content='Some data\n'))
101 self.imc.commit(u'Branch without a slash...', author=u'joe',
102 branch='123')
103 self.assertIn('issue/123', self.repo.branches)
104 self.assertIn('123', self.repo.branches)
105
106
107 # For each backend create test case class
108 for alias in SCM_TESTS:
109 attrs = {
110 'backend_alias': alias,
111 }
112 cls_name = ''.join(('%s branches test' % alias).title().split())
113 bases = (BranchesTestCaseMixin, unittest.TestCase)
114 globals()[cls_name] = type(cls_name, bases, attrs)
115
116
117 if __name__ == '__main__':
118 unittest.main()
@@ -0,0 +1,336 b''
1 from __future__ import with_statement
2
3 from rhodecode.lib import vcs
4 import datetime
5 from base import BackendTestMixin
6 from conf import SCM_TESTS
7 from rhodecode.lib.vcs.backends.base import BaseChangeset
8 from rhodecode.lib.vcs.nodes import FileNode
9 from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError
10 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
11 from rhodecode.lib.vcs.exceptions import RepositoryError
12 from rhodecode.lib.vcs.utils.compat import unittest
13
14
15 class TestBaseChangeset(unittest.TestCase):
16
17 def test_as_dict(self):
18 changeset = BaseChangeset()
19 changeset.id = 'ID'
20 changeset.raw_id = 'RAW_ID'
21 changeset.short_id = 'SHORT_ID'
22 changeset.revision = 1009
23 changeset.date = datetime.datetime(2011, 1, 30, 1, 45)
24 changeset.message = 'Message of a commit'
25 changeset.author = 'Joe Doe <joe.doe@example.com>'
26 changeset.added = [FileNode('foo/bar/baz'), FileNode('foobar')]
27 changeset.changed = []
28 changeset.removed = []
29 self.assertEqual(changeset.as_dict(), {
30 'id': 'ID',
31 'raw_id': 'RAW_ID',
32 'short_id': 'SHORT_ID',
33 'revision': 1009,
34 'date': datetime.datetime(2011, 1, 30, 1, 45),
35 'message': 'Message of a commit',
36 'author': {
37 'name': 'Joe Doe',
38 'email': 'joe.doe@example.com',
39 },
40 'added': ['foo/bar/baz', 'foobar'],
41 'changed': [],
42 'removed': [],
43 })
44
45 class ChangesetsWithCommitsTestCaseixin(BackendTestMixin):
46 recreate_repo_per_test = True
47
48 @classmethod
49 def _get_commits(cls):
50 start_date = datetime.datetime(2010, 1, 1, 20)
51 for x in xrange(5):
52 yield {
53 'message': 'Commit %d' % x,
54 'author': 'Joe Doe <joe.doe@example.com>',
55 'date': start_date + datetime.timedelta(hours=12 * x),
56 'added': [
57 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
58 ],
59 }
60
61 def test_new_branch(self):
62 self.imc.add(vcs.nodes.FileNode('docs/index.txt',
63 content='Documentation\n'))
64 foobar_tip = self.imc.commit(
65 message=u'New branch: foobar',
66 author=u'joe',
67 branch='foobar',
68 )
69 self.assertTrue('foobar' in self.repo.branches)
70 self.assertEqual(foobar_tip.branch, 'foobar')
71 # 'foobar' should be the only branch that contains the new commit
72 self.assertNotEqual(*self.repo.branches.values())
73
74 def test_new_head_in_default_branch(self):
75 tip = self.repo.get_changeset()
76 self.imc.add(vcs.nodes.FileNode('docs/index.txt',
77 content='Documentation\n'))
78 foobar_tip = self.imc.commit(
79 message=u'New branch: foobar',
80 author=u'joe',
81 branch='foobar',
82 parents=[tip],
83 )
84 self.imc.change(vcs.nodes.FileNode('docs/index.txt',
85 content='Documentation\nand more...\n'))
86 newtip = self.imc.commit(
87 message=u'At default branch',
88 author=u'joe',
89 branch=foobar_tip.branch,
90 parents=[foobar_tip],
91 )
92
93 newest_tip = self.imc.commit(
94 message=u'Merged with %s' % foobar_tip.raw_id,
95 author=u'joe',
96 branch=self.backend_class.DEFAULT_BRANCH_NAME,
97 parents=[newtip, foobar_tip],
98 )
99
100 self.assertEqual(newest_tip.branch,
101 self.backend_class.DEFAULT_BRANCH_NAME)
102
103 def test_get_changesets_respects_branch_name(self):
104 tip = self.repo.get_changeset()
105 self.imc.add(vcs.nodes.FileNode('docs/index.txt',
106 content='Documentation\n'))
107 doc_changeset = self.imc.commit(
108 message=u'New branch: docs',
109 author=u'joe',
110 branch='docs',
111 )
112 self.imc.add(vcs.nodes.FileNode('newfile', content=''))
113 self.imc.commit(
114 message=u'Back in default branch',
115 author=u'joe',
116 parents=[tip],
117 )
118 default_branch_changesets = self.repo.get_changesets(
119 branch_name=self.repo.DEFAULT_BRANCH_NAME)
120 self.assertNotIn(doc_changeset, default_branch_changesets)
121
122
123 class ChangesetsTestCaseMixin(BackendTestMixin):
124 recreate_repo_per_test = False
125
126 @classmethod
127 def _get_commits(cls):
128 start_date = datetime.datetime(2010, 1, 1, 20)
129 for x in xrange(5):
130 yield {
131 'message': u'Commit %d' % x,
132 'author': u'Joe Doe <joe.doe@example.com>',
133 'date': start_date + datetime.timedelta(hours=12 * x),
134 'added': [
135 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
136 ],
137 }
138
139 def test_simple(self):
140 tip = self.repo.get_changeset()
141 self.assertEqual(tip.date, datetime.datetime(2010, 1, 3, 20))
142
143 def test_get_changesets_is_ordered_by_date(self):
144 changesets = list(self.repo.get_changesets())
145 ordered_by_date = sorted(changesets,
146 key=lambda cs: cs.date)
147 self.assertItemsEqual(changesets, ordered_by_date)
148
149 def test_get_changesets_respects_start(self):
150 second_id = self.repo.revisions[1]
151 changesets = list(self.repo.get_changesets(start=second_id))
152 self.assertEqual(len(changesets), 4)
153
154 def test_get_changesets_numerical_id_respects_start(self):
155 second_id = 1
156 changesets = list(self.repo.get_changesets(start=second_id))
157 self.assertEqual(len(changesets), 4)
158
159 def test_get_changesets_includes_start_changeset(self):
160 second_id = self.repo.revisions[1]
161 changesets = list(self.repo.get_changesets(start=second_id))
162 self.assertEqual(changesets[0].raw_id, second_id)
163
164 def test_get_changesets_respects_end(self):
165 second_id = self.repo.revisions[1]
166 changesets = list(self.repo.get_changesets(end=second_id))
167 self.assertEqual(changesets[-1].raw_id, second_id)
168 self.assertEqual(len(changesets), 2)
169
170 def test_get_changesets_numerical_id_respects_end(self):
171 second_id = 1
172 changesets = list(self.repo.get_changesets(end=second_id))
173 self.assertEqual(changesets.index(changesets[-1]), second_id)
174 self.assertEqual(len(changesets), 2)
175
176 def test_get_changesets_respects_both_start_and_end(self):
177 second_id = self.repo.revisions[1]
178 third_id = self.repo.revisions[2]
179 changesets = list(self.repo.get_changesets(start=second_id,
180 end=third_id))
181 self.assertEqual(len(changesets), 2)
182
183 def test_get_changesets_numerical_id_respects_both_start_and_end(self):
184 changesets = list(self.repo.get_changesets(start=2, end=3))
185 self.assertEqual(len(changesets), 2)
186
187 def test_get_changesets_includes_end_changeset(self):
188 second_id = self.repo.revisions[1]
189 changesets = list(self.repo.get_changesets(end=second_id))
190 self.assertEqual(changesets[-1].raw_id, second_id)
191
192 def test_get_changesets_respects_start_date(self):
193 start_date = datetime.datetime(2010, 2, 1)
194 for cs in self.repo.get_changesets(start_date=start_date):
195 self.assertGreaterEqual(cs.date, start_date)
196
197 def test_get_changesets_respects_end_date(self):
198 end_date = datetime.datetime(2010, 2, 1)
199 for cs in self.repo.get_changesets(end_date=end_date):
200 self.assertLessEqual(cs.date, end_date)
201
202 def test_get_changesets_respects_reverse(self):
203 changesets_id_list = [cs.raw_id for cs in
204 self.repo.get_changesets(reverse=True)]
205 self.assertItemsEqual(changesets_id_list, reversed(self.repo.revisions))
206
207 def test_get_filenodes_generator(self):
208 tip = self.repo.get_changeset()
209 filepaths = [node.path for node in tip.get_filenodes_generator()]
210 self.assertItemsEqual(filepaths, ['file_%d.txt' % x for x in xrange(5)])
211
212 def test_size(self):
213 tip = self.repo.get_changeset()
214 size = 5 * len('Foobar N') # Size of 5 files
215 self.assertEqual(tip.size, size)
216
217 def test_author(self):
218 tip = self.repo.get_changeset()
219 self.assertEqual(tip.author, u'Joe Doe <joe.doe@example.com>')
220
221 def test_author_name(self):
222 tip = self.repo.get_changeset()
223 self.assertEqual(tip.author_name, u'Joe Doe')
224
225 def test_author_email(self):
226 tip = self.repo.get_changeset()
227 self.assertEqual(tip.author_email, u'joe.doe@example.com')
228
229 def test_get_changesets_raise_changesetdoesnotexist_for_wrong_start(self):
230 with self.assertRaises(ChangesetDoesNotExistError):
231 list(self.repo.get_changesets(start='foobar'))
232
233 def test_get_changesets_raise_changesetdoesnotexist_for_wrong_end(self):
234 with self.assertRaises(ChangesetDoesNotExistError):
235 list(self.repo.get_changesets(end='foobar'))
236
237 def test_get_changesets_raise_branchdoesnotexist_for_wrong_branch_name(self):
238 with self.assertRaises(BranchDoesNotExistError):
239 list(self.repo.get_changesets(branch_name='foobar'))
240
241 def test_get_changesets_raise_repositoryerror_for_wrong_start_end(self):
242 start = self.repo.revisions[-1]
243 end = self.repo.revisions[0]
244 with self.assertRaises(RepositoryError):
245 list(self.repo.get_changesets(start=start, end=end))
246
247 def test_get_changesets_numerical_id_reversed(self):
248 with self.assertRaises(RepositoryError):
249 [x for x in self.repo.get_changesets(start=3, end=2)]
250
251 def test_get_changesets_numerical_id_respects_both_start_and_end_last(self):
252 with self.assertRaises(RepositoryError):
253 last = len(self.repo.revisions)
254 list(self.repo.get_changesets(start=last-1, end=last-2))
255
256 def test_get_changesets_numerical_id_last_zero_error(self):
257 with self.assertRaises(RepositoryError):
258 last = len(self.repo.revisions)
259 list(self.repo.get_changesets(start=last-1, end=0))
260
261
262 class ChangesetsChangesTestCaseMixin(BackendTestMixin):
263 recreate_repo_per_test = False
264
265 @classmethod
266 def _get_commits(cls):
267 return [
268 {
269 'message': u'Initial',
270 'author': u'Joe Doe <joe.doe@example.com>',
271 'date': datetime.datetime(2010, 1, 1, 20),
272 'added': [
273 FileNode('foo/bar', content='foo'),
274 FileNode('foobar', content='foo'),
275 FileNode('qwe', content='foo'),
276 ],
277 },
278 {
279 'message': u'Massive changes',
280 'author': u'Joe Doe <joe.doe@example.com>',
281 'date': datetime.datetime(2010, 1, 1, 22),
282 'added': [FileNode('fallout', content='War never changes')],
283 'changed': [
284 FileNode('foo/bar', content='baz'),
285 FileNode('foobar', content='baz'),
286 ],
287 'removed': [FileNode('qwe')],
288 },
289 ]
290
291 def test_initial_commit(self):
292 changeset = self.repo.get_changeset(0)
293 self.assertItemsEqual(changeset.added, [
294 changeset.get_node('foo/bar'),
295 changeset.get_node('foobar'),
296 changeset.get_node('qwe'),
297 ])
298 self.assertItemsEqual(changeset.changed, [])
299 self.assertItemsEqual(changeset.removed, [])
300
301 def test_head_added(self):
302 changeset = self.repo.get_changeset()
303 self.assertItemsEqual(changeset.added, [
304 changeset.get_node('fallout'),
305 ])
306 self.assertItemsEqual(changeset.changed, [
307 changeset.get_node('foo/bar'),
308 changeset.get_node('foobar'),
309 ])
310 self.assertEqual(len(changeset.removed), 1)
311 self.assertEqual(list(changeset.removed)[0].path, 'qwe')
312
313
314 # For each backend create test case class
315 for alias in SCM_TESTS:
316 attrs = {
317 'backend_alias': alias,
318 }
319 # tests with additional commits
320 cls_name = ''.join(('%s changesets with commits test' % alias).title().split())
321 bases = (ChangesetsWithCommitsTestCaseixin, unittest.TestCase)
322 globals()[cls_name] = type(cls_name, bases, attrs)
323
324 # tests without additional commits
325 cls_name = ''.join(('%s changesets test' % alias).title().split())
326 bases = (ChangesetsTestCaseMixin, unittest.TestCase)
327 globals()[cls_name] = type(cls_name, bases, attrs)
328
329 # tests changes
330 cls_name = ''.join(('%s changesets changes test' % alias).title().split())
331 bases = (ChangesetsChangesTestCaseMixin, unittest.TestCase)
332 globals()[cls_name] = type(cls_name, bases, attrs)
333
334
335 if __name__ == '__main__':
336 unittest.main()
@@ -0,0 +1,49 b''
1 # encoding: utf8
2
3 from __future__ import with_statement
4
5 import datetime
6 from rhodecode.lib.vcs.nodes import FileNode
7 from rhodecode.lib.vcs.utils.compat import unittest
8 from test_inmemchangesets import BackendBaseTestCase
9 from conf import SCM_TESTS
10
11
12 class FileNodeUnicodePathTestsMixin(object):
13
14 fname = 'Δ…Ε›Γ°Δ…Δ™Ε‚Δ…Δ‡.txt'
15 ufname = (fname).decode('utf-8')
16
17 def get_commits(self):
18 self.nodes = [
19 FileNode(self.fname, content='Foobar'),
20 ]
21
22 commits = [
23 {
24 'message': 'Initial commit',
25 'author': 'Joe Doe <joe.doe@example.com>',
26 'date': datetime.datetime(2010, 1, 1, 20),
27 'added': self.nodes,
28 },
29 ]
30 return commits
31
32 def test_filenode_path(self):
33 node = self.tip.get_node(self.fname)
34 unode = self.tip.get_node(self.ufname)
35 self.assertEqual(node, unode)
36
37
38 for alias in SCM_TESTS:
39 attrs = {
40 'backend_alias': alias,
41 }
42 cls_name = ''.join(('%s file node unicode path test' % alias).title()
43 .split())
44 bases = (FileNodeUnicodePathTestsMixin, BackendBaseTestCase)
45 globals()[cls_name] = type(cls_name, bases, attrs)
46
47
48 if __name__ == '__main__':
49 unittest.main()
@@ -0,0 +1,44 b''
1 from __future__ import with_statement
2
3 import datetime
4 from base import BackendTestMixin
5 from conf import SCM_TESTS
6 from rhodecode.lib.vcs.nodes import FileNode
7 from rhodecode.lib.vcs.utils.compat import unittest
8
9
10 class GetitemTestCaseMixin(BackendTestMixin):
11
12 @classmethod
13 def _get_commits(cls):
14 start_date = datetime.datetime(2010, 1, 1, 20)
15 for x in xrange(5):
16 yield {
17 'message': 'Commit %d' % x,
18 'author': 'Joe Doe <joe.doe@example.com>',
19 'date': start_date + datetime.timedelta(hours=12 * x),
20 'added': [
21 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
22 ],
23 }
24
25 def test__getitem__last_item_is_tip(self):
26 self.assertEqual(self.repo[-1], self.repo.get_changeset())
27
28 def test__getitem__returns_correct_items(self):
29 changesets = [self.repo[x] for x in xrange(len(self.repo.revisions))]
30 self.assertEqual(changesets, list(self.repo.get_changesets()))
31
32
33 # For each backend create test case class
34 for alias in SCM_TESTS:
35 attrs = {
36 'backend_alias': alias,
37 }
38 cls_name = ''.join(('%s getitem test' % alias).title().split())
39 bases = (GetitemTestCaseMixin, unittest.TestCase)
40 globals()[cls_name] = type(cls_name, bases, attrs)
41
42
43 if __name__ == '__main__':
44 unittest.main()
@@ -0,0 +1,56 b''
1 from __future__ import with_statement
2
3 import datetime
4 from base import BackendTestMixin
5 from conf import SCM_TESTS
6 from rhodecode.lib.vcs.nodes import FileNode
7 from rhodecode.lib.vcs.utils.compat import unittest
8
9
10 class GetsliceTestCaseMixin(BackendTestMixin):
11
12 @classmethod
13 def _get_commits(cls):
14 start_date = datetime.datetime(2010, 1, 1, 20)
15 for x in xrange(5):
16 yield {
17 'message': 'Commit %d' % x,
18 'author': 'Joe Doe <joe.doe@example.com>',
19 'date': start_date + datetime.timedelta(hours=12 * x),
20 'added': [
21 FileNode('file_%d.txt' % x, content='Foobar %d' % x),
22 ],
23 }
24
25 def test__getslice__last_item_is_tip(self):
26 self.assertEqual(list(self.repo[-1:])[0], self.repo.get_changeset())
27
28 def test__getslice__respects_start_index(self):
29 self.assertEqual(list(self.repo[2:]),
30 [self.repo.get_changeset(rev) for rev in self.repo.revisions[2:]])
31
32 def test__getslice__respects_negative_start_index(self):
33 self.assertEqual(list(self.repo[-2:]),
34 [self.repo.get_changeset(rev) for rev in self.repo.revisions[-2:]])
35
36 def test__getslice__respects_end_index(self):
37 self.assertEqual(list(self.repo[:2]),
38 [self.repo.get_changeset(rev) for rev in self.repo.revisions[:2]])
39
40 def test__getslice__respects_negative_end_index(self):
41 self.assertEqual(list(self.repo[:-2]),
42 [self.repo.get_changeset(rev) for rev in self.repo.revisions[:-2]])
43
44
45 # For each backend create test case class
46 for alias in SCM_TESTS:
47 attrs = {
48 'backend_alias': alias,
49 }
50 cls_name = ''.join(('%s getslice test' % alias).title().split())
51 bases = (GetsliceTestCaseMixin, unittest.TestCase)
52 globals()[cls_name] = type(cls_name, bases, attrs)
53
54
55 if __name__ == '__main__':
56 unittest.main()
This diff has been collapsed as it changes many lines, (702 lines changed) Show them Hide them
@@ -0,0 +1,702 b''
1 from __future__ import with_statement
2
3 import os
4 import mock
5 import datetime
6 from rhodecode.lib.vcs.backends.git import GitRepository, GitChangeset
7 from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
8 from rhodecode.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState
9 from rhodecode.lib.vcs.utils.compat import unittest
10 from rhodecode.tests.vcs.base import BackendTestMixin
11 from conf import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir
12
13
14 class GitRepositoryTest(unittest.TestCase):
15
16 def __check_for_existing_repo(self):
17 if os.path.exists(TEST_GIT_REPO_CLONE):
18 self.fail('Cannot test git clone repo as location %s already '
19 'exists. You should manually remove it first.'
20 % TEST_GIT_REPO_CLONE)
21
22 def setUp(self):
23 self.repo = GitRepository(TEST_GIT_REPO)
24
25 def test_wrong_repo_path(self):
26 wrong_repo_path = '/tmp/errorrepo'
27 self.assertRaises(RepositoryError, GitRepository, wrong_repo_path)
28
29 def test_repo_clone(self):
30 self.__check_for_existing_repo()
31 repo = GitRepository(TEST_GIT_REPO)
32 repo_clone = GitRepository(TEST_GIT_REPO_CLONE,
33 src_url=TEST_GIT_REPO, create=True, update_after_clone=True)
34 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
35 # Checking hashes of changesets should be enough
36 for changeset in repo.get_changesets():
37 raw_id = changeset.raw_id
38 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
39
40 def test_repo_clone_without_create(self):
41 self.assertRaises(RepositoryError, GitRepository,
42 TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO)
43
44 def test_repo_clone_with_update(self):
45 repo = GitRepository(TEST_GIT_REPO)
46 clone_path = TEST_GIT_REPO_CLONE + '_with_update'
47 repo_clone = GitRepository(clone_path,
48 create=True, src_url=TEST_GIT_REPO, update_after_clone=True)
49 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
50
51 #check if current workdir was updated
52 fpath = os.path.join(clone_path, 'MANIFEST.in')
53 self.assertEqual(True, os.path.isfile(fpath),
54 'Repo was cloned and updated but file %s could not be found'
55 % fpath)
56
57 def test_repo_clone_without_update(self):
58 repo = GitRepository(TEST_GIT_REPO)
59 clone_path = TEST_GIT_REPO_CLONE + '_without_update'
60 repo_clone = GitRepository(clone_path,
61 create=True, src_url=TEST_GIT_REPO, update_after_clone=False)
62 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
63 #check if current workdir was *NOT* updated
64 fpath = os.path.join(clone_path, 'MANIFEST.in')
65 # Make sure it's not bare repo
66 self.assertFalse(repo_clone._repo.bare)
67 self.assertEqual(False, os.path.isfile(fpath),
68 'Repo was cloned and updated but file %s was found'
69 % fpath)
70
71 def test_repo_clone_into_bare_repo(self):
72 repo = GitRepository(TEST_GIT_REPO)
73 clone_path = TEST_GIT_REPO_CLONE + '_bare.git'
74 repo_clone = GitRepository(clone_path, create=True,
75 src_url=repo.path, bare=True)
76 self.assertTrue(repo_clone._repo.bare)
77
78 def test_create_repo_is_not_bare_by_default(self):
79 repo = GitRepository(get_new_dir('not-bare-by-default'), create=True)
80 self.assertFalse(repo._repo.bare)
81
82 def test_create_bare_repo(self):
83 repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True)
84 self.assertTrue(repo._repo.bare)
85
86 def test_revisions(self):
87 # there are 112 revisions (by now)
88 # so we can assume they would be available from now on
89 subset = set([
90 'c1214f7e79e02fc37156ff215cd71275450cffc3',
91 '38b5fe81f109cb111f549bfe9bb6b267e10bc557',
92 'fa6600f6848800641328adbf7811fd2372c02ab2',
93 '102607b09cdd60e2793929c4f90478be29f85a17',
94 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
95 '2d1028c054665b962fa3d307adfc923ddd528038',
96 'd7e0d30fbcae12c90680eb095a4f5f02505ce501',
97 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
98 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
99 '8430a588b43b5d6da365400117c89400326e7992',
100 'd955cd312c17b02143c04fa1099a352b04368118',
101 'f67b87e5c629c2ee0ba58f85197e423ff28d735b',
102 'add63e382e4aabc9e1afdc4bdc24506c269b7618',
103 'f298fe1189f1b69779a4423f40b48edf92a703fc',
104 'bd9b619eb41994cac43d67cf4ccc8399c1125808',
105 '6e125e7c890379446e98980d8ed60fba87d0f6d1',
106 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd',
107 '0b05e4ed56c802098dfc813cbe779b2f49e92500',
108 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
109 '45223f8f114c64bf4d6f853e3c35a369a6305520',
110 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
111 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68',
112 '27d48942240f5b91dfda77accd2caac94708cc7d',
113 '622f0eb0bafd619d2560c26f80f09e3b0b0d78af',
114 'e686b958768ee96af8029fe19c6050b1a8dd3b2b'])
115 self.assertTrue(subset.issubset(set(self.repo.revisions)))
116
117
118
119 def test_slicing(self):
120 #4 1 5 10 95
121 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
122 (10, 20, 10), (5, 100, 95)]:
123 revs = list(self.repo[sfrom:sto])
124 self.assertEqual(len(revs), size)
125 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
126 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
127
128
129 def test_branches(self):
130 # TODO: Need more tests here
131 # Removed (those are 'remotes' branches for cloned repo)
132 #self.assertTrue('master' in self.repo.branches)
133 #self.assertTrue('gittree' in self.repo.branches)
134 #self.assertTrue('web-branch' in self.repo.branches)
135 for name, id in self.repo.branches.items():
136 self.assertTrue(isinstance(
137 self.repo.get_changeset(id), GitChangeset))
138
139 def test_tags(self):
140 # TODO: Need more tests here
141 self.assertTrue('v0.1.1' in self.repo.tags)
142 self.assertTrue('v0.1.2' in self.repo.tags)
143 for name, id in self.repo.tags.items():
144 self.assertTrue(isinstance(
145 self.repo.get_changeset(id), GitChangeset))
146
147 def _test_single_changeset_cache(self, revision):
148 chset = self.repo.get_changeset(revision)
149 self.assertTrue(revision in self.repo.changesets)
150 self.assertTrue(chset is self.repo.changesets[revision])
151
152 def test_initial_changeset(self):
153 id = self.repo.revisions[0]
154 init_chset = self.repo.get_changeset(id)
155 self.assertEqual(init_chset.message, 'initial import\n')
156 self.assertEqual(init_chset.author,
157 'Marcin Kuzminski <marcin@python-blog.com>')
158 for path in ('vcs/__init__.py',
159 'vcs/backends/BaseRepository.py',
160 'vcs/backends/__init__.py'):
161 self.assertTrue(isinstance(init_chset.get_node(path), FileNode))
162 for path in ('', 'vcs', 'vcs/backends'):
163 self.assertTrue(isinstance(init_chset.get_node(path), DirNode))
164
165 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
166
167 node = init_chset.get_node('vcs/')
168 self.assertTrue(hasattr(node, 'kind'))
169 self.assertEqual(node.kind, NodeKind.DIR)
170
171 node = init_chset.get_node('vcs')
172 self.assertTrue(hasattr(node, 'kind'))
173 self.assertEqual(node.kind, NodeKind.DIR)
174
175 node = init_chset.get_node('vcs/__init__.py')
176 self.assertTrue(hasattr(node, 'kind'))
177 self.assertEqual(node.kind, NodeKind.FILE)
178
179 def test_not_existing_changeset(self):
180 self.assertRaises(RepositoryError, self.repo.get_changeset,
181 'f' * 40)
182
183 def test_changeset10(self):
184
185 chset10 = self.repo.get_changeset(self.repo.revisions[9])
186 README = """===
187 VCS
188 ===
189
190 Various Version Control System management abstraction layer for Python.
191
192 Introduction
193 ------------
194
195 TODO: To be written...
196
197 """
198 node = chset10.get_node('README.rst')
199 self.assertEqual(node.kind, NodeKind.FILE)
200 self.assertEqual(node.content, README)
201
202
203 class GitChangesetTest(unittest.TestCase):
204
205 def setUp(self):
206 self.repo = GitRepository(TEST_GIT_REPO)
207
208 def test_default_changeset(self):
209 tip = self.repo.get_changeset()
210 self.assertEqual(tip, self.repo.get_changeset(None))
211 self.assertEqual(tip, self.repo.get_changeset('tip'))
212
213 def test_root_node(self):
214 tip = self.repo.get_changeset()
215 self.assertTrue(tip.root is tip.get_node(''))
216
217 def test_lazy_fetch(self):
218 """
219 Test if changeset's nodes expands and are cached as we walk through
220 the revision. This test is somewhat hard to write as order of tests
221 is a key here. Written by running command after command in a shell.
222 """
223 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
224 self.assertTrue(hex in self.repo.revisions)
225 chset = self.repo.get_changeset(hex)
226 self.assertTrue(len(chset.nodes) == 0)
227 root = chset.root
228 self.assertTrue(len(chset.nodes) == 1)
229 self.assertTrue(len(root.nodes) == 8)
230 # accessing root.nodes updates chset.nodes
231 self.assertTrue(len(chset.nodes) == 9)
232
233 docs = root.get_node('docs')
234 # we haven't yet accessed anything new as docs dir was already cached
235 self.assertTrue(len(chset.nodes) == 9)
236 self.assertTrue(len(docs.nodes) == 8)
237 # accessing docs.nodes updates chset.nodes
238 self.assertTrue(len(chset.nodes) == 17)
239
240 self.assertTrue(docs is chset.get_node('docs'))
241 self.assertTrue(docs is root.nodes[0])
242 self.assertTrue(docs is root.dirs[0])
243 self.assertTrue(docs is chset.get_node('docs'))
244
245 def test_nodes_with_changeset(self):
246 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
247 chset = self.repo.get_changeset(hex)
248 root = chset.root
249 docs = root.get_node('docs')
250 self.assertTrue(docs is chset.get_node('docs'))
251 api = docs.get_node('api')
252 self.assertTrue(api is chset.get_node('docs/api'))
253 index = api.get_node('index.rst')
254 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
255 self.assertTrue(index is chset.get_node('docs')\
256 .get_node('api')\
257 .get_node('index.rst'))
258
259 def test_branch_and_tags(self):
260 '''
261 rev0 = self.repo.revisions[0]
262 chset0 = self.repo.get_changeset(rev0)
263 self.assertEqual(chset0.branch, 'master')
264 self.assertEqual(chset0.tags, [])
265
266 rev10 = self.repo.revisions[10]
267 chset10 = self.repo.get_changeset(rev10)
268 self.assertEqual(chset10.branch, 'master')
269 self.assertEqual(chset10.tags, [])
270
271 rev44 = self.repo.revisions[44]
272 chset44 = self.repo.get_changeset(rev44)
273 self.assertEqual(chset44.branch, 'web-branch')
274
275 tip = self.repo.get_changeset('tip')
276 self.assertTrue('tip' in tip.tags)
277 '''
278 # Those tests would fail - branches are now going
279 # to be changed at main API in order to support git backend
280 pass
281
282 def _test_slices(self, limit, offset):
283 count = self.repo.count()
284 changesets = self.repo.get_changesets(limit=limit, offset=offset)
285 idx = 0
286 for changeset in changesets:
287 rev = offset + idx
288 idx += 1
289 rev_id = self.repo.revisions[rev]
290 if idx > limit:
291 self.fail("Exceeded limit already (getting revision %s, "
292 "there are %s total revisions, offset=%s, limit=%s)"
293 % (rev_id, count, offset, limit))
294 self.assertEqual(changeset, self.repo.get_changeset(rev_id))
295 result = list(self.repo.get_changesets(limit=limit, offset=offset))
296 start = offset
297 end = limit and offset + limit or None
298 sliced = list(self.repo[start:end])
299 self.failUnlessEqual(result, sliced,
300 msg="Comparison failed for limit=%s, offset=%s"
301 "(get_changeset returned: %s and sliced: %s"
302 % (limit, offset, result, sliced))
303
304 def _test_file_size(self, revision, path, size):
305 node = self.repo.get_changeset(revision).get_node(path)
306 self.assertTrue(node.is_file())
307 self.assertEqual(node.size, size)
308
309 def test_file_size(self):
310 to_check = (
311 ('c1214f7e79e02fc37156ff215cd71275450cffc3',
312 'vcs/backends/BaseRepository.py', 502),
313 ('d7e0d30fbcae12c90680eb095a4f5f02505ce501',
314 'vcs/backends/hg.py', 854),
315 ('6e125e7c890379446e98980d8ed60fba87d0f6d1',
316 'setup.py', 1068),
317
318 ('d955cd312c17b02143c04fa1099a352b04368118',
319 'vcs/backends/base.py', 2921),
320 ('ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
321 'vcs/backends/base.py', 3936),
322 ('f50f42baeed5af6518ef4b0cb2f1423f3851a941',
323 'vcs/backends/base.py', 6189),
324 )
325 for revision, path, size in to_check:
326 self._test_file_size(revision, path, size)
327
328 def test_file_history(self):
329 # we can only check if those revisions are present in the history
330 # as we cannot update this test every time file is changed
331 files = {
332 'setup.py': [
333 '54386793436c938cff89326944d4c2702340037d',
334 '51d254f0ecf5df2ce50c0b115741f4cf13985dab',
335 '998ed409c795fec2012b1c0ca054d99888b22090',
336 '5e0eb4c47f56564395f76333f319d26c79e2fb09',
337 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
338 '7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e',
339 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
340 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
341 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
342 ],
343 'vcs/nodes.py': [
344 '33fa3223355104431402a888fa77a4e9956feb3e',
345 'fa014c12c26d10ba682fadb78f2a11c24c8118e1',
346 'e686b958768ee96af8029fe19c6050b1a8dd3b2b',
347 'ab5721ca0a081f26bf43d9051e615af2cc99952f',
348 'c877b68d18e792a66b7f4c529ea02c8f80801542',
349 '4313566d2e417cb382948f8d9d7c765330356054',
350 '6c2303a793671e807d1cfc70134c9ca0767d98c2',
351 '54386793436c938cff89326944d4c2702340037d',
352 '54000345d2e78b03a99d561399e8e548de3f3203',
353 '1c6b3677b37ea064cb4b51714d8f7498f93f4b2b',
354 '2d03ca750a44440fb5ea8b751176d1f36f8e8f46',
355 '2a08b128c206db48c2f0b8f70df060e6db0ae4f8',
356 '30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b',
357 'ac71e9503c2ca95542839af0ce7b64011b72ea7c',
358 '12669288fd13adba2a9b7dd5b870cc23ffab92d2',
359 '5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382',
360 '12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5',
361 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
362 'f50f42baeed5af6518ef4b0cb2f1423f3851a941',
363 'd7e390a45f6aa96f04f5e7f583ad4f867431aa25',
364 'f15c21f97864b4f071cddfbf2750ec2e23859414',
365 'e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade',
366 'ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b',
367 '84dec09632a4458f79f50ddbbd155506c460b4f9',
368 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
369 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
370 '3bf1c5868e570e39569d094f922d33ced2fa3b2b',
371 'b8d04012574729d2c29886e53b1a43ef16dd00a1',
372 '6970b057cffe4aab0a792aa634c89f4bebf01441',
373 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
374 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
375 ],
376 'vcs/backends/git.py': [
377 '4cf116ad5a457530381135e2f4c453e68a1b0105',
378 '9a751d84d8e9408e736329767387f41b36935153',
379 'cb681fb539c3faaedbcdf5ca71ca413425c18f01',
380 '428f81bb652bcba8d631bce926e8834ff49bdcc6',
381 '180ab15aebf26f98f714d8c68715e0f05fa6e1c7',
382 '2b8e07312a2e89e92b90426ab97f349f4bce2a3a',
383 '50e08c506174d8645a4bb517dd122ac946a0f3bf',
384 '54000345d2e78b03a99d561399e8e548de3f3203',
385 ],
386 }
387 for path, revs in files.items():
388 node = self.repo.get_changeset(revs[0]).get_node(path)
389 node_revs = [chset.raw_id for chset in node.history]
390 self.assertTrue(set(revs).issubset(set(node_revs)),
391 "We assumed that %s is subset of revisions for which file %s "
392 "has been changed, and history of that node returned: %s"
393 % (revs, path, node_revs))
394
395 def test_file_annotate(self):
396 files = {
397 'vcs/backends/__init__.py': {
398 'c1214f7e79e02fc37156ff215cd71275450cffc3': {
399 'lines_no': 1,
400 'changesets': [
401 'c1214f7e79e02fc37156ff215cd71275450cffc3',
402 ],
403 },
404 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647': {
405 'lines_no': 21,
406 'changesets': [
407 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
408 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
409 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
410 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
411 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
412 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
413 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
414 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
415 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
416 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
417 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
418 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
419 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
420 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
421 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
422 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
423 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
424 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
425 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
426 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
427 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
428 ],
429 },
430 'e29b67bd158580fc90fc5e9111240b90e6e86064': {
431 'lines_no': 32,
432 'changesets': [
433 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
434 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
435 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
436 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
437 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
438 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
439 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
440 '54000345d2e78b03a99d561399e8e548de3f3203',
441 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
442 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
443 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
444 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
445 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
446 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
447 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
448 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
449 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
450 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
451 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
452 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
453 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
454 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
455 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
456 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
457 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
458 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
459 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
460 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
461 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
462 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
463 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
464 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
465 ],
466 },
467 },
468 }
469
470 for fname, revision_dict in files.items():
471 for rev, data in revision_dict.items():
472 cs = self.repo.get_changeset(rev)
473 ann = cs.get_file_annotate(fname)
474
475 l1 = [x[1].raw_id for x in ann]
476 l2 = files[fname][rev]['changesets']
477 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev %s"
478 "from annotation list should match each other, "
479 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
480
481 def test_files_state(self):
482 """
483 Tests state of FileNodes.
484 """
485 node = self.repo\
486 .get_changeset('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0')\
487 .get_node('vcs/utils/diffs.py')
488 self.assertTrue(node.state, NodeState.ADDED)
489 self.assertTrue(node.added)
490 self.assertFalse(node.changed)
491 self.assertFalse(node.not_changed)
492 self.assertFalse(node.removed)
493
494 node = self.repo\
495 .get_changeset('33fa3223355104431402a888fa77a4e9956feb3e')\
496 .get_node('.hgignore')
497 self.assertTrue(node.state, NodeState.CHANGED)
498 self.assertFalse(node.added)
499 self.assertTrue(node.changed)
500 self.assertFalse(node.not_changed)
501 self.assertFalse(node.removed)
502
503 node = self.repo\
504 .get_changeset('e29b67bd158580fc90fc5e9111240b90e6e86064')\
505 .get_node('setup.py')
506 self.assertTrue(node.state, NodeState.NOT_CHANGED)
507 self.assertFalse(node.added)
508 self.assertFalse(node.changed)
509 self.assertTrue(node.not_changed)
510 self.assertFalse(node.removed)
511
512 # If node has REMOVED state then trying to fetch it would raise
513 # ChangesetError exception
514 chset = self.repo.get_changeset(
515 'fa6600f6848800641328adbf7811fd2372c02ab2')
516 path = 'vcs/backends/BaseRepository.py'
517 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
518 # but it would be one of ``removed`` (changeset's attribute)
519 self.assertTrue(path in [rf.path for rf in chset.removed])
520
521 chset = self.repo.get_changeset(
522 '54386793436c938cff89326944d4c2702340037d')
523 changed = ['setup.py', 'tests/test_nodes.py', 'vcs/backends/hg.py',
524 'vcs/nodes.py']
525 self.assertEqual(set(changed), set([f.path for f in chset.changed]))
526
527 def test_commit_message_is_unicode(self):
528 for cs in self.repo:
529 self.assertEqual(type(cs.message), unicode)
530
531 def test_changeset_author_is_unicode(self):
532 for cs in self.repo:
533 self.assertEqual(type(cs.author), unicode)
534
535 def test_repo_files_content_is_unicode(self):
536 changeset = self.repo.get_changeset()
537 for node in changeset.get_node('/'):
538 if node.is_file():
539 self.assertEqual(type(node.content), unicode)
540
541 def test_wrong_path(self):
542 # There is 'setup.py' in the root dir but not there:
543 path = 'foo/bar/setup.py'
544 tip = self.repo.get_changeset()
545 self.assertRaises(VCSError, tip.get_node, path)
546
547 def test_author_email(self):
548 self.assertEqual('marcin@python-blog.com',
549 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3')\
550 .author_email)
551 self.assertEqual('lukasz.balcerzak@python-center.pl',
552 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b')\
553 .author_email)
554 self.assertEqual('none@none',
555 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992')\
556 .author_email)
557
558 def test_author_username(self):
559 self.assertEqual('Marcin Kuzminski',
560 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3')\
561 .author_name)
562 self.assertEqual('Lukasz Balcerzak',
563 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b')\
564 .author_name)
565 self.assertEqual('marcink',
566 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992')\
567 .author_name)
568
569
570 class GitSpecificTest(unittest.TestCase):
571
572 def test_error_is_raised_for_added_if_diff_name_status_is_wrong(self):
573 repo = mock.MagicMock()
574 changeset = GitChangeset(repo, 'foobar')
575 changeset._diff_name_status = 'foobar'
576 with self.assertRaises(VCSError):
577 changeset.added
578
579 def test_error_is_raised_for_changed_if_diff_name_status_is_wrong(self):
580 repo = mock.MagicMock()
581 changeset = GitChangeset(repo, 'foobar')
582 changeset._diff_name_status = 'foobar'
583 with self.assertRaises(VCSError):
584 changeset.added
585
586 def test_error_is_raised_for_removed_if_diff_name_status_is_wrong(self):
587 repo = mock.MagicMock()
588 changeset = GitChangeset(repo, 'foobar')
589 changeset._diff_name_status = 'foobar'
590 with self.assertRaises(VCSError):
591 changeset.added
592
593
594 class GitSpecificWithRepoTest(BackendTestMixin, unittest.TestCase):
595 backend_alias = 'git'
596
597 @classmethod
598 def _get_commits(cls):
599 return [
600 {
601 'message': 'Initial',
602 'author': 'Joe Doe <joe.doe@example.com>',
603 'date': datetime.datetime(2010, 1, 1, 20),
604 'added': [
605 FileNode('foobar/static/js/admin/base.js', content='base'),
606 FileNode('foobar/static/admin', content='admin',
607 mode=0120000), # this is a link
608 FileNode('foo', content='foo'),
609 ],
610 },
611 {
612 'message': 'Second',
613 'author': 'Joe Doe <joe.doe@example.com>',
614 'date': datetime.datetime(2010, 1, 1, 22),
615 'added': [
616 FileNode('foo2', content='foo2'),
617 ],
618 },
619 ]
620
621 def test_paths_slow_traversing(self):
622 cs = self.repo.get_changeset()
623 self.assertEqual(cs.get_node('foobar').get_node('static').get_node('js')
624 .get_node('admin').get_node('base.js').content, 'base')
625
626 def test_paths_fast_traversing(self):
627 cs = self.repo.get_changeset()
628 self.assertEqual(cs.get_node('foobar/static/js/admin/base.js').content,
629 'base')
630
631 def test_workdir_get_branch(self):
632 self.repo.run_git_command('checkout -b production')
633 # Regression test: one of following would fail if we don't check
634 # .git/HEAD file
635 self.repo.run_git_command('checkout production')
636 self.assertEqual(self.repo.workdir.get_branch(), 'production')
637 self.repo.run_git_command('checkout master')
638 self.assertEqual(self.repo.workdir.get_branch(), 'master')
639
640 def test_get_diff_runs_git_command_with_hashes(self):
641 self.repo.run_git_command = mock.Mock(return_value=['', ''])
642 self.repo.get_diff(0, 1)
643 self.repo.run_git_command.assert_called_once_with('diff -U%s %s %s' %
644 (3, self.repo._get_revision(0), self.repo._get_revision(1)))
645
646 def test_get_diff_runs_git_command_with_str_hashes(self):
647 self.repo.run_git_command = mock.Mock(return_value=['', ''])
648 self.repo.get_diff(self.repo.EMPTY_CHANGESET, 1)
649 self.repo.run_git_command.assert_called_once_with('show -U%s %s' %
650 (3, self.repo._get_revision(1)))
651
652 def test_get_diff_runs_git_command_with_path_if_its_given(self):
653 self.repo.run_git_command = mock.Mock(return_value=['', ''])
654 self.repo.get_diff(0, 1, 'foo')
655 self.repo.run_git_command.assert_called_once_with('diff -U%s %s %s -- "foo"'
656 % (3, self.repo._get_revision(0), self.repo._get_revision(1)))
657
658
659 class GitRegressionTest(BackendTestMixin, unittest.TestCase):
660 backend_alias = 'git'
661
662 @classmethod
663 def _get_commits(cls):
664 return [
665 {
666 'message': 'Initial',
667 'author': 'Joe Doe <joe.doe@example.com>',
668 'date': datetime.datetime(2010, 1, 1, 20),
669 'added': [
670 FileNode('bot/__init__.py', content='base'),
671 FileNode('bot/templates/404.html', content='base'),
672 FileNode('bot/templates/500.html', content='base'),
673 ],
674 },
675 {
676 'message': 'Second',
677 'author': 'Joe Doe <joe.doe@example.com>',
678 'date': datetime.datetime(2010, 1, 1, 22),
679 'added': [
680 FileNode('bot/build/migrations/1.py', content='foo2'),
681 FileNode('bot/build/migrations/2.py', content='foo2'),
682 FileNode('bot/build/static/templates/f.html', content='foo2'),
683 FileNode('bot/build/static/templates/f1.html', content='foo2'),
684 FileNode('bot/build/templates/err.html', content='foo2'),
685 FileNode('bot/build/templates/err2.html', content='foo2'),
686 ],
687 },
688 ]
689
690 def test_similar_paths(self):
691 cs = self.repo.get_changeset()
692 paths = lambda *n:[x.path for x in n]
693 self.assertEqual(paths(*cs.get_nodes('bot')), ['bot/build', 'bot/templates', 'bot/__init__.py'])
694 self.assertEqual(paths(*cs.get_nodes('bot/build')), ['bot/build/migrations', 'bot/build/static', 'bot/build/templates'])
695 self.assertEqual(paths(*cs.get_nodes('bot/build/static')), ['bot/build/static/templates'])
696 # this get_nodes below causes troubles !
697 self.assertEqual(paths(*cs.get_nodes('bot/build/static/templates')), ['bot/build/static/templates/f.html', 'bot/build/static/templates/f1.html'])
698 self.assertEqual(paths(*cs.get_nodes('bot/build/templates')), ['bot/build/templates/err.html', 'bot/build/templates/err2.html'])
699 self.assertEqual(paths(*cs.get_nodes('bot/templates/')), ['bot/templates/404.html', 'bot/templates/500.html'])
700
701 if __name__ == '__main__':
702 unittest.main()
This diff has been collapsed as it changes many lines, (557 lines changed) Show them Hide them
@@ -0,0 +1,557 b''
1 from __future__ import with_statement
2
3 import os
4 from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset
5 from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
6 from rhodecode.lib.vcs.nodes import NodeKind, NodeState
7 from conf import PACKAGE_DIR, TEST_HG_REPO, TEST_HG_REPO_CLONE, \
8 TEST_HG_REPO_PULL
9 from rhodecode.lib.vcs.utils.compat import unittest
10
11
12 # Use only clean mercurial's ui
13 import mercurial.scmutil
14 mercurial.scmutil.rcpath()
15 if mercurial.scmutil._rcpath:
16 mercurial.scmutil._rcpath = mercurial.scmutil._rcpath[:1]
17
18
19 class MercurialRepositoryTest(unittest.TestCase):
20
21 def __check_for_existing_repo(self):
22 if os.path.exists(TEST_HG_REPO_CLONE):
23 self.fail('Cannot test mercurial clone repo as location %s already '
24 'exists. You should manually remove it first.'
25 % TEST_HG_REPO_CLONE)
26
27 def setUp(self):
28 self.repo = MercurialRepository(TEST_HG_REPO)
29
30 def test_wrong_repo_path(self):
31 wrong_repo_path = '/tmp/errorrepo'
32 self.assertRaises(RepositoryError, MercurialRepository, wrong_repo_path)
33
34 def test_unicode_path_repo(self):
35 self.assertRaises(VCSError,lambda:MercurialRepository(u'iShouldFail'))
36
37 def test_repo_clone(self):
38 self.__check_for_existing_repo()
39 repo = MercurialRepository(TEST_HG_REPO)
40 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
41 src_url=TEST_HG_REPO, update_after_clone=True)
42 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
43 # Checking hashes of changesets should be enough
44 for changeset in repo.get_changesets():
45 raw_id = changeset.raw_id
46 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
47
48 def test_repo_clone_with_update(self):
49 repo = MercurialRepository(TEST_HG_REPO)
50 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_w_update',
51 src_url=TEST_HG_REPO, update_after_clone=True)
52 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
53
54 #check if current workdir was updated
55 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
56 + '_w_update',
57 'MANIFEST.in')), True,)
58
59 def test_repo_clone_without_update(self):
60 repo = MercurialRepository(TEST_HG_REPO)
61 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_wo_update',
62 src_url=TEST_HG_REPO, update_after_clone=False)
63 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
64 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
65 + '_wo_update',
66 'MANIFEST.in')), False,)
67
68 def test_pull(self):
69 if os.path.exists(TEST_HG_REPO_PULL):
70 self.fail('Cannot test mercurial pull command as location %s '
71 'already exists. You should manually remove it first'
72 % TEST_HG_REPO_PULL)
73 repo_new = MercurialRepository(TEST_HG_REPO_PULL, create=True)
74 self.assertTrue(len(self.repo.revisions) > len(repo_new.revisions))
75
76 repo_new.pull(self.repo.path)
77 repo_new = MercurialRepository(TEST_HG_REPO_PULL)
78 self.assertTrue(len(self.repo.revisions) == len(repo_new.revisions))
79
80 def test_revisions(self):
81 # there are 21 revisions at bitbucket now
82 # so we can assume they would be available from now on
83 subset = set(['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
84 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
85 '6cba7170863a2411822803fa77a0a264f1310b35',
86 '56349e29c2af3ac913b28bde9a2c6154436e615b',
87 '2dda4e345facb0ccff1a191052dd1606dba6781d',
88 '6fff84722075f1607a30f436523403845f84cd9e',
89 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
90 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
91 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
92 'be90031137367893f1c406e0a8683010fd115b79',
93 'db8e58be770518cbb2b1cdfa69146e47cd481481',
94 '84478366594b424af694a6c784cb991a16b87c21',
95 '17f8e105dddb9f339600389c6dc7175d395a535c',
96 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
97 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
98 '786facd2c61deb9cf91e9534735124fb8fc11842',
99 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
100 'aa6a0de05b7612707db567078e130a6cd114a9a7',
101 'eada5a770da98ab0dd7325e29d00e0714f228d09'
102 ])
103 self.assertTrue(subset.issubset(set(self.repo.revisions)))
104
105
106 # check if we have the proper order of revisions
107 org = ['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
108 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
109 '6cba7170863a2411822803fa77a0a264f1310b35',
110 '56349e29c2af3ac913b28bde9a2c6154436e615b',
111 '2dda4e345facb0ccff1a191052dd1606dba6781d',
112 '6fff84722075f1607a30f436523403845f84cd9e',
113 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
114 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
115 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
116 'be90031137367893f1c406e0a8683010fd115b79',
117 'db8e58be770518cbb2b1cdfa69146e47cd481481',
118 '84478366594b424af694a6c784cb991a16b87c21',
119 '17f8e105dddb9f339600389c6dc7175d395a535c',
120 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
121 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
122 '786facd2c61deb9cf91e9534735124fb8fc11842',
123 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
124 'aa6a0de05b7612707db567078e130a6cd114a9a7',
125 'eada5a770da98ab0dd7325e29d00e0714f228d09',
126 '2c1885c735575ca478bf9e17b0029dca68824458',
127 'd9bcd465040bf869799b09ad732c04e0eea99fe9',
128 '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7',
129 '4fb8326d78e5120da2c7468dcf7098997be385da',
130 '62b4a097164940bd66030c4db51687f3ec035eed',
131 '536c1a19428381cfea92ac44985304f6a8049569',
132 '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4',
133 '9bb326a04ae5d98d437dece54be04f830cf1edd9',
134 'f8940bcb890a98c4702319fbe36db75ea309b475',
135 'ff5ab059786ebc7411e559a2cc309dfae3625a3b',
136 '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08',
137 'ee87846a61c12153b51543bf860e1026c6d3dcba', ]
138 self.assertEqual(org, self.repo.revisions[:31])
139
140 def test_iter_slice(self):
141 sliced = list(self.repo[:10])
142 itered = list(self.repo)[:10]
143 self.assertEqual(sliced, itered)
144
145 def test_slicing(self):
146 #4 1 5 10 95
147 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
148 (10, 20, 10), (5, 100, 95)]:
149 revs = list(self.repo[sfrom:sto])
150 self.assertEqual(len(revs), size)
151 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
152 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
153
154 def test_branches(self):
155 # TODO: Need more tests here
156
157 #active branches
158 self.assertTrue('default' in self.repo.branches)
159 self.assertTrue('git' in self.repo.branches)
160
161 # closed
162 self.assertTrue('web' in self.repo._get_branches(closed=True))
163
164 for name, id in self.repo.branches.items():
165 self.assertTrue(isinstance(
166 self.repo.get_changeset(id), MercurialChangeset))
167
168 def test_tip_in_tags(self):
169 # tip is always a tag
170 self.assertIn('tip', self.repo.tags)
171
172 def test_tip_changeset_in_tags(self):
173 tip = self.repo.get_changeset()
174 self.assertEqual(self.repo.tags['tip'], tip.raw_id)
175
176 def test_initial_changeset(self):
177
178 init_chset = self.repo.get_changeset(0)
179 self.assertEqual(init_chset.message, 'initial import')
180 self.assertEqual(init_chset.author,
181 'Marcin Kuzminski <marcin@python-blog.com>')
182 self.assertEqual(sorted(init_chset._file_paths),
183 sorted([
184 'vcs/__init__.py',
185 'vcs/backends/BaseRepository.py',
186 'vcs/backends/__init__.py',
187 ])
188 )
189 self.assertEqual(sorted(init_chset._dir_paths),
190 sorted(['', 'vcs', 'vcs/backends']))
191
192 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
193
194 node = init_chset.get_node('vcs/')
195 self.assertTrue(hasattr(node, 'kind'))
196 self.assertEqual(node.kind, NodeKind.DIR)
197
198 node = init_chset.get_node('vcs')
199 self.assertTrue(hasattr(node, 'kind'))
200 self.assertEqual(node.kind, NodeKind.DIR)
201
202 node = init_chset.get_node('vcs/__init__.py')
203 self.assertTrue(hasattr(node, 'kind'))
204 self.assertEqual(node.kind, NodeKind.FILE)
205
206 def test_not_existing_changeset(self):
207 #rawid
208 self.assertRaises(RepositoryError, self.repo.get_changeset,
209 'abcd' * 10)
210 #shortid
211 self.assertRaises(RepositoryError, self.repo.get_changeset,
212 'erro' * 4)
213 #numeric
214 self.assertRaises(RepositoryError, self.repo.get_changeset,
215 self.repo.count() + 1)
216
217
218 # Small chance we ever get to this one
219 revision = pow(2, 30)
220 self.assertRaises(RepositoryError, self.repo.get_changeset, revision)
221
222 def test_changeset10(self):
223
224 chset10 = self.repo.get_changeset(10)
225 README = """===
226 VCS
227 ===
228
229 Various Version Control System management abstraction layer for Python.
230
231 Introduction
232 ------------
233
234 TODO: To be written...
235
236 """
237 node = chset10.get_node('README.rst')
238 self.assertEqual(node.kind, NodeKind.FILE)
239 self.assertEqual(node.content, README)
240
241
242 class MercurialChangesetTest(unittest.TestCase):
243
244 def setUp(self):
245 self.repo = MercurialRepository(TEST_HG_REPO)
246
247 def _test_equality(self, changeset):
248 revision = changeset.revision
249 self.assertEqual(changeset, self.repo.get_changeset(revision))
250
251 def test_equality(self):
252 self.setUp()
253 revs = [0, 10, 20]
254 changesets = [self.repo.get_changeset(rev) for rev in revs]
255 for changeset in changesets:
256 self._test_equality(changeset)
257
258 def test_default_changeset(self):
259 tip = self.repo.get_changeset('tip')
260 self.assertEqual(tip, self.repo.get_changeset())
261 self.assertEqual(tip, self.repo.get_changeset(revision=None))
262 self.assertEqual(tip, list(self.repo[-1:])[0])
263
264 def test_root_node(self):
265 tip = self.repo.get_changeset('tip')
266 self.assertTrue(tip.root is tip.get_node(''))
267
268 def test_lazy_fetch(self):
269 """
270 Test if changeset's nodes expands and are cached as we walk through
271 the revision. This test is somewhat hard to write as order of tests
272 is a key here. Written by running command after command in a shell.
273 """
274 self.setUp()
275 chset = self.repo.get_changeset(45)
276 self.assertTrue(len(chset.nodes) == 0)
277 root = chset.root
278 self.assertTrue(len(chset.nodes) == 1)
279 self.assertTrue(len(root.nodes) == 8)
280 # accessing root.nodes updates chset.nodes
281 self.assertTrue(len(chset.nodes) == 9)
282
283 docs = root.get_node('docs')
284 # we haven't yet accessed anything new as docs dir was already cached
285 self.assertTrue(len(chset.nodes) == 9)
286 self.assertTrue(len(docs.nodes) == 8)
287 # accessing docs.nodes updates chset.nodes
288 self.assertTrue(len(chset.nodes) == 17)
289
290 self.assertTrue(docs is chset.get_node('docs'))
291 self.assertTrue(docs is root.nodes[0])
292 self.assertTrue(docs is root.dirs[0])
293 self.assertTrue(docs is chset.get_node('docs'))
294
295 def test_nodes_with_changeset(self):
296 self.setUp()
297 chset = self.repo.get_changeset(45)
298 root = chset.root
299 docs = root.get_node('docs')
300 self.assertTrue(docs is chset.get_node('docs'))
301 api = docs.get_node('api')
302 self.assertTrue(api is chset.get_node('docs/api'))
303 index = api.get_node('index.rst')
304 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
305 self.assertTrue(index is chset.get_node('docs')\
306 .get_node('api')\
307 .get_node('index.rst'))
308
309 def test_branch_and_tags(self):
310 chset0 = self.repo.get_changeset(0)
311 self.assertEqual(chset0.branch, 'default')
312 self.assertEqual(chset0.tags, [])
313
314 chset10 = self.repo.get_changeset(10)
315 self.assertEqual(chset10.branch, 'default')
316 self.assertEqual(chset10.tags, [])
317
318 chset44 = self.repo.get_changeset(44)
319 self.assertEqual(chset44.branch, 'web')
320
321 tip = self.repo.get_changeset('tip')
322 self.assertTrue('tip' in tip.tags)
323
324 def _test_file_size(self, revision, path, size):
325 node = self.repo.get_changeset(revision).get_node(path)
326 self.assertTrue(node.is_file())
327 self.assertEqual(node.size, size)
328
329 def test_file_size(self):
330 to_check = (
331 (10, 'setup.py', 1068),
332 (20, 'setup.py', 1106),
333 (60, 'setup.py', 1074),
334
335 (10, 'vcs/backends/base.py', 2921),
336 (20, 'vcs/backends/base.py', 3936),
337 (60, 'vcs/backends/base.py', 6189),
338 )
339 for revision, path, size in to_check:
340 self._test_file_size(revision, path, size)
341
342 def test_file_history(self):
343 # we can only check if those revisions are present in the history
344 # as we cannot update this test every time file is changed
345 files = {
346 'setup.py': [7, 18, 45, 46, 47, 69, 77],
347 'vcs/nodes.py': [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60,
348 61, 73, 76],
349 'vcs/backends/hg.py': [4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23,
350 26, 27, 28, 30, 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47,
351 48, 49, 53, 54, 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79,
352 82],
353 }
354 for path, revs in files.items():
355 tip = self.repo.get_changeset(revs[-1])
356 node = tip.get_node(path)
357 node_revs = [chset.revision for chset in node.history]
358 self.assertTrue(set(revs).issubset(set(node_revs)),
359 "We assumed that %s is subset of revisions for which file %s "
360 "has been changed, and history of that node returned: %s"
361 % (revs, path, node_revs))
362
363 def test_file_annotate(self):
364 files = {
365 'vcs/backends/__init__.py':
366 {89: {'lines_no': 31,
367 'changesets': [32, 32, 61, 32, 32, 37, 32, 32, 32, 44,
368 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
369 32, 32, 32, 32, 37, 32, 37, 37, 32,
370 32, 32]},
371 20: {'lines_no': 1,
372 'changesets': [4]},
373 55: {'lines_no': 31,
374 'changesets': [32, 32, 45, 32, 32, 37, 32, 32, 32, 44,
375 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
376 32, 32, 32, 32, 37, 32, 37, 37, 32,
377 32, 32]}},
378 'vcs/exceptions.py':
379 {89: {'lines_no': 18,
380 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
381 16, 16, 17, 16, 16, 18, 18, 18]},
382 20: {'lines_no': 18,
383 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
384 16, 16, 17, 16, 16, 18, 18, 18]},
385 55: {'lines_no': 18, 'changesets': [16, 16, 16, 16, 16, 16,
386 16, 16, 16, 16, 16, 16,
387 17, 16, 16, 18, 18, 18]}},
388 'MANIFEST.in': {89: {'lines_no': 5,
389 'changesets': [7, 7, 7, 71, 71]},
390 20: {'lines_no': 3,
391 'changesets': [7, 7, 7]},
392 55: {'lines_no': 3,
393 'changesets': [7, 7, 7]}}}
394
395
396 for fname, revision_dict in files.items():
397 for rev, data in revision_dict.items():
398 cs = self.repo.get_changeset(rev)
399 ann = cs.get_file_annotate(fname)
400
401 l1 = [x[1].revision for x in ann]
402 l2 = files[fname][rev]['changesets']
403 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev%s"
404 "from annotation list should match each other,"
405 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
406
407 def test_changeset_state(self):
408 """
409 Tests which files have been added/changed/removed at particular revision
410 """
411
412 # rev 46ad32a4f974:
413 # hg st --rev 46ad32a4f974
414 # changed: 13
415 # added: 20
416 # removed: 1
417 changed = set(['.hgignore'
418 , 'README.rst' , 'docs/conf.py' , 'docs/index.rst' , 'setup.py'
419 , 'tests/test_hg.py' , 'tests/test_nodes.py' , 'vcs/__init__.py'
420 , 'vcs/backends/__init__.py' , 'vcs/backends/base.py'
421 , 'vcs/backends/hg.py' , 'vcs/nodes.py' , 'vcs/utils/__init__.py'])
422
423 added = set(['docs/api/backends/hg.rst'
424 , 'docs/api/backends/index.rst' , 'docs/api/index.rst'
425 , 'docs/api/nodes.rst' , 'docs/api/web/index.rst'
426 , 'docs/api/web/simplevcs.rst' , 'docs/installation.rst'
427 , 'docs/quickstart.rst' , 'setup.cfg' , 'vcs/utils/baseui_config.py'
428 , 'vcs/utils/web.py' , 'vcs/web/__init__.py' , 'vcs/web/exceptions.py'
429 , 'vcs/web/simplevcs/__init__.py' , 'vcs/web/simplevcs/exceptions.py'
430 , 'vcs/web/simplevcs/middleware.py' , 'vcs/web/simplevcs/models.py'
431 , 'vcs/web/simplevcs/settings.py' , 'vcs/web/simplevcs/utils.py'
432 , 'vcs/web/simplevcs/views.py'])
433
434 removed = set(['docs/api.rst'])
435
436 chset64 = self.repo.get_changeset('46ad32a4f974')
437 self.assertEqual(set((node.path for node in chset64.added)), added)
438 self.assertEqual(set((node.path for node in chset64.changed)), changed)
439 self.assertEqual(set((node.path for node in chset64.removed)), removed)
440
441 # rev b090f22d27d6:
442 # hg st --rev b090f22d27d6
443 # changed: 13
444 # added: 20
445 # removed: 1
446 chset88 = self.repo.get_changeset('b090f22d27d6')
447 self.assertEqual(set((node.path for node in chset88.added)), set())
448 self.assertEqual(set((node.path for node in chset88.changed)),
449 set(['.hgignore']))
450 self.assertEqual(set((node.path for node in chset88.removed)), set())
451 #
452 # 85:
453 # added: 2 ['vcs/utils/diffs.py', 'vcs/web/simplevcs/views/diffs.py']
454 # changed: 4 ['vcs/web/simplevcs/models.py', ...]
455 # removed: 1 ['vcs/utils/web.py']
456 chset85 = self.repo.get_changeset(85)
457 self.assertEqual(set((node.path for node in chset85.added)), set([
458 'vcs/utils/diffs.py',
459 'vcs/web/simplevcs/views/diffs.py']))
460 self.assertEqual(set((node.path for node in chset85.changed)), set([
461 'vcs/web/simplevcs/models.py',
462 'vcs/web/simplevcs/utils.py',
463 'vcs/web/simplevcs/views/__init__.py',
464 'vcs/web/simplevcs/views/repository.py',
465 ]))
466 self.assertEqual(set((node.path for node in chset85.removed)),
467 set(['vcs/utils/web.py']))
468
469
470 def test_files_state(self):
471 """
472 Tests state of FileNodes.
473 """
474 chset = self.repo.get_changeset(85)
475 node = chset.get_node('vcs/utils/diffs.py')
476 self.assertTrue(node.state, NodeState.ADDED)
477 self.assertTrue(node.added)
478 self.assertFalse(node.changed)
479 self.assertFalse(node.not_changed)
480 self.assertFalse(node.removed)
481
482 chset = self.repo.get_changeset(88)
483 node = chset.get_node('.hgignore')
484 self.assertTrue(node.state, NodeState.CHANGED)
485 self.assertFalse(node.added)
486 self.assertTrue(node.changed)
487 self.assertFalse(node.not_changed)
488 self.assertFalse(node.removed)
489
490 chset = self.repo.get_changeset(85)
491 node = chset.get_node('setup.py')
492 self.assertTrue(node.state, NodeState.NOT_CHANGED)
493 self.assertFalse(node.added)
494 self.assertFalse(node.changed)
495 self.assertTrue(node.not_changed)
496 self.assertFalse(node.removed)
497
498 # If node has REMOVED state then trying to fetch it would raise
499 # ChangesetError exception
500 chset = self.repo.get_changeset(2)
501 path = 'vcs/backends/BaseRepository.py'
502 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
503 # but it would be one of ``removed`` (changeset's attribute)
504 self.assertTrue(path in [rf.path for rf in chset.removed])
505
506 def test_commit_message_is_unicode(self):
507 for cm in self.repo:
508 self.assertEqual(type(cm.message), unicode)
509
510 def test_changeset_author_is_unicode(self):
511 for cm in self.repo:
512 self.assertEqual(type(cm.author), unicode)
513
514 def test_repo_files_content_is_unicode(self):
515 test_changeset = self.repo.get_changeset(100)
516 for node in test_changeset.get_node('/'):
517 if node.is_file():
518 self.assertEqual(type(node.content), unicode)
519
520 def test_wrong_path(self):
521 # There is 'setup.py' in the root dir but not there:
522 path = 'foo/bar/setup.py'
523 self.assertRaises(VCSError, self.repo.get_changeset().get_node, path)
524
525
526 def test_archival_file(self):
527 #TODO:
528 pass
529
530 def test_archival_as_generator(self):
531 #TODO:
532 pass
533
534 def test_archival_wrong_kind(self):
535 tip = self.repo.get_changeset()
536 self.assertRaises(VCSError, tip.fill_archive, kind='error')
537
538 def test_archival_empty_prefix(self):
539 #TODO:
540 pass
541
542
543 def test_author_email(self):
544 self.assertEqual('marcin@python-blog.com',
545 self.repo.get_changeset('b986218ba1c9').author_email)
546 self.assertEqual('lukasz.balcerzak@python-center.pl',
547 self.repo.get_changeset('3803844fdbd3').author_email)
548 self.assertEqual('',
549 self.repo.get_changeset('84478366594b').author_email)
550
551 def test_author_username(self):
552 self.assertEqual('Marcin Kuzminski',
553 self.repo.get_changeset('b986218ba1c9').author_name)
554 self.assertEqual('Lukasz Balcerzak',
555 self.repo.get_changeset('3803844fdbd3').author_name)
556 self.assertEqual('marcink',
557 self.repo.get_changeset('84478366594b').author_name)
@@ -0,0 +1,340 b''
1 """
2 Tests so called "in memory changesets" commit API of vcs.
3 """
4 from __future__ import with_statement
5
6 from rhodecode.lib import vcs
7 import time
8 import datetime
9 from conf import SCM_TESTS, get_new_dir
10 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
11 from rhodecode.lib.vcs.exceptions import NodeAlreadyAddedError
12 from rhodecode.lib.vcs.exceptions import NodeAlreadyExistsError
13 from rhodecode.lib.vcs.exceptions import NodeAlreadyRemovedError
14 from rhodecode.lib.vcs.exceptions import NodeAlreadyChangedError
15 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
16 from rhodecode.lib.vcs.exceptions import NodeNotChangedError
17 from rhodecode.lib.vcs.nodes import DirNode
18 from rhodecode.lib.vcs.nodes import FileNode
19 from rhodecode.lib.vcs.utils.compat import unittest
20
21
22 class InMemoryChangesetTestMixin(object):
23 """
24 This is a backend independent test case class which should be created
25 with ``type`` method.
26
27 It is required to set following attributes at subclass:
28
29 - ``backend_alias``: alias of used backend (see ``vcs.BACKENDS``)
30 - ``repo_path``: path to the repository which would be created for set of
31 tests
32 """
33
34 def get_backend(self):
35 return vcs.get_backend(self.backend_alias)
36
37 def setUp(self):
38 Backend = self.get_backend()
39 self.repo_path = get_new_dir(str(time.time()))
40 self.repo = Backend(self.repo_path, create=True)
41 self.imc = self.repo.in_memory_changeset
42 self.nodes = [
43 FileNode('foobar', content='Foo & bar'),
44 FileNode('foobar2', content='Foo & bar, doubled!'),
45 FileNode('foo bar with spaces', content=''),
46 FileNode('foo/bar/baz', content='Inside'),
47 ]
48
49 def test_add(self):
50 rev_count = len(self.repo.revisions)
51 to_add = [FileNode(node.path, content=node.content)
52 for node in self.nodes]
53 for node in to_add:
54 self.imc.add(node)
55 message = u'Added: %s' % ', '.join((node.path for node in self.nodes))
56 author = unicode(self.__class__)
57 changeset = self.imc.commit(message=message, author=author)
58
59 newtip = self.repo.get_changeset()
60 self.assertEqual(changeset, newtip)
61 self.assertEqual(rev_count + 1, len(self.repo.revisions))
62 self.assertEqual(newtip.message, message)
63 self.assertEqual(newtip.author, author)
64 self.assertTrue(not any((self.imc.added, self.imc.changed,
65 self.imc.removed)))
66 for node in to_add:
67 self.assertEqual(newtip.get_node(node.path).content, node.content)
68
69 def test_add_in_bulk(self):
70 rev_count = len(self.repo.revisions)
71 to_add = [FileNode(node.path, content=node.content)
72 for node in self.nodes]
73 self.imc.add(*to_add)
74 message = u'Added: %s' % ', '.join((node.path for node in self.nodes))
75 author = unicode(self.__class__)
76 changeset = self.imc.commit(message=message, author=author)
77
78 newtip = self.repo.get_changeset()
79 self.assertEqual(changeset, newtip)
80 self.assertEqual(rev_count + 1, len(self.repo.revisions))
81 self.assertEqual(newtip.message, message)
82 self.assertEqual(newtip.author, author)
83 self.assertTrue(not any((self.imc.added, self.imc.changed,
84 self.imc.removed)))
85 for node in to_add:
86 self.assertEqual(newtip.get_node(node.path).content, node.content)
87
88 def test_add_actually_adds_all_nodes_at_second_commit_too(self):
89 self.imc.add(FileNode('foo/bar/image.png', content='\0'))
90 self.imc.add(FileNode('foo/README.txt', content='readme!'))
91 changeset = self.imc.commit(u'Initial', u'joe.doe@example.com')
92 self.assertTrue(isinstance(changeset.get_node('foo'), DirNode))
93 self.assertTrue(isinstance(changeset.get_node('foo/bar'), DirNode))
94 self.assertEqual(changeset.get_node('foo/bar/image.png').content, '\0')
95 self.assertEqual(changeset.get_node('foo/README.txt').content, 'readme!')
96
97 # commit some more files again
98 to_add = [
99 FileNode('foo/bar/foobaz/bar', content='foo'),
100 FileNode('foo/bar/another/bar', content='foo'),
101 FileNode('foo/baz.txt', content='foo'),
102 FileNode('foobar/foobaz/file', content='foo'),
103 FileNode('foobar/barbaz', content='foo'),
104 ]
105 self.imc.add(*to_add)
106 changeset = self.imc.commit(u'Another', u'joe.doe@example.com')
107 self.assertEqual(changeset.get_node('foo/bar/foobaz/bar').content, 'foo')
108 self.assertEqual(changeset.get_node('foo/bar/another/bar').content, 'foo')
109 self.assertEqual(changeset.get_node('foo/baz.txt').content, 'foo')
110 self.assertEqual(changeset.get_node('foobar/foobaz/file').content, 'foo')
111 self.assertEqual(changeset.get_node('foobar/barbaz').content, 'foo')
112
113 def test_add_raise_already_added(self):
114 node = FileNode('foobar', content='baz')
115 self.imc.add(node)
116 self.assertRaises(NodeAlreadyAddedError, self.imc.add, node)
117
118 def test_check_integrity_raise_already_exist(self):
119 node = FileNode('foobar', content='baz')
120 self.imc.add(node)
121 self.imc.commit(message=u'Added foobar', author=unicode(self))
122 self.imc.add(node)
123 self.assertRaises(NodeAlreadyExistsError, self.imc.commit,
124 message='new message',
125 author=str(self))
126
127 def test_change(self):
128 self.imc.add(FileNode('foo/bar/baz', content='foo'))
129 self.imc.add(FileNode('foo/fbar', content='foobar'))
130 tip = self.imc.commit(u'Initial', u'joe.doe@example.com')
131
132 # Change node's content
133 node = FileNode('foo/bar/baz', content='My **changed** content')
134 self.imc.change(node)
135 self.imc.commit(u'Changed %s' % node.path, u'joe.doe@example.com')
136
137 newtip = self.repo.get_changeset()
138 self.assertNotEqual(tip, newtip)
139 self.assertNotEqual(tip.id, newtip.id)
140 self.assertEqual(newtip.get_node('foo/bar/baz').content,
141 'My **changed** content')
142
143 def test_change_raise_empty_repository(self):
144 node = FileNode('foobar')
145 self.assertRaises(EmptyRepositoryError, self.imc.change, node)
146
147 def test_check_integrity_change_raise_node_does_not_exist(self):
148 node = FileNode('foobar', content='baz')
149 self.imc.add(node)
150 self.imc.commit(message=u'Added foobar', author=unicode(self))
151 node = FileNode('not-foobar', content='')
152 self.imc.change(node)
153 self.assertRaises(NodeDoesNotExistError, self.imc.commit,
154 message='Changed not existing node',
155 author=str(self))
156
157 def test_change_raise_node_already_changed(self):
158 node = FileNode('foobar', content='baz')
159 self.imc.add(node)
160 self.imc.commit(message=u'Added foobar', author=unicode(self))
161 node = FileNode('foobar', content='more baz')
162 self.imc.change(node)
163 self.assertRaises(NodeAlreadyChangedError, self.imc.change, node)
164
165 def test_check_integrity_change_raise_node_not_changed(self):
166 self.test_add() # Performs first commit
167
168 node = FileNode(self.nodes[0].path, content=self.nodes[0].content)
169 self.imc.change(node)
170 self.assertRaises(NodeNotChangedError, self.imc.commit,
171 message=u'Trying to mark node as changed without touching it',
172 author=unicode(self))
173
174 def test_change_raise_node_already_removed(self):
175 node = FileNode('foobar', content='baz')
176 self.imc.add(node)
177 self.imc.commit(message=u'Added foobar', author=unicode(self))
178 self.imc.remove(FileNode('foobar'))
179 self.assertRaises(NodeAlreadyRemovedError, self.imc.change, node)
180
181 def test_remove(self):
182 self.test_add() # Performs first commit
183
184 tip = self.repo.get_changeset()
185 node = self.nodes[0]
186 self.assertEqual(node.content, tip.get_node(node.path).content)
187 self.imc.remove(node)
188 self.imc.commit(message=u'Removed %s' % node.path, author=unicode(self))
189
190 newtip = self.repo.get_changeset()
191 self.assertNotEqual(tip, newtip)
192 self.assertNotEqual(tip.id, newtip.id)
193 self.assertRaises(NodeDoesNotExistError, newtip.get_node, node.path)
194
195 def test_remove_last_file_from_directory(self):
196 node = FileNode('omg/qwe/foo/bar', content='foobar')
197 self.imc.add(node)
198 self.imc.commit(u'added', u'joe doe')
199
200 self.imc.remove(node)
201 tip = self.imc.commit(u'removed', u'joe doe')
202 self.assertRaises(NodeDoesNotExistError, tip.get_node, 'omg/qwe/foo/bar')
203
204 def test_remove_raise_node_does_not_exist(self):
205 self.imc.remove(self.nodes[0])
206 self.assertRaises(NodeDoesNotExistError, self.imc.commit,
207 message='Trying to remove node at empty repository',
208 author=str(self))
209
210 def test_check_integrity_remove_raise_node_does_not_exist(self):
211 self.test_add() # Performs first commit
212
213 node = FileNode('no-such-file')
214 self.imc.remove(node)
215 self.assertRaises(NodeDoesNotExistError, self.imc.commit,
216 message=u'Trying to remove not existing node',
217 author=unicode(self))
218
219 def test_remove_raise_node_already_removed(self):
220 self.test_add() # Performs first commit
221
222 node = FileNode(self.nodes[0].path)
223 self.imc.remove(node)
224 self.assertRaises(NodeAlreadyRemovedError, self.imc.remove, node)
225
226 def test_remove_raise_node_already_changed(self):
227 self.test_add() # Performs first commit
228
229 node = FileNode(self.nodes[0].path, content='Bending time')
230 self.imc.change(node)
231 self.assertRaises(NodeAlreadyChangedError, self.imc.remove, node)
232
233 def test_reset(self):
234 self.imc.add(FileNode('foo', content='bar'))
235 #self.imc.change(FileNode('baz', content='new'))
236 #self.imc.remove(FileNode('qwe'))
237 self.imc.reset()
238 self.assertTrue(not any((self.imc.added, self.imc.changed,
239 self.imc.removed)))
240
241 def test_multiple_commits(self):
242 N = 3 # number of commits to perform
243 last = None
244 for x in xrange(N):
245 fname = 'file%s' % str(x).rjust(5, '0')
246 content = 'foobar\n' * x
247 node = FileNode(fname, content=content)
248 self.imc.add(node)
249 commit = self.imc.commit(u"Commit no. %s" % (x + 1), author=u'vcs')
250 self.assertTrue(last != commit)
251 last = commit
252
253 # Check commit number for same repo
254 self.assertEqual(len(self.repo.revisions), N)
255
256 # Check commit number for recreated repo
257 backend = self.get_backend()
258 repo = backend(self.repo_path)
259 self.assertEqual(len(repo.revisions), N)
260
261 def test_date_attr(self):
262 node = FileNode('foobar.txt', content='Foobared!')
263 self.imc.add(node)
264 date = datetime.datetime(1985, 1, 30, 1, 45)
265 commit = self.imc.commit(u"Committed at time when I was born ;-)",
266 author=u'lb', date=date)
267
268 self.assertEqual(commit.date, date)
269
270
271 class BackendBaseTestCase(unittest.TestCase):
272 """
273 Base test class for tests which requires repository.
274 """
275 backend_alias = 'hg'
276 commits = [
277 {
278 'message': 'Initial commit',
279 'author': 'Joe Doe <joe.doe@example.com>',
280 'date': datetime.datetime(2010, 1, 1, 20),
281 'added': [
282 FileNode('foobar', content='Foobar'),
283 FileNode('foobar2', content='Foobar II'),
284 FileNode('foo/bar/baz', content='baz here!'),
285 ],
286 },
287 ]
288
289 def get_backend(self):
290 return vcs.get_backend(self.backend_alias)
291
292 def get_commits(self):
293 """
294 Returns list of commits which builds repository for each tests.
295 """
296 if hasattr(self, 'commits'):
297 return self.commits
298
299 def get_new_repo_path(self):
300 """
301 Returns newly created repository's directory.
302 """
303 backend = self.get_backend()
304 key = '%s-%s' % (backend.alias, str(time.time()))
305 repo_path = get_new_dir(key)
306 return repo_path
307
308 def setUp(self):
309 Backend = self.get_backend()
310 self.backend_class = Backend
311 self.repo_path = self.get_new_repo_path()
312 self.repo = Backend(self.repo_path, create=True)
313 self.imc = self.repo.in_memory_changeset
314
315 for commit in self.get_commits():
316 for node in commit.get('added', []):
317 self.imc.add(FileNode(node.path, content=node.content))
318 for node in commit.get('changed', []):
319 self.imc.change(FileNode(node.path, content=node.content))
320 for node in commit.get('removed', []):
321 self.imc.remove(FileNode(node.path))
322 self.imc.commit(message=unicode(commit['message']),
323 author=unicode(commit['author']),
324 date=commit['date'])
325
326 self.tip = self.repo.get_changeset()
327
328
329 # For each backend create test case class
330 for alias in SCM_TESTS:
331 attrs = {
332 'backend_alias': alias,
333 }
334 cls_name = ''.join(('%s in memory changeset test' % alias).title().split())
335 bases = (InMemoryChangesetTestMixin, unittest.TestCase)
336 globals()[cls_name] = type(cls_name, bases, attrs)
337
338
339 if __name__ == '__main__':
340 unittest.main()
@@ -0,0 +1,183 b''
1 from __future__ import with_statement
2
3 import stat
4 from rhodecode.lib.vcs.nodes import DirNode
5 from rhodecode.lib.vcs.nodes import FileNode
6 from rhodecode.lib.vcs.nodes import Node
7 from rhodecode.lib.vcs.nodes import NodeError
8 from rhodecode.lib.vcs.nodes import NodeKind
9 from rhodecode.lib.vcs.utils.compat import unittest
10
11
12 class NodeBasicTest(unittest.TestCase):
13
14 def test_init(self):
15 """
16 Cannot innitialize Node objects with path with slash at the beginning.
17 """
18 wrong_paths = (
19 '/foo',
20 '/foo/bar'
21 )
22 for path in wrong_paths:
23 self.assertRaises(NodeError, Node, path, NodeKind.FILE)
24
25 wrong_paths = (
26 '/foo/',
27 '/foo/bar/'
28 )
29 for path in wrong_paths:
30 self.assertRaises(NodeError, Node, path, NodeKind.DIR)
31
32 def test_name(self):
33 node = Node('', NodeKind.DIR)
34 self.assertEqual(node.name, '')
35
36 node = Node('path', NodeKind.FILE)
37 self.assertEqual(node.name, 'path')
38
39 node = Node('path/', NodeKind.DIR)
40 self.assertEqual(node.name, 'path')
41
42 node = Node('some/path', NodeKind.FILE)
43 self.assertEqual(node.name, 'path')
44
45 node = Node('some/path/', NodeKind.DIR)
46 self.assertEqual(node.name, 'path')
47
48 def test_root_node(self):
49 self.assertRaises(NodeError, Node, '', NodeKind.FILE)
50
51 def test_kind_setter(self):
52 node = Node('', NodeKind.DIR)
53 self.assertRaises(NodeError, setattr, node, 'kind', NodeKind.FILE)
54
55 def _test_parent_path(self, node_path, expected_parent_path):
56 """
57 Tests if node's parent path are properly computed.
58 """
59 node = Node(node_path, NodeKind.DIR)
60 parent_path = node.get_parent_path()
61 self.assertTrue(parent_path.endswith('/') or \
62 node.is_root() and parent_path == '')
63 self.assertEqual(parent_path, expected_parent_path,
64 "Node's path is %r and parent path is %r but should be %r"
65 % (node.path, parent_path, expected_parent_path))
66
67 def test_parent_path(self):
68 test_paths = (
69 # (node_path, expected_parent_path)
70 ('', ''),
71 ('some/path/', 'some/'),
72 ('some/longer/path/', 'some/longer/'),
73 )
74 for node_path, expected_parent_path in test_paths:
75 self._test_parent_path(node_path, expected_parent_path)
76
77 '''
78 def _test_trailing_slash(self, path):
79 if not path.endswith('/'):
80 self.fail("Trailing slash tests needs paths to end with slash")
81 for kind in NodeKind.FILE, NodeKind.DIR:
82 self.assertRaises(NodeError, Node, path=path, kind=kind)
83
84 def test_trailing_slash(self):
85 for path in ('/', 'foo/', 'foo/bar/', 'foo/bar/biz/'):
86 self._test_trailing_slash(path)
87 '''
88
89 def test_is_file(self):
90 node = Node('any', NodeKind.FILE)
91 self.assertTrue(node.is_file())
92
93 node = FileNode('any')
94 self.assertTrue(node.is_file())
95 self.assertRaises(AttributeError, getattr, node, 'nodes')
96
97 def test_is_dir(self):
98 node = Node('any_dir', NodeKind.DIR)
99 self.assertTrue(node.is_dir())
100
101 node = DirNode('any_dir')
102
103 self.assertTrue(node.is_dir())
104 self.assertRaises(NodeError, getattr, node, 'content')
105
106 def test_dir_node_iter(self):
107 nodes = [
108 DirNode('docs'),
109 DirNode('tests'),
110 FileNode('bar'),
111 FileNode('foo'),
112 FileNode('readme.txt'),
113 FileNode('setup.py'),
114 ]
115 dirnode = DirNode('', nodes=nodes)
116 for node in dirnode:
117 node == dirnode.get_node(node.path)
118
119 def test_node_state(self):
120 """
121 Without link to changeset nodes should raise NodeError.
122 """
123 node = FileNode('anything')
124 self.assertRaises(NodeError, getattr, node, 'state')
125 node = DirNode('anything')
126 self.assertRaises(NodeError, getattr, node, 'state')
127
128 def test_file_node_stat(self):
129 node = FileNode('foobar', 'empty... almost')
130 mode = node.mode # default should be 0100644
131 self.assertTrue(mode & stat.S_IRUSR)
132 self.assertTrue(mode & stat.S_IWUSR)
133 self.assertTrue(mode & stat.S_IRGRP)
134 self.assertTrue(mode & stat.S_IROTH)
135 self.assertFalse(mode & stat.S_IWGRP)
136 self.assertFalse(mode & stat.S_IWOTH)
137 self.assertFalse(mode & stat.S_IXUSR)
138 self.assertFalse(mode & stat.S_IXGRP)
139 self.assertFalse(mode & stat.S_IXOTH)
140
141 def test_file_node_is_executable(self):
142 node = FileNode('foobar', 'empty... almost', mode=0100755)
143 self.assertTrue(node.is_executable())
144
145 node = FileNode('foobar', 'empty... almost', mode=0100500)
146 self.assertTrue(node.is_executable())
147
148 node = FileNode('foobar', 'empty... almost', mode=0100644)
149 self.assertFalse(node.is_executable())
150
151 def test_mimetype(self):
152 py_node = FileNode('test.py')
153 tar_node = FileNode('test.tar.gz')
154
155 ext = 'CustomExtension'
156
157 my_node2 = FileNode('myfile2')
158 my_node2._mimetype = [ext]
159
160 my_node3 = FileNode('myfile3')
161 my_node3._mimetype = [ext,ext]
162
163 self.assertEqual(py_node.mimetype,'text/x-python')
164 self.assertEqual(py_node.get_mimetype(),('text/x-python',None))
165
166 self.assertEqual(tar_node.mimetype,'application/x-tar')
167 self.assertEqual(tar_node.get_mimetype(),('application/x-tar','gzip'))
168
169 self.assertRaises(NodeError,my_node2.get_mimetype)
170
171 self.assertEqual(my_node3.mimetype,ext)
172 self.assertEqual(my_node3.get_mimetype(),[ext,ext])
173
174 class NodeContentTest(unittest.TestCase):
175
176 def test_if_binary(self):
177 data = """\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f??a\x00\x00\x00\x04gAMA\x00\x00\xaf?7\x05\x8a?\x00\x00\x00\x19tEXtSoftware\x00Adobe ImageReadyq?e<\x00\x00\x025IDAT8?\xa5\x93?K\x94Q\x14\x87\x9f\xf7?Q\x1bs4?\x03\x9a\xa8?B\x02\x8b$\x10[U;i\x13?6h?&h[?"\x14j?\xa2M\x7fB\x14F\x9aQ?&\x842?\x0b\x89"\x82??!?\x9c!\x9c2l??{N\x8bW\x9dY\xb4\t/\x1c?=\x9b?}????\xa9*;9!?\x83\x91?[?\\v*?D\x04\'`EpNp\xa2X\'U?pVq"Sw.\x1e?\x08\x01D?jw????\xbc??7{|\x9b?\x89$\x01??W@\x15\x9c\x05q`Lt/\x97?\x94\xa1d?\x18~?\x18?\x18W[%\xb0?\x83??\x14\x88\x8dB?\xa6H\tL\tl\x19>/\x01`\xac\xabx?\x9cl\nx\xb0\x98\x07\x95\x88D$"q[\x19?d\x00(o\n\xa0??\x7f\xb9\xa4?\x1bF\x1f\x8e\xac\xa8?j??eUU}?.?\x9f\x8cE??x\x94??\r\xbdtoJU5"0N\x10U?\x00??V\t\x02\x9f\x81?U?\x00\x9eM\xae2?r\x9b7\x83\x82\x8aP3????.?&"?\xb7ZP \x0c<?O\xa5\t}\xb8?\x99\xa6?\x87?\x1di|/\xa0??0\xbe\x1fp?d&\x1a\xad\x95\x8a\x07?\t*\x10??b:?d?.\x13C\x8a?\x12\xbe\xbf\x8e?{???\x08?\x80\xa7\x13+d\x13>J?\x80\x15T\x95\x9a\x00??S\x8c\r?\xa1\x03\x07?\x96\x9b\xa7\xab=E??\xa4\xb3?\x19q??B\x91=\x8d??k?J\x0bV"??\xf7x?\xa1\x00?\\.\x87\x87???\x02F@D\x99],??\x10#?X\xb7=\xb9\x10?Z\x1by???cI??\x1ag?\x92\xbc?T?t[\x92\x81?<_\x17~\x92\x88?H%?\x10Q\x02\x9f\n\x81qQ\x0bm?\x1bX?\xb1AK\xa6\x9e\xb9?u\xb2?1\xbe|/\x92M@\xa2!F?\xa9>"\r<DT?>\x92\x8e?>\x9a9Qv\x127?a\xac?Y?8?:??]X???9\x80\xb7?u?\x0b#BZ\x8d=\x1d?p\x00\x00\x00\x00IEND\xaeB`\x82"""
178 filenode = FileNode('calendar.png', content=data)
179 self.assertTrue(filenode.is_binary)
180
181
182 if __name__ == '__main__':
183 unittest.main()
@@ -0,0 +1,215 b''
1 from __future__ import with_statement
2 import datetime
3 from base import BackendTestMixin
4 from conf import SCM_TESTS
5 from conf import TEST_USER_CONFIG_FILE
6 from rhodecode.lib.vcs.nodes import FileNode
7 from rhodecode.lib.vcs.utils.compat import unittest
8 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
9
10
11 class RepositoryBaseTest(BackendTestMixin):
12 recreate_repo_per_test = False
13
14 @classmethod
15 def _get_commits(cls):
16 return super(RepositoryBaseTest, cls)._get_commits()[:1]
17
18 def test_get_config_value(self):
19 self.assertEqual(self.repo.get_config_value('universal', 'foo',
20 TEST_USER_CONFIG_FILE), 'bar')
21
22 def test_get_config_value_defaults_to_None(self):
23 self.assertEqual(self.repo.get_config_value('universal', 'nonexist',
24 TEST_USER_CONFIG_FILE), None)
25
26 def test_get_user_name(self):
27 self.assertEqual(self.repo.get_user_name(TEST_USER_CONFIG_FILE),
28 'Foo Bar')
29
30 def test_get_user_email(self):
31 self.assertEqual(self.repo.get_user_email(TEST_USER_CONFIG_FILE),
32 'foo.bar@example.com')
33
34
35
36 class RepositoryGetDiffTest(BackendTestMixin):
37
38 @classmethod
39 def _get_commits(cls):
40 commits = [
41 {
42 'message': 'Initial commit',
43 'author': 'Joe Doe <joe.doe@example.com>',
44 'date': datetime.datetime(2010, 1, 1, 20),
45 'added': [
46 FileNode('foobar', content='foobar'),
47 FileNode('foobar2', content='foobar2'),
48 ],
49 },
50 {
51 'message': 'Changed foobar, added foobar3',
52 'author': 'Jane Doe <jane.doe@example.com>',
53 'date': datetime.datetime(2010, 1, 1, 21),
54 'added': [
55 FileNode('foobar3', content='foobar3'),
56 ],
57 'changed': [
58 FileNode('foobar', 'FOOBAR'),
59 ],
60 },
61 {
62 'message': 'Removed foobar, changed foobar3',
63 'author': 'Jane Doe <jane.doe@example.com>',
64 'date': datetime.datetime(2010, 1, 1, 22),
65 'changed': [
66 FileNode('foobar3', content='FOOBAR\nFOOBAR\nFOOBAR\n'),
67 ],
68 'removed': [FileNode('foobar')],
69 },
70 ]
71 return commits
72
73 def test_raise_for_wrong(self):
74 with self.assertRaises(ChangesetDoesNotExistError):
75 self.repo.get_diff('a' * 40, 'b' * 40)
76
77 class GitRepositoryGetDiffTest(RepositoryGetDiffTest, unittest.TestCase):
78 backend_alias = 'git'
79
80 def test_initial_commit_diff(self):
81 initial_rev = self.repo.revisions[0]
82 self.assertEqual(self.repo.get_diff(self.repo.EMPTY_CHANGESET, initial_rev), '''diff --git a/foobar b/foobar
83 new file mode 100644
84 index 0000000..f6ea049
85 --- /dev/null
86 +++ b/foobar
87 @@ -0,0 +1 @@
88 +foobar
89 \ No newline at end of file
90 diff --git a/foobar2 b/foobar2
91 new file mode 100644
92 index 0000000..e8c9d6b
93 --- /dev/null
94 +++ b/foobar2
95 @@ -0,0 +1 @@
96 +foobar2
97 \ No newline at end of file
98 ''')
99
100 def test_second_changeset_diff(self):
101 revs = self.repo.revisions
102 self.assertEqual(self.repo.get_diff(revs[0], revs[1]), '''diff --git a/foobar b/foobar
103 index f6ea049..389865b 100644
104 --- a/foobar
105 +++ b/foobar
106 @@ -1 +1 @@
107 -foobar
108 \ No newline at end of file
109 +FOOBAR
110 \ No newline at end of file
111 diff --git a/foobar3 b/foobar3
112 new file mode 100644
113 index 0000000..c11c37d
114 --- /dev/null
115 +++ b/foobar3
116 @@ -0,0 +1 @@
117 +foobar3
118 \ No newline at end of file
119 ''')
120
121 def test_third_changeset_diff(self):
122 revs = self.repo.revisions
123 self.assertEqual(self.repo.get_diff(revs[1], revs[2]), '''diff --git a/foobar b/foobar
124 deleted file mode 100644
125 index 389865b..0000000
126 --- a/foobar
127 +++ /dev/null
128 @@ -1 +0,0 @@
129 -FOOBAR
130 \ No newline at end of file
131 diff --git a/foobar3 b/foobar3
132 index c11c37d..f932447 100644
133 --- a/foobar3
134 +++ b/foobar3
135 @@ -1 +1,3 @@
136 -foobar3
137 \ No newline at end of file
138 +FOOBAR
139 +FOOBAR
140 +FOOBAR
141 ''')
142
143
144 class HgRepositoryGetDiffTest(RepositoryGetDiffTest, unittest.TestCase):
145 backend_alias = 'hg'
146
147 def test_initial_commit_diff(self):
148 initial_rev = self.repo.revisions[0]
149 self.assertEqual(self.repo.get_diff(self.repo.EMPTY_CHANGESET, initial_rev), '''diff --git a/foobar b/foobar
150 new file mode 100755
151 --- /dev/null
152 +++ b/foobar
153 @@ -0,0 +1,1 @@
154 +foobar
155 \ No newline at end of file
156 diff --git a/foobar2 b/foobar2
157 new file mode 100755
158 --- /dev/null
159 +++ b/foobar2
160 @@ -0,0 +1,1 @@
161 +foobar2
162 \ No newline at end of file
163 ''')
164
165 def test_second_changeset_diff(self):
166 revs = self.repo.revisions
167 self.assertEqual(self.repo.get_diff(revs[0], revs[1]), '''diff --git a/foobar b/foobar
168 --- a/foobar
169 +++ b/foobar
170 @@ -1,1 +1,1 @@
171 -foobar
172 \ No newline at end of file
173 +FOOBAR
174 \ No newline at end of file
175 diff --git a/foobar3 b/foobar3
176 new file mode 100755
177 --- /dev/null
178 +++ b/foobar3
179 @@ -0,0 +1,1 @@
180 +foobar3
181 \ No newline at end of file
182 ''')
183
184 def test_third_changeset_diff(self):
185 revs = self.repo.revisions
186 self.assertEqual(self.repo.get_diff(revs[1], revs[2]), '''diff --git a/foobar b/foobar
187 deleted file mode 100755
188 --- a/foobar
189 +++ /dev/null
190 @@ -1,1 +0,0 @@
191 -FOOBAR
192 \ No newline at end of file
193 diff --git a/foobar3 b/foobar3
194 --- a/foobar3
195 +++ b/foobar3
196 @@ -1,1 +1,3 @@
197 -foobar3
198 \ No newline at end of file
199 +FOOBAR
200 +FOOBAR
201 +FOOBAR
202 ''')
203
204
205 # For each backend create test case class
206 for alias in SCM_TESTS:
207 attrs = {
208 'backend_alias': alias,
209 }
210 cls_name = alias.capitalize() + RepositoryBaseTest.__name__
211 bases = (RepositoryBaseTest, unittest.TestCase)
212 globals()[cls_name] = type(cls_name, bases, attrs)
213
214 if __name__ == '__main__':
215 unittest.main()
@@ -0,0 +1,61 b''
1 from __future__ import with_statement
2
3 from base import BackendTestMixin
4 from conf import SCM_TESTS
5 from rhodecode.lib.vcs.exceptions import TagAlreadyExistError
6 from rhodecode.lib.vcs.exceptions import TagDoesNotExistError
7 from rhodecode.lib.vcs.utils.compat import unittest
8
9
10 class TagsTestCaseMixin(BackendTestMixin):
11
12 def test_new_tag(self):
13 tip = self.repo.get_changeset()
14 tagsize = len(self.repo.tags)
15 tag = self.repo.tag('last-commit', 'joe', tip.raw_id)
16
17 self.assertEqual(len(self.repo.tags), tagsize + 1)
18 for top, dirs, files in tip.walk():
19 self.assertEqual(top, tag.get_node(top.path))
20
21 def test_tag_already_exist(self):
22 tip = self.repo.get_changeset()
23 self.repo.tag('last-commit', 'joe', tip.raw_id)
24
25 self.assertRaises(TagAlreadyExistError,
26 self.repo.tag, 'last-commit', 'joe', tip.raw_id)
27
28 chset = self.repo.get_changeset(0)
29 self.assertRaises(TagAlreadyExistError,
30 self.repo.tag, 'last-commit', 'jane', chset.raw_id)
31
32 def test_remove_tag(self):
33 tip = self.repo.get_changeset()
34 self.repo.tag('last-commit', 'joe', tip.raw_id)
35 tagsize = len(self.repo.tags)
36
37 self.repo.remove_tag('last-commit', user='evil joe')
38 self.assertEqual(len(self.repo.tags), tagsize - 1)
39
40 def test_remove_tag_which_does_not_exist(self):
41 self.assertRaises(TagDoesNotExistError,
42 self.repo.remove_tag, 'last-commit', user='evil joe')
43
44 def test_name_with_slash(self):
45 self.repo.tag('19/10/11', 'joe')
46 self.assertTrue('19/10/11' in self.repo.tags)
47 self.repo.tag('11', 'joe')
48 self.assertTrue('11' in self.repo.tags)
49
50 # For each backend create test case class
51 for alias in SCM_TESTS:
52 attrs = {
53 'backend_alias': alias,
54 }
55 cls_name = ''.join(('%s tags test' % alias).title().split())
56 bases = (TagsTestCaseMixin, unittest.TestCase)
57 globals()[cls_name] = type(cls_name, bases, attrs)
58
59
60 if __name__ == '__main__':
61 unittest.main()
@@ -0,0 +1,279 b''
1 from __future__ import with_statement
2
3 import os
4 import mock
5 import time
6 import shutil
7 import tempfile
8 import datetime
9 from rhodecode.lib.vcs.utils.compat import unittest
10 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
11 from rhodecode.lib.vcs.utils.helpers import get_dict_for_attrs
12 from rhodecode.lib.vcs.utils.helpers import get_scm
13 from rhodecode.lib.vcs.utils.helpers import get_scms_for_path
14 from rhodecode.lib.vcs.utils.helpers import get_total_seconds
15 from rhodecode.lib.vcs.utils.helpers import parse_changesets
16 from rhodecode.lib.vcs.utils.helpers import parse_datetime
17 from rhodecode.lib.vcs.utils import author_email, author_name
18 from rhodecode.lib.vcs.utils.paths import get_user_home
19 from rhodecode.lib.vcs.exceptions import VCSError
20
21 from conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH
22
23
24 class PathsTest(unittest.TestCase):
25
26 def _test_get_dirs_for_path(self, path, expected):
27 """
28 Tests if get_dirs_for_path returns same as expected.
29 """
30 expected = sorted(expected)
31 result = sorted(get_dirs_for_path(path))
32 self.assertEqual(result, expected,
33 msg="%s != %s which was expected result for path %s"
34 % (result, expected, path))
35
36 def test_get_dirs_for_path(self):
37 path = 'foo/bar/baz/file'
38 paths_and_results = (
39 ('foo/bar/baz/file', ['foo', 'foo/bar', 'foo/bar/baz']),
40 ('foo/bar/', ['foo', 'foo/bar']),
41 ('foo/bar', ['foo']),
42 )
43 for path, expected in paths_and_results:
44 self._test_get_dirs_for_path(path, expected)
45
46
47 def test_get_scm(self):
48 self.assertEqual(('hg', TEST_HG_REPO), get_scm(TEST_HG_REPO))
49 self.assertEqual(('git', TEST_GIT_REPO), get_scm(TEST_GIT_REPO))
50
51 def test_get_two_scms_for_path(self):
52 multialias_repo_path = os.path.join(TEST_TMP_PATH, 'hg-git-repo-2')
53 if os.path.isdir(multialias_repo_path):
54 shutil.rmtree(multialias_repo_path)
55
56 os.mkdir(multialias_repo_path)
57
58 self.assertRaises(VCSError, get_scm, multialias_repo_path)
59
60 def test_get_scm_error_path(self):
61 self.assertRaises(VCSError, get_scm, 'err')
62
63 def test_get_scms_for_path(self):
64 dirpath = tempfile.gettempdir()
65 new = os.path.join(dirpath, 'vcs-scms-for-path-%s' % time.time())
66 os.mkdir(new)
67 self.assertEqual(get_scms_for_path(new), [])
68
69 os.mkdir(os.path.join(new, '.tux'))
70 self.assertEqual(get_scms_for_path(new), [])
71
72 os.mkdir(os.path.join(new, '.git'))
73 self.assertEqual(set(get_scms_for_path(new)), set(['git']))
74
75 os.mkdir(os.path.join(new, '.hg'))
76 self.assertEqual(set(get_scms_for_path(new)), set(['git', 'hg']))
77
78
79 class TestParseChangesets(unittest.TestCase):
80
81 def test_main_is_returned_correctly(self):
82 self.assertEqual(parse_changesets('123456'), {
83 'start': None,
84 'main': '123456',
85 'end': None,
86 })
87
88 def test_start_is_returned_correctly(self):
89 self.assertEqual(parse_changesets('aaabbb..'), {
90 'start': 'aaabbb',
91 'main': None,
92 'end': None,
93 })
94
95 def test_end_is_returned_correctly(self):
96 self.assertEqual(parse_changesets('..cccddd'), {
97 'start': None,
98 'main': None,
99 'end': 'cccddd',
100 })
101
102 def test_that_two_or_three_dots_are_allowed(self):
103 text1 = 'a..b'
104 text2 = 'a...b'
105 self.assertEqual(parse_changesets(text1), parse_changesets(text2))
106
107 def test_that_input_is_stripped_first(self):
108 text1 = 'a..bb'
109 text2 = ' a..bb\t\n\t '
110 self.assertEqual(parse_changesets(text1), parse_changesets(text2))
111
112 def test_that_exception_is_raised(self):
113 text = '123456.789012' # single dot is not recognized
114 with self.assertRaises(ValueError):
115 parse_changesets(text)
116
117 def test_non_alphanumeric_raises_exception(self):
118 with self.assertRaises(ValueError):
119 parse_changesets('aaa@bbb')
120
121
122 class TestParseDatetime(unittest.TestCase):
123
124 def test_datetime_text(self):
125 self.assertEqual(parse_datetime('2010-04-07 21:29:41'),
126 datetime.datetime(2010, 4, 7, 21, 29, 41))
127
128 def test_no_seconds(self):
129 self.assertEqual(parse_datetime('2010-04-07 21:29'),
130 datetime.datetime(2010, 4, 7, 21, 29))
131
132 def test_date_only(self):
133 self.assertEqual(parse_datetime('2010-04-07'),
134 datetime.datetime(2010, 4, 7))
135
136 def test_another_format(self):
137 self.assertEqual(parse_datetime('04/07/10 21:29:41'),
138 datetime.datetime(2010, 4, 7, 21, 29, 41))
139
140 def test_now(self):
141 self.assertTrue(parse_datetime('now') - datetime.datetime.now() <
142 datetime.timedelta(seconds=1))
143
144 def test_today(self):
145 today = datetime.date.today()
146 self.assertEqual(parse_datetime('today'),
147 datetime.datetime(*today.timetuple()[:3]))
148
149 def test_yesterday(self):
150 yesterday = datetime.date.today() - datetime.timedelta(days=1)
151 self.assertEqual(parse_datetime('yesterday'),
152 datetime.datetime(*yesterday.timetuple()[:3]))
153
154 def test_tomorrow(self):
155 tomorrow = datetime.date.today() + datetime.timedelta(days=1)
156 args = tomorrow.timetuple()[:3] + (23, 59, 59)
157 self.assertEqual(parse_datetime('tomorrow'), datetime.datetime(*args))
158
159 def test_days(self):
160 timestamp = datetime.datetime.today() - datetime.timedelta(days=3)
161 args = timestamp.timetuple()[:3] + (0, 0, 0, 0)
162 expected = datetime.datetime(*args)
163 self.assertEqual(parse_datetime('3d'), expected)
164 self.assertEqual(parse_datetime('3 d'), expected)
165 self.assertEqual(parse_datetime('3 day'), expected)
166 self.assertEqual(parse_datetime('3 days'), expected)
167
168 def test_weeks(self):
169 timestamp = datetime.datetime.today() - datetime.timedelta(days=3 * 7)
170 args = timestamp.timetuple()[:3] + (0, 0, 0, 0)
171 expected = datetime.datetime(*args)
172 self.assertEqual(parse_datetime('3w'), expected)
173 self.assertEqual(parse_datetime('3 w'), expected)
174 self.assertEqual(parse_datetime('3 week'), expected)
175 self.assertEqual(parse_datetime('3 weeks'), expected)
176
177 def test_mixed(self):
178 timestamp = datetime.datetime.today() - datetime.timedelta(days=2 * 7 + 3)
179 args = timestamp.timetuple()[:3] + (0, 0, 0, 0)
180 expected = datetime.datetime(*args)
181 self.assertEqual(parse_datetime('2w3d'), expected)
182 self.assertEqual(parse_datetime('2w 3d'), expected)
183 self.assertEqual(parse_datetime('2w 3 days'), expected)
184 self.assertEqual(parse_datetime('2 weeks 3 days'), expected)
185
186
187 class TestAuthorExtractors(unittest.TestCase):
188 TEST_AUTHORS = [('Marcin Kuzminski <marcin@python-works.com>',
189 ('Marcin Kuzminski', 'marcin@python-works.com')),
190 ('Marcin Kuzminski Spaces < marcin@python-works.com >',
191 ('Marcin Kuzminski Spaces', 'marcin@python-works.com')),
192 ('Marcin Kuzminski <marcin.kuzminski@python-works.com>',
193 ('Marcin Kuzminski', 'marcin.kuzminski@python-works.com')),
194 ('mrf RFC_SPEC <marcin+kuzminski@python-works.com>',
195 ('mrf RFC_SPEC', 'marcin+kuzminski@python-works.com')),
196 ('username <user@email.com>',
197 ('username', 'user@email.com')),
198 ('username <user@email.com',
199 ('username', 'user@email.com')),
200 ('broken missing@email.com',
201 ('broken', 'missing@email.com')),
202 ('<justemail@mail.com>',
203 ('', 'justemail@mail.com')),
204 ('justname',
205 ('justname', '')),
206 ('Mr Double Name withemail@email.com ',
207 ('Mr Double Name', 'withemail@email.com')),
208 ]
209
210 def test_author_email(self):
211
212 for test_str, result in self.TEST_AUTHORS:
213 self.assertEqual(result[1], author_email(test_str))
214
215
216 def test_author_name(self):
217
218 for test_str, result in self.TEST_AUTHORS:
219 self.assertEqual(result[0], author_name(test_str))
220
221
222 class TestGetDictForAttrs(unittest.TestCase):
223
224 def test_returned_dict_has_expected_attrs(self):
225 obj = mock.Mock()
226 obj.NOT_INCLUDED = 'this key/value should not be included'
227 obj.CONST = True
228 obj.foo = 'aaa'
229 obj.attrs = {'foo': 'bar'}
230 obj.date = datetime.datetime(2010, 12, 31)
231 obj.count = 1001
232
233 self.assertEqual(get_dict_for_attrs(obj, ['CONST', 'foo', 'attrs',
234 'date', 'count']), {
235 'CONST': True,
236 'foo': 'aaa',
237 'attrs': {'foo': 'bar'},
238 'date': datetime.datetime(2010, 12, 31),
239 'count': 1001,
240 })
241
242
243 class TestGetTotalSeconds(unittest.TestCase):
244
245 def assertTotalSecondsEqual(self, timedelta, expected_seconds):
246 result = get_total_seconds(timedelta)
247 self.assertEqual(result, expected_seconds,
248 "We computed %s seconds for %s but expected %s"
249 % (result, timedelta, expected_seconds))
250
251 def test_get_total_seconds_returns_proper_value(self):
252 self.assertTotalSecondsEqual(datetime.timedelta(seconds=1001), 1001)
253
254 def test_get_total_seconds_returns_proper_value_for_partial_seconds(self):
255 self.assertTotalSecondsEqual(datetime.timedelta(seconds=50.65), 50.65)
256
257
258 class TestGetUserHome(unittest.TestCase):
259
260 @mock.patch.object(os, 'environ', {})
261 def test_defaults_to_none(self):
262 self.assertEqual(get_user_home(), None)
263
264 @mock.patch.object(os, 'environ', {'HOME': '/home/foobar'})
265 def test_unix_like(self):
266 self.assertEqual(get_user_home(), '/home/foobar')
267
268 @mock.patch.object(os, 'environ', {'USERPROFILE': '/Users/foobar'})
269 def test_windows_like(self):
270 self.assertEqual(get_user_home(), '/Users/foobar')
271
272 @mock.patch.object(os, 'environ', {'HOME': '/home/foobar',
273 'USERPROFILE': '/Users/foobar'})
274 def test_prefers_home_over_userprofile(self):
275 self.assertEqual(get_user_home(), '/home/foobar')
276
277
278 if __name__ == '__main__':
279 unittest.main()
@@ -0,0 +1,26 b''
1 from __future__ import with_statement
2
3 from rhodecode.lib.vcs.utils.filesize import filesizeformat
4 from rhodecode.lib.vcs.utils.compat import unittest
5
6
7 class TestFilesizeformat(unittest.TestCase):
8
9 def test_bytes(self):
10 self.assertEqual(filesizeformat(10), '10 B')
11
12 def test_kilobytes(self):
13 self.assertEqual(filesizeformat(1024 * 2), '2 KB')
14
15 def test_megabytes(self):
16 self.assertEqual(filesizeformat(1024 * 1024 * 2.3), '2.3 MB')
17
18 def test_gigabytes(self):
19 self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 12.92), '12.92 GB')
20
21 def test_that_function_respects_sep_paramtere(self):
22 self.assertEqual(filesizeformat(1, ''), '1B')
23
24
25 if __name__ == '__main__':
26 unittest.main()
@@ -0,0 +1,84 b''
1 from __future__ import with_statement
2
3 from rhodecode.lib.vcs import VCSError, get_repo, get_backend
4 from rhodecode.lib.vcs.backends.hg import MercurialRepository
5 from rhodecode.lib.vcs.utils.compat import unittest
6 from conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH
7 import os
8 import shutil
9
10
11 class VCSTest(unittest.TestCase):
12 """
13 Tests for main module's methods.
14 """
15
16 def test_get_backend(self):
17 hg = get_backend('hg')
18 self.assertEqual(hg, MercurialRepository)
19
20 def test_alias_detect_hg(self):
21 alias = 'hg'
22 path = TEST_HG_REPO
23 backend = get_backend(alias)
24 repo = backend(path)
25 self.assertEqual('hg',repo.alias)
26
27 def test_alias_detect_git(self):
28 alias = 'git'
29 path = TEST_GIT_REPO
30 backend = get_backend(alias)
31 repo = backend(path)
32 self.assertEqual('git',repo.alias)
33
34 def test_wrong_alias(self):
35 alias = 'wrong_alias'
36 self.assertRaises(VCSError, get_backend, alias)
37
38 def test_get_repo(self):
39 alias = 'hg'
40 path = TEST_HG_REPO
41 backend = get_backend(alias)
42 repo = backend(path)
43
44 self.assertEqual(repo.__class__, get_repo(path, alias).__class__)
45 self.assertEqual(repo.path, get_repo(path, alias).path)
46
47 def test_get_repo_autoalias_hg(self):
48 alias = 'hg'
49 path = TEST_HG_REPO
50 backend = get_backend(alias)
51 repo = backend(path)
52
53 self.assertEqual(repo.__class__, get_repo(path).__class__)
54 self.assertEqual(repo.path, get_repo(path).path)
55
56 def test_get_repo_autoalias_git(self):
57 alias = 'git'
58 path = TEST_GIT_REPO
59 backend = get_backend(alias)
60 repo = backend(path)
61
62 self.assertEqual(repo.__class__, get_repo(path).__class__)
63 self.assertEqual(repo.path, get_repo(path).path)
64
65
66 def test_get_repo_err(self):
67 blank_repo_path = os.path.join(TEST_TMP_PATH, 'blank-error-repo')
68 if os.path.isdir(blank_repo_path):
69 shutil.rmtree(blank_repo_path)
70
71 os.mkdir(blank_repo_path)
72 self.assertRaises(VCSError, get_repo, blank_repo_path)
73 self.assertRaises(VCSError, get_repo, blank_repo_path + 'non_existing')
74
75 def test_get_repo_multialias(self):
76 multialias_repo_path = os.path.join(TEST_TMP_PATH, 'hg-git-repo')
77 if os.path.isdir(multialias_repo_path):
78 shutil.rmtree(multialias_repo_path)
79
80 os.mkdir(multialias_repo_path)
81
82 os.mkdir(os.path.join(multialias_repo_path, '.git'))
83 os.mkdir(os.path.join(multialias_repo_path, '.hg'))
84 self.assertRaises(VCSError, get_repo, multialias_repo_path)
@@ -0,0 +1,90 b''
1 from __future__ import with_statement
2
3 import datetime
4 from rhodecode.lib.vcs.nodes import FileNode
5 from rhodecode.lib.vcs.utils.compat import unittest
6 from base import BackendTestMixin
7 from conf import SCM_TESTS
8
9
10 class WorkdirTestCaseMixin(BackendTestMixin):
11
12 @classmethod
13 def _get_commits(cls):
14 commits = [
15 {
16 'message': u'Initial commit',
17 'author': u'Joe Doe <joe.doe@example.com>',
18 'date': datetime.datetime(2010, 1, 1, 20),
19 'added': [
20 FileNode('foobar', content='Foobar'),
21 FileNode('foobar2', content='Foobar II'),
22 FileNode('foo/bar/baz', content='baz here!'),
23 ],
24 },
25 {
26 'message': u'Changes...',
27 'author': u'Jane Doe <jane.doe@example.com>',
28 'date': datetime.datetime(2010, 1, 1, 21),
29 'added': [
30 FileNode('some/new.txt', content='news...'),
31 ],
32 'changed': [
33 FileNode('foobar', 'Foobar I'),
34 ],
35 'removed': [],
36 },
37 ]
38 return commits
39
40 def test_get_branch_for_default_branch(self):
41 self.assertEqual(self.repo.workdir.get_branch(),
42 self.repo.DEFAULT_BRANCH_NAME)
43
44 def test_get_branch_after_adding_one(self):
45 self.imc.add(FileNode('docs/index.txt',
46 content='Documentation\n'))
47 self.imc.commit(
48 message=u'New branch: foobar',
49 author=u'joe',
50 branch='foobar',
51 )
52
53 def test_get_changeset(self):
54 self.imc.add(FileNode('docs/index.txt',
55 content='Documentation\n'))
56 head = self.imc.commit(
57 message=u'New branch: foobar',
58 author=u'joe',
59 branch='foobar',
60 )
61 self.assertEqual(self.repo.workdir.get_changeset(), head)
62
63 def test_checkout_branch(self):
64 from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError
65 # first, 'foobranch' does not exist.
66 self.assertRaises(BranchDoesNotExistError, self.repo.workdir.checkout_branch,
67 branch='foobranch')
68 # create new branch 'foobranch'.
69 self.imc.add(FileNode('file1', content='blah'))
70 self.imc.commit(message=u'asd', author=u'john', branch='foobranch')
71 # go back to the default branch
72 self.repo.workdir.checkout_branch()
73 self.assertEqual(self.repo.workdir.get_branch(), self.backend_class.DEFAULT_BRANCH_NAME)
74 # checkout 'foobranch'
75 self.repo.workdir.checkout_branch('foobranch')
76 self.assertEqual(self.repo.workdir.get_branch(), 'foobranch')
77
78
79 # For each backend create test case class
80 for alias in SCM_TESTS:
81 attrs = {
82 'backend_alias': alias,
83 }
84 cls_name = ''.join(('%s branch test' % alias).title().split())
85 bases = (WorkdirTestCaseMixin, unittest.TestCase)
86 globals()[cls_name] = type(cls_name, bases, attrs)
87
88
89 if __name__ == '__main__':
90 unittest.main()
@@ -0,0 +1,97 b''
1 """
2 Utilities for tests only. These are not or should not be used normally -
3 functions here are crafted as we don't want to use ``vcs`` to verify tests.
4 """
5 import os
6 import re
7 import sys
8
9 from subprocess import Popen
10
11
12 class VCSTestError(Exception):
13 pass
14
15
16 def run_command(cmd, args):
17 """
18 Runs command on the system with given ``args``.
19 """
20 command = ' '.join((cmd, args))
21 p = Popen(command, shell=True)
22 status = os.waitpid(p.pid, 0)[1]
23 return status
24
25
26 def eprint(msg):
27 """
28 Prints given ``msg`` into sys.stderr as nose test runner hides all output
29 from sys.stdout by default and if we want to pipe stream somewhere we don't
30 need those verbose messages anyway.
31 Appends line break.
32 """
33 sys.stderr.write(msg)
34 sys.stderr.write('\n')
35
36
37 class SCMFetcher(object):
38
39 def __init__(self, alias, test_repo_path, remote_repo, clone_cmd):
40 """
41 :param clone_cmd: command which would clone remote repository; pass
42 only first bits - remote path and destination would be appended
43 using ``remote_repo`` and ``test_repo_path``
44 """
45 self.alias = alias
46 self.test_repo_path = test_repo_path
47 self.remote_repo = remote_repo
48 self.clone_cmd = clone_cmd
49
50 def setup(self):
51 if not os.path.isdir(self.test_repo_path):
52 self.fetch_repo()
53
54 def fetch_repo(self):
55 """
56 Tries to fetch repository from remote path.
57 """
58 remote = self.remote_repo
59 eprint("Fetching repository %s into %s" % (remote, self.test_repo_path))
60 run_command(self.clone_cmd, '%s %s' % (remote, self.test_repo_path))
61
62
63 def get_normalized_path(path):
64 """
65 If given path exists, new path would be generated and returned. Otherwise
66 same whats given is returned. Assumes that there would be no more than
67 10000 same named files.
68 """
69 if os.path.exists(path):
70 dir, basename = os.path.split(path)
71 splitted_name = basename.split('.')
72 if len(splitted_name) > 1:
73 ext = splitted_name[-1]
74 else:
75 ext = None
76 name = '.'.join(splitted_name[:-1])
77 matcher = re.compile(r'^.*-(\d{5})$')
78 start = 0
79 m = matcher.match(name)
80 if not m:
81 # Haven't append number yet so return first
82 newname = '%s-00000' % name
83 newpath = os.path.join(dir, newname)
84 if ext:
85 newpath = '.'.join((newpath, ext))
86 return get_normalized_path(newpath)
87 else:
88 start = int(m.group(1)[-5:]) + 1
89 for x in xrange(start, 10000):
90 newname = name[:-5] + str(x).rjust(5, '0')
91 newpath = os.path.join(dir, newname)
92 if ext:
93 newpath = '.'.join((newpath, ext))
94 if not os.path.exists(newpath):
95 return newpath
96 raise VCSTestError("Couldn't compute new path for %s" % path)
97 return path
1 NO CONTENT: new file 100644, binary diff hidden
@@ -50,6 +50,7 b' fixes'
50 50 - fixed issue #459. Changed the way of obtaining logger in reindex task.
51 51 - fixed #453 added ID field in whoosh SCHEMA that solves the issue of
52 52 reindexing modified files
53 - fixes #481 rhodecode emails are sent without Date header
53 54
54 55 1.3.6 (**2012-05-17**)
55 56 ----------------------
@@ -70,6 +70,8 b' class ReposController(BaseController):'
70 70 repo_model = RepoModel()
71 71 c.users_array = repo_model.get_users_js()
72 72 c.users_groups_array = repo_model.get_users_groups_js()
73 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
74 c.landing_revs_choices = choices
73 75
74 76 def __load_data(self, repo_name=None):
75 77 """
@@ -91,6 +93,9 b' class ReposController(BaseController):'
91 93
92 94 return redirect(url('repos'))
93 95
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
97 c.landing_revs_choices = choices
98
94 99 c.default_user_id = User.get_by_username('default').user_id
95 100 c.in_public_journal = UserFollowing.query()\
96 101 .filter(UserFollowing.user_id == c.default_user_id)\
@@ -116,6 +121,7 b' class ReposController(BaseController):'
116 121 c.repos_list = [('', _('--REMOVE FORK--'))]
117 122 c.repos_list += [(x.repo_id, x.repo_name) for x in
118 123 Repository.query().order_by(Repository.repo_name).all()]
124
119 125 return defaults
120 126
121 127 @HasPermissionAllDecorator('hg.admin')
@@ -137,7 +143,8 b' class ReposController(BaseController):'
137 143 self.__load_defaults()
138 144 form_result = {}
139 145 try:
140 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
146 form_result = RepoForm(repo_groups=c.repo_groups_choices,
147 landing_revs=c.landing_revs_choices)()\
141 148 .to_python(dict(request.POST))
142 149 RepoModel().create(form_result, self.rhodecode_user)
143 150 if form_result['clone_uri']:
@@ -205,7 +212,8 b' class ReposController(BaseController):'
205 212 repo_model = RepoModel()
206 213 changed_name = repo_name
207 214 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
208 repo_groups=c.repo_groups_choices)()
215 repo_groups=c.repo_groups_choices,
216 landing_revs=c.landing_revs_choices)()
209 217 try:
210 218 form_result = _form.to_python(dict(request.POST))
211 219 repo = repo_model.update(repo_name, form_result)
@@ -397,6 +397,7 b' class SettingsController(BaseController)'
397 397
398 398 c.repo_groups = RepoGroup.groups_choices()
399 399 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
400 c.landing_revs = ScmModel().get_repo_landing_revs()
400 401
401 402 new_repo = request.GET.get('repo', '')
402 403 c.new_repo = repo_name_slug(new_repo)
@@ -61,7 +61,7 b' log = logging.getLogger(__name__)'
61 61
62 62 class FilesController(BaseRepoController):
63 63
64 @LoginRequired()
64
65 65 def __before__(self):
66 66 super(FilesController, self).__before__()
67 67 c.cut_off_limit = self.cut_off_limit
@@ -113,6 +113,7 b' class FilesController(BaseRepoController'
113 113
114 114 return file_node
115 115
116 @LoginRequired()
116 117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
117 118 'repository.admin')
118 119 def index(self, repo_name, revision, f_path, annotate=False):
@@ -154,9 +155,14 b' class FilesController(BaseRepoController'
154 155 c.file = c.changeset.get_node(f_path)
155 156
156 157 if c.file.is_file():
157 c.file_history = self._get_node_history(c.changeset, f_path)
158 _hist = c.changeset.get_file_history(f_path)
159 c.file_history = self._get_node_history(c.changeset, f_path,
160 _hist)
161 c.authors = []
162 for a in set([x.author for x in _hist]):
163 c.authors.append((h.email(a), h.person(a)))
158 164 else:
159 c.file_history = []
165 c.authors = c.file_history = []
160 166 except RepositoryError, e:
161 167 h.flash(str(e), category='warning')
162 168 redirect(h.url('files_home', repo_name=repo_name,
@@ -164,6 +170,7 b' class FilesController(BaseRepoController'
164 170
165 171 return render('files/files.html')
166 172
173 @LoginRequired()
167 174 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
168 175 'repository.admin')
169 176 def rawfile(self, repo_name, revision, f_path):
@@ -176,6 +183,7 b' class FilesController(BaseRepoController'
176 183 response.content_type = file_node.mimetype
177 184 return file_node.content
178 185
186 @LoginRequired()
179 187 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
180 188 'repository.admin')
181 189 def raw(self, repo_name, revision, f_path):
@@ -222,6 +230,7 b' class FilesController(BaseRepoController'
222 230 response.content_type = mimetype
223 231 return file_node.content
224 232
233 @LoginRequired()
225 234 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
226 235 def edit(self, repo_name, revision, f_path):
227 236 r_post = request.POST
@@ -271,6 +280,7 b' class FilesController(BaseRepoController'
271 280
272 281 return render('files/files_edit.html')
273 282
283 @LoginRequired()
274 284 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
275 285 def add(self, repo_name, revision, f_path):
276 286 r_post = request.POST
@@ -325,6 +335,7 b' class FilesController(BaseRepoController'
325 335
326 336 return render('files/files_add.html')
327 337
338 @LoginRequired()
328 339 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
329 340 'repository.admin')
330 341 def archivefile(self, repo_name, fname):
@@ -382,6 +393,7 b' class FilesController(BaseRepoController'
382 393 response.content_type = str(content_type)
383 394 return get_chunked_archive(archive)
384 395
396 @LoginRequired()
385 397 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
386 398 'repository.admin')
387 399 def diff(self, repo_name, f_path):
@@ -454,8 +466,9 b' class FilesController(BaseRepoController'
454 466
455 467 return render('files/file_diff.html')
456 468
457 def _get_node_history(self, cs, f_path):
458 changesets = cs.get_file_history(f_path)
469 def _get_node_history(self, cs, f_path, changesets=None):
470 if changesets is None:
471 changesets = cs.get_file_history(f_path)
459 472 hist_l = []
460 473
461 474 changesets_group = ([], _("Changesets"))
@@ -479,9 +492,10 b' class FilesController(BaseRepoController'
479 492
480 493 return hist_l
481 494
482 @jsonify
495 @LoginRequired()
483 496 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
484 497 'repository.admin')
498 @jsonify
485 499 def nodelist(self, repo_name, revision, f_path):
486 500 if request.environ.get('HTTP_X_PARTIAL_XHR'):
487 501 cs = self.__get_cs_or_redirect(revision, repo_name)
@@ -43,6 +43,7 b' from rhodecode.model.forms import RepoSe'
43 43 from rhodecode.model.repo import RepoModel
44 44 from rhodecode.model.db import RepoGroup
45 45 from rhodecode.model.meta import Session
46 from rhodecode.model.scm import ScmModel
46 47
47 48 log = logging.getLogger(__name__)
48 49
@@ -60,6 +61,8 b' class SettingsController(BaseRepoControl'
60 61 repo_model = RepoModel()
61 62 c.users_array = repo_model.get_users_js()
62 63 c.users_groups_array = repo_model.get_users_groups_js()
64 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
65 c.landing_revs_choices = choices
63 66
64 67 @HasRepoPermissionAllDecorator('repository.admin')
65 68 def index(self, repo_name):
@@ -94,7 +97,8 b' class SettingsController(BaseRepoControl'
94 97
95 98 _form = RepoSettingsForm(edit=True,
96 99 old_data={'repo_name': repo_name},
97 repo_groups=c.repo_groups_choices)()
100 repo_groups=c.repo_groups_choices,
101 landing_revs=c.landing_revs_choices)()
98 102 try:
99 103 form_result = _form.to_python(dict(request.POST))
100 104
@@ -460,8 +460,9 b' class LoginRequired(object):'
460 460 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
461 461 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
462 462 if user.is_authenticated or api_access_ok:
463 log.info('user %s is authenticated and granted access to %s' % (
464 user.username, loc)
463 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
464 log.info('user %s is authenticated and granted access to %s '
465 'using %s' % (user.username, loc, reason)
465 466 )
466 467 return func(*fargs, **fkwargs)
467 468 else:
@@ -3,6 +3,7 b' from rhodecode.lib.rcmail.response impor'
3 3 from rhodecode.lib.rcmail.exceptions import BadHeaders
4 4 from rhodecode.lib.rcmail.exceptions import InvalidMessage
5 5
6
6 7 class Attachment(object):
7 8 """
8 9 Encapsulates file attachment information.
@@ -134,13 +135,13 b' class Message(object):'
134 135 """
135 136
136 137 if not self.recipients:
137 raise InvalidMessage, "No recipients have been added"
138 raise InvalidMessage("No recipients have been added")
138 139
139 140 if not self.body and not self.html:
140 raise InvalidMessage, "No body has been set"
141 raise InvalidMessage("No body has been set")
141 142
142 143 if not self.sender:
143 raise InvalidMessage, "No sender address has been set"
144 raise InvalidMessage("No sender address has been set")
144 145
145 146 if self.is_bad_headers():
146 147 raise BadHeaders
@@ -364,6 +364,7 b' def to_message(mail, separator="; "):'
364 364
365 365 return out
366 366
367
367 368 class MIMEPart(MIMEBase):
368 369 """
369 370 A reimplementation of nearly everything in email.mime to be more useful
@@ -387,7 +388,8 b' class MIMEPart(MIMEBase):'
387 388 self.set_payload(encoded, charset=charset)
388 389
389 390 def extract_payload(self, mail):
390 if mail.body == None: return # only None, '' is still ok
391 if mail.body == None:
392 return # only None, '' is still ok
391 393
392 394 ctype, ctype_params = mail.content_encoding['Content-Type']
393 395 cdisp, cdisp_params = mail.content_encoding['Content-Disposition']
@@ -415,7 +417,8 b' class MIMEPart(MIMEBase):'
415 417
416 418
417 419 def header_to_mime_encoding(value, not_email=False, separator=", "):
418 if not value: return ""
420 if not value:
421 return ""
419 422
420 423 encoder = Charset(DEFAULT_ENCODING)
421 424 if type(value) == list:
@@ -424,6 +427,7 b' def header_to_mime_encoding(value, not_e'
424 427 else:
425 428 return properly_encode_header(value, encoder, not_email)
426 429
430
427 431 def properly_encode_header(value, encoder, not_email):
428 432 """
429 433 The only thing special (weird) about this function is that it tries
@@ -21,10 +21,11 b''
21 21 #
22 22 # You should have received a copy of the GNU General Public License
23 23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
24 import time
25 25 import logging
26 26 import smtplib
27 27 from socket import sslerror
28 from email.utils import formatdate
28 29 from rhodecode.lib.rcmail.message import Message
29 30
30 31
@@ -59,8 +60,11 b' class SmtpMailer(object):'
59 60
60 61 if isinstance(recipients, basestring):
61 62 recipients = [recipients]
63 headers = {
64 'Date': formatdate(time.time())
65 }
62 66 msg = Message(subject, recipients, body, html, self.mail_from,
63 recipients_separator=", ")
67 recipients_separator=", ", extra_headers=headers)
64 68 raw_msg = msg.to_message()
65 69
66 70 if self.ssl:
@@ -448,7 +448,8 b' def repo2db_mapper(initial_repo_list, re'
448 448 'description': repo.description \
449 449 if repo.description != 'unknown' else '%s repository' % name,
450 450 'private': False,
451 'group_id': getattr(group, 'group_id', None)
451 'group_id': getattr(group, 'group_id', None),
452 'landing_rev': repo.DEFAULT_BRANCH_NAME
452 453 }
453 454 rm.create(form_data, user, just_db=True)
454 455 sa.commit()
@@ -558,7 +559,7 b' def create_test_env(repos_test_path, con'
558 559 install test repository into tmp dir
559 560 """
560 561 from rhodecode.lib.db_manage import DbManage
561 from rhodecode.tests import HG_REPO, TESTS_TMP_PATH
562 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
562 563
563 564 # PART ONE create db
564 565 dbconf = config['sqlalchemy.db1.url']
@@ -593,12 +594,21 b' def create_test_env(repos_test_path, con'
593 594 log.debug('remove %s' % data_path)
594 595 shutil.rmtree(data_path)
595 596
596 #CREATE DEFAULT HG REPOSITORY
597 #CREATE DEFAULT TEST REPOS
597 598 cur_dir = dn(dn(abspath(__file__)))
598 599 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
599 600 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
600 601 tar.close()
601 602
603 cur_dir = dn(dn(abspath(__file__)))
604 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
605 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
606 tar.close()
607
608 #LOAD VCS test stuff
609 from rhodecode.tests.vcs import setup_package
610 setup_package()
611
602 612
603 613 #==============================================================================
604 614 # PASTER COMMANDS
@@ -259,10 +259,11 b' class GitChangeset(BaseChangeset):'
259 259 # --root ==> doesn't put '^' character for bounderies
260 260 # -r sha ==> blames for the given revision
261 261 so, se = self.repository.run_git_command(cmd)
262
262 263 annotate = []
263 264 for i, blame_line in enumerate(so.split('\n')[:-1]):
264 265 ln_no = i + 1
265 id, line = re.split(r' \(.+?\) ', blame_line, 1)
266 id, line = re.split(r' ', blame_line, 1)
266 267 annotate.append((ln_no, self.repository.get_changeset(id), line))
267 268 return annotate
268 269
@@ -83,7 +83,8 b' class GitInMemoryChangeset(BaseInMemoryC'
83 83 curtree = newtree
84 84 parent[reversed_dirnames[-1]] = DIRMOD, curtree.id
85 85 else:
86 parent.add(node.mode, node_path, blob.id)
86 parent.add(name=node_path, mode=node.mode, hexsha=blob.id)
87
87 88 new_trees.append(parent)
88 89 # Update ancestors
89 90 for parent, tree, path in reversed([(a[1], b[1], b[0]) for a, b in
@@ -123,7 +124,7 b' class GitInMemoryChangeset(BaseInMemoryC'
123 124 commit.parents = [p._commit.id for p in self.parents if p]
124 125 commit.author = commit.committer = safe_str(author)
125 126 commit.encoding = ENCODING
126 commit.message = safe_str(message) + ' '
127 commit.message = safe_str(message)
127 128
128 129 # Compute date
129 130 if date is None:
@@ -192,7 +192,11 b' class GitRepository(BaseRepository):'
192 192 "for this repository %s" % (revision, self))
193 193
194 194 elif is_bstr(revision):
195 if not pattern.match(revision) or revision not in self.revisions:
195 _ref_revision = self._parsed_refs.get(revision)
196 if _ref_revision: # and _ref_revision[1] in ['H', 'RH', 'T']:
197 return _ref_revision[0]
198
199 elif not pattern.match(revision) or revision not in self.revisions:
196 200 raise ChangesetDoesNotExistError("Revision %r does not exist "
197 201 "for this repository %s" % (revision, self))
198 202
@@ -267,18 +271,9 b' class GitRepository(BaseRepository):'
267 271 if ref.startswith('refs/heads/') and not ref.endswith('/HEAD')]
268 272 return OrderedDict(sorted(_branches, key=sortkey, reverse=False))
269 273
270 def _heads(self, reverse=False):
271 refs = self._repo.get_refs()
272 heads = {}
273
274 for key, val in refs.items():
275 for ref_key in ['refs/heads/', 'refs/remotes/origin/']:
276 if key.startswith(ref_key):
277 n = key[len(ref_key):]
278 if n not in ['HEAD']:
279 heads[n] = val
280
281 return heads if reverse else dict((y,x) for x,y in heads.iteritems())
274 @LazyProperty
275 def tags(self):
276 return self._get_tags()
282 277
283 278 def _get_tags(self):
284 279 if not self.revisions:
@@ -288,10 +283,6 b' class GitRepository(BaseRepository):'
288 283 self._repo.get_refs().items() if ref.startswith('refs/tags/')]
289 284 return OrderedDict(sorted(_tags, key=sortkey, reverse=True))
290 285
291 @LazyProperty
292 def tags(self):
293 return self._get_tags()
294
295 286 def tag(self, name, user, revision=None, message=None, date=None,
296 287 **kwargs):
297 288 """
@@ -335,6 +326,34 b' class GitRepository(BaseRepository):'
335 326 except OSError, e:
336 327 raise RepositoryError(e.strerror)
337 328
329 @LazyProperty
330 def _parsed_refs(self):
331 refs = self._repo.get_refs()
332 keys = [('refs/heads/', 'H'),
333 ('refs/remotes/origin/', 'RH'),
334 ('refs/tags/', 'T')]
335 _refs = {}
336 for ref, sha in refs.iteritems():
337 for k, type_ in keys:
338 if ref.startswith(k):
339 _key = ref[len(k):]
340 _refs[_key] = [sha, type_]
341 break
342 return _refs
343
344 def _heads(self, reverse=False):
345 refs = self._repo.get_refs()
346 heads = {}
347
348 for key, val in refs.items():
349 for ref_key in ['refs/heads/', 'refs/remotes/origin/']:
350 if key.startswith(ref_key):
351 n = key[len(ref_key):]
352 if n not in ['HEAD']:
353 heads[n] = val
354
355 return heads if reverse else dict((y, x) for x, y in heads.iteritems())
356
338 357 def get_changeset(self, revision=None):
339 358 """
340 359 Returns ``GitChangeset`` object representing commit from git repository
@@ -32,7 +32,8 b' class MercurialInMemoryChangeset(BaseInM'
32 32 from .repository import MercurialRepository
33 33 if not isinstance(message, unicode) or not isinstance(author, unicode):
34 34 raise RepositoryError('Given message and author needs to be '
35 'an <unicode> instance')
35 'an <unicode> instance got %r & %r instead'
36 % (type(message), type(author)))
36 37
37 38 if branch is None:
38 39 branch = MercurialRepository.DEFAULT_BRANCH_NAME
@@ -422,7 +422,7 b' class FileNode(Node):'
422 422
423 423 def __repr__(self):
424 424 return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
425 self.changeset.short_id)
425 getattr(self.changeset, 'short_id', ''))
426 426
427 427
428 428 class RemovedFileNode(FileNode):
@@ -559,7 +559,7 b' class DirNode(Node):'
559 559
560 560 def __repr__(self):
561 561 return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
562 self.changeset.short_id)
562 getattr(self.changeset, 'short_id', ''))
563 563
564 564
565 565 class RootNode(DirNode):
@@ -593,7 +593,7 b' class SubModuleNode(Node):'
593 593
594 594 def __repr__(self):
595 595 return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
596 self.changeset.short_id)
596 getattr(self.changeset, 'short_id', ''))
597 597
598 598 def _extract_submodule_url(self):
599 599 if self.alias == 'git':
@@ -299,7 +299,7 b' class User(Base, BaseModel):'
299 299 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 300 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
301 301 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
302 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302 name = Column("firstname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 303 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304 304 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
305 305 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
@@ -551,13 +551,14 b' class Repository(Base, BaseModel):'
551 551 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 552 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
553 553 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
554 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
555 555 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
556 556 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
557 557 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
558 558 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
559 559 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
560 560 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
561 landing_rev = Column("landing_revision", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
561 562
562 563 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
563 564 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
@@ -653,7 +653,7 b' def PasswordResetForm():'
653 653
654 654
655 655 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
656 repo_groups=[]):
656 repo_groups=[], landing_revs=[]):
657 657 class _RepoForm(formencode.Schema):
658 658 allow_extra_fields = True
659 659 filter_extra_fields = False
@@ -662,10 +662,11 b' def RepoForm(edit=False, old_data={}, su'
662 662 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False))
663 663 repo_group = OneOf(repo_groups, hideList=True)
664 664 repo_type = OneOf(supported_backends)
665 description = UnicodeString(strip=True, min=1, not_empty=True)
665 description = UnicodeString(strip=True, min=1, not_empty=False)
666 666 private = StringBoolean(if_missing=False)
667 667 enable_statistics = StringBoolean(if_missing=False)
668 668 enable_downloads = StringBoolean(if_missing=False)
669 landing_rev = OneOf(landing_revs, hideList=True)
669 670
670 671 if edit:
671 672 #this is repo owner
@@ -697,7 +698,7 b' def RepoForkForm(edit=False, old_data={}'
697 698
698 699
699 700 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
700 repo_groups=[]):
701 repo_groups=[], landing_revs=[]):
701 702 class _RepoForm(formencode.Schema):
702 703 allow_extra_fields = True
703 704 filter_extra_fields = False
@@ -706,7 +707,7 b' def RepoSettingsForm(edit=False, old_dat'
706 707 description = UnicodeString(strip=True, min=1, not_empty=True)
707 708 repo_group = OneOf(repo_groups, hideList=True)
708 709 private = StringBoolean(if_missing=False)
709
710 landing_rev = OneOf(landing_revs, hideList=True)
710 711 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
711 712 ValidSettings]
712 713 return _RepoForm
@@ -29,6 +29,7 b' import logging'
29 29 import cStringIO
30 30
31 31 from sqlalchemy import func
32 from pylons.i18n.translation import _
32 33
33 34 from rhodecode.lib.vcs import get_backend
34 35 from rhodecode.lib.vcs.exceptions import RepositoryError
@@ -474,3 +475,40 b' class ScmModel(BaseModel):'
474 475
475 476 def get_unread_journal(self):
476 477 return self.sa.query(UserLog).count()
478
479 def get_repo_landing_revs(self, repo=None):
480 """
481 Generates select option with tags branches and bookmarks (for hg only)
482 grouped by type
483
484 :param repo:
485 :type repo:
486 """
487
488 hist_l = []
489 choices = []
490 repo = self.__get_repo(repo)
491 hist_l.append(['tip', _('latest tip')])
492 choices.append('tip')
493 if not repo:
494 return choices, hist_l
495
496 repo = repo.scm_instance
497
498 branches_group = ([(k, k) for k, v in
499 repo.branches.iteritems()], _("Branches"))
500 hist_l.append(branches_group)
501 choices.extend([x[0] for x in branches_group[0]])
502
503 if repo.alias == 'hg':
504 bookmarks_group = ([(k, k) for k, v in
505 repo.bookmarks.iteritems()], _("Bookmarks"))
506 hist_l.append(bookmarks_group)
507 choices.extend([x[0] for x in bookmarks_group[0]])
508
509 tags_group = ([(k, k) for k, v in
510 repo.tags.iteritems()], _("Tags"))
511 hist_l.append(tags_group)
512 choices.extend([x[0] for x in tags_group[0]])
513
514 return choices, hist_l
@@ -2252,6 +2252,20 b' h3.files_location {'
2252 2252 padding: 5px !important;
2253 2253 }
2254 2254
2255 .file_history{
2256 padding-top:10px;
2257 font-size:16px;
2258 }
2259 .file_author{
2260 float: left;
2261 }
2262
2263 .file_author .item{
2264 float:left;
2265 padding:5px;
2266 color: #888;
2267 }
2268
2255 2269 .tablerow0 {
2256 2270 background-color: #F8F8F8;
2257 2271 }
@@ -42,6 +42,15 b''
42 42 <span class="help-block">${_('Type of repository to create.')}</span>
43 43 </div>
44 44 </div>
45 <div class="field">
46 <div class="label">
47 <label for="landing_rev">${_('Landing revision')}:</label>
48 </div>
49 <div class="input">
50 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
51 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
52 </div>
53 </div>
45 54 <div class="field">
46 55 <div class="label label-textarea">
47 56 <label for="description">${_('Description')}:</label>
@@ -62,6 +62,15 b''
62 62 </div>
63 63 </div>
64 64 <div class="field">
65 <div class="label">
66 <label for="landing_rev">${_('Landing revision')}:</label>
67 </div>
68 <div class="input">
69 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
70 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
71 </div>
72 </div>
73 <div class="field">
65 74 <div class="label label-textarea">
66 75 <label for="description">${_('Description')}:</label>
67 76 </div>
@@ -210,7 +219,7 b''
210 219 </div>
211 220 ${h.end_form()}
212 221
213 <h3>${_('Set as fork')}</h3>
222 <h3>${_('Set as fork of')}</h3>
214 223 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
215 224 <div class="form">
216 225 <div class="fields">
@@ -219,7 +228,7 b''
219 228 </div>
220 229 <div class="field" style="border:none;color:#888">
221 230 <ul>
222 <li>${_('''Manually set this repository as a fork of another''')}</li>
231 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
223 232 </ul>
224 233 </div>
225 234 </div>
@@ -162,7 +162,7 b''
162 162 <div id="perms" class="table">
163 163 %for section in sorted(c.perm_user.permissions.keys()):
164 164 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
165
165
166 166 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
167 167 <table id="tbl_list_${section}">
168 168 <thead>
@@ -202,7 +202,7 b''
202 202 </table>
203 203 </div>
204 204 %endfor
205 </div>
205 </div>
206 206 </div>
207 207 <div class="box box-right">
208 208 <!-- box / title -->
@@ -45,7 +45,7 b''
45 45 'Group':"${_('Group')}",
46 46 'members':"${_('members')}",
47 47 'search truncated': "${_('search truncated')}",
48 'no matching files': "${_('no matching files')}"
48 'no matching files': "${_('no matching files')}"
49 49
50 50 };
51 51 var _TM = TRANSLATION_MAP;
@@ -69,7 +69,7 b''
69 69 ##${comment.comment_inline_form(cs)}
70 70 ## diff block
71 71 <h3 style="padding-top:8px;">${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</h3>
72
72
73 73 ${diff_block.diff_block(c.changes[cs.raw_id])}
74 74 ##${comment.comments(cs)}
75 75
@@ -1,15 +1,27 b''
1 1 <dl>
2 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
2 <dt class="file_history">${_('History')}</dt>
3 3 <dd>
4 <div>
5 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
6 ${h.hidden('diff2',c.file.changeset.raw_id)}
7 ${h.select('diff1',c.file.changeset.raw_id,c.file_history)}
8 ${h.submit('diff','diff to revision',class_="ui-btn")}
9 ${h.submit('show_rev','show at revision',class_="ui-btn")}
10 ${h.end_form()}
11 </div>
4 <div>
5 <div style="float:left">
6 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
7 ${h.hidden('diff2',c.file.changeset.raw_id)}
8 ${h.select('diff1',c.file.changeset.raw_id,c.file_history)}
9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
11 ${h.end_form()}
12 </div>
13 <div class="file_author">
14 <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
15 %for email, user in c.authors:
16 <div class="contributor tooltip" style="float:left" title="${h.tooltip(user)}">
17 <div class="gravatar" style="margin:1px"><img alt="gravatar" src="${h.gravatar_url(email, 20)}"/> </div>
18 </div>
19 %endfor
20 </div>
21 </div>
22 <div style="clear:both"></div>
12 23 </dd>
24
13 25 </dl>
14 26
15 27 <div id="body" class="codeblock">
@@ -119,7 +119,7 b''
119 119 </div>
120 120 </div>
121 121 <script>
122 YUD.get('repo_count').innerHTML = ${cnt+1};
122 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
123 123 var func = function(node){
124 124 return node.parentNode.parentNode.parentNode.parentNode;
125 125 }
@@ -10,6 +10,9 b' setup-app`) and provides the base testin'
10 10 import os
11 11 import time
12 12 import logging
13 import datetime
14 import hashlib
15 import tempfile
13 16 from os.path import join as jn
14 17
15 18 from unittest import TestCase
@@ -27,6 +30,7 b' from rhodecode.model.db import User'
27 30
28 31 import pylons.test
29 32
33
30 34 os.environ['TZ'] = 'UTC'
31 35 if not is_windows:
32 36 time.tzset()
@@ -34,11 +38,14 b' if not is_windows:'
34 38 log = logging.getLogger(__name__)
35 39
36 40 __all__ = [
37 'environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
38 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
39 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
40 'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
41 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL'
41 'environ', 'url', 'get_new_dir', 'TestController', 'TESTS_TMP_PATH',
42 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK',
43 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN',
44 'TEST_USER_REGULAR_PASS', 'TEST_USER_REGULAR_EMAIL',
45 'TEST_USER_REGULAR2_LOGIN', 'TEST_USER_REGULAR2_PASS',
46 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', 'TEST_HG_REPO_CLONE',
47 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO', 'TEST_GIT_REPO_CLONE',
48 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', 'SCM_TESTS',
42 49 ]
43 50
44 51 # Invoke websetup with the current config file
@@ -73,6 +80,45 b" NEW_GIT_REPO = 'vcs_test_git_new'"
73 80 HG_FORK = 'vcs_test_hg_fork'
74 81 GIT_FORK = 'vcs_test_git_fork'
75 82
83 ## VCS
84 SCM_TESTS = ['hg', 'git']
85 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
86
87 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
88
89 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
90 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
91 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
92
93
94 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
95
96 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
97 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
98 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
99
100 TEST_DIR = tempfile.gettempdir()
101 TEST_REPO_PREFIX = 'vcs-test'
102
103 # cached repos if any !
104 # comment out to get some other repos from bb or github
105 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
106 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
107
108
109 def get_new_dir(title):
110 """
111 Returns always new directory path.
112 """
113 from rhodecode.tests.vcs.utils import get_normalized_path
114 name = TEST_REPO_PREFIX
115 if title:
116 name = '-'.join((name, title))
117 hex = hashlib.sha1(str(time.time())).hexdigest()
118 name = '-'.join((name, hex))
119 path = os.path.join(TEST_DIR, name)
120 return get_normalized_path(path)
121
76 122
77 123 class TestController(TestCase):
78 124
@@ -90,8 +136,8 b' class TestController(TestCase):'
90 136 password=TEST_USER_ADMIN_PASS):
91 137 self._logged_username = username
92 138 response = self.app.post(url(controller='login', action='index'),
93 {'username':username,
94 'password':password})
139 {'username': username,
140 'password': password})
95 141
96 142 if 'invalid user name' in response.body:
97 143 self.fail('could not login using %s %s' % (username, password))
@@ -6,6 +6,7 b' from rhodecode.lib import vcs'
6 6 from rhodecode.model.db import Repository
7 7 from rhodecode.tests import *
8 8
9
9 10 class TestAdminReposController(TestController):
10 11
11 12 def __make_repo(self):
@@ -24,17 +25,19 b' class TestAdminReposController(TestContr'
24 25 repo_name = NEW_HG_REPO
25 26 description = 'description for newly created repo'
26 27 private = False
27 response = self.app.post(url('repos'), {'repo_name':repo_name,
28 'repo_type':'hg',
29 'clone_uri':'',
30 'repo_group':'',
31 'description':description,
32 'private':private})
33 self.checkSessionFlash(response, 'created repository %s' % (repo_name))
28 response = self.app.post(url('repos'), {'repo_name': repo_name,
29 'repo_type': 'hg',
30 'clone_uri': '',
31 'repo_group': '',
32 'description': description,
33 'private': private,
34 'landing_rev': 'tip'})
35 self.checkSessionFlash(response,
36 'created repository %s' % (repo_name))
34 37
35 38 #test if the repo was created in the database
36 new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
37 repo_name).one()
39 new_repo = self.Session.query(Repository)\
40 .filter(Repository.repo_name == repo_name).one()
38 41
39 42 self.assertEqual(new_repo.repo_name, repo_name)
40 43 self.assertEqual(new_repo.description, description)
@@ -42,15 +45,13 b' class TestAdminReposController(TestContr'
42 45 #test if repository is visible in the list ?
43 46 response = response.follow()
44 47
45 self.assertTrue(repo_name in response.body)
46
48 response.mustcontain(repo_name)
47 49
48 50 #test if repository was created on filesystem
49 51 try:
50 52 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
51 53 except:
52 self.fail('no repo in filesystem')
53
54 self.fail('no repo %s in filesystem' % repo_name)
54 55
55 56 def test_create_hg_non_ascii(self):
56 57 self.log_user()
@@ -60,18 +61,19 b' class TestAdminReposController(TestContr'
60 61 description = 'description for newly created repo' + non_ascii
61 62 description_unicode = description.decode('utf8')
62 63 private = False
63 response = self.app.post(url('repos'), {'repo_name':repo_name,
64 'repo_type':'hg',
65 'clone_uri':'',
66 'repo_group':'',
67 'description':description,
68 'private':private})
64 response = self.app.post(url('repos'), {'repo_name': repo_name,
65 'repo_type': 'hg',
66 'clone_uri': '',
67 'repo_group': '',
68 'description': description,
69 'private': private,
70 'landing_rev': 'tip'})
69 71 self.checkSessionFlash(response,
70 72 'created repository %s' % (repo_name_unicode))
71 73
72 74 #test if the repo was created in the database
73 new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
74 repo_name_unicode).one()
75 new_repo = self.Session.query(Repository)\
76 .filter(Repository.repo_name == repo_name_unicode).one()
75 77
76 78 self.assertEqual(new_repo.repo_name, repo_name_unicode)
77 79 self.assertEqual(new_repo.description, description_unicode)
@@ -79,52 +81,86 b' class TestAdminReposController(TestContr'
79 81 #test if repository is visible in the list ?
80 82 response = response.follow()
81 83
82 self.assertTrue(repo_name in response.body)
84 response.mustcontain(repo_name)
83 85
84 86 #test if repository was created on filesystem
85 87 try:
86 88 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
87 89 except:
88 self.fail('no repo in filesystem')
89
90 self.fail('no repo %s in filesystem' % repo_name)
90 91
91 92 def test_create_hg_in_group(self):
92 93 #TODO: write test !
93 94 pass
94 95
95 96 def test_create_git(self):
96 return
97 97 self.log_user()
98 98 repo_name = NEW_GIT_REPO
99 99 description = 'description for newly created repo'
100 100 private = False
101 response = self.app.post(url('repos'), {'repo_name':repo_name,
102 'repo_type':'git',
103 'clone_uri':'',
104 'repo_group':'',
105 'description':description,
106 'private':private})
107
101 response = self.app.post(url('repos'), {'repo_name': repo_name,
102 'repo_type': 'git',
103 'clone_uri': '',
104 'repo_group': '',
105 'description': description,
106 'private': private,
107 'landing_rev': 'tip'})
108 self.checkSessionFlash(response,
109 'created repository %s' % (repo_name))
108 110
109 #test if we have a message for that repository
110 assert '''created repository %s''' % (repo_name) in response.session['flash'][0], 'No flash message about new repo'
111 #test if the repo was created in the database
112 new_repo = self.Session.query(Repository)\
113 .filter(Repository.repo_name == repo_name).one()
111 114
112 #test if the fork was created in the database
113 new_repo = self.Session.query(Repository).filter(Repository.repo_name == repo_name).one()
114
115 assert new_repo.repo_name == repo_name, 'wrong name of repo name in db'
116 assert new_repo.description == description, 'wrong description'
115 self.assertEqual(new_repo.repo_name, repo_name)
116 self.assertEqual(new_repo.description, description)
117 117
118 118 #test if repository is visible in the list ?
119 119 response = response.follow()
120 120
121 assert repo_name in response.body, 'missing new repo from the main repos list'
121 response.mustcontain(repo_name)
122 122
123 123 #test if repository was created on filesystem
124 124 try:
125 125 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
126 126 except:
127 assert False , 'no repo in filesystem'
127 self.fail('no repo %s in filesystem' % repo_name)
128
129 def test_create_git_non_ascii(self):
130 self.log_user()
131 non_ascii = "Δ…Δ™Ε‚"
132 repo_name = "%s%s" % (NEW_GIT_REPO, non_ascii)
133 repo_name_unicode = repo_name.decode('utf8')
134 description = 'description for newly created repo' + non_ascii
135 description_unicode = description.decode('utf8')
136 private = False
137 response = self.app.post(url('repos'), {'repo_name': repo_name,
138 'repo_type': 'git',
139 'clone_uri': '',
140 'repo_group': '',
141 'description': description,
142 'private': private,
143 'landing_rev': 'tip'})
144 self.checkSessionFlash(response,
145 'created repository %s' % (repo_name_unicode))
146
147 #test if the repo was created in the database
148 new_repo = self.Session.query(Repository)\
149 .filter(Repository.repo_name == repo_name_unicode).one()
150
151 self.assertEqual(new_repo.repo_name, repo_name_unicode)
152 self.assertEqual(new_repo.description, description_unicode)
153
154 #test if repository is visible in the list ?
155 response = response.follow()
156
157 response.mustcontain(repo_name)
158
159 #test if repository was created on filesystem
160 try:
161 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
162 except:
163 self.fail('no repo %s in filesystem' % repo_name)
128 164
129 165 def test_new(self):
130 166 self.log_user()
@@ -140,27 +176,24 b' class TestAdminReposController(TestContr'
140 176 response = self.app.post(url('repo', repo_name=HG_REPO),
141 177 params=dict(_method='put'))
142 178
143 def test_delete(self):
179 def test_delete_hg(self):
144 180 self.log_user()
145 181 repo_name = 'vcs_test_new_to_delete'
146 182 description = 'description for newly created repo'
147 183 private = False
148
149 response = self.app.post(url('repos'), {'repo_name':repo_name,
150 'repo_type':'hg',
151 'clone_uri':'',
152 'repo_group':'',
153 'description':description,
154 'private':private})
155 self.assertTrue('flash' in response.session)
156
157 #test if we have a message for that repository
158 self.assertTrue('''created repository %s''' % (repo_name) in
159 response.session['flash'][0])
184 response = self.app.post(url('repos'), {'repo_name': repo_name,
185 'repo_type': 'hg',
186 'clone_uri': '',
187 'repo_group': '',
188 'description': description,
189 'private': private,
190 'landing_rev': 'tip'})
191 self.checkSessionFlash(response,
192 'created repository %s' % (repo_name))
160 193
161 194 #test if the repo was created in the database
162 new_repo = self.Session.query(Repository).filter(Repository.repo_name ==
163 repo_name).one()
195 new_repo = self.Session.query(Repository)\
196 .filter(Repository.repo_name == repo_name).one()
164 197
165 198 self.assertEqual(new_repo.repo_name, repo_name)
166 199 self.assertEqual(new_repo.description, description)
@@ -168,8 +201,13 b' class TestAdminReposController(TestContr'
168 201 #test if repository is visible in the list ?
169 202 response = response.follow()
170 203
171 self.assertTrue(repo_name in response.body)
204 response.mustcontain(repo_name)
172 205
206 #test if repository was created on filesystem
207 try:
208 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
209 except:
210 self.fail('no repo %s in filesystem' % repo_name)
173 211
174 212 response = self.app.delete(url('repo', repo_name=repo_name))
175 213
@@ -179,32 +217,79 b' class TestAdminReposController(TestContr'
179 217 response.follow()
180 218
181 219 #check if repo was deleted from db
182 deleted_repo = self.Session.query(Repository).filter(Repository.repo_name
183 == repo_name).scalar()
220 deleted_repo = self.Session.query(Repository)\
221 .filter(Repository.repo_name == repo_name).scalar()
184 222
185 223 self.assertEqual(deleted_repo, None)
186 224
225 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
226 False)
227
228 def test_delete_git(self):
229 self.log_user()
230 repo_name = 'vcs_test_new_to_delete'
231 description = 'description for newly created repo'
232 private = False
233 response = self.app.post(url('repos'), {'repo_name': repo_name,
234 'repo_type': 'git',
235 'clone_uri': '',
236 'repo_group': '',
237 'description': description,
238 'private': private,
239 'landing_rev': 'tip'})
240 self.checkSessionFlash(response,
241 'created repository %s' % (repo_name))
242
243 #test if the repo was created in the database
244 new_repo = self.Session.query(Repository)\
245 .filter(Repository.repo_name == repo_name).one()
246
247 self.assertEqual(new_repo.repo_name, repo_name)
248 self.assertEqual(new_repo.description, description)
249
250 #test if repository is visible in the list ?
251 response = response.follow()
252
253 response.mustcontain(repo_name)
254
255 #test if repository was created on filesystem
256 try:
257 vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name))
258 except:
259 self.fail('no repo %s in filesystem' % repo_name)
260
261 response = self.app.delete(url('repo', repo_name=repo_name))
262
263 self.assertTrue('''deleted repository %s''' % (repo_name) in
264 response.session['flash'][0])
265
266 response.follow()
267
268 #check if repo was deleted from db
269 deleted_repo = self.Session.query(Repository)\
270 .filter(Repository.repo_name == repo_name).scalar()
271
272 self.assertEqual(deleted_repo, None)
273
274 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
275 False)
187 276
188 277 def test_delete_repo_with_group(self):
189 278 #TODO:
190 279 pass
191 280
192
193 281 def test_delete_browser_fakeout(self):
194 282 response = self.app.post(url('repo', repo_name=HG_REPO),
195 283 params=dict(_method='delete'))
196 284
197 def test_show(self):
285 def test_show_hg(self):
198 286 self.log_user()
199 287 response = self.app.get(url('repo', repo_name=HG_REPO))
200 288
201 def test_show_as_xml(self):
202 response = self.app.get(url('formatted_repo', repo_name=HG_REPO,
203 format='xml'))
289 def test_show_git(self):
290 self.log_user()
291 response = self.app.get(url('repo', repo_name=GIT_REPO))
292
204 293
205 294 def test_edit(self):
206 295 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
207
208 def test_edit_as_xml(self):
209 response = self.app.get(url('formatted_edit_repo', repo_name=HG_REPO,
210 format='xml'))
@@ -186,6 +186,14 b' class TestFilesController(TestController'
186 186
187 187 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
188 188
189 def test_file_annotation_git(self):
190 self.log_user()
191 response = self.app.get(url(controller='files', action='index',
192 repo_name=GIT_REPO,
193 revision='master',
194 f_path='vcs/nodes.py',
195 annotate=True))
196
189 197 def test_archival(self):
190 198 self.log_user()
191 199
@@ -28,7 +28,7 b' class TestForksController(TestController'
28 28
29 29 self.assertTrue("""There are no forks yet""" in response.body)
30 30
31 def test_index_with_fork(self):
31 def test_index_with_fork_hg(self):
32 32 self.log_user()
33 33
34 34 # create a fork
@@ -39,19 +39,49 b' class TestForksController(TestController'
39 39 response = self.app.post(url(controller='forks',
40 40 action='fork_create',
41 41 repo_name=repo_name),
42 {'repo_name':fork_name,
43 'repo_group':'',
44 'fork_parent_id':org_repo.repo_id,
45 'repo_type':'hg',
46 'description':description,
47 'private':'False'})
42 {'repo_name': fork_name,
43 'repo_group': '',
44 'fork_parent_id': org_repo.repo_id,
45 'repo_type': 'hg',
46 'description': description,
47 'private': 'False',
48 'landing_rev': 'tip'})
48 49
49 50 response = self.app.get(url(controller='forks', action='forks',
50 51 repo_name=repo_name))
51 52
52 self.assertTrue("""<a href="/%s/summary">"""
53 """vcs_test_hg_fork</a>""" % fork_name
54 in response.body)
53 response.mustcontain(
54 """<a href="/%s/summary">%s</a>""" % (fork_name, fork_name)
55 )
56
57 #remove this fork
58 response = self.app.delete(url('repo', repo_name=fork_name))
59
60 def test_index_with_fork_git(self):
61 self.log_user()
62
63 # create a fork
64 fork_name = GIT_FORK
65 description = 'fork of vcs test'
66 repo_name = GIT_REPO
67 org_repo = Repository.get_by_repo_name(repo_name)
68 response = self.app.post(url(controller='forks',
69 action='fork_create',
70 repo_name=repo_name),
71 {'repo_name': fork_name,
72 'repo_group': '',
73 'fork_parent_id': org_repo.repo_id,
74 'repo_type': 'git',
75 'description': description,
76 'private': 'False',
77 'landing_rev': 'tip'})
78
79 response = self.app.get(url(controller='forks', action='forks',
80 repo_name=repo_name))
81
82 response.mustcontain(
83 """<a href="/%s/summary">%s</a>""" % (fork_name, fork_name)
84 )
55 85
56 86 #remove this fork
57 87 response = self.app.delete(url('repo', repo_name=fork_name))
@@ -69,11 +99,12 b' class TestForksController(TestController'
69 99 'fork_parent_id':org_repo.repo_id,
70 100 'repo_type':'hg',
71 101 'description':description,
72 'private':'False'})
102 'private':'False',
103 'landing_rev': 'tip'})
73 104
74 105 #test if we have a message that fork is ok
75 self.assertTrue('forked %s repository as %s' \
76 % (repo_name, fork_name) in response.session['flash'][0])
106 self.checkSessionFlash(response,
107 'forked %s repository as %s' % (repo_name, fork_name))
77 108
78 109 #test if the fork was created in the database
79 110 fork_repo = self.Session.query(Repository)\
@@ -85,10 +116,6 b' class TestForksController(TestController'
85 116 #test if fork is visible in the list ?
86 117 response = response.follow()
87 118
88 # check if fork is marked as fork
89 # wait for cache to expire
90 import time
91 time.sleep(10)
92 119 response = self.app.get(url(controller='summary', action='index',
93 120 repo_name=fork_name))
94 121
@@ -1,7 +1,8 b''
1 import os
1 2 from rhodecode.tests import *
2 import os
3 3 from nose.plugins.skip import SkipTest
4 4
5
5 6 class TestSearchController(TestController):
6 7
7 8 def test_index(self):
@@ -18,20 +19,19 b' class TestSearchController(TestControlle'
18 19 else:
19 20 self.log_user()
20 21 response = self.app.get(url(controller='search', action='index'),
21 {'q':HG_REPO})
22 {'q': HG_REPO})
22 23 self.assertTrue('There is no index to search in. '
23 24 'Please run whoosh indexer' in response.body)
24 25
25 26 def test_normal_search(self):
26 27 self.log_user()
27 28 response = self.app.get(url(controller='search', action='index'),
28 {'q':'def repo'})
29 self.assertTrue('10 results' in response.body)
30 self.assertTrue('Permission denied' not in response.body)
29 {'q': 'def repo'})
30 response.mustcontain('39 results')
31 31
32 32 def test_repo_search(self):
33 33 self.log_user()
34 34 response = self.app.get(url(controller='search', action='index'),
35 {'q':'repository:%s def test' % HG_REPO})
36 self.assertTrue('4 results' in response.body)
37 self.assertTrue('Permission denied' not in response.body)
35 {'q': 'repository:%s def test' % HG_REPO})
36
37 response.mustcontain('4 results')
@@ -15,8 +15,8 b' class TestSummaryController(TestControll'
15 15 #repo type
16 16 response.mustcontain(
17 17 """<img style="margin-bottom:2px" class="icon" """
18 """title="Mercurial repository" alt="Mercurial """
19 """repository" src="/images/icons/hgicon.png"/>"""
18 """title="Mercurial repository" alt="Mercurial repository" """
19 """src="/images/icons/hgicon.png"/>"""
20 20 )
21 21 response.mustcontain(
22 22 """<img style="margin-bottom:2px" class="icon" """
@@ -41,10 +41,33 b' class TestSummaryController(TestControll'
41 41 )
42 42
43 43 # clone url...
44 response.mustcontain("""<input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/vcs_test_hg"/>""")
45 response.mustcontain("""<input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_1"/>""")
44 response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % HG_REPO)
45 response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
46
47 def test_index_git(self):
48 self.log_user()
49 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
50 response = self.app.get(url(controller='summary',
51 action='index',
52 repo_name=GIT_REPO))
46 53
47 def test_index_by_id(self):
54 #repo type
55 response.mustcontain(
56 """<img style="margin-bottom:2px" class="icon" """
57 """title="Git repository" alt="Git repository" """
58 """src="/images/icons/giticon.png"/>"""
59 )
60 response.mustcontain(
61 """<img style="margin-bottom:2px" class="icon" """
62 """title="public repository" alt="public """
63 """repository" src="/images/icons/lock_open.png"/>"""
64 )
65
66 # clone url...
67 response.mustcontain("""<input style="width:80%%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"/>""" % GIT_REPO)
68 response.mustcontain("""<input style="display:none;width:80%%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"/>""" % ID)
69
70 def test_index_by_id_hg(self):
48 71 self.log_user()
49 72 ID = Repository.get_by_repo_name(HG_REPO).repo_id
50 73 response = self.app.get(url(controller='summary',
@@ -59,6 +82,21 b' class TestSummaryController(TestControll'
59 82 """title="public repository" alt="public """
60 83 """repository" src="/images/icons/lock_open.png"/>""")
61 84
85 def test_index_by_id_git(self):
86 self.log_user()
87 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
88 response = self.app.get(url(controller='summary',
89 action='index',
90 repo_name='_%s' % ID))
91
92 #repo type
93 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
94 """title="Git repository" alt="Git """
95 """repository" src="/images/icons/giticon.png"/>""")
96 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
97 """title="public repository" alt="public """
98 """repository" src="/images/icons/lock_open.png"/>""")
99
62 100 def _enable_stats(self):
63 101 r = Repository.get_by_repo_name(HG_REPO)
64 102 r.enable_statistics = True
@@ -136,7 +136,8 b' class TestReposGroups(unittest.TestCase)'
136 136 repo_group=None,
137 137 private=False,
138 138 repo_type='hg',
139 clone_uri=None)
139 clone_uri=None,
140 landing_rev='tip')
140 141 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
141 142 r = RepoModel().create(form_data, cur_user)
142 143
@@ -653,7 +654,6 b' class TestPermissions(unittest.TestCase)'
653 654 user=self.anon,
654 655 perm='group.none')
655 656
656
657 657 u1_auth = AuthUser(user_id=self.u1.user_id)
658 658 self.assertEqual(u1_auth.permissions['repositories_groups'],
659 659 {u'group1': u'group.none', u'group2': u'group.none'})
@@ -664,13 +664,14 b' class TestPermissions(unittest.TestCase)'
664 664
665 665 # add repo to group
666 666 form_data = {
667 'repo_name':HG_REPO,
668 'repo_name_full':RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
669 'repo_type':'hg',
670 'clone_uri':'',
671 'repo_group':self.g1.group_id,
672 'description':'desc',
673 'private':False
667 'repo_name': HG_REPO,
668 'repo_name_full': RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
669 'repo_type': 'hg',
670 'clone_uri': '',
671 'repo_group': self.g1.group_id,
672 'description': 'desc',
673 'private': False,
674 'landing_rev': 'tip'
674 675 }
675 676 self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
676 677 Session.commit()
1 NO CONTENT: modified file, binary diff hidden
General Comments 0
You need to be logged in to leave comments. Login now