##// END OF EJS Templates
tests: self.fail is better written as pytest.fail now
domruf -
r5723:1883a4e4 default
parent child Browse files
Show More
@@ -1,274 +1,274 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
14
15 """
15 """
16 Pylons application test package
16 Pylons application test package
17
17
18 This package assumes the Pylons environment is already loaded.
18 This package assumes the Pylons environment is already loaded.
19
19
20 This module initializes the application via ``websetup`` (`paster
20 This module initializes the application via ``websetup`` (`paster
21 setup-app`) and provides the base testing objects.
21 setup-app`) and provides the base testing objects.
22
22
23 Refer to docs/contributing.rst for details on running the test suite.
23 Refer to docs/contributing.rst for details on running the test suite.
24 """
24 """
25 import os
25 import os
26 import re
26 import re
27 import time
27 import time
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import hashlib
30 import hashlib
31 import tempfile
31 import tempfile
32 from os.path import join as jn
32 from os.path import join as jn
33
33
34 from tempfile import _RandomNameSequence
34 from tempfile import _RandomNameSequence
35
35
36 import pylons
36 import pylons
37 import pylons.test
37 import pylons.test
38 from pylons import config, url
38 from pylons import config, url
39 from pylons.i18n.translation import _get_translator
39 from pylons.i18n.translation import _get_translator
40 from pylons.util import ContextObj
40 from pylons.util import ContextObj
41
41
42 from routes.util import URLGenerator
42 from routes.util import URLGenerator
43 from webtest import TestApp
43 from webtest import TestApp
44 import pytest
44 import pytest
45
45
46 from kallithea.lib.compat import unittest
46 from kallithea.lib.compat import unittest
47 from kallithea import is_windows
47 from kallithea import is_windows
48 from kallithea.model.db import Notification, User, UserNotification
48 from kallithea.model.db import Notification, User, UserNotification
49 from kallithea.model.meta import Session
49 from kallithea.model.meta import Session
50 from kallithea.tests.parameterized import parameterized
50 from kallithea.tests.parameterized import parameterized
51 from kallithea.lib.utils2 import safe_str
51 from kallithea.lib.utils2 import safe_str
52
52
53
53
54 os.environ['TZ'] = 'UTC'
54 os.environ['TZ'] = 'UTC'
55 if not is_windows:
55 if not is_windows:
56 time.tzset()
56 time.tzset()
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60 skipif = pytest.mark.skipif
60 skipif = pytest.mark.skipif
61 parametrize = pytest.mark.parametrize
61 parametrize = pytest.mark.parametrize
62
62
63 __all__ = [
63 __all__ = [
64 'skipif', 'parametrize', 'parameterized', 'environ', 'url', 'TestController', 'TestControllerPytest',
64 'skipif', 'parametrize', 'parameterized', 'environ', 'url', 'TestController', 'TestControllerPytest',
65 'ldap_lib_installed', 'pam_lib_installed', 'BaseTestCase', 'init_stack',
65 'ldap_lib_installed', 'pam_lib_installed', 'BaseTestCase', 'init_stack',
66 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
66 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
67 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
67 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
68 'TEST_USER_ADMIN_EMAIL', 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
68 'TEST_USER_ADMIN_EMAIL', 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
69 'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
69 'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
70 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
70 'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
71 'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
71 'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
72 'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
72 'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
73 'GIT_REMOTE_REPO', 'SCM_TESTS', 'remove_all_notifications',
73 'GIT_REMOTE_REPO', 'SCM_TESTS', 'remove_all_notifications',
74 ]
74 ]
75
75
76 # Invoke websetup with the current config file
76 # Invoke websetup with the current config file
77 # SetupCommand('setup-app').run([config_file])
77 # SetupCommand('setup-app').run([config_file])
78
78
79 environ = {}
79 environ = {}
80
80
81 #SOME GLOBALS FOR TESTS
81 #SOME GLOBALS FOR TESTS
82
82
83 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
83 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
84 TEST_USER_ADMIN_LOGIN = 'test_admin'
84 TEST_USER_ADMIN_LOGIN = 'test_admin'
85 TEST_USER_ADMIN_PASS = 'test12'
85 TEST_USER_ADMIN_PASS = 'test12'
86 TEST_USER_ADMIN_EMAIL = 'test_admin@example.com'
86 TEST_USER_ADMIN_EMAIL = 'test_admin@example.com'
87
87
88 TEST_USER_REGULAR_LOGIN = 'test_regular'
88 TEST_USER_REGULAR_LOGIN = 'test_regular'
89 TEST_USER_REGULAR_PASS = 'test12'
89 TEST_USER_REGULAR_PASS = 'test12'
90 TEST_USER_REGULAR_EMAIL = 'test_regular@example.com'
90 TEST_USER_REGULAR_EMAIL = 'test_regular@example.com'
91
91
92 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
92 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
93 TEST_USER_REGULAR2_PASS = 'test12'
93 TEST_USER_REGULAR2_PASS = 'test12'
94 TEST_USER_REGULAR2_EMAIL = 'test_regular2@example.com'
94 TEST_USER_REGULAR2_EMAIL = 'test_regular2@example.com'
95
95
96 HG_REPO = u'vcs_test_hg'
96 HG_REPO = u'vcs_test_hg'
97 GIT_REPO = u'vcs_test_git'
97 GIT_REPO = u'vcs_test_git'
98
98
99 NEW_HG_REPO = u'vcs_test_hg_new'
99 NEW_HG_REPO = u'vcs_test_hg_new'
100 NEW_GIT_REPO = u'vcs_test_git_new'
100 NEW_GIT_REPO = u'vcs_test_git_new'
101
101
102 HG_FORK = u'vcs_test_hg_fork'
102 HG_FORK = u'vcs_test_hg_fork'
103 GIT_FORK = u'vcs_test_git_fork'
103 GIT_FORK = u'vcs_test_git_fork'
104
104
105 ## VCS
105 ## VCS
106 SCM_TESTS = ['hg', 'git']
106 SCM_TESTS = ['hg', 'git']
107 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
107 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
108
108
109 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
109 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
110
110
111 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
111 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
112 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
112 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
113 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
113 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
114
114
115
115
116 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
116 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
117
117
118 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
118 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
119 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
119 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
120 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
120 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
121
121
122 TEST_DIR = tempfile.gettempdir()
122 TEST_DIR = tempfile.gettempdir()
123 TEST_REPO_PREFIX = 'vcs-test'
123 TEST_REPO_PREFIX = 'vcs-test'
124
124
125 # cached repos if any !
125 # cached repos if any !
126 # comment out to get some other repos from bb or github
126 # comment out to get some other repos from bb or github
127 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
127 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
128 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
128 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
129
129
130 #skip ldap tests if LDAP lib is not installed
130 #skip ldap tests if LDAP lib is not installed
131 ldap_lib_installed = False
131 ldap_lib_installed = False
132 try:
132 try:
133 import ldap
133 import ldap
134 ldap.API_VERSION
134 ldap.API_VERSION
135 ldap_lib_installed = True
135 ldap_lib_installed = True
136 except ImportError:
136 except ImportError:
137 # means that python-ldap is not installed
137 # means that python-ldap is not installed
138 pass
138 pass
139
139
140 try:
140 try:
141 import pam
141 import pam
142 pam.PAM_TEXT_INFO
142 pam.PAM_TEXT_INFO
143 pam_lib_installed = True
143 pam_lib_installed = True
144 except ImportError:
144 except ImportError:
145 pam_lib_installed = False
145 pam_lib_installed = False
146
146
147 class NullHandler(logging.Handler):
147 class NullHandler(logging.Handler):
148 def emit(self, record):
148 def emit(self, record):
149 pass
149 pass
150
150
151 def init_stack(config=None):
151 def init_stack(config=None):
152 if not config:
152 if not config:
153 config = pylons.test.pylonsapp.config
153 config = pylons.test.pylonsapp.config
154 url._push_object(URLGenerator(config['routes.map'], environ))
154 url._push_object(URLGenerator(config['routes.map'], environ))
155 pylons.app_globals._push_object(config['pylons.app_globals'])
155 pylons.app_globals._push_object(config['pylons.app_globals'])
156 pylons.config._push_object(config)
156 pylons.config._push_object(config)
157 pylons.tmpl_context._push_object(ContextObj())
157 pylons.tmpl_context._push_object(ContextObj())
158 # Initialize a translator for tests that utilize i18n
158 # Initialize a translator for tests that utilize i18n
159 translator = _get_translator(pylons.config.get('lang'))
159 translator = _get_translator(pylons.config.get('lang'))
160 pylons.translator._push_object(translator)
160 pylons.translator._push_object(translator)
161 h = NullHandler()
161 h = NullHandler()
162 logging.getLogger("kallithea").addHandler(h)
162 logging.getLogger("kallithea").addHandler(h)
163
163
164 def remove_all_notifications():
164 def remove_all_notifications():
165 Notification.query().delete()
165 Notification.query().delete()
166
166
167 # Because query().delete() does not (by default) trigger cascades.
167 # Because query().delete() does not (by default) trigger cascades.
168 # http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html#passive-deletes
168 # http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html#passive-deletes
169 UserNotification.query().delete()
169 UserNotification.query().delete()
170
170
171 Session().commit()
171 Session().commit()
172
172
173 class BaseTestCase(unittest.TestCase):
173 class BaseTestCase(unittest.TestCase):
174 """Unittest-style base test case. Deprecated in favor of pytest style."""
174 """Unittest-style base test case. Deprecated in favor of pytest style."""
175
175
176 def __init__(self, *args, **kwargs):
176 def __init__(self, *args, **kwargs):
177 self.wsgiapp = pylons.test.pylonsapp
177 self.wsgiapp = pylons.test.pylonsapp
178 init_stack(self.wsgiapp.config)
178 init_stack(self.wsgiapp.config)
179 unittest.TestCase.__init__(self, *args, **kwargs)
179 unittest.TestCase.__init__(self, *args, **kwargs)
180
180
181 class BaseTestController(object):
181 class BaseTestController(object):
182 """Base test controller used by pytest and unittest tests controllers."""
182 """Base test controller used by pytest and unittest tests controllers."""
183
183
184 # Note: pytest base classes cannot have an __init__ method
184 # Note: pytest base classes cannot have an __init__ method
185
185
186 def init(self):
186 def init(self):
187 self.app = TestApp(self.wsgiapp)
187 self.app = TestApp(self.wsgiapp)
188 self.maxDiff = None
188 self.maxDiff = None
189 self.index_location = config['app_conf']['index_dir']
189 self.index_location = config['app_conf']['index_dir']
190
190
191 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
191 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
192 password=TEST_USER_ADMIN_PASS):
192 password=TEST_USER_ADMIN_PASS):
193 self._logged_username = username
193 self._logged_username = username
194 response = self.app.post(url(controller='login', action='index'),
194 response = self.app.post(url(controller='login', action='index'),
195 {'username': username,
195 {'username': username,
196 'password': password})
196 'password': password})
197
197
198 if 'Invalid username or password' in response.body:
198 if 'Invalid username or password' in response.body:
199 self.fail('could not login using %s %s' % (username, password))
199 pytest.fail('could not login using %s %s' % (username, password))
200
200
201 self.assertEqual(response.status, '302 Found')
201 self.assertEqual(response.status, '302 Found')
202 self.assert_authenticated_user(response, username)
202 self.assert_authenticated_user(response, username)
203
203
204 response = response.follow()
204 response = response.follow()
205 return response.session['authuser']
205 return response.session['authuser']
206
206
207 def _get_logged_user(self):
207 def _get_logged_user(self):
208 return User.get_by_username(self._logged_username)
208 return User.get_by_username(self._logged_username)
209
209
210 def assert_authenticated_user(self, response, expected_username):
210 def assert_authenticated_user(self, response, expected_username):
211 cookie = response.session.get('authuser')
211 cookie = response.session.get('authuser')
212 user = cookie and cookie.get('user_id')
212 user = cookie and cookie.get('user_id')
213 user = user and User.get(user)
213 user = user and User.get(user)
214 user = user and user.username
214 user = user and user.username
215 self.assertEqual(user, expected_username)
215 self.assertEqual(user, expected_username)
216
216
217 def authentication_token(self):
217 def authentication_token(self):
218 return self.app.get(url('authentication_token')).body
218 return self.app.get(url('authentication_token')).body
219
219
220 def checkSessionFlash(self, response, msg=None, skip=0, _matcher=lambda msg, m: msg in m):
220 def checkSessionFlash(self, response, msg=None, skip=0, _matcher=lambda msg, m: msg in m):
221 if 'flash' not in response.session:
221 if 'flash' not in response.session:
222 self.fail(safe_str(u'msg `%s` not found - session has no flash:\n%s' % (msg, response)))
222 pytest.fail(safe_str(u'msg `%s` not found - session has no flash:\n%s' % (msg, response)))
223 try:
223 try:
224 level, m = response.session['flash'][-1 - skip]
224 level, m = response.session['flash'][-1 - skip]
225 if _matcher(msg, m):
225 if _matcher(msg, m):
226 return
226 return
227 except IndexError:
227 except IndexError:
228 pass
228 pass
229 self.fail(safe_str(u'msg `%s` not found in session flash (skipping %s): %s' %
229 pytest.fail(safe_str(u'msg `%s` not found in session flash (skipping %s): %s' %
230 (msg, skip,
230 (msg, skip,
231 ', '.join('`%s`' % m for level, m in response.session['flash']))))
231 ', '.join('`%s`' % m for level, m in response.session['flash']))))
232
232
233 def checkSessionFlashRegex(self, response, regex, skip=0):
233 def checkSessionFlashRegex(self, response, regex, skip=0):
234 self.checkSessionFlash(response, regex, skip=skip, _matcher=re.search)
234 self.checkSessionFlash(response, regex, skip=skip, _matcher=re.search)
235
235
236 class TestController(BaseTestCase, BaseTestController):
236 class TestController(BaseTestCase, BaseTestController):
237 """Deprecated unittest-style test controller"""
237 """Deprecated unittest-style test controller"""
238
238
239 def __init__(self, *args, **kwargs):
239 def __init__(self, *args, **kwargs):
240 super(TestController, self).__init__(*args, **kwargs)
240 super(TestController, self).__init__(*args, **kwargs)
241 self.init()
241 self.init()
242
242
243 class TestControllerPytest(BaseTestController):
243 class TestControllerPytest(BaseTestController):
244 """Pytest-style test controller"""
244 """Pytest-style test controller"""
245
245
246 # Note: pytest base classes cannot have an __init__ method
246 # Note: pytest base classes cannot have an __init__ method
247
247
248 @pytest.fixture(autouse=True)
248 @pytest.fixture(autouse=True)
249 def app_fixture(self):
249 def app_fixture(self):
250 self.wsgiapp = pylons.test.pylonsapp
250 self.wsgiapp = pylons.test.pylonsapp
251 init_stack(self.wsgiapp.config)
251 init_stack(self.wsgiapp.config)
252 self.init()
252 self.init()
253 return self.app
253 return self.app
254
254
255 # transitional implementations of unittest.TestCase asserts
255 # transitional implementations of unittest.TestCase asserts
256 # Users of these should be converted to pytest's single 'assert' call
256 # Users of these should be converted to pytest's single 'assert' call
257 def assertEqual(self, first, second, msg=None):
257 def assertEqual(self, first, second, msg=None):
258 assert first == second
258 assert first == second
259 def assertNotEqual(self, first, second, msg=None):
259 def assertNotEqual(self, first, second, msg=None):
260 assert first != second
260 assert first != second
261 def assertTrue(self, expr, msg=None):
261 def assertTrue(self, expr, msg=None):
262 assert bool(expr) is True
262 assert bool(expr) is True
263 def assertFalse(self, expr, msg=None):
263 def assertFalse(self, expr, msg=None):
264 assert bool(expr) is False
264 assert bool(expr) is False
265 def assertIn(self, first, second, msg=None):
265 def assertIn(self, first, second, msg=None):
266 assert first in second
266 assert first in second
267 def assertNotIn(self, first, second, msg=None):
267 def assertNotIn(self, first, second, msg=None):
268 assert first not in second
268 assert first not in second
269 def assertSetEqual(self, first, second, msg=None):
269 def assertSetEqual(self, first, second, msg=None):
270 assert first == second
270 assert first == second
271 def assertListEqual(self, first, second, msg=None):
271 def assertListEqual(self, first, second, msg=None):
272 assert first == second
272 assert first == second
273 def assertDictEqual(self, first, second, msg=None):
273 def assertDictEqual(self, first, second, msg=None):
274 assert first == second
274 assert first == second
@@ -1,620 +1,620 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 import os
3 import os
4 import mock
4 import mock
5 import urllib
5 import urllib
6
6
7 from kallithea.lib import vcs
7 from kallithea.lib import vcs
8 from kallithea.lib.utils2 import safe_str, safe_unicode
8 from kallithea.lib.utils2 import safe_str, safe_unicode
9 from kallithea.model.db import Repository, RepoGroup, UserRepoToPerm, User, \
9 from kallithea.model.db import Repository, RepoGroup, UserRepoToPerm, User, \
10 Permission
10 Permission
11 from kallithea.model.user import UserModel
11 from kallithea.model.user import UserModel
12 from kallithea.tests import *
12 from kallithea.tests import *
13 from kallithea.model.repo_group import RepoGroupModel
13 from kallithea.model.repo_group import RepoGroupModel
14 from kallithea.model.repo import RepoModel
14 from kallithea.model.repo import RepoModel
15 from kallithea.model.meta import Session
15 from kallithea.model.meta import Session
16 from kallithea.tests.fixture import Fixture, error_function
16 from kallithea.tests.fixture import Fixture, error_function
17
17
18 fixture = Fixture()
18 fixture = Fixture()
19
19
20
20
21 def _get_permission_for_user(user, repo):
21 def _get_permission_for_user(user, repo):
22 perm = UserRepoToPerm.query() \
22 perm = UserRepoToPerm.query() \
23 .filter(UserRepoToPerm.repository ==
23 .filter(UserRepoToPerm.repository ==
24 Repository.get_by_repo_name(repo)) \
24 Repository.get_by_repo_name(repo)) \
25 .filter(UserRepoToPerm.user == User.get_by_username(user)) \
25 .filter(UserRepoToPerm.user == User.get_by_username(user)) \
26 .all()
26 .all()
27 return perm
27 return perm
28
28
29
29
30 class _BaseTest(object):
30 class _BaseTest(object):
31 """
31 """
32 Write all tests here
32 Write all tests here
33 """
33 """
34 REPO = None
34 REPO = None
35 REPO_TYPE = None
35 REPO_TYPE = None
36 NEW_REPO = None
36 NEW_REPO = None
37 OTHER_TYPE_REPO = None
37 OTHER_TYPE_REPO = None
38 OTHER_TYPE = None
38 OTHER_TYPE = None
39
39
40 @classmethod
40 @classmethod
41 def setup_class(cls):
41 def setup_class(cls):
42 pass
42 pass
43
43
44 @classmethod
44 @classmethod
45 def teardown_class(cls):
45 def teardown_class(cls):
46 pass
46 pass
47
47
48 def test_index(self):
48 def test_index(self):
49 self.log_user()
49 self.log_user()
50 response = self.app.get(url('repos'))
50 response = self.app.get(url('repos'))
51
51
52 def test_create(self):
52 def test_create(self):
53 self.log_user()
53 self.log_user()
54 repo_name = self.NEW_REPO
54 repo_name = self.NEW_REPO
55 description = u'description for newly created repo'
55 description = u'description for newly created repo'
56 response = self.app.post(url('repos'),
56 response = self.app.post(url('repos'),
57 fixture._get_repo_create_params(repo_private=False,
57 fixture._get_repo_create_params(repo_private=False,
58 repo_name=repo_name,
58 repo_name=repo_name,
59 repo_type=self.REPO_TYPE,
59 repo_type=self.REPO_TYPE,
60 repo_description=description,
60 repo_description=description,
61 _authentication_token=self.authentication_token()))
61 _authentication_token=self.authentication_token()))
62 ## run the check page that triggers the flash message
62 ## run the check page that triggers the flash message
63 response = self.app.get(url('repo_check_home', repo_name=repo_name))
63 response = self.app.get(url('repo_check_home', repo_name=repo_name))
64 self.assertEqual(response.json, {u'result': True})
64 self.assertEqual(response.json, {u'result': True})
65 self.checkSessionFlash(response,
65 self.checkSessionFlash(response,
66 'Created repository <a href="/%s">%s</a>'
66 'Created repository <a href="/%s">%s</a>'
67 % (repo_name, repo_name))
67 % (repo_name, repo_name))
68
68
69 # test if the repo was created in the database
69 # test if the repo was created in the database
70 new_repo = Session().query(Repository) \
70 new_repo = Session().query(Repository) \
71 .filter(Repository.repo_name == repo_name).one()
71 .filter(Repository.repo_name == repo_name).one()
72
72
73 self.assertEqual(new_repo.repo_name, repo_name)
73 self.assertEqual(new_repo.repo_name, repo_name)
74 self.assertEqual(new_repo.description, description)
74 self.assertEqual(new_repo.description, description)
75
75
76 # test if the repository is visible in the list ?
76 # test if the repository is visible in the list ?
77 response = self.app.get(url('summary_home', repo_name=repo_name))
77 response = self.app.get(url('summary_home', repo_name=repo_name))
78 response.mustcontain(repo_name)
78 response.mustcontain(repo_name)
79 response.mustcontain(self.REPO_TYPE)
79 response.mustcontain(self.REPO_TYPE)
80
80
81 # test if the repository was created on filesystem
81 # test if the repository was created on filesystem
82 try:
82 try:
83 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name)))
83 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name)))
84 except vcs.exceptions.VCSError:
84 except vcs.exceptions.VCSError:
85 self.fail('no repo %s in filesystem' % repo_name)
85 pytest.fail('no repo %s in filesystem' % repo_name)
86
86
87 RepoModel().delete(repo_name)
87 RepoModel().delete(repo_name)
88 Session().commit()
88 Session().commit()
89
89
90 def test_create_in_group(self):
90 def test_create_in_group(self):
91 self.log_user()
91 self.log_user()
92
92
93 ## create GROUP
93 ## create GROUP
94 group_name = u'sometest_%s' % self.REPO_TYPE
94 group_name = u'sometest_%s' % self.REPO_TYPE
95 gr = RepoGroupModel().create(group_name=group_name,
95 gr = RepoGroupModel().create(group_name=group_name,
96 group_description=u'test',
96 group_description=u'test',
97 owner=TEST_USER_ADMIN_LOGIN)
97 owner=TEST_USER_ADMIN_LOGIN)
98 Session().commit()
98 Session().commit()
99
99
100 repo_name = u'ingroup'
100 repo_name = u'ingroup'
101 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
101 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
102 description = u'description for newly created repo'
102 description = u'description for newly created repo'
103 response = self.app.post(url('repos'),
103 response = self.app.post(url('repos'),
104 fixture._get_repo_create_params(repo_private=False,
104 fixture._get_repo_create_params(repo_private=False,
105 repo_name=repo_name,
105 repo_name=repo_name,
106 repo_type=self.REPO_TYPE,
106 repo_type=self.REPO_TYPE,
107 repo_description=description,
107 repo_description=description,
108 repo_group=gr.group_id,
108 repo_group=gr.group_id,
109 _authentication_token=self.authentication_token()))
109 _authentication_token=self.authentication_token()))
110 ## run the check page that triggers the flash message
110 ## run the check page that triggers the flash message
111 response = self.app.get(url('repo_check_home', repo_name=repo_name_full))
111 response = self.app.get(url('repo_check_home', repo_name=repo_name_full))
112 self.assertEqual(response.json, {u'result': True})
112 self.assertEqual(response.json, {u'result': True})
113 self.checkSessionFlash(response,
113 self.checkSessionFlash(response,
114 'Created repository <a href="/%s">%s</a>'
114 'Created repository <a href="/%s">%s</a>'
115 % (repo_name_full, repo_name_full))
115 % (repo_name_full, repo_name_full))
116 # test if the repo was created in the database
116 # test if the repo was created in the database
117 new_repo = Session().query(Repository) \
117 new_repo = Session().query(Repository) \
118 .filter(Repository.repo_name == repo_name_full).one()
118 .filter(Repository.repo_name == repo_name_full).one()
119 new_repo_id = new_repo.repo_id
119 new_repo_id = new_repo.repo_id
120
120
121 self.assertEqual(new_repo.repo_name, repo_name_full)
121 self.assertEqual(new_repo.repo_name, repo_name_full)
122 self.assertEqual(new_repo.description, description)
122 self.assertEqual(new_repo.description, description)
123
123
124 # test if the repository is visible in the list ?
124 # test if the repository is visible in the list ?
125 response = self.app.get(url('summary_home', repo_name=repo_name_full))
125 response = self.app.get(url('summary_home', repo_name=repo_name_full))
126 response.mustcontain(repo_name_full)
126 response.mustcontain(repo_name_full)
127 response.mustcontain(self.REPO_TYPE)
127 response.mustcontain(self.REPO_TYPE)
128
128
129 inherited_perms = UserRepoToPerm.query() \
129 inherited_perms = UserRepoToPerm.query() \
130 .filter(UserRepoToPerm.repository_id == new_repo_id).all()
130 .filter(UserRepoToPerm.repository_id == new_repo_id).all()
131 self.assertEqual(len(inherited_perms), 1)
131 self.assertEqual(len(inherited_perms), 1)
132
132
133 # test if the repository was created on filesystem
133 # test if the repository was created on filesystem
134 try:
134 try:
135 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name_full)))
135 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name_full)))
136 except vcs.exceptions.VCSError:
136 except vcs.exceptions.VCSError:
137 RepoGroupModel().delete(group_name)
137 RepoGroupModel().delete(group_name)
138 Session().commit()
138 Session().commit()
139 self.fail('no repo %s in filesystem' % repo_name)
139 pytest.fail('no repo %s in filesystem' % repo_name)
140
140
141 RepoModel().delete(repo_name_full)
141 RepoModel().delete(repo_name_full)
142 RepoGroupModel().delete(group_name)
142 RepoGroupModel().delete(group_name)
143 Session().commit()
143 Session().commit()
144
144
145 def test_create_in_group_without_needed_permissions(self):
145 def test_create_in_group_without_needed_permissions(self):
146 usr = self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
146 usr = self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
147 # avoid spurious RepoGroup DetachedInstanceError ...
147 # avoid spurious RepoGroup DetachedInstanceError ...
148 authentication_token = self.authentication_token()
148 authentication_token = self.authentication_token()
149 # revoke
149 # revoke
150 user_model = UserModel()
150 user_model = UserModel()
151 # disable fork and create on default user
151 # disable fork and create on default user
152 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
152 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
153 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
153 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
154 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
154 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
155 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
155 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
156
156
157 # disable on regular user
157 # disable on regular user
158 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
158 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
159 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
159 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
160 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
160 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
161 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
161 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
162 Session().commit()
162 Session().commit()
163
163
164 ## create GROUP
164 ## create GROUP
165 group_name = u'reg_sometest_%s' % self.REPO_TYPE
165 group_name = u'reg_sometest_%s' % self.REPO_TYPE
166 gr = RepoGroupModel().create(group_name=group_name,
166 gr = RepoGroupModel().create(group_name=group_name,
167 group_description=u'test',
167 group_description=u'test',
168 owner=TEST_USER_ADMIN_LOGIN)
168 owner=TEST_USER_ADMIN_LOGIN)
169 Session().commit()
169 Session().commit()
170
170
171 group_name_allowed = u'reg_sometest_allowed_%s' % self.REPO_TYPE
171 group_name_allowed = u'reg_sometest_allowed_%s' % self.REPO_TYPE
172 gr_allowed = RepoGroupModel().create(group_name=group_name_allowed,
172 gr_allowed = RepoGroupModel().create(group_name=group_name_allowed,
173 group_description=u'test',
173 group_description=u'test',
174 owner=TEST_USER_REGULAR_LOGIN)
174 owner=TEST_USER_REGULAR_LOGIN)
175 Session().commit()
175 Session().commit()
176
176
177 repo_name = u'ingroup'
177 repo_name = u'ingroup'
178 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
178 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
179 description = u'description for newly created repo'
179 description = u'description for newly created repo'
180 response = self.app.post(url('repos'),
180 response = self.app.post(url('repos'),
181 fixture._get_repo_create_params(repo_private=False,
181 fixture._get_repo_create_params(repo_private=False,
182 repo_name=repo_name,
182 repo_name=repo_name,
183 repo_type=self.REPO_TYPE,
183 repo_type=self.REPO_TYPE,
184 repo_description=description,
184 repo_description=description,
185 repo_group=gr.group_id,
185 repo_group=gr.group_id,
186 _authentication_token=authentication_token))
186 _authentication_token=authentication_token))
187
187
188 response.mustcontain('Invalid value')
188 response.mustcontain('Invalid value')
189
189
190 # user is allowed to create in this group
190 # user is allowed to create in this group
191 repo_name = u'ingroup'
191 repo_name = u'ingroup'
192 repo_name_full = RepoGroup.url_sep().join([group_name_allowed, repo_name])
192 repo_name_full = RepoGroup.url_sep().join([group_name_allowed, repo_name])
193 description = u'description for newly created repo'
193 description = u'description for newly created repo'
194 response = self.app.post(url('repos'),
194 response = self.app.post(url('repos'),
195 fixture._get_repo_create_params(repo_private=False,
195 fixture._get_repo_create_params(repo_private=False,
196 repo_name=repo_name,
196 repo_name=repo_name,
197 repo_type=self.REPO_TYPE,
197 repo_type=self.REPO_TYPE,
198 repo_description=description,
198 repo_description=description,
199 repo_group=gr_allowed.group_id,
199 repo_group=gr_allowed.group_id,
200 _authentication_token=authentication_token))
200 _authentication_token=authentication_token))
201
201
202 ## run the check page that triggers the flash message
202 ## run the check page that triggers the flash message
203 response = self.app.get(url('repo_check_home', repo_name=repo_name_full))
203 response = self.app.get(url('repo_check_home', repo_name=repo_name_full))
204 self.assertEqual(response.json, {u'result': True})
204 self.assertEqual(response.json, {u'result': True})
205 self.checkSessionFlash(response,
205 self.checkSessionFlash(response,
206 'Created repository <a href="/%s">%s</a>'
206 'Created repository <a href="/%s">%s</a>'
207 % (repo_name_full, repo_name_full))
207 % (repo_name_full, repo_name_full))
208 # test if the repo was created in the database
208 # test if the repo was created in the database
209 new_repo = Session().query(Repository) \
209 new_repo = Session().query(Repository) \
210 .filter(Repository.repo_name == repo_name_full).one()
210 .filter(Repository.repo_name == repo_name_full).one()
211 new_repo_id = new_repo.repo_id
211 new_repo_id = new_repo.repo_id
212
212
213 self.assertEqual(new_repo.repo_name, repo_name_full)
213 self.assertEqual(new_repo.repo_name, repo_name_full)
214 self.assertEqual(new_repo.description, description)
214 self.assertEqual(new_repo.description, description)
215
215
216 # test if the repository is visible in the list ?
216 # test if the repository is visible in the list ?
217 response = self.app.get(url('summary_home', repo_name=repo_name_full))
217 response = self.app.get(url('summary_home', repo_name=repo_name_full))
218 response.mustcontain(repo_name_full)
218 response.mustcontain(repo_name_full)
219 response.mustcontain(self.REPO_TYPE)
219 response.mustcontain(self.REPO_TYPE)
220
220
221 inherited_perms = UserRepoToPerm.query() \
221 inherited_perms = UserRepoToPerm.query() \
222 .filter(UserRepoToPerm.repository_id == new_repo_id).all()
222 .filter(UserRepoToPerm.repository_id == new_repo_id).all()
223 self.assertEqual(len(inherited_perms), 1)
223 self.assertEqual(len(inherited_perms), 1)
224
224
225 # test if the repository was created on filesystem
225 # test if the repository was created on filesystem
226 try:
226 try:
227 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name_full)))
227 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name_full)))
228 except vcs.exceptions.VCSError:
228 except vcs.exceptions.VCSError:
229 RepoGroupModel().delete(group_name)
229 RepoGroupModel().delete(group_name)
230 Session().commit()
230 Session().commit()
231 self.fail('no repo %s in filesystem' % repo_name)
231 pytest.fail('no repo %s in filesystem' % repo_name)
232
232
233 RepoModel().delete(repo_name_full)
233 RepoModel().delete(repo_name_full)
234 RepoGroupModel().delete(group_name)
234 RepoGroupModel().delete(group_name)
235 RepoGroupModel().delete(group_name_allowed)
235 RepoGroupModel().delete(group_name_allowed)
236 Session().commit()
236 Session().commit()
237
237
238 def test_create_in_group_inherit_permissions(self):
238 def test_create_in_group_inherit_permissions(self):
239 self.log_user()
239 self.log_user()
240
240
241 ## create GROUP
241 ## create GROUP
242 group_name = u'sometest_%s' % self.REPO_TYPE
242 group_name = u'sometest_%s' % self.REPO_TYPE
243 gr = RepoGroupModel().create(group_name=group_name,
243 gr = RepoGroupModel().create(group_name=group_name,
244 group_description=u'test',
244 group_description=u'test',
245 owner=TEST_USER_ADMIN_LOGIN)
245 owner=TEST_USER_ADMIN_LOGIN)
246 perm = Permission.get_by_key('repository.write')
246 perm = Permission.get_by_key('repository.write')
247 RepoGroupModel().grant_user_permission(gr, TEST_USER_REGULAR_LOGIN, perm)
247 RepoGroupModel().grant_user_permission(gr, TEST_USER_REGULAR_LOGIN, perm)
248
248
249 ## add repo permissions
249 ## add repo permissions
250 Session().commit()
250 Session().commit()
251
251
252 repo_name = u'ingroup_inherited_%s' % self.REPO_TYPE
252 repo_name = u'ingroup_inherited_%s' % self.REPO_TYPE
253 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
253 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
254 description = u'description for newly created repo'
254 description = u'description for newly created repo'
255 response = self.app.post(url('repos'),
255 response = self.app.post(url('repos'),
256 fixture._get_repo_create_params(repo_private=False,
256 fixture._get_repo_create_params(repo_private=False,
257 repo_name=repo_name,
257 repo_name=repo_name,
258 repo_type=self.REPO_TYPE,
258 repo_type=self.REPO_TYPE,
259 repo_description=description,
259 repo_description=description,
260 repo_group=gr.group_id,
260 repo_group=gr.group_id,
261 repo_copy_permissions=True,
261 repo_copy_permissions=True,
262 _authentication_token=self.authentication_token()))
262 _authentication_token=self.authentication_token()))
263
263
264 ## run the check page that triggers the flash message
264 ## run the check page that triggers the flash message
265 response = self.app.get(url('repo_check_home', repo_name=repo_name_full))
265 response = self.app.get(url('repo_check_home', repo_name=repo_name_full))
266 self.checkSessionFlash(response,
266 self.checkSessionFlash(response,
267 'Created repository <a href="/%s">%s</a>'
267 'Created repository <a href="/%s">%s</a>'
268 % (repo_name_full, repo_name_full))
268 % (repo_name_full, repo_name_full))
269 # test if the repo was created in the database
269 # test if the repo was created in the database
270 new_repo = Session().query(Repository) \
270 new_repo = Session().query(Repository) \
271 .filter(Repository.repo_name == repo_name_full).one()
271 .filter(Repository.repo_name == repo_name_full).one()
272 new_repo_id = new_repo.repo_id
272 new_repo_id = new_repo.repo_id
273
273
274 self.assertEqual(new_repo.repo_name, repo_name_full)
274 self.assertEqual(new_repo.repo_name, repo_name_full)
275 self.assertEqual(new_repo.description, description)
275 self.assertEqual(new_repo.description, description)
276
276
277 # test if the repository is visible in the list ?
277 # test if the repository is visible in the list ?
278 response = self.app.get(url('summary_home', repo_name=repo_name_full))
278 response = self.app.get(url('summary_home', repo_name=repo_name_full))
279 response.mustcontain(repo_name_full)
279 response.mustcontain(repo_name_full)
280 response.mustcontain(self.REPO_TYPE)
280 response.mustcontain(self.REPO_TYPE)
281
281
282 # test if the repository was created on filesystem
282 # test if the repository was created on filesystem
283 try:
283 try:
284 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name_full)))
284 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name_full)))
285 except vcs.exceptions.VCSError:
285 except vcs.exceptions.VCSError:
286 RepoGroupModel().delete(group_name)
286 RepoGroupModel().delete(group_name)
287 Session().commit()
287 Session().commit()
288 self.fail('no repo %s in filesystem' % repo_name)
288 pytest.fail('no repo %s in filesystem' % repo_name)
289
289
290 #check if inherited permissiona are applied
290 #check if inherited permissiona are applied
291 inherited_perms = UserRepoToPerm.query() \
291 inherited_perms = UserRepoToPerm.query() \
292 .filter(UserRepoToPerm.repository_id == new_repo_id).all()
292 .filter(UserRepoToPerm.repository_id == new_repo_id).all()
293 self.assertEqual(len(inherited_perms), 2)
293 self.assertEqual(len(inherited_perms), 2)
294
294
295 self.assertTrue(TEST_USER_REGULAR_LOGIN in [x.user.username
295 self.assertTrue(TEST_USER_REGULAR_LOGIN in [x.user.username
296 for x in inherited_perms])
296 for x in inherited_perms])
297 self.assertTrue('repository.write' in [x.permission.permission_name
297 self.assertTrue('repository.write' in [x.permission.permission_name
298 for x in inherited_perms])
298 for x in inherited_perms])
299
299
300 RepoModel().delete(repo_name_full)
300 RepoModel().delete(repo_name_full)
301 RepoGroupModel().delete(group_name)
301 RepoGroupModel().delete(group_name)
302 Session().commit()
302 Session().commit()
303
303
304 def test_create_remote_repo_wrong_clone_uri(self):
304 def test_create_remote_repo_wrong_clone_uri(self):
305 self.log_user()
305 self.log_user()
306 repo_name = self.NEW_REPO
306 repo_name = self.NEW_REPO
307 description = u'description for newly created repo'
307 description = u'description for newly created repo'
308 response = self.app.post(url('repos'),
308 response = self.app.post(url('repos'),
309 fixture._get_repo_create_params(repo_private=False,
309 fixture._get_repo_create_params(repo_private=False,
310 repo_name=repo_name,
310 repo_name=repo_name,
311 repo_type=self.REPO_TYPE,
311 repo_type=self.REPO_TYPE,
312 repo_description=description,
312 repo_description=description,
313 clone_uri='http://127.0.0.1/repo',
313 clone_uri='http://127.0.0.1/repo',
314 _authentication_token=self.authentication_token()))
314 _authentication_token=self.authentication_token()))
315 response.mustcontain('Invalid repository URL')
315 response.mustcontain('Invalid repository URL')
316
316
317
317
318 def test_create_remote_repo_wrong_clone_uri_hg_svn(self):
318 def test_create_remote_repo_wrong_clone_uri_hg_svn(self):
319 self.log_user()
319 self.log_user()
320 repo_name = self.NEW_REPO
320 repo_name = self.NEW_REPO
321 description = u'description for newly created repo'
321 description = u'description for newly created repo'
322 response = self.app.post(url('repos'),
322 response = self.app.post(url('repos'),
323 fixture._get_repo_create_params(repo_private=False,
323 fixture._get_repo_create_params(repo_private=False,
324 repo_name=repo_name,
324 repo_name=repo_name,
325 repo_type=self.REPO_TYPE,
325 repo_type=self.REPO_TYPE,
326 repo_description=description,
326 repo_description=description,
327 clone_uri='svn+http://127.0.0.1/repo',
327 clone_uri='svn+http://127.0.0.1/repo',
328 _authentication_token=self.authentication_token()))
328 _authentication_token=self.authentication_token()))
329 response.mustcontain('Invalid repository URL')
329 response.mustcontain('Invalid repository URL')
330
330
331
331
332 def test_delete(self):
332 def test_delete(self):
333 self.log_user()
333 self.log_user()
334 repo_name = u'vcs_test_new_to_delete_%s' % self.REPO_TYPE
334 repo_name = u'vcs_test_new_to_delete_%s' % self.REPO_TYPE
335 description = u'description for newly created repo'
335 description = u'description for newly created repo'
336 response = self.app.post(url('repos'),
336 response = self.app.post(url('repos'),
337 fixture._get_repo_create_params(repo_private=False,
337 fixture._get_repo_create_params(repo_private=False,
338 repo_type=self.REPO_TYPE,
338 repo_type=self.REPO_TYPE,
339 repo_name=repo_name,
339 repo_name=repo_name,
340 repo_description=description,
340 repo_description=description,
341 _authentication_token=self.authentication_token()))
341 _authentication_token=self.authentication_token()))
342 ## run the check page that triggers the flash message
342 ## run the check page that triggers the flash message
343 response = self.app.get(url('repo_check_home', repo_name=repo_name))
343 response = self.app.get(url('repo_check_home', repo_name=repo_name))
344 self.checkSessionFlash(response,
344 self.checkSessionFlash(response,
345 'Created repository <a href="/%s">%s</a>'
345 'Created repository <a href="/%s">%s</a>'
346 % (repo_name, repo_name))
346 % (repo_name, repo_name))
347 # test if the repo was created in the database
347 # test if the repo was created in the database
348 new_repo = Session().query(Repository) \
348 new_repo = Session().query(Repository) \
349 .filter(Repository.repo_name == repo_name).one()
349 .filter(Repository.repo_name == repo_name).one()
350
350
351 self.assertEqual(new_repo.repo_name, repo_name)
351 self.assertEqual(new_repo.repo_name, repo_name)
352 self.assertEqual(new_repo.description, description)
352 self.assertEqual(new_repo.description, description)
353
353
354 # test if the repository is visible in the list ?
354 # test if the repository is visible in the list ?
355 response = self.app.get(url('summary_home', repo_name=repo_name))
355 response = self.app.get(url('summary_home', repo_name=repo_name))
356 response.mustcontain(repo_name)
356 response.mustcontain(repo_name)
357 response.mustcontain(self.REPO_TYPE)
357 response.mustcontain(self.REPO_TYPE)
358
358
359 # test if the repository was created on filesystem
359 # test if the repository was created on filesystem
360 try:
360 try:
361 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name)))
361 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name)))
362 except vcs.exceptions.VCSError:
362 except vcs.exceptions.VCSError:
363 self.fail('no repo %s in filesystem' % repo_name)
363 pytest.fail('no repo %s in filesystem' % repo_name)
364
364
365 response = self.app.post(url('delete_repo', repo_name=repo_name),
365 response = self.app.post(url('delete_repo', repo_name=repo_name),
366 params={'_method': 'delete', '_authentication_token': self.authentication_token()})
366 params={'_method': 'delete', '_authentication_token': self.authentication_token()})
367
367
368 self.checkSessionFlash(response, 'Deleted repository %s' % (repo_name))
368 self.checkSessionFlash(response, 'Deleted repository %s' % (repo_name))
369
369
370 response.follow()
370 response.follow()
371
371
372 #check if repo was deleted from db
372 #check if repo was deleted from db
373 deleted_repo = Session().query(Repository) \
373 deleted_repo = Session().query(Repository) \
374 .filter(Repository.repo_name == repo_name).scalar()
374 .filter(Repository.repo_name == repo_name).scalar()
375
375
376 self.assertEqual(deleted_repo, None)
376 self.assertEqual(deleted_repo, None)
377
377
378 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
378 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
379 False)
379 False)
380
380
381 def test_delete_non_ascii(self):
381 def test_delete_non_ascii(self):
382 self.log_user()
382 self.log_user()
383 non_ascii = "Δ…Δ™Ε‚"
383 non_ascii = "Δ…Δ™Ε‚"
384 repo_name = "%s%s" % (safe_str(self.NEW_REPO), non_ascii)
384 repo_name = "%s%s" % (safe_str(self.NEW_REPO), non_ascii)
385 repo_name_unicode = safe_unicode(repo_name)
385 repo_name_unicode = safe_unicode(repo_name)
386 description = 'description for newly created repo' + non_ascii
386 description = 'description for newly created repo' + non_ascii
387 description_unicode = safe_unicode(description)
387 description_unicode = safe_unicode(description)
388 response = self.app.post(url('repos'),
388 response = self.app.post(url('repos'),
389 fixture._get_repo_create_params(repo_private=False,
389 fixture._get_repo_create_params(repo_private=False,
390 repo_name=repo_name,
390 repo_name=repo_name,
391 repo_type=self.REPO_TYPE,
391 repo_type=self.REPO_TYPE,
392 repo_description=description,
392 repo_description=description,
393 _authentication_token=self.authentication_token()))
393 _authentication_token=self.authentication_token()))
394 ## run the check page that triggers the flash message
394 ## run the check page that triggers the flash message
395 response = self.app.get(url('repo_check_home', repo_name=repo_name))
395 response = self.app.get(url('repo_check_home', repo_name=repo_name))
396 self.assertEqual(response.json, {u'result': True})
396 self.assertEqual(response.json, {u'result': True})
397 self.checkSessionFlash(response,
397 self.checkSessionFlash(response,
398 u'Created repository <a href="/%s">%s</a>'
398 u'Created repository <a href="/%s">%s</a>'
399 % (urllib.quote(repo_name), repo_name_unicode))
399 % (urllib.quote(repo_name), repo_name_unicode))
400 # test if the repo was created in the database
400 # test if the repo was created in the database
401 new_repo = Session().query(Repository) \
401 new_repo = Session().query(Repository) \
402 .filter(Repository.repo_name == repo_name_unicode).one()
402 .filter(Repository.repo_name == repo_name_unicode).one()
403
403
404 self.assertEqual(new_repo.repo_name, repo_name_unicode)
404 self.assertEqual(new_repo.repo_name, repo_name_unicode)
405 self.assertEqual(new_repo.description, description_unicode)
405 self.assertEqual(new_repo.description, description_unicode)
406
406
407 # test if the repository is visible in the list ?
407 # test if the repository is visible in the list ?
408 response = self.app.get(url('summary_home', repo_name=repo_name))
408 response = self.app.get(url('summary_home', repo_name=repo_name))
409 response.mustcontain(repo_name)
409 response.mustcontain(repo_name)
410 response.mustcontain(self.REPO_TYPE)
410 response.mustcontain(self.REPO_TYPE)
411
411
412 # test if the repository was created on filesystem
412 # test if the repository was created on filesystem
413 try:
413 try:
414 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name)))
414 vcs.get_repo(safe_str(os.path.join(TESTS_TMP_PATH, repo_name)))
415 except vcs.exceptions.VCSError:
415 except vcs.exceptions.VCSError:
416 self.fail('no repo %s in filesystem' % repo_name)
416 pytest.fail('no repo %s in filesystem' % repo_name)
417
417
418 response = self.app.post(url('delete_repo', repo_name=repo_name),
418 response = self.app.post(url('delete_repo', repo_name=repo_name),
419 params={'_method': 'delete', '_authentication_token': self.authentication_token()})
419 params={'_method': 'delete', '_authentication_token': self.authentication_token()})
420 self.checkSessionFlash(response, 'Deleted repository %s' % (repo_name_unicode))
420 self.checkSessionFlash(response, 'Deleted repository %s' % (repo_name_unicode))
421 response.follow()
421 response.follow()
422
422
423 #check if repo was deleted from db
423 #check if repo was deleted from db
424 deleted_repo = Session().query(Repository) \
424 deleted_repo = Session().query(Repository) \
425 .filter(Repository.repo_name == repo_name_unicode).scalar()
425 .filter(Repository.repo_name == repo_name_unicode).scalar()
426
426
427 self.assertEqual(deleted_repo, None)
427 self.assertEqual(deleted_repo, None)
428
428
429 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
429 self.assertEqual(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)),
430 False)
430 False)
431
431
432 def test_delete_repo_with_group(self):
432 def test_delete_repo_with_group(self):
433 #TODO:
433 #TODO:
434 pass
434 pass
435
435
436 def test_delete_browser_fakeout(self):
436 def test_delete_browser_fakeout(self):
437 response = self.app.post(url('delete_repo', repo_name=self.REPO),
437 response = self.app.post(url('delete_repo', repo_name=self.REPO),
438 params=dict(_method='delete', _authentication_token=self.authentication_token()))
438 params=dict(_method='delete', _authentication_token=self.authentication_token()))
439
439
440 def test_show(self):
440 def test_show(self):
441 self.log_user()
441 self.log_user()
442 response = self.app.get(url('summary_home', repo_name=self.REPO))
442 response = self.app.get(url('summary_home', repo_name=self.REPO))
443
443
444 def test_edit(self):
444 def test_edit(self):
445 response = self.app.get(url('edit_repo', repo_name=self.REPO))
445 response = self.app.get(url('edit_repo', repo_name=self.REPO))
446
446
447 def test_set_private_flag_sets_default_to_none(self):
447 def test_set_private_flag_sets_default_to_none(self):
448 self.log_user()
448 self.log_user()
449 #initially repository perm should be read
449 #initially repository perm should be read
450 perm = _get_permission_for_user(user='default', repo=self.REPO)
450 perm = _get_permission_for_user(user='default', repo=self.REPO)
451 self.assertTrue(len(perm), 1)
451 self.assertTrue(len(perm), 1)
452 self.assertEqual(perm[0].permission.permission_name, 'repository.read')
452 self.assertEqual(perm[0].permission.permission_name, 'repository.read')
453 self.assertEqual(Repository.get_by_repo_name(self.REPO).private, False)
453 self.assertEqual(Repository.get_by_repo_name(self.REPO).private, False)
454
454
455 response = self.app.put(url('put_repo', repo_name=self.REPO),
455 response = self.app.put(url('put_repo', repo_name=self.REPO),
456 fixture._get_repo_create_params(repo_private=1,
456 fixture._get_repo_create_params(repo_private=1,
457 repo_name=self.REPO,
457 repo_name=self.REPO,
458 repo_type=self.REPO_TYPE,
458 repo_type=self.REPO_TYPE,
459 user=TEST_USER_ADMIN_LOGIN,
459 user=TEST_USER_ADMIN_LOGIN,
460 _authentication_token=self.authentication_token()))
460 _authentication_token=self.authentication_token()))
461 self.checkSessionFlash(response,
461 self.checkSessionFlash(response,
462 msg='Repository %s updated successfully' % (self.REPO))
462 msg='Repository %s updated successfully' % (self.REPO))
463 self.assertEqual(Repository.get_by_repo_name(self.REPO).private, True)
463 self.assertEqual(Repository.get_by_repo_name(self.REPO).private, True)
464
464
465 #now the repo default permission should be None
465 #now the repo default permission should be None
466 perm = _get_permission_for_user(user='default', repo=self.REPO)
466 perm = _get_permission_for_user(user='default', repo=self.REPO)
467 self.assertTrue(len(perm), 1)
467 self.assertTrue(len(perm), 1)
468 self.assertEqual(perm[0].permission.permission_name, 'repository.none')
468 self.assertEqual(perm[0].permission.permission_name, 'repository.none')
469
469
470 response = self.app.put(url('put_repo', repo_name=self.REPO),
470 response = self.app.put(url('put_repo', repo_name=self.REPO),
471 fixture._get_repo_create_params(repo_private=False,
471 fixture._get_repo_create_params(repo_private=False,
472 repo_name=self.REPO,
472 repo_name=self.REPO,
473 repo_type=self.REPO_TYPE,
473 repo_type=self.REPO_TYPE,
474 user=TEST_USER_ADMIN_LOGIN,
474 user=TEST_USER_ADMIN_LOGIN,
475 _authentication_token=self.authentication_token()))
475 _authentication_token=self.authentication_token()))
476 self.checkSessionFlash(response,
476 self.checkSessionFlash(response,
477 msg='Repository %s updated successfully' % (self.REPO))
477 msg='Repository %s updated successfully' % (self.REPO))
478 self.assertEqual(Repository.get_by_repo_name(self.REPO).private, False)
478 self.assertEqual(Repository.get_by_repo_name(self.REPO).private, False)
479
479
480 #we turn off private now the repo default permission should stay None
480 #we turn off private now the repo default permission should stay None
481 perm = _get_permission_for_user(user='default', repo=self.REPO)
481 perm = _get_permission_for_user(user='default', repo=self.REPO)
482 self.assertTrue(len(perm), 1)
482 self.assertTrue(len(perm), 1)
483 self.assertEqual(perm[0].permission.permission_name, 'repository.none')
483 self.assertEqual(perm[0].permission.permission_name, 'repository.none')
484
484
485 #update this permission back
485 #update this permission back
486 perm[0].permission = Permission.get_by_key('repository.read')
486 perm[0].permission = Permission.get_by_key('repository.read')
487 Session().add(perm[0])
487 Session().add(perm[0])
488 Session().commit()
488 Session().commit()
489
489
490 def test_set_repo_fork_has_no_self_id(self):
490 def test_set_repo_fork_has_no_self_id(self):
491 self.log_user()
491 self.log_user()
492 repo = Repository.get_by_repo_name(self.REPO)
492 repo = Repository.get_by_repo_name(self.REPO)
493 response = self.app.get(url('edit_repo_advanced', repo_name=self.REPO))
493 response = self.app.get(url('edit_repo_advanced', repo_name=self.REPO))
494 opt = """<option value="%s">%s</option>""" % (repo.repo_id, self.REPO)
494 opt = """<option value="%s">%s</option>""" % (repo.repo_id, self.REPO)
495 response.mustcontain(no=[opt])
495 response.mustcontain(no=[opt])
496
496
497 def test_set_fork_of_other_repo(self):
497 def test_set_fork_of_other_repo(self):
498 self.log_user()
498 self.log_user()
499 other_repo = u'other_%s' % self.REPO_TYPE
499 other_repo = u'other_%s' % self.REPO_TYPE
500 fixture.create_repo(other_repo, repo_type=self.REPO_TYPE)
500 fixture.create_repo(other_repo, repo_type=self.REPO_TYPE)
501 repo = Repository.get_by_repo_name(self.REPO)
501 repo = Repository.get_by_repo_name(self.REPO)
502 repo2 = Repository.get_by_repo_name(other_repo)
502 repo2 = Repository.get_by_repo_name(other_repo)
503 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
503 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
504 params=dict(id_fork_of=repo2.repo_id, _authentication_token=self.authentication_token()))
504 params=dict(id_fork_of=repo2.repo_id, _authentication_token=self.authentication_token()))
505 repo = Repository.get_by_repo_name(self.REPO)
505 repo = Repository.get_by_repo_name(self.REPO)
506 repo2 = Repository.get_by_repo_name(other_repo)
506 repo2 = Repository.get_by_repo_name(other_repo)
507 self.checkSessionFlash(response,
507 self.checkSessionFlash(response,
508 'Marked repository %s as fork of %s' % (repo.repo_name, repo2.repo_name))
508 'Marked repository %s as fork of %s' % (repo.repo_name, repo2.repo_name))
509
509
510 assert repo.fork == repo2
510 assert repo.fork == repo2
511 response = response.follow()
511 response = response.follow()
512 # check if given repo is selected
512 # check if given repo is selected
513
513
514 opt = """<option value="%s" selected="selected">%s</option>""" % (
514 opt = """<option value="%s" selected="selected">%s</option>""" % (
515 repo2.repo_id, repo2.repo_name)
515 repo2.repo_id, repo2.repo_name)
516 response.mustcontain(opt)
516 response.mustcontain(opt)
517
517
518 fixture.destroy_repo(other_repo, forks='detach')
518 fixture.destroy_repo(other_repo, forks='detach')
519
519
520 def test_set_fork_of_other_type_repo(self):
520 def test_set_fork_of_other_type_repo(self):
521 self.log_user()
521 self.log_user()
522 repo = Repository.get_by_repo_name(self.REPO)
522 repo = Repository.get_by_repo_name(self.REPO)
523 repo2 = Repository.get_by_repo_name(self.OTHER_TYPE_REPO)
523 repo2 = Repository.get_by_repo_name(self.OTHER_TYPE_REPO)
524 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
524 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
525 params=dict(id_fork_of=repo2.repo_id, _authentication_token=self.authentication_token()))
525 params=dict(id_fork_of=repo2.repo_id, _authentication_token=self.authentication_token()))
526 repo = Repository.get_by_repo_name(self.REPO)
526 repo = Repository.get_by_repo_name(self.REPO)
527 repo2 = Repository.get_by_repo_name(self.OTHER_TYPE_REPO)
527 repo2 = Repository.get_by_repo_name(self.OTHER_TYPE_REPO)
528 self.checkSessionFlash(response,
528 self.checkSessionFlash(response,
529 'Cannot set repository as fork of repository with other type')
529 'Cannot set repository as fork of repository with other type')
530
530
531 def test_set_fork_of_none(self):
531 def test_set_fork_of_none(self):
532 self.log_user()
532 self.log_user()
533 ## mark it as None
533 ## mark it as None
534 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
534 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
535 params=dict(id_fork_of=None, _authentication_token=self.authentication_token()))
535 params=dict(id_fork_of=None, _authentication_token=self.authentication_token()))
536 repo = Repository.get_by_repo_name(self.REPO)
536 repo = Repository.get_by_repo_name(self.REPO)
537 repo2 = Repository.get_by_repo_name(self.OTHER_TYPE_REPO)
537 repo2 = Repository.get_by_repo_name(self.OTHER_TYPE_REPO)
538 self.checkSessionFlash(response,
538 self.checkSessionFlash(response,
539 'Marked repository %s as fork of %s'
539 'Marked repository %s as fork of %s'
540 % (repo.repo_name, "Nothing"))
540 % (repo.repo_name, "Nothing"))
541 assert repo.fork is None
541 assert repo.fork is None
542
542
543 def test_set_fork_of_same_repo(self):
543 def test_set_fork_of_same_repo(self):
544 self.log_user()
544 self.log_user()
545 repo = Repository.get_by_repo_name(self.REPO)
545 repo = Repository.get_by_repo_name(self.REPO)
546 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
546 response = self.app.put(url('edit_repo_advanced_fork', repo_name=self.REPO),
547 params=dict(id_fork_of=repo.repo_id, _authentication_token=self.authentication_token()))
547 params=dict(id_fork_of=repo.repo_id, _authentication_token=self.authentication_token()))
548 self.checkSessionFlash(response,
548 self.checkSessionFlash(response,
549 'An error occurred during this operation')
549 'An error occurred during this operation')
550
550
551 def test_create_on_top_level_without_permissions(self):
551 def test_create_on_top_level_without_permissions(self):
552 usr = self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
552 usr = self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
553 # revoke
553 # revoke
554 user_model = UserModel()
554 user_model = UserModel()
555 # disable fork and create on default user
555 # disable fork and create on default user
556 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
556 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
557 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
557 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
558 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
558 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
559 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
559 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
560
560
561 # disable on regular user
561 # disable on regular user
562 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
562 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
563 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
563 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
564 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
564 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
565 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
565 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
566 Session().commit()
566 Session().commit()
567
567
568
568
569 user = User.get(usr['user_id'])
569 user = User.get(usr['user_id'])
570
570
571 repo_name = self.NEW_REPO + u'no_perms'
571 repo_name = self.NEW_REPO + u'no_perms'
572 description = 'description for newly created repo'
572 description = 'description for newly created repo'
573 response = self.app.post(url('repos'),
573 response = self.app.post(url('repos'),
574 fixture._get_repo_create_params(repo_private=False,
574 fixture._get_repo_create_params(repo_private=False,
575 repo_name=repo_name,
575 repo_name=repo_name,
576 repo_type=self.REPO_TYPE,
576 repo_type=self.REPO_TYPE,
577 repo_description=description,
577 repo_description=description,
578 _authentication_token=self.authentication_token()))
578 _authentication_token=self.authentication_token()))
579
579
580 response.mustcontain('<span class="error-message">Invalid value</span>')
580 response.mustcontain('<span class="error-message">Invalid value</span>')
581
581
582 RepoModel().delete(repo_name)
582 RepoModel().delete(repo_name)
583 Session().commit()
583 Session().commit()
584
584
585 @mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
585 @mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
586 def test_create_repo_when_filesystem_op_fails(self):
586 def test_create_repo_when_filesystem_op_fails(self):
587 self.log_user()
587 self.log_user()
588 repo_name = self.NEW_REPO
588 repo_name = self.NEW_REPO
589 description = 'description for newly created repo'
589 description = 'description for newly created repo'
590
590
591 response = self.app.post(url('repos'),
591 response = self.app.post(url('repos'),
592 fixture._get_repo_create_params(repo_private=False,
592 fixture._get_repo_create_params(repo_private=False,
593 repo_name=repo_name,
593 repo_name=repo_name,
594 repo_type=self.REPO_TYPE,
594 repo_type=self.REPO_TYPE,
595 repo_description=description,
595 repo_description=description,
596 _authentication_token=self.authentication_token()))
596 _authentication_token=self.authentication_token()))
597
597
598 self.checkSessionFlash(response,
598 self.checkSessionFlash(response,
599 'Error creating repository %s' % repo_name)
599 'Error creating repository %s' % repo_name)
600 # repo must not be in db
600 # repo must not be in db
601 repo = Repository.get_by_repo_name(repo_name)
601 repo = Repository.get_by_repo_name(repo_name)
602 self.assertEqual(repo, None)
602 self.assertEqual(repo, None)
603
603
604 # repo must not be in filesystem !
604 # repo must not be in filesystem !
605 self.assertFalse(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)))
605 self.assertFalse(os.path.isdir(os.path.join(TESTS_TMP_PATH, repo_name)))
606
606
607 class TestAdminReposControllerGIT(TestController, _BaseTest):
607 class TestAdminReposControllerGIT(TestController, _BaseTest):
608 REPO = GIT_REPO
608 REPO = GIT_REPO
609 REPO_TYPE = 'git'
609 REPO_TYPE = 'git'
610 NEW_REPO = NEW_GIT_REPO
610 NEW_REPO = NEW_GIT_REPO
611 OTHER_TYPE_REPO = HG_REPO
611 OTHER_TYPE_REPO = HG_REPO
612 OTHER_TYPE = 'hg'
612 OTHER_TYPE = 'hg'
613
613
614
614
615 class TestAdminReposControllerHG(TestController, _BaseTest):
615 class TestAdminReposControllerHG(TestController, _BaseTest):
616 REPO = HG_REPO
616 REPO = HG_REPO
617 REPO_TYPE = 'hg'
617 REPO_TYPE = 'hg'
618 NEW_REPO = NEW_HG_REPO
618 NEW_REPO = NEW_HG_REPO
619 OTHER_TYPE_REPO = GIT_REPO
619 OTHER_TYPE_REPO = GIT_REPO
620 OTHER_TYPE = 'git'
620 OTHER_TYPE = 'git'
@@ -1,834 +1,837 b''
1
1
2 import os
2 import os
3 import sys
3 import sys
4 import mock
4 import mock
5 import datetime
5 import datetime
6 import urllib2
6 import urllib2
7
8 import pytest
9
7 from kallithea.lib.vcs.backends.git import GitRepository, GitChangeset
10 from kallithea.lib.vcs.backends.git import GitRepository, GitChangeset
8 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
11 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
9 from kallithea.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState
12 from kallithea.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState
10 from kallithea.lib.vcs.utils.compat import unittest
13 from kallithea.lib.vcs.utils.compat import unittest
11 from kallithea.model.scm import ScmModel
14 from kallithea.model.scm import ScmModel
12 from kallithea.tests.vcs.base import _BackendTestMixin
15 from kallithea.tests.vcs.base import _BackendTestMixin
13 from kallithea.tests.vcs.conf import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir
16 from kallithea.tests.vcs.conf import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir
14
17
15
18
16 class GitRepositoryTest(unittest.TestCase):
19 class GitRepositoryTest(unittest.TestCase):
17
20
18 def __check_for_existing_repo(self):
21 def __check_for_existing_repo(self):
19 if os.path.exists(TEST_GIT_REPO_CLONE):
22 if os.path.exists(TEST_GIT_REPO_CLONE):
20 self.fail('Cannot test git clone repo as location %s already '
23 pytest.fail('Cannot test git clone repo as location %s already '
21 'exists. You should manually remove it first.'
24 'exists. You should manually remove it first.'
22 % TEST_GIT_REPO_CLONE)
25 % TEST_GIT_REPO_CLONE)
23
26
24 def setUp(self):
27 def setUp(self):
25 self.repo = GitRepository(TEST_GIT_REPO)
28 self.repo = GitRepository(TEST_GIT_REPO)
26
29
27 def test_wrong_repo_path(self):
30 def test_wrong_repo_path(self):
28 wrong_repo_path = '/tmp/errorrepo'
31 wrong_repo_path = '/tmp/errorrepo'
29 self.assertRaises(RepositoryError, GitRepository, wrong_repo_path)
32 self.assertRaises(RepositoryError, GitRepository, wrong_repo_path)
30
33
31 def test_git_cmd_injection(self):
34 def test_git_cmd_injection(self):
32 repo_inject_path = TEST_GIT_REPO + '; echo "Cake";'
35 repo_inject_path = TEST_GIT_REPO + '; echo "Cake";'
33 with self.assertRaises(urllib2.URLError):
36 with self.assertRaises(urllib2.URLError):
34 # Should fail because URL will contain the parts after ; too
37 # Should fail because URL will contain the parts after ; too
35 urlerror_fail_repo = GitRepository(get_new_dir('injection-repo'), src_url=repo_inject_path, update_after_clone=True, create=True)
38 urlerror_fail_repo = GitRepository(get_new_dir('injection-repo'), src_url=repo_inject_path, update_after_clone=True, create=True)
36
39
37 with self.assertRaises(RepositoryError):
40 with self.assertRaises(RepositoryError):
38 # Should fail on direct clone call, which as of this writing does not happen outside of class
41 # Should fail on direct clone call, which as of this writing does not happen outside of class
39 clone_fail_repo = GitRepository(get_new_dir('injection-repo'), create=True)
42 clone_fail_repo = GitRepository(get_new_dir('injection-repo'), create=True)
40 clone_fail_repo.clone(repo_inject_path, update_after_clone=True,)
43 clone_fail_repo.clone(repo_inject_path, update_after_clone=True,)
41
44
42 # Verify correct quoting of evil characters that should work on posix file systems
45 # Verify correct quoting of evil characters that should work on posix file systems
43 if sys.platform == 'win32':
46 if sys.platform == 'win32':
44 # windows does not allow '"' in dir names
47 # windows does not allow '"' in dir names
45 tricky_path = get_new_dir("tricky-path-repo-$'`")
48 tricky_path = get_new_dir("tricky-path-repo-$'`")
46 else:
49 else:
47 tricky_path = get_new_dir("tricky-path-repo-$'\"`")
50 tricky_path = get_new_dir("tricky-path-repo-$'\"`")
48 successfully_cloned = GitRepository(tricky_path, src_url=TEST_GIT_REPO, update_after_clone=True, create=True)
51 successfully_cloned = GitRepository(tricky_path, src_url=TEST_GIT_REPO, update_after_clone=True, create=True)
49 # Repo should have been created
52 # Repo should have been created
50 self.assertFalse(successfully_cloned._repo.bare)
53 self.assertFalse(successfully_cloned._repo.bare)
51
54
52 if sys.platform == 'win32':
55 if sys.platform == 'win32':
53 # windows does not allow '"' in dir names
56 # windows does not allow '"' in dir names
54 tricky_path_2 = get_new_dir("tricky-path-2-repo-$'`")
57 tricky_path_2 = get_new_dir("tricky-path-2-repo-$'`")
55 else:
58 else:
56 tricky_path_2 = get_new_dir("tricky-path-2-repo-$'\"`")
59 tricky_path_2 = get_new_dir("tricky-path-2-repo-$'\"`")
57 successfully_cloned2 = GitRepository(tricky_path_2, src_url=tricky_path, bare=True, create=True)
60 successfully_cloned2 = GitRepository(tricky_path_2, src_url=tricky_path, bare=True, create=True)
58 # Repo should have been created and thus used correct quoting for clone
61 # Repo should have been created and thus used correct quoting for clone
59 self.assertTrue(successfully_cloned2._repo.bare)
62 self.assertTrue(successfully_cloned2._repo.bare)
60
63
61 # Should pass because URL has been properly quoted
64 # Should pass because URL has been properly quoted
62 successfully_cloned.pull(tricky_path_2)
65 successfully_cloned.pull(tricky_path_2)
63 successfully_cloned2.fetch(tricky_path)
66 successfully_cloned2.fetch(tricky_path)
64
67
65 def test_repo_create_with_spaces_in_path(self):
68 def test_repo_create_with_spaces_in_path(self):
66 repo_path = get_new_dir("path with spaces")
69 repo_path = get_new_dir("path with spaces")
67 repo = GitRepository(repo_path, src_url=None, bare=True, create=True)
70 repo = GitRepository(repo_path, src_url=None, bare=True, create=True)
68 # Repo should have been created
71 # Repo should have been created
69 self.assertTrue(repo._repo.bare)
72 self.assertTrue(repo._repo.bare)
70
73
71 def test_repo_clone(self):
74 def test_repo_clone(self):
72 self.__check_for_existing_repo()
75 self.__check_for_existing_repo()
73 repo = GitRepository(TEST_GIT_REPO)
76 repo = GitRepository(TEST_GIT_REPO)
74 repo_clone = GitRepository(TEST_GIT_REPO_CLONE,
77 repo_clone = GitRepository(TEST_GIT_REPO_CLONE,
75 src_url=TEST_GIT_REPO, create=True, update_after_clone=True)
78 src_url=TEST_GIT_REPO, create=True, update_after_clone=True)
76 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
79 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
77 # Checking hashes of changesets should be enough
80 # Checking hashes of changesets should be enough
78 for changeset in repo.get_changesets():
81 for changeset in repo.get_changesets():
79 raw_id = changeset.raw_id
82 raw_id = changeset.raw_id
80 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
83 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
81
84
82 def test_repo_clone_with_spaces_in_path(self):
85 def test_repo_clone_with_spaces_in_path(self):
83 repo_path = get_new_dir("path with spaces")
86 repo_path = get_new_dir("path with spaces")
84 successfully_cloned = GitRepository(repo_path, src_url=TEST_GIT_REPO, update_after_clone=True, create=True)
87 successfully_cloned = GitRepository(repo_path, src_url=TEST_GIT_REPO, update_after_clone=True, create=True)
85 # Repo should have been created
88 # Repo should have been created
86 self.assertFalse(successfully_cloned._repo.bare)
89 self.assertFalse(successfully_cloned._repo.bare)
87
90
88 successfully_cloned.pull(TEST_GIT_REPO)
91 successfully_cloned.pull(TEST_GIT_REPO)
89 self.repo.fetch(repo_path)
92 self.repo.fetch(repo_path)
90
93
91 def test_repo_clone_without_create(self):
94 def test_repo_clone_without_create(self):
92 self.assertRaises(RepositoryError, GitRepository,
95 self.assertRaises(RepositoryError, GitRepository,
93 TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO)
96 TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO)
94
97
95 def test_repo_clone_with_update(self):
98 def test_repo_clone_with_update(self):
96 repo = GitRepository(TEST_GIT_REPO)
99 repo = GitRepository(TEST_GIT_REPO)
97 clone_path = TEST_GIT_REPO_CLONE + '_with_update'
100 clone_path = TEST_GIT_REPO_CLONE + '_with_update'
98 repo_clone = GitRepository(clone_path,
101 repo_clone = GitRepository(clone_path,
99 create=True, src_url=TEST_GIT_REPO, update_after_clone=True)
102 create=True, src_url=TEST_GIT_REPO, update_after_clone=True)
100 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
103 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
101
104
102 #check if current workdir was updated
105 #check if current workdir was updated
103 fpath = os.path.join(clone_path, 'MANIFEST.in')
106 fpath = os.path.join(clone_path, 'MANIFEST.in')
104 self.assertEqual(True, os.path.isfile(fpath),
107 self.assertEqual(True, os.path.isfile(fpath),
105 'Repo was cloned and updated but file %s could not be found'
108 'Repo was cloned and updated but file %s could not be found'
106 % fpath)
109 % fpath)
107
110
108 def test_repo_clone_without_update(self):
111 def test_repo_clone_without_update(self):
109 repo = GitRepository(TEST_GIT_REPO)
112 repo = GitRepository(TEST_GIT_REPO)
110 clone_path = TEST_GIT_REPO_CLONE + '_without_update'
113 clone_path = TEST_GIT_REPO_CLONE + '_without_update'
111 repo_clone = GitRepository(clone_path,
114 repo_clone = GitRepository(clone_path,
112 create=True, src_url=TEST_GIT_REPO, update_after_clone=False)
115 create=True, src_url=TEST_GIT_REPO, update_after_clone=False)
113 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
116 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
114 #check if current workdir was *NOT* updated
117 #check if current workdir was *NOT* updated
115 fpath = os.path.join(clone_path, 'MANIFEST.in')
118 fpath = os.path.join(clone_path, 'MANIFEST.in')
116 # Make sure it's not bare repo
119 # Make sure it's not bare repo
117 self.assertFalse(repo_clone._repo.bare)
120 self.assertFalse(repo_clone._repo.bare)
118 self.assertEqual(False, os.path.isfile(fpath),
121 self.assertEqual(False, os.path.isfile(fpath),
119 'Repo was cloned and updated but file %s was found'
122 'Repo was cloned and updated but file %s was found'
120 % fpath)
123 % fpath)
121
124
122 def test_repo_clone_into_bare_repo(self):
125 def test_repo_clone_into_bare_repo(self):
123 repo = GitRepository(TEST_GIT_REPO)
126 repo = GitRepository(TEST_GIT_REPO)
124 clone_path = TEST_GIT_REPO_CLONE + '_bare.git'
127 clone_path = TEST_GIT_REPO_CLONE + '_bare.git'
125 repo_clone = GitRepository(clone_path, create=True,
128 repo_clone = GitRepository(clone_path, create=True,
126 src_url=repo.path, bare=True)
129 src_url=repo.path, bare=True)
127 self.assertTrue(repo_clone._repo.bare)
130 self.assertTrue(repo_clone._repo.bare)
128
131
129 def test_create_repo_is_not_bare_by_default(self):
132 def test_create_repo_is_not_bare_by_default(self):
130 repo = GitRepository(get_new_dir('not-bare-by-default'), create=True)
133 repo = GitRepository(get_new_dir('not-bare-by-default'), create=True)
131 self.assertFalse(repo._repo.bare)
134 self.assertFalse(repo._repo.bare)
132
135
133 def test_create_bare_repo(self):
136 def test_create_bare_repo(self):
134 repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True)
137 repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True)
135 self.assertTrue(repo._repo.bare)
138 self.assertTrue(repo._repo.bare)
136
139
137 def test_revisions(self):
140 def test_revisions(self):
138 # there are 112 revisions (by now)
141 # there are 112 revisions (by now)
139 # so we can assume they would be available from now on
142 # so we can assume they would be available from now on
140 subset = set([
143 subset = set([
141 'c1214f7e79e02fc37156ff215cd71275450cffc3',
144 'c1214f7e79e02fc37156ff215cd71275450cffc3',
142 '38b5fe81f109cb111f549bfe9bb6b267e10bc557',
145 '38b5fe81f109cb111f549bfe9bb6b267e10bc557',
143 'fa6600f6848800641328adbf7811fd2372c02ab2',
146 'fa6600f6848800641328adbf7811fd2372c02ab2',
144 '102607b09cdd60e2793929c4f90478be29f85a17',
147 '102607b09cdd60e2793929c4f90478be29f85a17',
145 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
148 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
146 '2d1028c054665b962fa3d307adfc923ddd528038',
149 '2d1028c054665b962fa3d307adfc923ddd528038',
147 'd7e0d30fbcae12c90680eb095a4f5f02505ce501',
150 'd7e0d30fbcae12c90680eb095a4f5f02505ce501',
148 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
151 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
149 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
152 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
150 '8430a588b43b5d6da365400117c89400326e7992',
153 '8430a588b43b5d6da365400117c89400326e7992',
151 'd955cd312c17b02143c04fa1099a352b04368118',
154 'd955cd312c17b02143c04fa1099a352b04368118',
152 'f67b87e5c629c2ee0ba58f85197e423ff28d735b',
155 'f67b87e5c629c2ee0ba58f85197e423ff28d735b',
153 'add63e382e4aabc9e1afdc4bdc24506c269b7618',
156 'add63e382e4aabc9e1afdc4bdc24506c269b7618',
154 'f298fe1189f1b69779a4423f40b48edf92a703fc',
157 'f298fe1189f1b69779a4423f40b48edf92a703fc',
155 'bd9b619eb41994cac43d67cf4ccc8399c1125808',
158 'bd9b619eb41994cac43d67cf4ccc8399c1125808',
156 '6e125e7c890379446e98980d8ed60fba87d0f6d1',
159 '6e125e7c890379446e98980d8ed60fba87d0f6d1',
157 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd',
160 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd',
158 '0b05e4ed56c802098dfc813cbe779b2f49e92500',
161 '0b05e4ed56c802098dfc813cbe779b2f49e92500',
159 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
162 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
160 '45223f8f114c64bf4d6f853e3c35a369a6305520',
163 '45223f8f114c64bf4d6f853e3c35a369a6305520',
161 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
164 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
162 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68',
165 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68',
163 '27d48942240f5b91dfda77accd2caac94708cc7d',
166 '27d48942240f5b91dfda77accd2caac94708cc7d',
164 '622f0eb0bafd619d2560c26f80f09e3b0b0d78af',
167 '622f0eb0bafd619d2560c26f80f09e3b0b0d78af',
165 'e686b958768ee96af8029fe19c6050b1a8dd3b2b'])
168 'e686b958768ee96af8029fe19c6050b1a8dd3b2b'])
166 self.assertTrue(subset.issubset(set(self.repo.revisions)))
169 self.assertTrue(subset.issubset(set(self.repo.revisions)))
167
170
168
171
169
172
170 def test_slicing(self):
173 def test_slicing(self):
171 #4 1 5 10 95
174 #4 1 5 10 95
172 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
175 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
173 (10, 20, 10), (5, 100, 95)]:
176 (10, 20, 10), (5, 100, 95)]:
174 revs = list(self.repo[sfrom:sto])
177 revs = list(self.repo[sfrom:sto])
175 self.assertEqual(len(revs), size)
178 self.assertEqual(len(revs), size)
176 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
179 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
177 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
180 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
178
181
179
182
180 def test_branches(self):
183 def test_branches(self):
181 # TODO: Need more tests here
184 # TODO: Need more tests here
182 # Removed (those are 'remotes' branches for cloned repo)
185 # Removed (those are 'remotes' branches for cloned repo)
183 #self.assertTrue('master' in self.repo.branches)
186 #self.assertTrue('master' in self.repo.branches)
184 #self.assertTrue('gittree' in self.repo.branches)
187 #self.assertTrue('gittree' in self.repo.branches)
185 #self.assertTrue('web-branch' in self.repo.branches)
188 #self.assertTrue('web-branch' in self.repo.branches)
186 for name, id in self.repo.branches.items():
189 for name, id in self.repo.branches.items():
187 self.assertTrue(isinstance(
190 self.assertTrue(isinstance(
188 self.repo.get_changeset(id), GitChangeset))
191 self.repo.get_changeset(id), GitChangeset))
189
192
190 def test_tags(self):
193 def test_tags(self):
191 # TODO: Need more tests here
194 # TODO: Need more tests here
192 self.assertTrue('v0.1.1' in self.repo.tags)
195 self.assertTrue('v0.1.1' in self.repo.tags)
193 self.assertTrue('v0.1.2' in self.repo.tags)
196 self.assertTrue('v0.1.2' in self.repo.tags)
194 for name, id in self.repo.tags.items():
197 for name, id in self.repo.tags.items():
195 self.assertTrue(isinstance(
198 self.assertTrue(isinstance(
196 self.repo.get_changeset(id), GitChangeset))
199 self.repo.get_changeset(id), GitChangeset))
197
200
198 def _test_single_changeset_cache(self, revision):
201 def _test_single_changeset_cache(self, revision):
199 chset = self.repo.get_changeset(revision)
202 chset = self.repo.get_changeset(revision)
200 self.assertTrue(revision in self.repo.changesets)
203 self.assertTrue(revision in self.repo.changesets)
201 self.assertTrue(chset is self.repo.changesets[revision])
204 self.assertTrue(chset is self.repo.changesets[revision])
202
205
203 def test_initial_changeset(self):
206 def test_initial_changeset(self):
204 id = self.repo.revisions[0]
207 id = self.repo.revisions[0]
205 init_chset = self.repo.get_changeset(id)
208 init_chset = self.repo.get_changeset(id)
206 self.assertEqual(init_chset.message, 'initial import\n')
209 self.assertEqual(init_chset.message, 'initial import\n')
207 self.assertEqual(init_chset.author,
210 self.assertEqual(init_chset.author,
208 'Marcin Kuzminski <marcin@python-blog.com>')
211 'Marcin Kuzminski <marcin@python-blog.com>')
209 for path in ('vcs/__init__.py',
212 for path in ('vcs/__init__.py',
210 'vcs/backends/BaseRepository.py',
213 'vcs/backends/BaseRepository.py',
211 'vcs/backends/__init__.py'):
214 'vcs/backends/__init__.py'):
212 self.assertTrue(isinstance(init_chset.get_node(path), FileNode))
215 self.assertTrue(isinstance(init_chset.get_node(path), FileNode))
213 for path in ('', 'vcs', 'vcs/backends'):
216 for path in ('', 'vcs', 'vcs/backends'):
214 self.assertTrue(isinstance(init_chset.get_node(path), DirNode))
217 self.assertTrue(isinstance(init_chset.get_node(path), DirNode))
215
218
216 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
219 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
217
220
218 node = init_chset.get_node('vcs/')
221 node = init_chset.get_node('vcs/')
219 self.assertTrue(hasattr(node, 'kind'))
222 self.assertTrue(hasattr(node, 'kind'))
220 self.assertEqual(node.kind, NodeKind.DIR)
223 self.assertEqual(node.kind, NodeKind.DIR)
221
224
222 node = init_chset.get_node('vcs')
225 node = init_chset.get_node('vcs')
223 self.assertTrue(hasattr(node, 'kind'))
226 self.assertTrue(hasattr(node, 'kind'))
224 self.assertEqual(node.kind, NodeKind.DIR)
227 self.assertEqual(node.kind, NodeKind.DIR)
225
228
226 node = init_chset.get_node('vcs/__init__.py')
229 node = init_chset.get_node('vcs/__init__.py')
227 self.assertTrue(hasattr(node, 'kind'))
230 self.assertTrue(hasattr(node, 'kind'))
228 self.assertEqual(node.kind, NodeKind.FILE)
231 self.assertEqual(node.kind, NodeKind.FILE)
229
232
230 def test_not_existing_changeset(self):
233 def test_not_existing_changeset(self):
231 self.assertRaises(RepositoryError, self.repo.get_changeset,
234 self.assertRaises(RepositoryError, self.repo.get_changeset,
232 'f' * 40)
235 'f' * 40)
233
236
234 def test_changeset10(self):
237 def test_changeset10(self):
235
238
236 chset10 = self.repo.get_changeset(self.repo.revisions[9])
239 chset10 = self.repo.get_changeset(self.repo.revisions[9])
237 README = """===
240 README = """===
238 VCS
241 VCS
239 ===
242 ===
240
243
241 Various Version Control System management abstraction layer for Python.
244 Various Version Control System management abstraction layer for Python.
242
245
243 Introduction
246 Introduction
244 ------------
247 ------------
245
248
246 TODO: To be written...
249 TODO: To be written...
247
250
248 """
251 """
249 node = chset10.get_node('README.rst')
252 node = chset10.get_node('README.rst')
250 self.assertEqual(node.kind, NodeKind.FILE)
253 self.assertEqual(node.kind, NodeKind.FILE)
251 self.assertEqual(node.content, README)
254 self.assertEqual(node.content, README)
252
255
253
256
254 class GitChangesetTest(unittest.TestCase):
257 class GitChangesetTest(unittest.TestCase):
255
258
256 def setUp(self):
259 def setUp(self):
257 self.repo = GitRepository(TEST_GIT_REPO)
260 self.repo = GitRepository(TEST_GIT_REPO)
258
261
259 def test_default_changeset(self):
262 def test_default_changeset(self):
260 tip = self.repo.get_changeset()
263 tip = self.repo.get_changeset()
261 self.assertEqual(tip, self.repo.get_changeset(None))
264 self.assertEqual(tip, self.repo.get_changeset(None))
262 self.assertEqual(tip, self.repo.get_changeset('tip'))
265 self.assertEqual(tip, self.repo.get_changeset('tip'))
263
266
264 def test_root_node(self):
267 def test_root_node(self):
265 tip = self.repo.get_changeset()
268 tip = self.repo.get_changeset()
266 self.assertTrue(tip.root is tip.get_node(''))
269 self.assertTrue(tip.root is tip.get_node(''))
267
270
268 def test_lazy_fetch(self):
271 def test_lazy_fetch(self):
269 """
272 """
270 Test if changeset's nodes expands and are cached as we walk through
273 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
274 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.
275 is a key here. Written by running command after command in a shell.
273 """
276 """
274 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
277 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
275 self.assertTrue(hex in self.repo.revisions)
278 self.assertTrue(hex in self.repo.revisions)
276 chset = self.repo.get_changeset(hex)
279 chset = self.repo.get_changeset(hex)
277 self.assertTrue(len(chset.nodes) == 0)
280 self.assertTrue(len(chset.nodes) == 0)
278 root = chset.root
281 root = chset.root
279 self.assertTrue(len(chset.nodes) == 1)
282 self.assertTrue(len(chset.nodes) == 1)
280 self.assertTrue(len(root.nodes) == 8)
283 self.assertTrue(len(root.nodes) == 8)
281 # accessing root.nodes updates chset.nodes
284 # accessing root.nodes updates chset.nodes
282 self.assertTrue(len(chset.nodes) == 9)
285 self.assertTrue(len(chset.nodes) == 9)
283
286
284 docs = root.get_node('docs')
287 docs = root.get_node('docs')
285 # we haven't yet accessed anything new as docs dir was already cached
288 # we haven't yet accessed anything new as docs dir was already cached
286 self.assertTrue(len(chset.nodes) == 9)
289 self.assertTrue(len(chset.nodes) == 9)
287 self.assertTrue(len(docs.nodes) == 8)
290 self.assertTrue(len(docs.nodes) == 8)
288 # accessing docs.nodes updates chset.nodes
291 # accessing docs.nodes updates chset.nodes
289 self.assertTrue(len(chset.nodes) == 17)
292 self.assertTrue(len(chset.nodes) == 17)
290
293
291 self.assertTrue(docs is chset.get_node('docs'))
294 self.assertTrue(docs is chset.get_node('docs'))
292 self.assertTrue(docs is root.nodes[0])
295 self.assertTrue(docs is root.nodes[0])
293 self.assertTrue(docs is root.dirs[0])
296 self.assertTrue(docs is root.dirs[0])
294 self.assertTrue(docs is chset.get_node('docs'))
297 self.assertTrue(docs is chset.get_node('docs'))
295
298
296 def test_nodes_with_changeset(self):
299 def test_nodes_with_changeset(self):
297 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
300 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
298 chset = self.repo.get_changeset(hex)
301 chset = self.repo.get_changeset(hex)
299 root = chset.root
302 root = chset.root
300 docs = root.get_node('docs')
303 docs = root.get_node('docs')
301 self.assertTrue(docs is chset.get_node('docs'))
304 self.assertTrue(docs is chset.get_node('docs'))
302 api = docs.get_node('api')
305 api = docs.get_node('api')
303 self.assertTrue(api is chset.get_node('docs/api'))
306 self.assertTrue(api is chset.get_node('docs/api'))
304 index = api.get_node('index.rst')
307 index = api.get_node('index.rst')
305 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
308 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
306 self.assertTrue(index is chset.get_node('docs') \
309 self.assertTrue(index is chset.get_node('docs') \
307 .get_node('api') \
310 .get_node('api') \
308 .get_node('index.rst'))
311 .get_node('index.rst'))
309
312
310 def test_branch_and_tags(self):
313 def test_branch_and_tags(self):
311 """
314 """
312 rev0 = self.repo.revisions[0]
315 rev0 = self.repo.revisions[0]
313 chset0 = self.repo.get_changeset(rev0)
316 chset0 = self.repo.get_changeset(rev0)
314 self.assertEqual(chset0.branch, 'master')
317 self.assertEqual(chset0.branch, 'master')
315 self.assertEqual(chset0.tags, [])
318 self.assertEqual(chset0.tags, [])
316
319
317 rev10 = self.repo.revisions[10]
320 rev10 = self.repo.revisions[10]
318 chset10 = self.repo.get_changeset(rev10)
321 chset10 = self.repo.get_changeset(rev10)
319 self.assertEqual(chset10.branch, 'master')
322 self.assertEqual(chset10.branch, 'master')
320 self.assertEqual(chset10.tags, [])
323 self.assertEqual(chset10.tags, [])
321
324
322 rev44 = self.repo.revisions[44]
325 rev44 = self.repo.revisions[44]
323 chset44 = self.repo.get_changeset(rev44)
326 chset44 = self.repo.get_changeset(rev44)
324 self.assertEqual(chset44.branch, 'web-branch')
327 self.assertEqual(chset44.branch, 'web-branch')
325
328
326 tip = self.repo.get_changeset('tip')
329 tip = self.repo.get_changeset('tip')
327 self.assertTrue('tip' in tip.tags)
330 self.assertTrue('tip' in tip.tags)
328 """
331 """
329 # Those tests would fail - branches are now going
332 # Those tests would fail - branches are now going
330 # to be changed at main API in order to support git backend
333 # to be changed at main API in order to support git backend
331 pass
334 pass
332
335
333 def _test_slices(self, limit, offset):
336 def _test_slices(self, limit, offset):
334 count = self.repo.count()
337 count = self.repo.count()
335 changesets = self.repo.get_changesets(limit=limit, offset=offset)
338 changesets = self.repo.get_changesets(limit=limit, offset=offset)
336 idx = 0
339 idx = 0
337 for changeset in changesets:
340 for changeset in changesets:
338 rev = offset + idx
341 rev = offset + idx
339 idx += 1
342 idx += 1
340 rev_id = self.repo.revisions[rev]
343 rev_id = self.repo.revisions[rev]
341 if idx > limit:
344 if idx > limit:
342 self.fail("Exceeded limit already (getting revision %s, "
345 pytest.fail("Exceeded limit already (getting revision %s, "
343 "there are %s total revisions, offset=%s, limit=%s)"
346 "there are %s total revisions, offset=%s, limit=%s)"
344 % (rev_id, count, offset, limit))
347 % (rev_id, count, offset, limit))
345 self.assertEqual(changeset, self.repo.get_changeset(rev_id))
348 self.assertEqual(changeset, self.repo.get_changeset(rev_id))
346 result = list(self.repo.get_changesets(limit=limit, offset=offset))
349 result = list(self.repo.get_changesets(limit=limit, offset=offset))
347 start = offset
350 start = offset
348 end = limit and offset + limit or None
351 end = limit and offset + limit or None
349 sliced = list(self.repo[start:end])
352 sliced = list(self.repo[start:end])
350 self.failUnlessEqual(result, sliced,
353 pytest.failUnlessEqual(result, sliced,
351 msg="Comparison failed for limit=%s, offset=%s"
354 msg="Comparison failed for limit=%s, offset=%s"
352 "(get_changeset returned: %s and sliced: %s"
355 "(get_changeset returned: %s and sliced: %s"
353 % (limit, offset, result, sliced))
356 % (limit, offset, result, sliced))
354
357
355 def _test_file_size(self, revision, path, size):
358 def _test_file_size(self, revision, path, size):
356 node = self.repo.get_changeset(revision).get_node(path)
359 node = self.repo.get_changeset(revision).get_node(path)
357 self.assertTrue(node.is_file())
360 self.assertTrue(node.is_file())
358 self.assertEqual(node.size, size)
361 self.assertEqual(node.size, size)
359
362
360 def test_file_size(self):
363 def test_file_size(self):
361 to_check = (
364 to_check = (
362 ('c1214f7e79e02fc37156ff215cd71275450cffc3',
365 ('c1214f7e79e02fc37156ff215cd71275450cffc3',
363 'vcs/backends/BaseRepository.py', 502),
366 'vcs/backends/BaseRepository.py', 502),
364 ('d7e0d30fbcae12c90680eb095a4f5f02505ce501',
367 ('d7e0d30fbcae12c90680eb095a4f5f02505ce501',
365 'vcs/backends/hg.py', 854),
368 'vcs/backends/hg.py', 854),
366 ('6e125e7c890379446e98980d8ed60fba87d0f6d1',
369 ('6e125e7c890379446e98980d8ed60fba87d0f6d1',
367 'setup.py', 1068),
370 'setup.py', 1068),
368
371
369 ('d955cd312c17b02143c04fa1099a352b04368118',
372 ('d955cd312c17b02143c04fa1099a352b04368118',
370 'vcs/backends/base.py', 2921),
373 'vcs/backends/base.py', 2921),
371 ('ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
374 ('ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
372 'vcs/backends/base.py', 3936),
375 'vcs/backends/base.py', 3936),
373 ('f50f42baeed5af6518ef4b0cb2f1423f3851a941',
376 ('f50f42baeed5af6518ef4b0cb2f1423f3851a941',
374 'vcs/backends/base.py', 6189),
377 'vcs/backends/base.py', 6189),
375 )
378 )
376 for revision, path, size in to_check:
379 for revision, path, size in to_check:
377 self._test_file_size(revision, path, size)
380 self._test_file_size(revision, path, size)
378
381
379 def test_file_history(self):
382 def test_file_history(self):
380 # we can only check if those revisions are present in the history
383 # we can only check if those revisions are present in the history
381 # as we cannot update this test every time file is changed
384 # as we cannot update this test every time file is changed
382 files = {
385 files = {
383 'setup.py': [
386 'setup.py': [
384 '54386793436c938cff89326944d4c2702340037d',
387 '54386793436c938cff89326944d4c2702340037d',
385 '51d254f0ecf5df2ce50c0b115741f4cf13985dab',
388 '51d254f0ecf5df2ce50c0b115741f4cf13985dab',
386 '998ed409c795fec2012b1c0ca054d99888b22090',
389 '998ed409c795fec2012b1c0ca054d99888b22090',
387 '5e0eb4c47f56564395f76333f319d26c79e2fb09',
390 '5e0eb4c47f56564395f76333f319d26c79e2fb09',
388 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
391 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
389 '7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e',
392 '7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e',
390 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
393 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
391 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
394 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
392 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
395 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
393 ],
396 ],
394 'vcs/nodes.py': [
397 'vcs/nodes.py': [
395 '33fa3223355104431402a888fa77a4e9956feb3e',
398 '33fa3223355104431402a888fa77a4e9956feb3e',
396 'fa014c12c26d10ba682fadb78f2a11c24c8118e1',
399 'fa014c12c26d10ba682fadb78f2a11c24c8118e1',
397 'e686b958768ee96af8029fe19c6050b1a8dd3b2b',
400 'e686b958768ee96af8029fe19c6050b1a8dd3b2b',
398 'ab5721ca0a081f26bf43d9051e615af2cc99952f',
401 'ab5721ca0a081f26bf43d9051e615af2cc99952f',
399 'c877b68d18e792a66b7f4c529ea02c8f80801542',
402 'c877b68d18e792a66b7f4c529ea02c8f80801542',
400 '4313566d2e417cb382948f8d9d7c765330356054',
403 '4313566d2e417cb382948f8d9d7c765330356054',
401 '6c2303a793671e807d1cfc70134c9ca0767d98c2',
404 '6c2303a793671e807d1cfc70134c9ca0767d98c2',
402 '54386793436c938cff89326944d4c2702340037d',
405 '54386793436c938cff89326944d4c2702340037d',
403 '54000345d2e78b03a99d561399e8e548de3f3203',
406 '54000345d2e78b03a99d561399e8e548de3f3203',
404 '1c6b3677b37ea064cb4b51714d8f7498f93f4b2b',
407 '1c6b3677b37ea064cb4b51714d8f7498f93f4b2b',
405 '2d03ca750a44440fb5ea8b751176d1f36f8e8f46',
408 '2d03ca750a44440fb5ea8b751176d1f36f8e8f46',
406 '2a08b128c206db48c2f0b8f70df060e6db0ae4f8',
409 '2a08b128c206db48c2f0b8f70df060e6db0ae4f8',
407 '30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b',
410 '30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b',
408 'ac71e9503c2ca95542839af0ce7b64011b72ea7c',
411 'ac71e9503c2ca95542839af0ce7b64011b72ea7c',
409 '12669288fd13adba2a9b7dd5b870cc23ffab92d2',
412 '12669288fd13adba2a9b7dd5b870cc23ffab92d2',
410 '5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382',
413 '5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382',
411 '12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5',
414 '12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5',
412 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
415 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
413 'f50f42baeed5af6518ef4b0cb2f1423f3851a941',
416 'f50f42baeed5af6518ef4b0cb2f1423f3851a941',
414 'd7e390a45f6aa96f04f5e7f583ad4f867431aa25',
417 'd7e390a45f6aa96f04f5e7f583ad4f867431aa25',
415 'f15c21f97864b4f071cddfbf2750ec2e23859414',
418 'f15c21f97864b4f071cddfbf2750ec2e23859414',
416 'e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade',
419 'e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade',
417 'ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b',
420 'ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b',
418 '84dec09632a4458f79f50ddbbd155506c460b4f9',
421 '84dec09632a4458f79f50ddbbd155506c460b4f9',
419 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
422 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
420 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
423 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
421 '3bf1c5868e570e39569d094f922d33ced2fa3b2b',
424 '3bf1c5868e570e39569d094f922d33ced2fa3b2b',
422 'b8d04012574729d2c29886e53b1a43ef16dd00a1',
425 'b8d04012574729d2c29886e53b1a43ef16dd00a1',
423 '6970b057cffe4aab0a792aa634c89f4bebf01441',
426 '6970b057cffe4aab0a792aa634c89f4bebf01441',
424 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
427 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
425 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
428 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
426 ],
429 ],
427 'vcs/backends/git.py': [
430 'vcs/backends/git.py': [
428 '4cf116ad5a457530381135e2f4c453e68a1b0105',
431 '4cf116ad5a457530381135e2f4c453e68a1b0105',
429 '9a751d84d8e9408e736329767387f41b36935153',
432 '9a751d84d8e9408e736329767387f41b36935153',
430 'cb681fb539c3faaedbcdf5ca71ca413425c18f01',
433 'cb681fb539c3faaedbcdf5ca71ca413425c18f01',
431 '428f81bb652bcba8d631bce926e8834ff49bdcc6',
434 '428f81bb652bcba8d631bce926e8834ff49bdcc6',
432 '180ab15aebf26f98f714d8c68715e0f05fa6e1c7',
435 '180ab15aebf26f98f714d8c68715e0f05fa6e1c7',
433 '2b8e07312a2e89e92b90426ab97f349f4bce2a3a',
436 '2b8e07312a2e89e92b90426ab97f349f4bce2a3a',
434 '50e08c506174d8645a4bb517dd122ac946a0f3bf',
437 '50e08c506174d8645a4bb517dd122ac946a0f3bf',
435 '54000345d2e78b03a99d561399e8e548de3f3203',
438 '54000345d2e78b03a99d561399e8e548de3f3203',
436 ],
439 ],
437 }
440 }
438 for path, revs in files.items():
441 for path, revs in files.items():
439 node = self.repo.get_changeset(revs[0]).get_node(path)
442 node = self.repo.get_changeset(revs[0]).get_node(path)
440 node_revs = [chset.raw_id for chset in node.history]
443 node_revs = [chset.raw_id for chset in node.history]
441 self.assertTrue(set(revs).issubset(set(node_revs)),
444 self.assertTrue(set(revs).issubset(set(node_revs)),
442 "We assumed that %s is subset of revisions for which file %s "
445 "We assumed that %s is subset of revisions for which file %s "
443 "has been changed, and history of that node returned: %s"
446 "has been changed, and history of that node returned: %s"
444 % (revs, path, node_revs))
447 % (revs, path, node_revs))
445
448
446 def test_file_annotate(self):
449 def test_file_annotate(self):
447 files = {
450 files = {
448 'vcs/backends/__init__.py': {
451 'vcs/backends/__init__.py': {
449 'c1214f7e79e02fc37156ff215cd71275450cffc3': {
452 'c1214f7e79e02fc37156ff215cd71275450cffc3': {
450 'lines_no': 1,
453 'lines_no': 1,
451 'changesets': [
454 'changesets': [
452 'c1214f7e79e02fc37156ff215cd71275450cffc3',
455 'c1214f7e79e02fc37156ff215cd71275450cffc3',
453 ],
456 ],
454 },
457 },
455 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647': {
458 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647': {
456 'lines_no': 21,
459 'lines_no': 21,
457 'changesets': [
460 'changesets': [
458 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
461 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
459 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
462 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
460 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
463 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
461 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
464 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
462 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
465 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
463 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
466 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
464 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
467 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
465 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
468 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
466 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
469 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
467 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
470 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
468 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
471 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
469 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
472 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
470 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
473 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
471 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
474 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
472 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
475 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
473 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
476 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
474 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
477 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
475 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
478 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
476 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
479 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
477 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
480 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
478 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
481 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
479 ],
482 ],
480 },
483 },
481 'e29b67bd158580fc90fc5e9111240b90e6e86064': {
484 'e29b67bd158580fc90fc5e9111240b90e6e86064': {
482 'lines_no': 32,
485 'lines_no': 32,
483 'changesets': [
486 'changesets': [
484 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
487 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
485 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
488 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
486 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
489 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
487 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
490 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
488 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
491 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
489 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
492 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
490 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
493 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
491 '54000345d2e78b03a99d561399e8e548de3f3203',
494 '54000345d2e78b03a99d561399e8e548de3f3203',
492 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
495 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
493 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
496 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
494 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
497 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
495 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
498 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
496 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
499 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
497 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
500 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
498 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
501 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
499 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
502 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
500 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
503 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
501 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
504 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
502 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
505 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
503 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
506 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
504 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
507 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
505 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
508 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
506 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
509 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
507 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
510 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
508 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
511 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
509 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
512 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
510 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
513 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
511 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
514 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
512 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
515 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
513 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
516 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
514 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
517 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
515 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
518 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
516 ],
519 ],
517 },
520 },
518 },
521 },
519 }
522 }
520
523
521 for fname, revision_dict in files.items():
524 for fname, revision_dict in files.items():
522 for rev, data in revision_dict.items():
525 for rev, data in revision_dict.items():
523 cs = self.repo.get_changeset(rev)
526 cs = self.repo.get_changeset(rev)
524
527
525 l1_1 = [x[1] for x in cs.get_file_annotate(fname)]
528 l1_1 = [x[1] for x in cs.get_file_annotate(fname)]
526 l1_2 = [x[2]().raw_id for x in cs.get_file_annotate(fname)]
529 l1_2 = [x[2]().raw_id for x in cs.get_file_annotate(fname)]
527 self.assertEqual(l1_1, l1_2)
530 self.assertEqual(l1_1, l1_2)
528 l1 = l1_1
531 l1 = l1_1
529 l2 = files[fname][rev]['changesets']
532 l2 = files[fname][rev]['changesets']
530 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev %s"
533 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev %s"
531 "from annotation list should match each other, "
534 "from annotation list should match each other, "
532 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
535 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
533
536
534 def test_files_state(self):
537 def test_files_state(self):
535 """
538 """
536 Tests state of FileNodes.
539 Tests state of FileNodes.
537 """
540 """
538 node = self.repo \
541 node = self.repo \
539 .get_changeset('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0') \
542 .get_changeset('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0') \
540 .get_node('vcs/utils/diffs.py')
543 .get_node('vcs/utils/diffs.py')
541 self.assertTrue(node.state, NodeState.ADDED)
544 self.assertTrue(node.state, NodeState.ADDED)
542 self.assertTrue(node.added)
545 self.assertTrue(node.added)
543 self.assertFalse(node.changed)
546 self.assertFalse(node.changed)
544 self.assertFalse(node.not_changed)
547 self.assertFalse(node.not_changed)
545 self.assertFalse(node.removed)
548 self.assertFalse(node.removed)
546
549
547 node = self.repo \
550 node = self.repo \
548 .get_changeset('33fa3223355104431402a888fa77a4e9956feb3e') \
551 .get_changeset('33fa3223355104431402a888fa77a4e9956feb3e') \
549 .get_node('.hgignore')
552 .get_node('.hgignore')
550 self.assertTrue(node.state, NodeState.CHANGED)
553 self.assertTrue(node.state, NodeState.CHANGED)
551 self.assertFalse(node.added)
554 self.assertFalse(node.added)
552 self.assertTrue(node.changed)
555 self.assertTrue(node.changed)
553 self.assertFalse(node.not_changed)
556 self.assertFalse(node.not_changed)
554 self.assertFalse(node.removed)
557 self.assertFalse(node.removed)
555
558
556 node = self.repo \
559 node = self.repo \
557 .get_changeset('e29b67bd158580fc90fc5e9111240b90e6e86064') \
560 .get_changeset('e29b67bd158580fc90fc5e9111240b90e6e86064') \
558 .get_node('setup.py')
561 .get_node('setup.py')
559 self.assertTrue(node.state, NodeState.NOT_CHANGED)
562 self.assertTrue(node.state, NodeState.NOT_CHANGED)
560 self.assertFalse(node.added)
563 self.assertFalse(node.added)
561 self.assertFalse(node.changed)
564 self.assertFalse(node.changed)
562 self.assertTrue(node.not_changed)
565 self.assertTrue(node.not_changed)
563 self.assertFalse(node.removed)
566 self.assertFalse(node.removed)
564
567
565 # If node has REMOVED state then trying to fetch it would raise
568 # If node has REMOVED state then trying to fetch it would raise
566 # ChangesetError exception
569 # ChangesetError exception
567 chset = self.repo.get_changeset(
570 chset = self.repo.get_changeset(
568 'fa6600f6848800641328adbf7811fd2372c02ab2')
571 'fa6600f6848800641328adbf7811fd2372c02ab2')
569 path = 'vcs/backends/BaseRepository.py'
572 path = 'vcs/backends/BaseRepository.py'
570 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
573 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
571 # but it would be one of ``removed`` (changeset's attribute)
574 # but it would be one of ``removed`` (changeset's attribute)
572 self.assertTrue(path in [rf.path for rf in chset.removed])
575 self.assertTrue(path in [rf.path for rf in chset.removed])
573
576
574 chset = self.repo.get_changeset(
577 chset = self.repo.get_changeset(
575 '54386793436c938cff89326944d4c2702340037d')
578 '54386793436c938cff89326944d4c2702340037d')
576 changed = ['setup.py', 'tests/test_nodes.py', 'vcs/backends/hg.py',
579 changed = ['setup.py', 'tests/test_nodes.py', 'vcs/backends/hg.py',
577 'vcs/nodes.py']
580 'vcs/nodes.py']
578 self.assertEqual(set(changed), set([f.path for f in chset.changed]))
581 self.assertEqual(set(changed), set([f.path for f in chset.changed]))
579
582
580 def test_commit_message_is_unicode(self):
583 def test_commit_message_is_unicode(self):
581 for cs in self.repo:
584 for cs in self.repo:
582 self.assertEqual(type(cs.message), unicode)
585 self.assertEqual(type(cs.message), unicode)
583
586
584 def test_changeset_author_is_unicode(self):
587 def test_changeset_author_is_unicode(self):
585 for cs in self.repo:
588 for cs in self.repo:
586 self.assertEqual(type(cs.author), unicode)
589 self.assertEqual(type(cs.author), unicode)
587
590
588 def test_repo_files_content_is_unicode(self):
591 def test_repo_files_content_is_unicode(self):
589 changeset = self.repo.get_changeset()
592 changeset = self.repo.get_changeset()
590 for node in changeset.get_node('/'):
593 for node in changeset.get_node('/'):
591 if node.is_file():
594 if node.is_file():
592 self.assertEqual(type(node.content), unicode)
595 self.assertEqual(type(node.content), unicode)
593
596
594 def test_wrong_path(self):
597 def test_wrong_path(self):
595 # There is 'setup.py' in the root dir but not there:
598 # There is 'setup.py' in the root dir but not there:
596 path = 'foo/bar/setup.py'
599 path = 'foo/bar/setup.py'
597 tip = self.repo.get_changeset()
600 tip = self.repo.get_changeset()
598 self.assertRaises(VCSError, tip.get_node, path)
601 self.assertRaises(VCSError, tip.get_node, path)
599
602
600 def test_author_email(self):
603 def test_author_email(self):
601 self.assertEqual('marcin@python-blog.com',
604 self.assertEqual('marcin@python-blog.com',
602 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
605 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
603 .author_email)
606 .author_email)
604 self.assertEqual('lukasz.balcerzak@python-center.pl',
607 self.assertEqual('lukasz.balcerzak@python-center.pl',
605 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
608 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
606 .author_email)
609 .author_email)
607 self.assertEqual('',
610 self.assertEqual('',
608 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
611 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
609 .author_email)
612 .author_email)
610
613
611 def test_author_username(self):
614 def test_author_username(self):
612 self.assertEqual('Marcin Kuzminski',
615 self.assertEqual('Marcin Kuzminski',
613 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
616 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
614 .author_name)
617 .author_name)
615 self.assertEqual('Lukasz Balcerzak',
618 self.assertEqual('Lukasz Balcerzak',
616 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
619 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
617 .author_name)
620 .author_name)
618 self.assertEqual('marcink none@none',
621 self.assertEqual('marcink none@none',
619 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
622 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
620 .author_name)
623 .author_name)
621
624
622
625
623 class GitSpecificTest(unittest.TestCase):
626 class GitSpecificTest(unittest.TestCase):
624
627
625 def test_error_is_raised_for_added_if_diff_name_status_is_wrong(self):
628 def test_error_is_raised_for_added_if_diff_name_status_is_wrong(self):
626 repo = mock.MagicMock()
629 repo = mock.MagicMock()
627 changeset = GitChangeset(repo, 'foobar')
630 changeset = GitChangeset(repo, 'foobar')
628 changeset._diff_name_status = 'foobar'
631 changeset._diff_name_status = 'foobar'
629 with self.assertRaises(VCSError):
632 with self.assertRaises(VCSError):
630 changeset.added
633 changeset.added
631
634
632 def test_error_is_raised_for_changed_if_diff_name_status_is_wrong(self):
635 def test_error_is_raised_for_changed_if_diff_name_status_is_wrong(self):
633 repo = mock.MagicMock()
636 repo = mock.MagicMock()
634 changeset = GitChangeset(repo, 'foobar')
637 changeset = GitChangeset(repo, 'foobar')
635 changeset._diff_name_status = 'foobar'
638 changeset._diff_name_status = 'foobar'
636 with self.assertRaises(VCSError):
639 with self.assertRaises(VCSError):
637 changeset.added
640 changeset.added
638
641
639 def test_error_is_raised_for_removed_if_diff_name_status_is_wrong(self):
642 def test_error_is_raised_for_removed_if_diff_name_status_is_wrong(self):
640 repo = mock.MagicMock()
643 repo = mock.MagicMock()
641 changeset = GitChangeset(repo, 'foobar')
644 changeset = GitChangeset(repo, 'foobar')
642 changeset._diff_name_status = 'foobar'
645 changeset._diff_name_status = 'foobar'
643 with self.assertRaises(VCSError):
646 with self.assertRaises(VCSError):
644 changeset.added
647 changeset.added
645
648
646
649
647 class GitSpecificWithRepoTest(_BackendTestMixin, unittest.TestCase):
650 class GitSpecificWithRepoTest(_BackendTestMixin, unittest.TestCase):
648 backend_alias = 'git'
651 backend_alias = 'git'
649
652
650 @classmethod
653 @classmethod
651 def _get_commits(cls):
654 def _get_commits(cls):
652 return [
655 return [
653 {
656 {
654 'message': 'Initial',
657 'message': 'Initial',
655 'author': 'Joe Doe <joe.doe@example.com>',
658 'author': 'Joe Doe <joe.doe@example.com>',
656 'date': datetime.datetime(2010, 1, 1, 20),
659 'date': datetime.datetime(2010, 1, 1, 20),
657 'added': [
660 'added': [
658 FileNode('foobar/static/js/admin/base.js', content='base'),
661 FileNode('foobar/static/js/admin/base.js', content='base'),
659 FileNode('foobar/static/admin', content='admin',
662 FileNode('foobar/static/admin', content='admin',
660 mode=0120000), # this is a link
663 mode=0120000), # this is a link
661 FileNode('foo', content='foo'),
664 FileNode('foo', content='foo'),
662 ],
665 ],
663 },
666 },
664 {
667 {
665 'message': 'Second',
668 'message': 'Second',
666 'author': 'Joe Doe <joe.doe@example.com>',
669 'author': 'Joe Doe <joe.doe@example.com>',
667 'date': datetime.datetime(2010, 1, 1, 22),
670 'date': datetime.datetime(2010, 1, 1, 22),
668 'added': [
671 'added': [
669 FileNode('foo2', content='foo2'),
672 FileNode('foo2', content='foo2'),
670 ],
673 ],
671 },
674 },
672 ]
675 ]
673
676
674 def test_paths_slow_traversing(self):
677 def test_paths_slow_traversing(self):
675 cs = self.repo.get_changeset()
678 cs = self.repo.get_changeset()
676 self.assertEqual(cs.get_node('foobar').get_node('static').get_node('js')
679 self.assertEqual(cs.get_node('foobar').get_node('static').get_node('js')
677 .get_node('admin').get_node('base.js').content, 'base')
680 .get_node('admin').get_node('base.js').content, 'base')
678
681
679 def test_paths_fast_traversing(self):
682 def test_paths_fast_traversing(self):
680 cs = self.repo.get_changeset()
683 cs = self.repo.get_changeset()
681 self.assertEqual(cs.get_node('foobar/static/js/admin/base.js').content,
684 self.assertEqual(cs.get_node('foobar/static/js/admin/base.js').content,
682 'base')
685 'base')
683
686
684 def test_workdir_get_branch(self):
687 def test_workdir_get_branch(self):
685 self.repo.run_git_command(['checkout', '-b', 'production'])
688 self.repo.run_git_command(['checkout', '-b', 'production'])
686 # Regression test: one of following would fail if we don't check
689 # Regression test: one of following would fail if we don't check
687 # .git/HEAD file
690 # .git/HEAD file
688 self.repo.run_git_command(['checkout', 'production'])
691 self.repo.run_git_command(['checkout', 'production'])
689 self.assertEqual(self.repo.workdir.get_branch(), 'production')
692 self.assertEqual(self.repo.workdir.get_branch(), 'production')
690 self.repo.run_git_command(['checkout', 'master'])
693 self.repo.run_git_command(['checkout', 'master'])
691 self.assertEqual(self.repo.workdir.get_branch(), 'master')
694 self.assertEqual(self.repo.workdir.get_branch(), 'master')
692
695
693 def test_get_diff_runs_git_command_with_hashes(self):
696 def test_get_diff_runs_git_command_with_hashes(self):
694 self.repo.run_git_command = mock.Mock(return_value=['', ''])
697 self.repo.run_git_command = mock.Mock(return_value=['', ''])
695 self.repo.get_diff(0, 1)
698 self.repo.get_diff(0, 1)
696 self.repo.run_git_command.assert_called_once_with(
699 self.repo.run_git_command.assert_called_once_with(
697 ['diff', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
700 ['diff', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
698 self.repo._get_revision(0), self.repo._get_revision(1)])
701 self.repo._get_revision(0), self.repo._get_revision(1)])
699
702
700 def test_get_diff_runs_git_command_with_str_hashes(self):
703 def test_get_diff_runs_git_command_with_str_hashes(self):
701 self.repo.run_git_command = mock.Mock(return_value=['', ''])
704 self.repo.run_git_command = mock.Mock(return_value=['', ''])
702 self.repo.get_diff(self.repo.EMPTY_CHANGESET, 1)
705 self.repo.get_diff(self.repo.EMPTY_CHANGESET, 1)
703 self.repo.run_git_command.assert_called_once_with(
706 self.repo.run_git_command.assert_called_once_with(
704 ['show', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
707 ['show', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
705 self.repo._get_revision(1)])
708 self.repo._get_revision(1)])
706
709
707 def test_get_diff_runs_git_command_with_path_if_its_given(self):
710 def test_get_diff_runs_git_command_with_path_if_its_given(self):
708 self.repo.run_git_command = mock.Mock(return_value=['', ''])
711 self.repo.run_git_command = mock.Mock(return_value=['', ''])
709 self.repo.get_diff(0, 1, 'foo')
712 self.repo.get_diff(0, 1, 'foo')
710 self.repo.run_git_command.assert_called_once_with(
713 self.repo.run_git_command.assert_called_once_with(
711 ['diff', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
714 ['diff', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
712 self.repo._get_revision(0), self.repo._get_revision(1), '--', 'foo'])
715 self.repo._get_revision(0), self.repo._get_revision(1), '--', 'foo'])
713
716
714
717
715 class GitRegressionTest(_BackendTestMixin, unittest.TestCase):
718 class GitRegressionTest(_BackendTestMixin, unittest.TestCase):
716 backend_alias = 'git'
719 backend_alias = 'git'
717
720
718 @classmethod
721 @classmethod
719 def _get_commits(cls):
722 def _get_commits(cls):
720 return [
723 return [
721 {
724 {
722 'message': 'Initial',
725 'message': 'Initial',
723 'author': 'Joe Doe <joe.doe@example.com>',
726 'author': 'Joe Doe <joe.doe@example.com>',
724 'date': datetime.datetime(2010, 1, 1, 20),
727 'date': datetime.datetime(2010, 1, 1, 20),
725 'added': [
728 'added': [
726 FileNode('bot/__init__.py', content='base'),
729 FileNode('bot/__init__.py', content='base'),
727 FileNode('bot/templates/404.html', content='base'),
730 FileNode('bot/templates/404.html', content='base'),
728 FileNode('bot/templates/500.html', content='base'),
731 FileNode('bot/templates/500.html', content='base'),
729 ],
732 ],
730 },
733 },
731 {
734 {
732 'message': 'Second',
735 'message': 'Second',
733 'author': 'Joe Doe <joe.doe@example.com>',
736 'author': 'Joe Doe <joe.doe@example.com>',
734 'date': datetime.datetime(2010, 1, 1, 22),
737 'date': datetime.datetime(2010, 1, 1, 22),
735 'added': [
738 'added': [
736 FileNode('bot/build/migrations/1.py', content='foo2'),
739 FileNode('bot/build/migrations/1.py', content='foo2'),
737 FileNode('bot/build/migrations/2.py', content='foo2'),
740 FileNode('bot/build/migrations/2.py', content='foo2'),
738 FileNode('bot/build/static/templates/f.html', content='foo2'),
741 FileNode('bot/build/static/templates/f.html', content='foo2'),
739 FileNode('bot/build/static/templates/f1.html', content='foo2'),
742 FileNode('bot/build/static/templates/f1.html', content='foo2'),
740 FileNode('bot/build/templates/err.html', content='foo2'),
743 FileNode('bot/build/templates/err.html', content='foo2'),
741 FileNode('bot/build/templates/err2.html', content='foo2'),
744 FileNode('bot/build/templates/err2.html', content='foo2'),
742 ],
745 ],
743 },
746 },
744 ]
747 ]
745
748
746 def test_similar_paths(self):
749 def test_similar_paths(self):
747 cs = self.repo.get_changeset()
750 cs = self.repo.get_changeset()
748 paths = lambda *n:[x.path for x in n]
751 paths = lambda *n:[x.path for x in n]
749 self.assertEqual(paths(*cs.get_nodes('bot')), ['bot/build', 'bot/templates', 'bot/__init__.py'])
752 self.assertEqual(paths(*cs.get_nodes('bot')), ['bot/build', 'bot/templates', 'bot/__init__.py'])
750 self.assertEqual(paths(*cs.get_nodes('bot/build')), ['bot/build/migrations', 'bot/build/static', 'bot/build/templates'])
753 self.assertEqual(paths(*cs.get_nodes('bot/build')), ['bot/build/migrations', 'bot/build/static', 'bot/build/templates'])
751 self.assertEqual(paths(*cs.get_nodes('bot/build/static')), ['bot/build/static/templates'])
754 self.assertEqual(paths(*cs.get_nodes('bot/build/static')), ['bot/build/static/templates'])
752 # this get_nodes below causes troubles !
755 # this get_nodes below causes troubles !
753 self.assertEqual(paths(*cs.get_nodes('bot/build/static/templates')), ['bot/build/static/templates/f.html', 'bot/build/static/templates/f1.html'])
756 self.assertEqual(paths(*cs.get_nodes('bot/build/static/templates')), ['bot/build/static/templates/f.html', 'bot/build/static/templates/f1.html'])
754 self.assertEqual(paths(*cs.get_nodes('bot/build/templates')), ['bot/build/templates/err.html', 'bot/build/templates/err2.html'])
757 self.assertEqual(paths(*cs.get_nodes('bot/build/templates')), ['bot/build/templates/err.html', 'bot/build/templates/err2.html'])
755 self.assertEqual(paths(*cs.get_nodes('bot/templates/')), ['bot/templates/404.html', 'bot/templates/500.html'])
758 self.assertEqual(paths(*cs.get_nodes('bot/templates/')), ['bot/templates/404.html', 'bot/templates/500.html'])
756
759
757
760
758 class GitHooksTest(unittest.TestCase):
761 class GitHooksTest(unittest.TestCase):
759 """
762 """
760 Tests related to hook functionality of Git repositories.
763 Tests related to hook functionality of Git repositories.
761 """
764 """
762
765
763 def setUp(self):
766 def setUp(self):
764 # For each run we want a fresh repo.
767 # For each run we want a fresh repo.
765 self.repo_directory = get_new_dir("githookrepo")
768 self.repo_directory = get_new_dir("githookrepo")
766 self.repo = GitRepository(self.repo_directory, create=True)
769 self.repo = GitRepository(self.repo_directory, create=True)
767
770
768 # Create a dictionary where keys are hook names, and values are paths to
771 # Create a dictionary where keys are hook names, and values are paths to
769 # them. Deduplicates code in tests a bit.
772 # them. Deduplicates code in tests a bit.
770 self.hook_directory = self.repo.get_hook_location()
773 self.hook_directory = self.repo.get_hook_location()
771 self.kallithea_hooks = {h: os.path.join(self.hook_directory, h) for h in ("pre-receive", "post-receive")}
774 self.kallithea_hooks = {h: os.path.join(self.hook_directory, h) for h in ("pre-receive", "post-receive")}
772
775
773 def test_hooks_created_if_missing(self):
776 def test_hooks_created_if_missing(self):
774 """
777 """
775 Tests if hooks are installed in repository if they are missing.
778 Tests if hooks are installed in repository if they are missing.
776 """
779 """
777
780
778 for hook, hook_path in self.kallithea_hooks.iteritems():
781 for hook, hook_path in self.kallithea_hooks.iteritems():
779 if os.path.exists(hook_path):
782 if os.path.exists(hook_path):
780 os.remove(hook_path)
783 os.remove(hook_path)
781
784
782 ScmModel().install_git_hooks(repo=self.repo)
785 ScmModel().install_git_hooks(repo=self.repo)
783
786
784 for hook, hook_path in self.kallithea_hooks.iteritems():
787 for hook, hook_path in self.kallithea_hooks.iteritems():
785 self.assertTrue(os.path.exists(hook_path))
788 self.assertTrue(os.path.exists(hook_path))
786
789
787 def test_kallithea_hooks_updated(self):
790 def test_kallithea_hooks_updated(self):
788 """
791 """
789 Tests if hooks are updated if they are Kallithea hooks already.
792 Tests if hooks are updated if they are Kallithea hooks already.
790 """
793 """
791
794
792 for hook, hook_path in self.kallithea_hooks.iteritems():
795 for hook, hook_path in self.kallithea_hooks.iteritems():
793 with open(hook_path, "w") as f:
796 with open(hook_path, "w") as f:
794 f.write("KALLITHEA_HOOK_VER=0.0.0\nJUST_BOGUS")
797 f.write("KALLITHEA_HOOK_VER=0.0.0\nJUST_BOGUS")
795
798
796 ScmModel().install_git_hooks(repo=self.repo)
799 ScmModel().install_git_hooks(repo=self.repo)
797
800
798 for hook, hook_path in self.kallithea_hooks.iteritems():
801 for hook, hook_path in self.kallithea_hooks.iteritems():
799 with open(hook_path) as f:
802 with open(hook_path) as f:
800 self.assertNotIn("JUST_BOGUS", f.read())
803 self.assertNotIn("JUST_BOGUS", f.read())
801
804
802 def test_custom_hooks_untouched(self):
805 def test_custom_hooks_untouched(self):
803 """
806 """
804 Tests if hooks are left untouched if they are not Kallithea hooks.
807 Tests if hooks are left untouched if they are not Kallithea hooks.
805 """
808 """
806
809
807 for hook, hook_path in self.kallithea_hooks.iteritems():
810 for hook, hook_path in self.kallithea_hooks.iteritems():
808 with open(hook_path, "w") as f:
811 with open(hook_path, "w") as f:
809 f.write("#!/bin/bash\n#CUSTOM_HOOK")
812 f.write("#!/bin/bash\n#CUSTOM_HOOK")
810
813
811 ScmModel().install_git_hooks(repo=self.repo)
814 ScmModel().install_git_hooks(repo=self.repo)
812
815
813 for hook, hook_path in self.kallithea_hooks.iteritems():
816 for hook, hook_path in self.kallithea_hooks.iteritems():
814 with open(hook_path) as f:
817 with open(hook_path) as f:
815 self.assertIn("CUSTOM_HOOK", f.read())
818 self.assertIn("CUSTOM_HOOK", f.read())
816
819
817 def test_custom_hooks_forced_update(self):
820 def test_custom_hooks_forced_update(self):
818 """
821 """
819 Tests if hooks are forcefully updated even though they are custom hooks.
822 Tests if hooks are forcefully updated even though they are custom hooks.
820 """
823 """
821
824
822 for hook, hook_path in self.kallithea_hooks.iteritems():
825 for hook, hook_path in self.kallithea_hooks.iteritems():
823 with open(hook_path, "w") as f:
826 with open(hook_path, "w") as f:
824 f.write("#!/bin/bash\n#CUSTOM_HOOK")
827 f.write("#!/bin/bash\n#CUSTOM_HOOK")
825
828
826 ScmModel().install_git_hooks(repo=self.repo, force_create=True)
829 ScmModel().install_git_hooks(repo=self.repo, force_create=True)
827
830
828 for hook, hook_path in self.kallithea_hooks.iteritems():
831 for hook, hook_path in self.kallithea_hooks.iteritems():
829 with open(hook_path) as f:
832 with open(hook_path) as f:
830 self.assertIn("KALLITHEA_HOOK_VER", f.read())
833 self.assertIn("KALLITHEA_HOOK_VER", f.read())
831
834
832
835
833 if __name__ == '__main__':
836 if __name__ == '__main__':
834 unittest.main()
837 unittest.main()
@@ -1,558 +1,561 b''
1
1
2 import os
2 import os
3
4 import pytest
5
3 from kallithea.lib.utils2 import safe_str
6 from kallithea.lib.utils2 import safe_str
4 from kallithea.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset
7 from kallithea.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset
5 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
8 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
6 from kallithea.lib.vcs.nodes import NodeKind, NodeState
9 from kallithea.lib.vcs.nodes import NodeKind, NodeState
7 from kallithea.tests.vcs.conf import TEST_HG_REPO, TEST_HG_REPO_CLONE, \
10 from kallithea.tests.vcs.conf import TEST_HG_REPO, TEST_HG_REPO_CLONE, \
8 TEST_HG_REPO_PULL
11 TEST_HG_REPO_PULL
9 from kallithea.lib.vcs.utils.compat import unittest
12 from kallithea.lib.vcs.utils.compat import unittest
10
13
11
14
12 # Use only clean mercurial's ui
15 # Use only clean mercurial's ui
13 from kallithea.lib.vcs.utils.hgcompat import mercurial
16 from kallithea.lib.vcs.utils.hgcompat import mercurial
14 mercurial.scmutil.rcpath()
17 mercurial.scmutil.rcpath()
15 if mercurial.scmutil._rcpath:
18 if mercurial.scmutil._rcpath:
16 mercurial.scmutil._rcpath = mercurial.scmutil._rcpath[:1]
19 mercurial.scmutil._rcpath = mercurial.scmutil._rcpath[:1]
17
20
18
21
19 class MercurialRepositoryTest(unittest.TestCase):
22 class MercurialRepositoryTest(unittest.TestCase):
20
23
21 def __check_for_existing_repo(self):
24 def __check_for_existing_repo(self):
22 if os.path.exists(TEST_HG_REPO_CLONE):
25 if os.path.exists(TEST_HG_REPO_CLONE):
23 self.fail('Cannot test mercurial clone repo as location %s already '
26 pytest.fail('Cannot test mercurial clone repo as location %s already '
24 'exists. You should manually remove it first.'
27 'exists. You should manually remove it first.'
25 % TEST_HG_REPO_CLONE)
28 % TEST_HG_REPO_CLONE)
26
29
27 def setUp(self):
30 def setUp(self):
28 self.repo = MercurialRepository(safe_str(TEST_HG_REPO))
31 self.repo = MercurialRepository(safe_str(TEST_HG_REPO))
29
32
30 def test_wrong_repo_path(self):
33 def test_wrong_repo_path(self):
31 wrong_repo_path = '/tmp/errorrepo'
34 wrong_repo_path = '/tmp/errorrepo'
32 self.assertRaises(RepositoryError, MercurialRepository, wrong_repo_path)
35 self.assertRaises(RepositoryError, MercurialRepository, wrong_repo_path)
33
36
34 def test_unicode_path_repo(self):
37 def test_unicode_path_repo(self):
35 self.assertRaises(VCSError,lambda:MercurialRepository(u'iShouldFail'))
38 self.assertRaises(VCSError,lambda:MercurialRepository(u'iShouldFail'))
36
39
37 def test_repo_clone(self):
40 def test_repo_clone(self):
38 self.__check_for_existing_repo()
41 self.__check_for_existing_repo()
39 repo = MercurialRepository(safe_str(TEST_HG_REPO))
42 repo = MercurialRepository(safe_str(TEST_HG_REPO))
40 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
43 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
41 src_url=TEST_HG_REPO, update_after_clone=True)
44 src_url=TEST_HG_REPO, update_after_clone=True)
42 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
45 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
43 # Checking hashes of changesets should be enough
46 # Checking hashes of changesets should be enough
44 for changeset in repo.get_changesets():
47 for changeset in repo.get_changesets():
45 raw_id = changeset.raw_id
48 raw_id = changeset.raw_id
46 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
49 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
47
50
48 def test_repo_clone_with_update(self):
51 def test_repo_clone_with_update(self):
49 repo = MercurialRepository(safe_str(TEST_HG_REPO))
52 repo = MercurialRepository(safe_str(TEST_HG_REPO))
50 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_w_update',
53 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_w_update',
51 src_url=TEST_HG_REPO, update_after_clone=True)
54 src_url=TEST_HG_REPO, update_after_clone=True)
52 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
55 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
53
56
54 #check if current workdir was updated
57 #check if current workdir was updated
55 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
58 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
56 + '_w_update',
59 + '_w_update',
57 'MANIFEST.in')), True,)
60 'MANIFEST.in')), True,)
58
61
59 def test_repo_clone_without_update(self):
62 def test_repo_clone_without_update(self):
60 repo = MercurialRepository(safe_str(TEST_HG_REPO))
63 repo = MercurialRepository(safe_str(TEST_HG_REPO))
61 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_wo_update',
64 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE + '_wo_update',
62 src_url=TEST_HG_REPO, update_after_clone=False)
65 src_url=TEST_HG_REPO, update_after_clone=False)
63 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
66 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
64 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
67 self.assertEqual(os.path.isfile(os.path.join(TEST_HG_REPO_CLONE \
65 + '_wo_update',
68 + '_wo_update',
66 'MANIFEST.in')), False,)
69 'MANIFEST.in')), False,)
67
70
68 def test_pull(self):
71 def test_pull(self):
69 if os.path.exists(TEST_HG_REPO_PULL):
72 if os.path.exists(TEST_HG_REPO_PULL):
70 self.fail('Cannot test mercurial pull command as location %s '
73 pytest.fail('Cannot test mercurial pull command as location %s '
71 'already exists. You should manually remove it first'
74 'already exists. You should manually remove it first'
72 % TEST_HG_REPO_PULL)
75 % TEST_HG_REPO_PULL)
73 repo_new = MercurialRepository(TEST_HG_REPO_PULL, create=True)
76 repo_new = MercurialRepository(TEST_HG_REPO_PULL, create=True)
74 self.assertTrue(len(self.repo.revisions) > len(repo_new.revisions))
77 self.assertTrue(len(self.repo.revisions) > len(repo_new.revisions))
75
78
76 repo_new.pull(self.repo.path)
79 repo_new.pull(self.repo.path)
77 repo_new = MercurialRepository(TEST_HG_REPO_PULL)
80 repo_new = MercurialRepository(TEST_HG_REPO_PULL)
78 self.assertTrue(len(self.repo.revisions) == len(repo_new.revisions))
81 self.assertTrue(len(self.repo.revisions) == len(repo_new.revisions))
79
82
80 def test_revisions(self):
83 def test_revisions(self):
81 # there are 21 revisions at bitbucket now
84 # there are 21 revisions at bitbucket now
82 # so we can assume they would be available from now on
85 # so we can assume they would be available from now on
83 subset = set(['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
86 subset = set(['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
84 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
87 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
85 '6cba7170863a2411822803fa77a0a264f1310b35',
88 '6cba7170863a2411822803fa77a0a264f1310b35',
86 '56349e29c2af3ac913b28bde9a2c6154436e615b',
89 '56349e29c2af3ac913b28bde9a2c6154436e615b',
87 '2dda4e345facb0ccff1a191052dd1606dba6781d',
90 '2dda4e345facb0ccff1a191052dd1606dba6781d',
88 '6fff84722075f1607a30f436523403845f84cd9e',
91 '6fff84722075f1607a30f436523403845f84cd9e',
89 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
92 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
90 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
93 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
91 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
94 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
92 'be90031137367893f1c406e0a8683010fd115b79',
95 'be90031137367893f1c406e0a8683010fd115b79',
93 'db8e58be770518cbb2b1cdfa69146e47cd481481',
96 'db8e58be770518cbb2b1cdfa69146e47cd481481',
94 '84478366594b424af694a6c784cb991a16b87c21',
97 '84478366594b424af694a6c784cb991a16b87c21',
95 '17f8e105dddb9f339600389c6dc7175d395a535c',
98 '17f8e105dddb9f339600389c6dc7175d395a535c',
96 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
99 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
97 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
100 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
98 '786facd2c61deb9cf91e9534735124fb8fc11842',
101 '786facd2c61deb9cf91e9534735124fb8fc11842',
99 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
102 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
100 'aa6a0de05b7612707db567078e130a6cd114a9a7',
103 'aa6a0de05b7612707db567078e130a6cd114a9a7',
101 'eada5a770da98ab0dd7325e29d00e0714f228d09'
104 'eada5a770da98ab0dd7325e29d00e0714f228d09'
102 ])
105 ])
103 self.assertTrue(subset.issubset(set(self.repo.revisions)))
106 self.assertTrue(subset.issubset(set(self.repo.revisions)))
104
107
105
108
106 # check if we have the proper order of revisions
109 # check if we have the proper order of revisions
107 org = ['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
110 org = ['b986218ba1c9b0d6a259fac9b050b1724ed8e545',
108 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
111 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
109 '6cba7170863a2411822803fa77a0a264f1310b35',
112 '6cba7170863a2411822803fa77a0a264f1310b35',
110 '56349e29c2af3ac913b28bde9a2c6154436e615b',
113 '56349e29c2af3ac913b28bde9a2c6154436e615b',
111 '2dda4e345facb0ccff1a191052dd1606dba6781d',
114 '2dda4e345facb0ccff1a191052dd1606dba6781d',
112 '6fff84722075f1607a30f436523403845f84cd9e',
115 '6fff84722075f1607a30f436523403845f84cd9e',
113 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
116 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
114 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
117 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
115 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
118 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
116 'be90031137367893f1c406e0a8683010fd115b79',
119 'be90031137367893f1c406e0a8683010fd115b79',
117 'db8e58be770518cbb2b1cdfa69146e47cd481481',
120 'db8e58be770518cbb2b1cdfa69146e47cd481481',
118 '84478366594b424af694a6c784cb991a16b87c21',
121 '84478366594b424af694a6c784cb991a16b87c21',
119 '17f8e105dddb9f339600389c6dc7175d395a535c',
122 '17f8e105dddb9f339600389c6dc7175d395a535c',
120 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
123 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
121 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
124 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
122 '786facd2c61deb9cf91e9534735124fb8fc11842',
125 '786facd2c61deb9cf91e9534735124fb8fc11842',
123 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
126 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
124 'aa6a0de05b7612707db567078e130a6cd114a9a7',
127 'aa6a0de05b7612707db567078e130a6cd114a9a7',
125 'eada5a770da98ab0dd7325e29d00e0714f228d09',
128 'eada5a770da98ab0dd7325e29d00e0714f228d09',
126 '2c1885c735575ca478bf9e17b0029dca68824458',
129 '2c1885c735575ca478bf9e17b0029dca68824458',
127 'd9bcd465040bf869799b09ad732c04e0eea99fe9',
130 'd9bcd465040bf869799b09ad732c04e0eea99fe9',
128 '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7',
131 '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7',
129 '4fb8326d78e5120da2c7468dcf7098997be385da',
132 '4fb8326d78e5120da2c7468dcf7098997be385da',
130 '62b4a097164940bd66030c4db51687f3ec035eed',
133 '62b4a097164940bd66030c4db51687f3ec035eed',
131 '536c1a19428381cfea92ac44985304f6a8049569',
134 '536c1a19428381cfea92ac44985304f6a8049569',
132 '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4',
135 '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4',
133 '9bb326a04ae5d98d437dece54be04f830cf1edd9',
136 '9bb326a04ae5d98d437dece54be04f830cf1edd9',
134 'f8940bcb890a98c4702319fbe36db75ea309b475',
137 'f8940bcb890a98c4702319fbe36db75ea309b475',
135 'ff5ab059786ebc7411e559a2cc309dfae3625a3b',
138 'ff5ab059786ebc7411e559a2cc309dfae3625a3b',
136 '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08',
139 '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08',
137 'ee87846a61c12153b51543bf860e1026c6d3dcba', ]
140 'ee87846a61c12153b51543bf860e1026c6d3dcba', ]
138 self.assertEqual(org, self.repo.revisions[:31])
141 self.assertEqual(org, self.repo.revisions[:31])
139
142
140 def test_iter_slice(self):
143 def test_iter_slice(self):
141 sliced = list(self.repo[:10])
144 sliced = list(self.repo[:10])
142 itered = list(self.repo)[:10]
145 itered = list(self.repo)[:10]
143 self.assertEqual(sliced, itered)
146 self.assertEqual(sliced, itered)
144
147
145 def test_slicing(self):
148 def test_slicing(self):
146 #4 1 5 10 95
149 #4 1 5 10 95
147 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
150 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
148 (10, 20, 10), (5, 100, 95)]:
151 (10, 20, 10), (5, 100, 95)]:
149 revs = list(self.repo[sfrom:sto])
152 revs = list(self.repo[sfrom:sto])
150 self.assertEqual(len(revs), size)
153 self.assertEqual(len(revs), size)
151 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
154 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
152 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
155 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
153
156
154 def test_branches(self):
157 def test_branches(self):
155 # TODO: Need more tests here
158 # TODO: Need more tests here
156
159
157 #active branches
160 #active branches
158 self.assertTrue('default' in self.repo.branches)
161 self.assertTrue('default' in self.repo.branches)
159 self.assertTrue('stable' in self.repo.branches)
162 self.assertTrue('stable' in self.repo.branches)
160
163
161 # closed
164 # closed
162 self.assertTrue('git' in self.repo._get_branches(closed=True))
165 self.assertTrue('git' in self.repo._get_branches(closed=True))
163 self.assertTrue('web' in self.repo._get_branches(closed=True))
166 self.assertTrue('web' in self.repo._get_branches(closed=True))
164
167
165 for name, id in self.repo.branches.items():
168 for name, id in self.repo.branches.items():
166 self.assertTrue(isinstance(
169 self.assertTrue(isinstance(
167 self.repo.get_changeset(id), MercurialChangeset))
170 self.repo.get_changeset(id), MercurialChangeset))
168
171
169 def test_tip_in_tags(self):
172 def test_tip_in_tags(self):
170 # tip is always a tag
173 # tip is always a tag
171 self.assertIn('tip', self.repo.tags)
174 self.assertIn('tip', self.repo.tags)
172
175
173 def test_tip_changeset_in_tags(self):
176 def test_tip_changeset_in_tags(self):
174 tip = self.repo.get_changeset()
177 tip = self.repo.get_changeset()
175 self.assertEqual(self.repo.tags['tip'], tip.raw_id)
178 self.assertEqual(self.repo.tags['tip'], tip.raw_id)
176
179
177 def test_initial_changeset(self):
180 def test_initial_changeset(self):
178
181
179 init_chset = self.repo.get_changeset(0)
182 init_chset = self.repo.get_changeset(0)
180 self.assertEqual(init_chset.message, 'initial import')
183 self.assertEqual(init_chset.message, 'initial import')
181 self.assertEqual(init_chset.author,
184 self.assertEqual(init_chset.author,
182 'Marcin Kuzminski <marcin@python-blog.com>')
185 'Marcin Kuzminski <marcin@python-blog.com>')
183 self.assertEqual(sorted(init_chset._file_paths),
186 self.assertEqual(sorted(init_chset._file_paths),
184 sorted([
187 sorted([
185 'vcs/__init__.py',
188 'vcs/__init__.py',
186 'vcs/backends/BaseRepository.py',
189 'vcs/backends/BaseRepository.py',
187 'vcs/backends/__init__.py',
190 'vcs/backends/__init__.py',
188 ])
191 ])
189 )
192 )
190 self.assertEqual(sorted(init_chset._dir_paths),
193 self.assertEqual(sorted(init_chset._dir_paths),
191 sorted(['', 'vcs', 'vcs/backends']))
194 sorted(['', 'vcs', 'vcs/backends']))
192
195
193 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
196 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
194
197
195 node = init_chset.get_node('vcs/')
198 node = init_chset.get_node('vcs/')
196 self.assertTrue(hasattr(node, 'kind'))
199 self.assertTrue(hasattr(node, 'kind'))
197 self.assertEqual(node.kind, NodeKind.DIR)
200 self.assertEqual(node.kind, NodeKind.DIR)
198
201
199 node = init_chset.get_node('vcs')
202 node = init_chset.get_node('vcs')
200 self.assertTrue(hasattr(node, 'kind'))
203 self.assertTrue(hasattr(node, 'kind'))
201 self.assertEqual(node.kind, NodeKind.DIR)
204 self.assertEqual(node.kind, NodeKind.DIR)
202
205
203 node = init_chset.get_node('vcs/__init__.py')
206 node = init_chset.get_node('vcs/__init__.py')
204 self.assertTrue(hasattr(node, 'kind'))
207 self.assertTrue(hasattr(node, 'kind'))
205 self.assertEqual(node.kind, NodeKind.FILE)
208 self.assertEqual(node.kind, NodeKind.FILE)
206
209
207 def test_not_existing_changeset(self):
210 def test_not_existing_changeset(self):
208 #rawid
211 #rawid
209 self.assertRaises(RepositoryError, self.repo.get_changeset,
212 self.assertRaises(RepositoryError, self.repo.get_changeset,
210 'abcd' * 10)
213 'abcd' * 10)
211 #shortid
214 #shortid
212 self.assertRaises(RepositoryError, self.repo.get_changeset,
215 self.assertRaises(RepositoryError, self.repo.get_changeset,
213 'erro' * 4)
216 'erro' * 4)
214 #numeric
217 #numeric
215 self.assertRaises(RepositoryError, self.repo.get_changeset,
218 self.assertRaises(RepositoryError, self.repo.get_changeset,
216 self.repo.count() + 1)
219 self.repo.count() + 1)
217
220
218
221
219 # Small chance we ever get to this one
222 # Small chance we ever get to this one
220 revision = pow(2, 30)
223 revision = pow(2, 30)
221 self.assertRaises(RepositoryError, self.repo.get_changeset, revision)
224 self.assertRaises(RepositoryError, self.repo.get_changeset, revision)
222
225
223 def test_changeset10(self):
226 def test_changeset10(self):
224
227
225 chset10 = self.repo.get_changeset(10)
228 chset10 = self.repo.get_changeset(10)
226 README = """===
229 README = """===
227 VCS
230 VCS
228 ===
231 ===
229
232
230 Various Version Control System management abstraction layer for Python.
233 Various Version Control System management abstraction layer for Python.
231
234
232 Introduction
235 Introduction
233 ------------
236 ------------
234
237
235 TODO: To be written...
238 TODO: To be written...
236
239
237 """
240 """
238 node = chset10.get_node('README.rst')
241 node = chset10.get_node('README.rst')
239 self.assertEqual(node.kind, NodeKind.FILE)
242 self.assertEqual(node.kind, NodeKind.FILE)
240 self.assertEqual(node.content, README)
243 self.assertEqual(node.content, README)
241
244
242
245
243 class MercurialChangesetTest(unittest.TestCase):
246 class MercurialChangesetTest(unittest.TestCase):
244
247
245 def setUp(self):
248 def setUp(self):
246 self.repo = MercurialRepository(safe_str(TEST_HG_REPO))
249 self.repo = MercurialRepository(safe_str(TEST_HG_REPO))
247
250
248 def _test_equality(self, changeset):
251 def _test_equality(self, changeset):
249 revision = changeset.revision
252 revision = changeset.revision
250 self.assertEqual(changeset, self.repo.get_changeset(revision))
253 self.assertEqual(changeset, self.repo.get_changeset(revision))
251
254
252 def test_equality(self):
255 def test_equality(self):
253 self.setUp()
256 self.setUp()
254 revs = [0, 10, 20]
257 revs = [0, 10, 20]
255 changesets = [self.repo.get_changeset(rev) for rev in revs]
258 changesets = [self.repo.get_changeset(rev) for rev in revs]
256 for changeset in changesets:
259 for changeset in changesets:
257 self._test_equality(changeset)
260 self._test_equality(changeset)
258
261
259 def test_default_changeset(self):
262 def test_default_changeset(self):
260 tip = self.repo.get_changeset('tip')
263 tip = self.repo.get_changeset('tip')
261 self.assertEqual(tip, self.repo.get_changeset())
264 self.assertEqual(tip, self.repo.get_changeset())
262 self.assertEqual(tip, self.repo.get_changeset(revision=None))
265 self.assertEqual(tip, self.repo.get_changeset(revision=None))
263 self.assertEqual(tip, list(self.repo[-1:])[0])
266 self.assertEqual(tip, list(self.repo[-1:])[0])
264
267
265 def test_root_node(self):
268 def test_root_node(self):
266 tip = self.repo.get_changeset('tip')
269 tip = self.repo.get_changeset('tip')
267 self.assertTrue(tip.root is tip.get_node(''))
270 self.assertTrue(tip.root is tip.get_node(''))
268
271
269 def test_lazy_fetch(self):
272 def test_lazy_fetch(self):
270 """
273 """
271 Test if changeset's nodes expands and are cached as we walk through
274 Test if changeset's nodes expands and are cached as we walk through
272 the revision. This test is somewhat hard to write as order of tests
275 the revision. This test is somewhat hard to write as order of tests
273 is a key here. Written by running command after command in a shell.
276 is a key here. Written by running command after command in a shell.
274 """
277 """
275 self.setUp()
278 self.setUp()
276 chset = self.repo.get_changeset(45)
279 chset = self.repo.get_changeset(45)
277 self.assertTrue(len(chset.nodes) == 0)
280 self.assertTrue(len(chset.nodes) == 0)
278 root = chset.root
281 root = chset.root
279 self.assertTrue(len(chset.nodes) == 1)
282 self.assertTrue(len(chset.nodes) == 1)
280 self.assertTrue(len(root.nodes) == 8)
283 self.assertTrue(len(root.nodes) == 8)
281 # accessing root.nodes updates chset.nodes
284 # accessing root.nodes updates chset.nodes
282 self.assertTrue(len(chset.nodes) == 9)
285 self.assertTrue(len(chset.nodes) == 9)
283
286
284 docs = root.get_node('docs')
287 docs = root.get_node('docs')
285 # we haven't yet accessed anything new as docs dir was already cached
288 # we haven't yet accessed anything new as docs dir was already cached
286 self.assertTrue(len(chset.nodes) == 9)
289 self.assertTrue(len(chset.nodes) == 9)
287 self.assertTrue(len(docs.nodes) == 8)
290 self.assertTrue(len(docs.nodes) == 8)
288 # accessing docs.nodes updates chset.nodes
291 # accessing docs.nodes updates chset.nodes
289 self.assertTrue(len(chset.nodes) == 17)
292 self.assertTrue(len(chset.nodes) == 17)
290
293
291 self.assertTrue(docs is chset.get_node('docs'))
294 self.assertTrue(docs is chset.get_node('docs'))
292 self.assertTrue(docs is root.nodes[0])
295 self.assertTrue(docs is root.nodes[0])
293 self.assertTrue(docs is root.dirs[0])
296 self.assertTrue(docs is root.dirs[0])
294 self.assertTrue(docs is chset.get_node('docs'))
297 self.assertTrue(docs is chset.get_node('docs'))
295
298
296 def test_nodes_with_changeset(self):
299 def test_nodes_with_changeset(self):
297 self.setUp()
300 self.setUp()
298 chset = self.repo.get_changeset(45)
301 chset = self.repo.get_changeset(45)
299 root = chset.root
302 root = chset.root
300 docs = root.get_node('docs')
303 docs = root.get_node('docs')
301 self.assertTrue(docs is chset.get_node('docs'))
304 self.assertTrue(docs is chset.get_node('docs'))
302 api = docs.get_node('api')
305 api = docs.get_node('api')
303 self.assertTrue(api is chset.get_node('docs/api'))
306 self.assertTrue(api is chset.get_node('docs/api'))
304 index = api.get_node('index.rst')
307 index = api.get_node('index.rst')
305 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
308 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
306 self.assertTrue(index is chset.get_node('docs') \
309 self.assertTrue(index is chset.get_node('docs') \
307 .get_node('api') \
310 .get_node('api') \
308 .get_node('index.rst'))
311 .get_node('index.rst'))
309
312
310 def test_branch_and_tags(self):
313 def test_branch_and_tags(self):
311 chset0 = self.repo.get_changeset(0)
314 chset0 = self.repo.get_changeset(0)
312 self.assertEqual(chset0.branch, 'default')
315 self.assertEqual(chset0.branch, 'default')
313 self.assertEqual(chset0.tags, [])
316 self.assertEqual(chset0.tags, [])
314
317
315 chset10 = self.repo.get_changeset(10)
318 chset10 = self.repo.get_changeset(10)
316 self.assertEqual(chset10.branch, 'default')
319 self.assertEqual(chset10.branch, 'default')
317 self.assertEqual(chset10.tags, [])
320 self.assertEqual(chset10.tags, [])
318
321
319 chset44 = self.repo.get_changeset(44)
322 chset44 = self.repo.get_changeset(44)
320 self.assertEqual(chset44.branch, 'web')
323 self.assertEqual(chset44.branch, 'web')
321
324
322 tip = self.repo.get_changeset('tip')
325 tip = self.repo.get_changeset('tip')
323 self.assertTrue('tip' in tip.tags)
326 self.assertTrue('tip' in tip.tags)
324
327
325 def _test_file_size(self, revision, path, size):
328 def _test_file_size(self, revision, path, size):
326 node = self.repo.get_changeset(revision).get_node(path)
329 node = self.repo.get_changeset(revision).get_node(path)
327 self.assertTrue(node.is_file())
330 self.assertTrue(node.is_file())
328 self.assertEqual(node.size, size)
331 self.assertEqual(node.size, size)
329
332
330 def test_file_size(self):
333 def test_file_size(self):
331 to_check = (
334 to_check = (
332 (10, 'setup.py', 1068),
335 (10, 'setup.py', 1068),
333 (20, 'setup.py', 1106),
336 (20, 'setup.py', 1106),
334 (60, 'setup.py', 1074),
337 (60, 'setup.py', 1074),
335
338
336 (10, 'vcs/backends/base.py', 2921),
339 (10, 'vcs/backends/base.py', 2921),
337 (20, 'vcs/backends/base.py', 3936),
340 (20, 'vcs/backends/base.py', 3936),
338 (60, 'vcs/backends/base.py', 6189),
341 (60, 'vcs/backends/base.py', 6189),
339 )
342 )
340 for revision, path, size in to_check:
343 for revision, path, size in to_check:
341 self._test_file_size(revision, path, size)
344 self._test_file_size(revision, path, size)
342
345
343 def test_file_history(self):
346 def test_file_history(self):
344 # we can only check if those revisions are present in the history
347 # we can only check if those revisions are present in the history
345 # as we cannot update this test every time file is changed
348 # as we cannot update this test every time file is changed
346 files = {
349 files = {
347 'setup.py': [7, 18, 45, 46, 47, 69, 77],
350 'setup.py': [7, 18, 45, 46, 47, 69, 77],
348 'vcs/nodes.py': [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60,
351 'vcs/nodes.py': [7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60,
349 61, 73, 76],
352 61, 73, 76],
350 'vcs/backends/hg.py': [4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23,
353 'vcs/backends/hg.py': [4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23,
351 26, 27, 28, 30, 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47,
354 26, 27, 28, 30, 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47,
352 48, 49, 53, 54, 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79,
355 48, 49, 53, 54, 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79,
353 82],
356 82],
354 }
357 }
355 for path, revs in files.items():
358 for path, revs in files.items():
356 tip = self.repo.get_changeset(revs[-1])
359 tip = self.repo.get_changeset(revs[-1])
357 node = tip.get_node(path)
360 node = tip.get_node(path)
358 node_revs = [chset.revision for chset in node.history]
361 node_revs = [chset.revision for chset in node.history]
359 self.assertTrue(set(revs).issubset(set(node_revs)),
362 self.assertTrue(set(revs).issubset(set(node_revs)),
360 "We assumed that %s is subset of revisions for which file %s "
363 "We assumed that %s is subset of revisions for which file %s "
361 "has been changed, and history of that node returned: %s"
364 "has been changed, and history of that node returned: %s"
362 % (revs, path, node_revs))
365 % (revs, path, node_revs))
363
366
364 def test_file_annotate(self):
367 def test_file_annotate(self):
365 files = {
368 files = {
366 'vcs/backends/__init__.py':
369 'vcs/backends/__init__.py':
367 {89: {'lines_no': 31,
370 {89: {'lines_no': 31,
368 'changesets': [32, 32, 61, 32, 32, 37, 32, 32, 32, 44,
371 'changesets': [32, 32, 61, 32, 32, 37, 32, 32, 32, 44,
369 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
372 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
370 32, 32, 32, 32, 37, 32, 37, 37, 32,
373 32, 32, 32, 32, 37, 32, 37, 37, 32,
371 32, 32]},
374 32, 32]},
372 20: {'lines_no': 1,
375 20: {'lines_no': 1,
373 'changesets': [4]},
376 'changesets': [4]},
374 55: {'lines_no': 31,
377 55: {'lines_no': 31,
375 'changesets': [32, 32, 45, 32, 32, 37, 32, 32, 32, 44,
378 'changesets': [32, 32, 45, 32, 32, 37, 32, 32, 32, 44,
376 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
379 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
377 32, 32, 32, 32, 37, 32, 37, 37, 32,
380 32, 32, 32, 32, 37, 32, 37, 37, 32,
378 32, 32]}},
381 32, 32]}},
379 'vcs/exceptions.py':
382 'vcs/exceptions.py':
380 {89: {'lines_no': 18,
383 {89: {'lines_no': 18,
381 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
384 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
382 16, 16, 17, 16, 16, 18, 18, 18]},
385 16, 16, 17, 16, 16, 18, 18, 18]},
383 20: {'lines_no': 18,
386 20: {'lines_no': 18,
384 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
387 'changesets': [16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
385 16, 16, 17, 16, 16, 18, 18, 18]},
388 16, 16, 17, 16, 16, 18, 18, 18]},
386 55: {'lines_no': 18, 'changesets': [16, 16, 16, 16, 16, 16,
389 55: {'lines_no': 18, 'changesets': [16, 16, 16, 16, 16, 16,
387 16, 16, 16, 16, 16, 16,
390 16, 16, 16, 16, 16, 16,
388 17, 16, 16, 18, 18, 18]}},
391 17, 16, 16, 18, 18, 18]}},
389 'MANIFEST.in': {89: {'lines_no': 5,
392 'MANIFEST.in': {89: {'lines_no': 5,
390 'changesets': [7, 7, 7, 71, 71]},
393 'changesets': [7, 7, 7, 71, 71]},
391 20: {'lines_no': 3,
394 20: {'lines_no': 3,
392 'changesets': [7, 7, 7]},
395 'changesets': [7, 7, 7]},
393 55: {'lines_no': 3,
396 55: {'lines_no': 3,
394 'changesets': [7, 7, 7]}}}
397 'changesets': [7, 7, 7]}}}
395
398
396 for fname, revision_dict in files.items():
399 for fname, revision_dict in files.items():
397 for rev, data in revision_dict.items():
400 for rev, data in revision_dict.items():
398 cs = self.repo.get_changeset(rev)
401 cs = self.repo.get_changeset(rev)
399 l1_1 = [x[1] for x in cs.get_file_annotate(fname)]
402 l1_1 = [x[1] for x in cs.get_file_annotate(fname)]
400 l1_2 = [x[2]().raw_id for x in cs.get_file_annotate(fname)]
403 l1_2 = [x[2]().raw_id for x in cs.get_file_annotate(fname)]
401 self.assertEqual(l1_1, l1_2)
404 self.assertEqual(l1_1, l1_2)
402 l1 = l1_2 = [x[2]().revision for x in cs.get_file_annotate(fname)]
405 l1 = l1_2 = [x[2]().revision for x in cs.get_file_annotate(fname)]
403 l2 = files[fname][rev]['changesets']
406 l2 = files[fname][rev]['changesets']
404 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev%s"
407 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev%s"
405 "from annotation list should match each other,"
408 "from annotation list should match each other,"
406 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
409 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
407
410
408 def test_changeset_state(self):
411 def test_changeset_state(self):
409 """
412 """
410 Tests which files have been added/changed/removed at particular revision
413 Tests which files have been added/changed/removed at particular revision
411 """
414 """
412
415
413 # rev 46ad32a4f974:
416 # rev 46ad32a4f974:
414 # hg st --rev 46ad32a4f974
417 # hg st --rev 46ad32a4f974
415 # changed: 13
418 # changed: 13
416 # added: 20
419 # added: 20
417 # removed: 1
420 # removed: 1
418 changed = set(['.hgignore'
421 changed = set(['.hgignore'
419 , 'README.rst' , 'docs/conf.py' , 'docs/index.rst' , 'setup.py'
422 , 'README.rst' , 'docs/conf.py' , 'docs/index.rst' , 'setup.py'
420 , 'tests/test_hg.py' , 'tests/test_nodes.py' , 'vcs/__init__.py'
423 , 'tests/test_hg.py' , 'tests/test_nodes.py' , 'vcs/__init__.py'
421 , 'vcs/backends/__init__.py' , 'vcs/backends/base.py'
424 , 'vcs/backends/__init__.py' , 'vcs/backends/base.py'
422 , 'vcs/backends/hg.py' , 'vcs/nodes.py' , 'vcs/utils/__init__.py'])
425 , 'vcs/backends/hg.py' , 'vcs/nodes.py' , 'vcs/utils/__init__.py'])
423
426
424 added = set(['docs/api/backends/hg.rst'
427 added = set(['docs/api/backends/hg.rst'
425 , 'docs/api/backends/index.rst' , 'docs/api/index.rst'
428 , 'docs/api/backends/index.rst' , 'docs/api/index.rst'
426 , 'docs/api/nodes.rst' , 'docs/api/web/index.rst'
429 , 'docs/api/nodes.rst' , 'docs/api/web/index.rst'
427 , 'docs/api/web/simplevcs.rst' , 'docs/installation.rst'
430 , 'docs/api/web/simplevcs.rst' , 'docs/installation.rst'
428 , 'docs/quickstart.rst' , 'setup.cfg' , 'vcs/utils/baseui_config.py'
431 , 'docs/quickstart.rst' , 'setup.cfg' , 'vcs/utils/baseui_config.py'
429 , 'vcs/utils/web.py' , 'vcs/web/__init__.py' , 'vcs/web/exceptions.py'
432 , 'vcs/utils/web.py' , 'vcs/web/__init__.py' , 'vcs/web/exceptions.py'
430 , 'vcs/web/simplevcs/__init__.py' , 'vcs/web/simplevcs/exceptions.py'
433 , 'vcs/web/simplevcs/__init__.py' , 'vcs/web/simplevcs/exceptions.py'
431 , 'vcs/web/simplevcs/middleware.py' , 'vcs/web/simplevcs/models.py'
434 , 'vcs/web/simplevcs/middleware.py' , 'vcs/web/simplevcs/models.py'
432 , 'vcs/web/simplevcs/settings.py' , 'vcs/web/simplevcs/utils.py'
435 , 'vcs/web/simplevcs/settings.py' , 'vcs/web/simplevcs/utils.py'
433 , 'vcs/web/simplevcs/views.py'])
436 , 'vcs/web/simplevcs/views.py'])
434
437
435 removed = set(['docs/api.rst'])
438 removed = set(['docs/api.rst'])
436
439
437 chset64 = self.repo.get_changeset('46ad32a4f974')
440 chset64 = self.repo.get_changeset('46ad32a4f974')
438 self.assertEqual(set((node.path for node in chset64.added)), added)
441 self.assertEqual(set((node.path for node in chset64.added)), added)
439 self.assertEqual(set((node.path for node in chset64.changed)), changed)
442 self.assertEqual(set((node.path for node in chset64.changed)), changed)
440 self.assertEqual(set((node.path for node in chset64.removed)), removed)
443 self.assertEqual(set((node.path for node in chset64.removed)), removed)
441
444
442 # rev b090f22d27d6:
445 # rev b090f22d27d6:
443 # hg st --rev b090f22d27d6
446 # hg st --rev b090f22d27d6
444 # changed: 13
447 # changed: 13
445 # added: 20
448 # added: 20
446 # removed: 1
449 # removed: 1
447 chset88 = self.repo.get_changeset('b090f22d27d6')
450 chset88 = self.repo.get_changeset('b090f22d27d6')
448 self.assertEqual(set((node.path for node in chset88.added)), set())
451 self.assertEqual(set((node.path for node in chset88.added)), set())
449 self.assertEqual(set((node.path for node in chset88.changed)),
452 self.assertEqual(set((node.path for node in chset88.changed)),
450 set(['.hgignore']))
453 set(['.hgignore']))
451 self.assertEqual(set((node.path for node in chset88.removed)), set())
454 self.assertEqual(set((node.path for node in chset88.removed)), set())
452 #
455 #
453 # 85:
456 # 85:
454 # added: 2 ['vcs/utils/diffs.py', 'vcs/web/simplevcs/views/diffs.py']
457 # added: 2 ['vcs/utils/diffs.py', 'vcs/web/simplevcs/views/diffs.py']
455 # changed: 4 ['vcs/web/simplevcs/models.py', ...]
458 # changed: 4 ['vcs/web/simplevcs/models.py', ...]
456 # removed: 1 ['vcs/utils/web.py']
459 # removed: 1 ['vcs/utils/web.py']
457 chset85 = self.repo.get_changeset(85)
460 chset85 = self.repo.get_changeset(85)
458 self.assertEqual(set((node.path for node in chset85.added)), set([
461 self.assertEqual(set((node.path for node in chset85.added)), set([
459 'vcs/utils/diffs.py',
462 'vcs/utils/diffs.py',
460 'vcs/web/simplevcs/views/diffs.py']))
463 'vcs/web/simplevcs/views/diffs.py']))
461 self.assertEqual(set((node.path for node in chset85.changed)), set([
464 self.assertEqual(set((node.path for node in chset85.changed)), set([
462 'vcs/web/simplevcs/models.py',
465 'vcs/web/simplevcs/models.py',
463 'vcs/web/simplevcs/utils.py',
466 'vcs/web/simplevcs/utils.py',
464 'vcs/web/simplevcs/views/__init__.py',
467 'vcs/web/simplevcs/views/__init__.py',
465 'vcs/web/simplevcs/views/repository.py',
468 'vcs/web/simplevcs/views/repository.py',
466 ]))
469 ]))
467 self.assertEqual(set((node.path for node in chset85.removed)),
470 self.assertEqual(set((node.path for node in chset85.removed)),
468 set(['vcs/utils/web.py']))
471 set(['vcs/utils/web.py']))
469
472
470
473
471 def test_files_state(self):
474 def test_files_state(self):
472 """
475 """
473 Tests state of FileNodes.
476 Tests state of FileNodes.
474 """
477 """
475 chset = self.repo.get_changeset(85)
478 chset = self.repo.get_changeset(85)
476 node = chset.get_node('vcs/utils/diffs.py')
479 node = chset.get_node('vcs/utils/diffs.py')
477 self.assertTrue(node.state, NodeState.ADDED)
480 self.assertTrue(node.state, NodeState.ADDED)
478 self.assertTrue(node.added)
481 self.assertTrue(node.added)
479 self.assertFalse(node.changed)
482 self.assertFalse(node.changed)
480 self.assertFalse(node.not_changed)
483 self.assertFalse(node.not_changed)
481 self.assertFalse(node.removed)
484 self.assertFalse(node.removed)
482
485
483 chset = self.repo.get_changeset(88)
486 chset = self.repo.get_changeset(88)
484 node = chset.get_node('.hgignore')
487 node = chset.get_node('.hgignore')
485 self.assertTrue(node.state, NodeState.CHANGED)
488 self.assertTrue(node.state, NodeState.CHANGED)
486 self.assertFalse(node.added)
489 self.assertFalse(node.added)
487 self.assertTrue(node.changed)
490 self.assertTrue(node.changed)
488 self.assertFalse(node.not_changed)
491 self.assertFalse(node.not_changed)
489 self.assertFalse(node.removed)
492 self.assertFalse(node.removed)
490
493
491 chset = self.repo.get_changeset(85)
494 chset = self.repo.get_changeset(85)
492 node = chset.get_node('setup.py')
495 node = chset.get_node('setup.py')
493 self.assertTrue(node.state, NodeState.NOT_CHANGED)
496 self.assertTrue(node.state, NodeState.NOT_CHANGED)
494 self.assertFalse(node.added)
497 self.assertFalse(node.added)
495 self.assertFalse(node.changed)
498 self.assertFalse(node.changed)
496 self.assertTrue(node.not_changed)
499 self.assertTrue(node.not_changed)
497 self.assertFalse(node.removed)
500 self.assertFalse(node.removed)
498
501
499 # If node has REMOVED state then trying to fetch it would raise
502 # If node has REMOVED state then trying to fetch it would raise
500 # ChangesetError exception
503 # ChangesetError exception
501 chset = self.repo.get_changeset(2)
504 chset = self.repo.get_changeset(2)
502 path = 'vcs/backends/BaseRepository.py'
505 path = 'vcs/backends/BaseRepository.py'
503 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
506 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
504 # but it would be one of ``removed`` (changeset's attribute)
507 # but it would be one of ``removed`` (changeset's attribute)
505 self.assertTrue(path in [rf.path for rf in chset.removed])
508 self.assertTrue(path in [rf.path for rf in chset.removed])
506
509
507 def test_commit_message_is_unicode(self):
510 def test_commit_message_is_unicode(self):
508 for cm in self.repo:
511 for cm in self.repo:
509 self.assertEqual(type(cm.message), unicode)
512 self.assertEqual(type(cm.message), unicode)
510
513
511 def test_changeset_author_is_unicode(self):
514 def test_changeset_author_is_unicode(self):
512 for cm in self.repo:
515 for cm in self.repo:
513 self.assertEqual(type(cm.author), unicode)
516 self.assertEqual(type(cm.author), unicode)
514
517
515 def test_repo_files_content_is_unicode(self):
518 def test_repo_files_content_is_unicode(self):
516 test_changeset = self.repo.get_changeset(100)
519 test_changeset = self.repo.get_changeset(100)
517 for node in test_changeset.get_node('/'):
520 for node in test_changeset.get_node('/'):
518 if node.is_file():
521 if node.is_file():
519 self.assertEqual(type(node.content), unicode)
522 self.assertEqual(type(node.content), unicode)
520
523
521 def test_wrong_path(self):
524 def test_wrong_path(self):
522 # There is 'setup.py' in the root dir but not there:
525 # There is 'setup.py' in the root dir but not there:
523 path = 'foo/bar/setup.py'
526 path = 'foo/bar/setup.py'
524 self.assertRaises(VCSError, self.repo.get_changeset().get_node, path)
527 self.assertRaises(VCSError, self.repo.get_changeset().get_node, path)
525
528
526
529
527 def test_archival_file(self):
530 def test_archival_file(self):
528 #TODO:
531 #TODO:
529 pass
532 pass
530
533
531 def test_archival_as_generator(self):
534 def test_archival_as_generator(self):
532 #TODO:
535 #TODO:
533 pass
536 pass
534
537
535 def test_archival_wrong_kind(self):
538 def test_archival_wrong_kind(self):
536 tip = self.repo.get_changeset()
539 tip = self.repo.get_changeset()
537 self.assertRaises(VCSError, tip.fill_archive, kind='error')
540 self.assertRaises(VCSError, tip.fill_archive, kind='error')
538
541
539 def test_archival_empty_prefix(self):
542 def test_archival_empty_prefix(self):
540 #TODO:
543 #TODO:
541 pass
544 pass
542
545
543
546
544 def test_author_email(self):
547 def test_author_email(self):
545 self.assertEqual('marcin@python-blog.com',
548 self.assertEqual('marcin@python-blog.com',
546 self.repo.get_changeset('b986218ba1c9').author_email)
549 self.repo.get_changeset('b986218ba1c9').author_email)
547 self.assertEqual('lukasz.balcerzak@python-center.pl',
550 self.assertEqual('lukasz.balcerzak@python-center.pl',
548 self.repo.get_changeset('3803844fdbd3').author_email)
551 self.repo.get_changeset('3803844fdbd3').author_email)
549 self.assertEqual('',
552 self.assertEqual('',
550 self.repo.get_changeset('84478366594b').author_email)
553 self.repo.get_changeset('84478366594b').author_email)
551
554
552 def test_author_username(self):
555 def test_author_username(self):
553 self.assertEqual('Marcin Kuzminski',
556 self.assertEqual('Marcin Kuzminski',
554 self.repo.get_changeset('b986218ba1c9').author_name)
557 self.repo.get_changeset('b986218ba1c9').author_name)
555 self.assertEqual('Lukasz Balcerzak',
558 self.assertEqual('Lukasz Balcerzak',
556 self.repo.get_changeset('3803844fdbd3').author_name)
559 self.repo.get_changeset('3803844fdbd3').author_name)
557 self.assertEqual('marcink',
560 self.assertEqual('marcink',
558 self.repo.get_changeset('84478366594b').author_name)
561 self.repo.get_changeset('84478366594b').author_name)
@@ -1,182 +1,182 b''
1
1
2 import stat
2 import stat
3 from kallithea.lib.vcs.nodes import DirNode
3 from kallithea.lib.vcs.nodes import DirNode
4 from kallithea.lib.vcs.nodes import FileNode
4 from kallithea.lib.vcs.nodes import FileNode
5 from kallithea.lib.vcs.nodes import Node
5 from kallithea.lib.vcs.nodes import Node
6 from kallithea.lib.vcs.nodes import NodeError
6 from kallithea.lib.vcs.nodes import NodeError
7 from kallithea.lib.vcs.nodes import NodeKind
7 from kallithea.lib.vcs.nodes import NodeKind
8 from kallithea.lib.vcs.utils.compat import unittest
8 from kallithea.lib.vcs.utils.compat import unittest
9
9
10
10
11 class NodeBasicTest(unittest.TestCase):
11 class NodeBasicTest(unittest.TestCase):
12
12
13 def test_init(self):
13 def test_init(self):
14 """
14 """
15 Cannot innitialize Node objects with path with slash at the beginning.
15 Cannot innitialize Node objects with path with slash at the beginning.
16 """
16 """
17 wrong_paths = (
17 wrong_paths = (
18 '/foo',
18 '/foo',
19 '/foo/bar'
19 '/foo/bar'
20 )
20 )
21 for path in wrong_paths:
21 for path in wrong_paths:
22 self.assertRaises(NodeError, Node, path, NodeKind.FILE)
22 self.assertRaises(NodeError, Node, path, NodeKind.FILE)
23
23
24 wrong_paths = (
24 wrong_paths = (
25 '/foo/',
25 '/foo/',
26 '/foo/bar/'
26 '/foo/bar/'
27 )
27 )
28 for path in wrong_paths:
28 for path in wrong_paths:
29 self.assertRaises(NodeError, Node, path, NodeKind.DIR)
29 self.assertRaises(NodeError, Node, path, NodeKind.DIR)
30
30
31 def test_name(self):
31 def test_name(self):
32 node = Node('', NodeKind.DIR)
32 node = Node('', NodeKind.DIR)
33 self.assertEqual(node.name, '')
33 self.assertEqual(node.name, '')
34
34
35 node = Node('path', NodeKind.FILE)
35 node = Node('path', NodeKind.FILE)
36 self.assertEqual(node.name, 'path')
36 self.assertEqual(node.name, 'path')
37
37
38 node = Node('path/', NodeKind.DIR)
38 node = Node('path/', NodeKind.DIR)
39 self.assertEqual(node.name, 'path')
39 self.assertEqual(node.name, 'path')
40
40
41 node = Node('some/path', NodeKind.FILE)
41 node = Node('some/path', NodeKind.FILE)
42 self.assertEqual(node.name, 'path')
42 self.assertEqual(node.name, 'path')
43
43
44 node = Node('some/path/', NodeKind.DIR)
44 node = Node('some/path/', NodeKind.DIR)
45 self.assertEqual(node.name, 'path')
45 self.assertEqual(node.name, 'path')
46
46
47 def test_root_node(self):
47 def test_root_node(self):
48 self.assertRaises(NodeError, Node, '', NodeKind.FILE)
48 self.assertRaises(NodeError, Node, '', NodeKind.FILE)
49
49
50 def test_kind_setter(self):
50 def test_kind_setter(self):
51 node = Node('', NodeKind.DIR)
51 node = Node('', NodeKind.DIR)
52 self.assertRaises(NodeError, setattr, node, 'kind', NodeKind.FILE)
52 self.assertRaises(NodeError, setattr, node, 'kind', NodeKind.FILE)
53
53
54 def _test_parent_path(self, node_path, expected_parent_path):
54 def _test_parent_path(self, node_path, expected_parent_path):
55 """
55 """
56 Tests if node's parent path are properly computed.
56 Tests if node's parent path are properly computed.
57 """
57 """
58 node = Node(node_path, NodeKind.DIR)
58 node = Node(node_path, NodeKind.DIR)
59 parent_path = node.get_parent_path()
59 parent_path = node.get_parent_path()
60 self.assertTrue(parent_path.endswith('/') or \
60 self.assertTrue(parent_path.endswith('/') or \
61 node.is_root() and parent_path == '')
61 node.is_root() and parent_path == '')
62 self.assertEqual(parent_path, expected_parent_path,
62 self.assertEqual(parent_path, expected_parent_path,
63 "Node's path is %r and parent path is %r but should be %r"
63 "Node's path is %r and parent path is %r but should be %r"
64 % (node.path, parent_path, expected_parent_path))
64 % (node.path, parent_path, expected_parent_path))
65
65
66 def test_parent_path(self):
66 def test_parent_path(self):
67 test_paths = (
67 test_paths = (
68 # (node_path, expected_parent_path)
68 # (node_path, expected_parent_path)
69 ('', ''),
69 ('', ''),
70 ('some/path/', 'some/'),
70 ('some/path/', 'some/'),
71 ('some/longer/path/', 'some/longer/'),
71 ('some/longer/path/', 'some/longer/'),
72 )
72 )
73 for node_path, expected_parent_path in test_paths:
73 for node_path, expected_parent_path in test_paths:
74 self._test_parent_path(node_path, expected_parent_path)
74 self._test_parent_path(node_path, expected_parent_path)
75
75
76 '''
76 '''
77 def _test_trailing_slash(self, path):
77 def _test_trailing_slash(self, path):
78 if not path.endswith('/'):
78 if not path.endswith('/'):
79 self.fail("Trailing slash tests needs paths to end with slash")
79 pytest.fail("Trailing slash tests needs paths to end with slash")
80 for kind in NodeKind.FILE, NodeKind.DIR:
80 for kind in NodeKind.FILE, NodeKind.DIR:
81 self.assertRaises(NodeError, Node, path=path, kind=kind)
81 self.assertRaises(NodeError, Node, path=path, kind=kind)
82
82
83 def test_trailing_slash(self):
83 def test_trailing_slash(self):
84 for path in ('/', 'foo/', 'foo/bar/', 'foo/bar/biz/'):
84 for path in ('/', 'foo/', 'foo/bar/', 'foo/bar/biz/'):
85 self._test_trailing_slash(path)
85 self._test_trailing_slash(path)
86 '''
86 '''
87
87
88 def test_is_file(self):
88 def test_is_file(self):
89 node = Node('any', NodeKind.FILE)
89 node = Node('any', NodeKind.FILE)
90 self.assertTrue(node.is_file())
90 self.assertTrue(node.is_file())
91
91
92 node = FileNode('any')
92 node = FileNode('any')
93 self.assertTrue(node.is_file())
93 self.assertTrue(node.is_file())
94 self.assertRaises(AttributeError, getattr, node, 'nodes')
94 self.assertRaises(AttributeError, getattr, node, 'nodes')
95
95
96 def test_is_dir(self):
96 def test_is_dir(self):
97 node = Node('any_dir', NodeKind.DIR)
97 node = Node('any_dir', NodeKind.DIR)
98 self.assertTrue(node.is_dir())
98 self.assertTrue(node.is_dir())
99
99
100 node = DirNode('any_dir')
100 node = DirNode('any_dir')
101
101
102 self.assertTrue(node.is_dir())
102 self.assertTrue(node.is_dir())
103 self.assertRaises(NodeError, getattr, node, 'content')
103 self.assertRaises(NodeError, getattr, node, 'content')
104
104
105 def test_dir_node_iter(self):
105 def test_dir_node_iter(self):
106 nodes = [
106 nodes = [
107 DirNode('docs'),
107 DirNode('docs'),
108 DirNode('tests'),
108 DirNode('tests'),
109 FileNode('bar'),
109 FileNode('bar'),
110 FileNode('foo'),
110 FileNode('foo'),
111 FileNode('readme.txt'),
111 FileNode('readme.txt'),
112 FileNode('setup.py'),
112 FileNode('setup.py'),
113 ]
113 ]
114 dirnode = DirNode('', nodes=nodes)
114 dirnode = DirNode('', nodes=nodes)
115 for node in dirnode:
115 for node in dirnode:
116 node == dirnode.get_node(node.path)
116 node == dirnode.get_node(node.path)
117
117
118 def test_node_state(self):
118 def test_node_state(self):
119 """
119 """
120 Without link to changeset nodes should raise NodeError.
120 Without link to changeset nodes should raise NodeError.
121 """
121 """
122 node = FileNode('anything')
122 node = FileNode('anything')
123 self.assertRaises(NodeError, getattr, node, 'state')
123 self.assertRaises(NodeError, getattr, node, 'state')
124 node = DirNode('anything')
124 node = DirNode('anything')
125 self.assertRaises(NodeError, getattr, node, 'state')
125 self.assertRaises(NodeError, getattr, node, 'state')
126
126
127 def test_file_node_stat(self):
127 def test_file_node_stat(self):
128 node = FileNode('foobar', 'empty... almost')
128 node = FileNode('foobar', 'empty... almost')
129 mode = node.mode # default should be 0100644
129 mode = node.mode # default should be 0100644
130 self.assertTrue(mode & stat.S_IRUSR)
130 self.assertTrue(mode & stat.S_IRUSR)
131 self.assertTrue(mode & stat.S_IWUSR)
131 self.assertTrue(mode & stat.S_IWUSR)
132 self.assertTrue(mode & stat.S_IRGRP)
132 self.assertTrue(mode & stat.S_IRGRP)
133 self.assertTrue(mode & stat.S_IROTH)
133 self.assertTrue(mode & stat.S_IROTH)
134 self.assertFalse(mode & stat.S_IWGRP)
134 self.assertFalse(mode & stat.S_IWGRP)
135 self.assertFalse(mode & stat.S_IWOTH)
135 self.assertFalse(mode & stat.S_IWOTH)
136 self.assertFalse(mode & stat.S_IXUSR)
136 self.assertFalse(mode & stat.S_IXUSR)
137 self.assertFalse(mode & stat.S_IXGRP)
137 self.assertFalse(mode & stat.S_IXGRP)
138 self.assertFalse(mode & stat.S_IXOTH)
138 self.assertFalse(mode & stat.S_IXOTH)
139
139
140 def test_file_node_is_executable(self):
140 def test_file_node_is_executable(self):
141 node = FileNode('foobar', 'empty... almost', mode=0100755)
141 node = FileNode('foobar', 'empty... almost', mode=0100755)
142 self.assertTrue(node.is_executable)
142 self.assertTrue(node.is_executable)
143
143
144 node = FileNode('foobar', 'empty... almost', mode=0100500)
144 node = FileNode('foobar', 'empty... almost', mode=0100500)
145 self.assertTrue(node.is_executable)
145 self.assertTrue(node.is_executable)
146
146
147 node = FileNode('foobar', 'empty... almost', mode=0100644)
147 node = FileNode('foobar', 'empty... almost', mode=0100644)
148 self.assertFalse(node.is_executable)
148 self.assertFalse(node.is_executable)
149
149
150 def test_mimetype(self):
150 def test_mimetype(self):
151 py_node = FileNode('test.py')
151 py_node = FileNode('test.py')
152 tar_node = FileNode('test.tar.gz')
152 tar_node = FileNode('test.tar.gz')
153
153
154 ext = 'CustomExtension'
154 ext = 'CustomExtension'
155
155
156 my_node2 = FileNode('myfile2')
156 my_node2 = FileNode('myfile2')
157 my_node2._mimetype = [ext]
157 my_node2._mimetype = [ext]
158
158
159 my_node3 = FileNode('myfile3')
159 my_node3 = FileNode('myfile3')
160 my_node3._mimetype = [ext,ext]
160 my_node3._mimetype = [ext,ext]
161
161
162 self.assertEqual(py_node.mimetype,'text/x-python')
162 self.assertEqual(py_node.mimetype,'text/x-python')
163 self.assertEqual(py_node.get_mimetype(),('text/x-python',None))
163 self.assertEqual(py_node.get_mimetype(),('text/x-python',None))
164
164
165 self.assertEqual(tar_node.mimetype,'application/x-tar')
165 self.assertEqual(tar_node.mimetype,'application/x-tar')
166 self.assertEqual(tar_node.get_mimetype(),('application/x-tar','gzip'))
166 self.assertEqual(tar_node.get_mimetype(),('application/x-tar','gzip'))
167
167
168 self.assertRaises(NodeError,my_node2.get_mimetype)
168 self.assertRaises(NodeError,my_node2.get_mimetype)
169
169
170 self.assertEqual(my_node3.mimetype,ext)
170 self.assertEqual(my_node3.mimetype,ext)
171 self.assertEqual(my_node3.get_mimetype(),[ext,ext])
171 self.assertEqual(my_node3.get_mimetype(),[ext,ext])
172
172
173 class NodeContentTest(unittest.TestCase):
173 class NodeContentTest(unittest.TestCase):
174
174
175 def test_if_binary(self):
175 def test_if_binary(self):
176 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"""
176 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"""
177 filenode = FileNode('calendar.png', content=data)
177 filenode = FileNode('calendar.png', content=data)
178 self.assertTrue(filenode.is_binary)
178 self.assertTrue(filenode.is_binary)
179
179
180
180
181 if __name__ == '__main__':
181 if __name__ == '__main__':
182 unittest.main()
182 unittest.main()
General Comments 0
You need to be logged in to leave comments. Login now