##// END OF EJS Templates
Added new random directory for each test to be better sandboxed
marcink -
r1397:dc960653 beta
parent child Browse files
Show More
@@ -1,619 +1,624
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import paste
30 import paste
31 import beaker
31 import beaker
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from paste.script.command import Command, BadCommand
34 from paste.script.command import Command, BadCommand
35
35
36 from UserDict import DictMixin
36 from UserDict import DictMixin
37
37
38 from mercurial import ui, config, hg
38 from mercurial import ui, config, hg
39 from mercurial.error import RepoError
39 from mercurial.error import RepoError
40
40
41 from webhelpers.text import collapse, remove_formatting, strip_tags
41 from webhelpers.text import collapse, remove_formatting, strip_tags
42
42
43 from vcs.backends.base import BaseChangeset
43 from vcs.backends.base import BaseChangeset
44 from vcs.utils.lazy import LazyProperty
44 from vcs.utils.lazy import LazyProperty
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
49 RhodeCodeSettings
49 RhodeCodeSettings
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 def recursive_replace(str, replace=' '):
56 def recursive_replace(str, replace=' '):
57 """Recursive replace of given sign to just one instance
57 """Recursive replace of given sign to just one instance
58
58
59 :param str: given string
59 :param str: given string
60 :param replace: char to find and replace multiple instances
60 :param replace: char to find and replace multiple instances
61
61
62 Examples::
62 Examples::
63 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
63 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
64 'Mighty-Mighty-Bo-sstones'
64 'Mighty-Mighty-Bo-sstones'
65 """
65 """
66
66
67 if str.find(replace * 2) == -1:
67 if str.find(replace * 2) == -1:
68 return str
68 return str
69 else:
69 else:
70 str = str.replace(replace * 2, replace)
70 str = str.replace(replace * 2, replace)
71 return recursive_replace(str, replace)
71 return recursive_replace(str, replace)
72
72
73
73
74 def repo_name_slug(value):
74 def repo_name_slug(value):
75 """Return slug of name of repository
75 """Return slug of name of repository
76 This function is called on each creation/modification
76 This function is called on each creation/modification
77 of repository to prevent bad names in repo
77 of repository to prevent bad names in repo
78 """
78 """
79
79
80 slug = remove_formatting(value)
80 slug = remove_formatting(value)
81 slug = strip_tags(slug)
81 slug = strip_tags(slug)
82
82
83 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
83 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
84 slug = slug.replace(c, '-')
84 slug = slug.replace(c, '-')
85 slug = recursive_replace(slug, '-')
85 slug = recursive_replace(slug, '-')
86 slug = collapse(slug, '-')
86 slug = collapse(slug, '-')
87 return slug
87 return slug
88
88
89
89
90 def get_repo_slug(request):
90 def get_repo_slug(request):
91 return request.environ['pylons.routes_dict'].get('repo_name')
91 return request.environ['pylons.routes_dict'].get('repo_name')
92
92
93
93
94 def action_logger(user, action, repo, ipaddr='', sa=None):
94 def action_logger(user, action, repo, ipaddr='', sa=None):
95 """
95 """
96 Action logger for various actions made by users
96 Action logger for various actions made by users
97
97
98 :param user: user that made this action, can be a unique username string or
98 :param user: user that made this action, can be a unique username string or
99 object containing user_id attribute
99 object containing user_id attribute
100 :param action: action to log, should be on of predefined unique actions for
100 :param action: action to log, should be on of predefined unique actions for
101 easy translations
101 easy translations
102 :param repo: string name of repository or object containing repo_id,
102 :param repo: string name of repository or object containing repo_id,
103 that action was made on
103 that action was made on
104 :param ipaddr: optional ip address from what the action was made
104 :param ipaddr: optional ip address from what the action was made
105 :param sa: optional sqlalchemy session
105 :param sa: optional sqlalchemy session
106
106
107 """
107 """
108
108
109 if not sa:
109 if not sa:
110 sa = meta.Session()
110 sa = meta.Session()
111
111
112 try:
112 try:
113 um = UserModel()
113 um = UserModel()
114 if hasattr(user, 'user_id'):
114 if hasattr(user, 'user_id'):
115 user_obj = user
115 user_obj = user
116 elif isinstance(user, basestring):
116 elif isinstance(user, basestring):
117 user_obj = um.get_by_username(user, cache=False)
117 user_obj = um.get_by_username(user, cache=False)
118 else:
118 else:
119 raise Exception('You have to provide user object or username')
119 raise Exception('You have to provide user object or username')
120
120
121 rm = RepoModel()
121 rm = RepoModel()
122 if hasattr(repo, 'repo_id'):
122 if hasattr(repo, 'repo_id'):
123 repo_obj = rm.get(repo.repo_id, cache=False)
123 repo_obj = rm.get(repo.repo_id, cache=False)
124 repo_name = repo_obj.repo_name
124 repo_name = repo_obj.repo_name
125 elif isinstance(repo, basestring):
125 elif isinstance(repo, basestring):
126 repo_name = repo.lstrip('/')
126 repo_name = repo.lstrip('/')
127 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
127 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
128 else:
128 else:
129 raise Exception('You have to provide repository to action logger')
129 raise Exception('You have to provide repository to action logger')
130
130
131 user_log = UserLog()
131 user_log = UserLog()
132 user_log.user_id = user_obj.user_id
132 user_log.user_id = user_obj.user_id
133 user_log.action = action
133 user_log.action = action
134
134
135 user_log.repository_id = repo_obj.repo_id
135 user_log.repository_id = repo_obj.repo_id
136 user_log.repository_name = repo_name
136 user_log.repository_name = repo_name
137
137
138 user_log.action_date = datetime.datetime.now()
138 user_log.action_date = datetime.datetime.now()
139 user_log.user_ip = ipaddr
139 user_log.user_ip = ipaddr
140 sa.add(user_log)
140 sa.add(user_log)
141 sa.commit()
141 sa.commit()
142
142
143 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
143 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
144 except:
144 except:
145 log.error(traceback.format_exc())
145 log.error(traceback.format_exc())
146 sa.rollback()
146 sa.rollback()
147
147
148
148
149 def get_repos(path, recursive=False):
149 def get_repos(path, recursive=False):
150 """
150 """
151 Scans given path for repos and return (name,(type,path)) tuple
151 Scans given path for repos and return (name,(type,path)) tuple
152
152
153 :param path: path to scann for repositories
153 :param path: path to scann for repositories
154 :param recursive: recursive search and return names with subdirs in front
154 :param recursive: recursive search and return names with subdirs in front
155 """
155 """
156 from vcs.utils.helpers import get_scm
156 from vcs.utils.helpers import get_scm
157 from vcs.exceptions import VCSError
157 from vcs.exceptions import VCSError
158
158
159 if path.endswith(os.sep):
159 if path.endswith(os.sep):
160 #remove ending slash for better results
160 #remove ending slash for better results
161 path = path[:-1]
161 path = path[:-1]
162
162
163 def _get_repos(p):
163 def _get_repos(p):
164 if not os.access(p, os.W_OK):
164 if not os.access(p, os.W_OK):
165 return
165 return
166 for dirpath in os.listdir(p):
166 for dirpath in os.listdir(p):
167 if os.path.isfile(os.path.join(p, dirpath)):
167 if os.path.isfile(os.path.join(p, dirpath)):
168 continue
168 continue
169 cur_path = os.path.join(p, dirpath)
169 cur_path = os.path.join(p, dirpath)
170 try:
170 try:
171 scm_info = get_scm(cur_path)
171 scm_info = get_scm(cur_path)
172 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
172 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
173 except VCSError:
173 except VCSError:
174 if not recursive:
174 if not recursive:
175 continue
175 continue
176 #check if this dir containts other repos for recursive scan
176 #check if this dir containts other repos for recursive scan
177 rec_path = os.path.join(p, dirpath)
177 rec_path = os.path.join(p, dirpath)
178 if os.path.isdir(rec_path):
178 if os.path.isdir(rec_path):
179 for inner_scm in _get_repos(rec_path):
179 for inner_scm in _get_repos(rec_path):
180 yield inner_scm
180 yield inner_scm
181
181
182 return _get_repos(path)
182 return _get_repos(path)
183
183
184
184
185 def check_repo_fast(repo_name, base_path):
185 def check_repo_fast(repo_name, base_path):
186 """
186 """
187 Check given path for existence of directory
187 Check given path for existence of directory
188 :param repo_name:
188 :param repo_name:
189 :param base_path:
189 :param base_path:
190
190
191 :return False: if this directory is present
191 :return False: if this directory is present
192 """
192 """
193 if os.path.isdir(os.path.join(base_path, repo_name)):
193 if os.path.isdir(os.path.join(base_path, repo_name)):
194 return False
194 return False
195 return True
195 return True
196
196
197
197
198 def check_repo(repo_name, base_path, verify=True):
198 def check_repo(repo_name, base_path, verify=True):
199
199
200 repo_path = os.path.join(base_path, repo_name)
200 repo_path = os.path.join(base_path, repo_name)
201
201
202 try:
202 try:
203 if not check_repo_fast(repo_name, base_path):
203 if not check_repo_fast(repo_name, base_path):
204 return False
204 return False
205 r = hg.repository(ui.ui(), repo_path)
205 r = hg.repository(ui.ui(), repo_path)
206 if verify:
206 if verify:
207 hg.verify(r)
207 hg.verify(r)
208 #here we hnow that repo exists it was verified
208 #here we hnow that repo exists it was verified
209 log.info('%s repo is already created', repo_name)
209 log.info('%s repo is already created', repo_name)
210 return False
210 return False
211 except RepoError:
211 except RepoError:
212 #it means that there is no valid repo there...
212 #it means that there is no valid repo there...
213 log.info('%s repo is free for creation', repo_name)
213 log.info('%s repo is free for creation', repo_name)
214 return True
214 return True
215
215
216
216
217 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
217 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
218 while True:
218 while True:
219 ok = raw_input(prompt)
219 ok = raw_input(prompt)
220 if ok in ('y', 'ye', 'yes'):
220 if ok in ('y', 'ye', 'yes'):
221 return True
221 return True
222 if ok in ('n', 'no', 'nop', 'nope'):
222 if ok in ('n', 'no', 'nop', 'nope'):
223 return False
223 return False
224 retries = retries - 1
224 retries = retries - 1
225 if retries < 0:
225 if retries < 0:
226 raise IOError
226 raise IOError
227 print complaint
227 print complaint
228
228
229 #propagated from mercurial documentation
229 #propagated from mercurial documentation
230 ui_sections = ['alias', 'auth',
230 ui_sections = ['alias', 'auth',
231 'decode/encode', 'defaults',
231 'decode/encode', 'defaults',
232 'diff', 'email',
232 'diff', 'email',
233 'extensions', 'format',
233 'extensions', 'format',
234 'merge-patterns', 'merge-tools',
234 'merge-patterns', 'merge-tools',
235 'hooks', 'http_proxy',
235 'hooks', 'http_proxy',
236 'smtp', 'patch',
236 'smtp', 'patch',
237 'paths', 'profiling',
237 'paths', 'profiling',
238 'server', 'trusted',
238 'server', 'trusted',
239 'ui', 'web', ]
239 'ui', 'web', ]
240
240
241
241
242 def make_ui(read_from='file', path=None, checkpaths=True):
242 def make_ui(read_from='file', path=None, checkpaths=True):
243 """A function that will read python rc files or database
243 """A function that will read python rc files or database
244 and make an mercurial ui object from read options
244 and make an mercurial ui object from read options
245
245
246 :param path: path to mercurial config file
246 :param path: path to mercurial config file
247 :param checkpaths: check the path
247 :param checkpaths: check the path
248 :param read_from: read from 'file' or 'db'
248 :param read_from: read from 'file' or 'db'
249 """
249 """
250
250
251 baseui = ui.ui()
251 baseui = ui.ui()
252
252
253 #clean the baseui object
253 #clean the baseui object
254 baseui._ocfg = config.config()
254 baseui._ocfg = config.config()
255 baseui._ucfg = config.config()
255 baseui._ucfg = config.config()
256 baseui._tcfg = config.config()
256 baseui._tcfg = config.config()
257
257
258 if read_from == 'file':
258 if read_from == 'file':
259 if not os.path.isfile(path):
259 if not os.path.isfile(path):
260 log.warning('Unable to read config file %s' % path)
260 log.warning('Unable to read config file %s' % path)
261 return False
261 return False
262 log.debug('reading hgrc from %s', path)
262 log.debug('reading hgrc from %s', path)
263 cfg = config.config()
263 cfg = config.config()
264 cfg.read(path)
264 cfg.read(path)
265 for section in ui_sections:
265 for section in ui_sections:
266 for k, v in cfg.items(section):
266 for k, v in cfg.items(section):
267 log.debug('settings ui from file[%s]%s:%s', section, k, v)
267 log.debug('settings ui from file[%s]%s:%s', section, k, v)
268 baseui.setconfig(section, k, v)
268 baseui.setconfig(section, k, v)
269
269
270 elif read_from == 'db':
270 elif read_from == 'db':
271 sa = meta.Session()
271 sa = meta.Session()
272 ret = sa.query(RhodeCodeUi)\
272 ret = sa.query(RhodeCodeUi)\
273 .options(FromCache("sql_cache_short",
273 .options(FromCache("sql_cache_short",
274 "get_hg_ui_settings")).all()
274 "get_hg_ui_settings")).all()
275
275
276 hg_ui = ret
276 hg_ui = ret
277 for ui_ in hg_ui:
277 for ui_ in hg_ui:
278 if ui_.ui_active:
278 if ui_.ui_active:
279 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
279 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
280 ui_.ui_key, ui_.ui_value)
280 ui_.ui_key, ui_.ui_value)
281 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
281 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
282
282
283 meta.Session.remove()
283 meta.Session.remove()
284 return baseui
284 return baseui
285
285
286
286
287 def set_rhodecode_config(config):
287 def set_rhodecode_config(config):
288 """Updates pylons config with new settings from database
288 """Updates pylons config with new settings from database
289
289
290 :param config:
290 :param config:
291 """
291 """
292 hgsettings = RhodeCodeSettings.get_app_settings()
292 hgsettings = RhodeCodeSettings.get_app_settings()
293
293
294 for k, v in hgsettings.items():
294 for k, v in hgsettings.items():
295 config[k] = v
295 config[k] = v
296
296
297
297
298 def invalidate_cache(cache_key, *args):
298 def invalidate_cache(cache_key, *args):
299 """Puts cache invalidation task into db for
299 """Puts cache invalidation task into db for
300 further global cache invalidation
300 further global cache invalidation
301 """
301 """
302
302
303 from rhodecode.model.scm import ScmModel
303 from rhodecode.model.scm import ScmModel
304
304
305 if cache_key.startswith('get_repo_cached_'):
305 if cache_key.startswith('get_repo_cached_'):
306 name = cache_key.split('get_repo_cached_')[-1]
306 name = cache_key.split('get_repo_cached_')[-1]
307 ScmModel().mark_for_invalidation(name)
307 ScmModel().mark_for_invalidation(name)
308
308
309
309
310 class EmptyChangeset(BaseChangeset):
310 class EmptyChangeset(BaseChangeset):
311 """
311 """
312 An dummy empty changeset. It's possible to pass hash when creating
312 An dummy empty changeset. It's possible to pass hash when creating
313 an EmptyChangeset
313 an EmptyChangeset
314 """
314 """
315
315
316 def __init__(self, cs='0' * 40, repo=None):
316 def __init__(self, cs='0' * 40, repo=None):
317 self._empty_cs = cs
317 self._empty_cs = cs
318 self.revision = -1
318 self.revision = -1
319 self.message = ''
319 self.message = ''
320 self.author = ''
320 self.author = ''
321 self.date = ''
321 self.date = ''
322 self.repository = repo
322 self.repository = repo
323
323
324 @LazyProperty
324 @LazyProperty
325 def raw_id(self):
325 def raw_id(self):
326 """Returns raw string identifying this changeset, useful for web
326 """Returns raw string identifying this changeset, useful for web
327 representation.
327 representation.
328 """
328 """
329
329
330 return self._empty_cs
330 return self._empty_cs
331
331
332 @LazyProperty
332 @LazyProperty
333 def short_id(self):
333 def short_id(self):
334 return self.raw_id[:12]
334 return self.raw_id[:12]
335
335
336 def get_file_changeset(self, path):
336 def get_file_changeset(self, path):
337 return self
337 return self
338
338
339 def get_file_content(self, path):
339 def get_file_content(self, path):
340 return u''
340 return u''
341
341
342 def get_file_size(self, path):
342 def get_file_size(self, path):
343 return 0
343 return 0
344
344
345
345
346 def map_groups(groups):
346 def map_groups(groups):
347 """Checks for groups existence, and creates groups structures.
347 """Checks for groups existence, and creates groups structures.
348 It returns last group in structure
348 It returns last group in structure
349
349
350 :param groups: list of groups structure
350 :param groups: list of groups structure
351 """
351 """
352 sa = meta.Session()
352 sa = meta.Session()
353
353
354 parent = None
354 parent = None
355 group = None
355 group = None
356 for lvl, group_name in enumerate(groups[:-1]):
356 for lvl, group_name in enumerate(groups[:-1]):
357 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
357 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
358
358
359 if group is None:
359 if group is None:
360 group = Group(group_name, parent)
360 group = Group(group_name, parent)
361 sa.add(group)
361 sa.add(group)
362 sa.commit()
362 sa.commit()
363
363
364 parent = group
364 parent = group
365
365
366 return group
366 return group
367
367
368
368
369 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
369 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
370 """maps all repos given in initial_repo_list, non existing repositories
370 """maps all repos given in initial_repo_list, non existing repositories
371 are created, if remove_obsolete is True it also check for db entries
371 are created, if remove_obsolete is True it also check for db entries
372 that are not in initial_repo_list and removes them.
372 that are not in initial_repo_list and removes them.
373
373
374 :param initial_repo_list: list of repositories found by scanning methods
374 :param initial_repo_list: list of repositories found by scanning methods
375 :param remove_obsolete: check for obsolete entries in database
375 :param remove_obsolete: check for obsolete entries in database
376 """
376 """
377
377
378 sa = meta.Session()
378 sa = meta.Session()
379 rm = RepoModel()
379 rm = RepoModel()
380 user = sa.query(User).filter(User.admin == True).first()
380 user = sa.query(User).filter(User.admin == True).first()
381 added = []
381 added = []
382 for name, repo in initial_repo_list.items():
382 for name, repo in initial_repo_list.items():
383 group = map_groups(name.split('/'))
383 group = map_groups(name.split('/'))
384 if not rm.get_by_repo_name(name, cache=False):
384 if not rm.get_by_repo_name(name, cache=False):
385 log.info('repository %s not found creating default', name)
385 log.info('repository %s not found creating default', name)
386 added.append(name)
386 added.append(name)
387 form_data = {
387 form_data = {
388 'repo_name': name,
388 'repo_name': name,
389 'repo_name_full': name,
389 'repo_name_full': name,
390 'repo_type': repo.alias,
390 'repo_type': repo.alias,
391 'description': repo.description \
391 'description': repo.description \
392 if repo.description != 'unknown' else \
392 if repo.description != 'unknown' else \
393 '%s repository' % name,
393 '%s repository' % name,
394 'private': False,
394 'private': False,
395 'group_id': getattr(group, 'group_id', None)
395 'group_id': getattr(group, 'group_id', None)
396 }
396 }
397 rm.create(form_data, user, just_db=True)
397 rm.create(form_data, user, just_db=True)
398
398
399 removed = []
399 removed = []
400 if remove_obsolete:
400 if remove_obsolete:
401 #remove from database those repositories that are not in the filesystem
401 #remove from database those repositories that are not in the filesystem
402 for repo in sa.query(Repository).all():
402 for repo in sa.query(Repository).all():
403 if repo.repo_name not in initial_repo_list.keys():
403 if repo.repo_name not in initial_repo_list.keys():
404 removed.append(repo.repo_name)
404 removed.append(repo.repo_name)
405 sa.delete(repo)
405 sa.delete(repo)
406 sa.commit()
406 sa.commit()
407
407
408 return added, removed
408 return added, removed
409
409
410 #set cache regions for beaker so celery can utilise it
410 #set cache regions for beaker so celery can utilise it
411 def add_cache(settings):
411 def add_cache(settings):
412 cache_settings = {'regions': None}
412 cache_settings = {'regions': None}
413 for key in settings.keys():
413 for key in settings.keys():
414 for prefix in ['beaker.cache.', 'cache.']:
414 for prefix in ['beaker.cache.', 'cache.']:
415 if key.startswith(prefix):
415 if key.startswith(prefix):
416 name = key.split(prefix)[1].strip()
416 name = key.split(prefix)[1].strip()
417 cache_settings[name] = settings[key].strip()
417 cache_settings[name] = settings[key].strip()
418 if cache_settings['regions']:
418 if cache_settings['regions']:
419 for region in cache_settings['regions'].split(','):
419 for region in cache_settings['regions'].split(','):
420 region = region.strip()
420 region = region.strip()
421 region_settings = {}
421 region_settings = {}
422 for key, value in cache_settings.items():
422 for key, value in cache_settings.items():
423 if key.startswith(region):
423 if key.startswith(region):
424 region_settings[key.split('.')[1]] = value
424 region_settings[key.split('.')[1]] = value
425 region_settings['expire'] = int(region_settings.get('expire',
425 region_settings['expire'] = int(region_settings.get('expire',
426 60))
426 60))
427 region_settings.setdefault('lock_dir',
427 region_settings.setdefault('lock_dir',
428 cache_settings.get('lock_dir'))
428 cache_settings.get('lock_dir'))
429 region_settings.setdefault('data_dir',
429 region_settings.setdefault('data_dir',
430 cache_settings.get('data_dir'))
430 cache_settings.get('data_dir'))
431
431
432 if 'type' not in region_settings:
432 if 'type' not in region_settings:
433 region_settings['type'] = cache_settings.get('type',
433 region_settings['type'] = cache_settings.get('type',
434 'memory')
434 'memory')
435 beaker.cache.cache_regions[region] = region_settings
435 beaker.cache.cache_regions[region] = region_settings
436
436
437
437
438 def get_current_revision():
438 def get_current_revision():
439 """Returns tuple of (number, id) from repository containing this package
439 """Returns tuple of (number, id) from repository containing this package
440 or None if repository could not be found.
440 or None if repository could not be found.
441 """
441 """
442
442
443 try:
443 try:
444 from vcs import get_repo
444 from vcs import get_repo
445 from vcs.utils.helpers import get_scm
445 from vcs.utils.helpers import get_scm
446 from vcs.exceptions import RepositoryError, VCSError
446 from vcs.exceptions import RepositoryError, VCSError
447 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
447 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
448 scm = get_scm(repopath)[0]
448 scm = get_scm(repopath)[0]
449 repo = get_repo(path=repopath, alias=scm)
449 repo = get_repo(path=repopath, alias=scm)
450 tip = repo.get_changeset()
450 tip = repo.get_changeset()
451 return (tip.revision, tip.short_id)
451 return (tip.revision, tip.short_id)
452 except (ImportError, RepositoryError, VCSError), err:
452 except (ImportError, RepositoryError, VCSError), err:
453 logging.debug("Cannot retrieve rhodecode's revision. Original error "
453 logging.debug("Cannot retrieve rhodecode's revision. Original error "
454 "was: %s" % err)
454 "was: %s" % err)
455 return None
455 return None
456
456
457
457
458 #==============================================================================
458 #==============================================================================
459 # TEST FUNCTIONS AND CREATORS
459 # TEST FUNCTIONS AND CREATORS
460 #==============================================================================
460 #==============================================================================
461 def create_test_index(repo_location, full_index):
461 def create_test_index(repo_location, full_index):
462 """Makes default test index
462 """Makes default test index
463 :param repo_location:
463 :param repo_location:
464 :param full_index:
464 :param full_index:
465 """
465 """
466 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
466 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
467 from rhodecode.lib.pidlock import DaemonLock, LockHeld
467 from rhodecode.lib.pidlock import DaemonLock, LockHeld
468 import shutil
468 import shutil
469
469
470 index_location = os.path.join(repo_location, 'index')
470 index_location = os.path.join(repo_location, 'index')
471 if os.path.exists(index_location):
471 if os.path.exists(index_location):
472 shutil.rmtree(index_location)
472 shutil.rmtree(index_location)
473
473
474 try:
474 try:
475 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
475 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
476 WhooshIndexingDaemon(index_location=index_location,
476 WhooshIndexingDaemon(index_location=index_location,
477 repo_location=repo_location)\
477 repo_location=repo_location)\
478 .run(full_index=full_index)
478 .run(full_index=full_index)
479 l.release()
479 l.release()
480 except LockHeld:
480 except LockHeld:
481 pass
481 pass
482
482
483
483
484 def create_test_env(repos_test_path, config):
484 def create_test_env(repos_test_path, config):
485 """Makes a fresh database and
485 """Makes a fresh database and
486 install test repository into tmp dir
486 install test repository into tmp dir
487 """
487 """
488 from rhodecode.lib.db_manage import DbManage
488 from rhodecode.lib.db_manage import DbManage
489 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
489 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
490 HG_FORK, GIT_FORK, TESTS_TMP_PATH
490 HG_FORK, GIT_FORK, TESTS_TMP_PATH
491 import tarfile
491 import tarfile
492 import shutil
492 import shutil
493 from os.path import dirname as dn, join as jn, abspath
493 from os.path import dirname as dn, join as jn, abspath
494
494
495 log = logging.getLogger('TestEnvCreator')
495 log = logging.getLogger('TestEnvCreator')
496 # create logger
496 # create logger
497 log.setLevel(logging.DEBUG)
497 log.setLevel(logging.DEBUG)
498 log.propagate = True
498 log.propagate = True
499 # create console handler and set level to debug
499 # create console handler and set level to debug
500 ch = logging.StreamHandler()
500 ch = logging.StreamHandler()
501 ch.setLevel(logging.DEBUG)
501 ch.setLevel(logging.DEBUG)
502
502
503 # create formatter
503 # create formatter
504 formatter = logging.Formatter("%(asctime)s - %(name)s -"
504 formatter = logging.Formatter("%(asctime)s - %(name)s -"
505 " %(levelname)s - %(message)s")
505 " %(levelname)s - %(message)s")
506
506
507 # add formatter to ch
507 # add formatter to ch
508 ch.setFormatter(formatter)
508 ch.setFormatter(formatter)
509
509
510 # add ch to logger
510 # add ch to logger
511 log.addHandler(ch)
511 log.addHandler(ch)
512
512
513 #PART ONE create db
513 #PART ONE create db
514 dbconf = config['sqlalchemy.db1.url']
514 dbconf = config['sqlalchemy.db1.url']
515 log.debug('making test db %s', dbconf)
515 log.debug('making test db %s', dbconf)
516
516
517 # create test dir if it doesn't exist
518 if not os.path.isdir(repos_test_path):
519 log.debug('Creating testdir %s' % repos_test_path)
520 os.makedirs(repos_test_path)
521
517 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
522 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
518 tests=True)
523 tests=True)
519 dbmanage.create_tables(override=True)
524 dbmanage.create_tables(override=True)
520 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
525 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
521 dbmanage.create_default_user()
526 dbmanage.create_default_user()
522 dbmanage.admin_prompt()
527 dbmanage.admin_prompt()
523 dbmanage.create_permissions()
528 dbmanage.create_permissions()
524 dbmanage.populate_default_permissions()
529 dbmanage.populate_default_permissions()
525
530
526 #PART TWO make test repo
531 #PART TWO make test repo
527 log.debug('making test vcs repositories')
532 log.debug('making test vcs repositories')
528
533
529 #remove old one from previos tests
534 #remove old one from previos tests
530 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
535 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
531
536
532 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
537 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
533 log.debug('removing %s', r)
538 log.debug('removing %s', r)
534 shutil.rmtree(jn(TESTS_TMP_PATH, r))
539 shutil.rmtree(jn(TESTS_TMP_PATH, r))
535
540
536 idx_path = config['app_conf']['index_dir']
541 idx_path = config['app_conf']['index_dir']
537 data_path = config['app_conf']['cache_dir']
542 data_path = config['app_conf']['cache_dir']
538
543
539 #clean index and data
544 #clean index and data
540 if idx_path and os.path.exists(idx_path):
545 if idx_path and os.path.exists(idx_path):
541 log.debug('remove %s' % idx_path)
546 log.debug('remove %s' % idx_path)
542 shutil.rmtree(idx_path)
547 shutil.rmtree(idx_path)
543
548
544 if data_path and os.path.exists(data_path):
549 if data_path and os.path.exists(data_path):
545 log.debug('remove %s' % data_path)
550 log.debug('remove %s' % data_path)
546 shutil.rmtree(data_path)
551 shutil.rmtree(data_path)
547
552
548 #CREATE DEFAULT HG REPOSITORY
553 #CREATE DEFAULT HG REPOSITORY
549 cur_dir = dn(dn(abspath(__file__)))
554 cur_dir = dn(dn(abspath(__file__)))
550 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
555 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
551 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
556 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
552 tar.close()
557 tar.close()
553
558
554
559
555 #==============================================================================
560 #==============================================================================
556 # PASTER COMMANDS
561 # PASTER COMMANDS
557 #==============================================================================
562 #==============================================================================
558 class BasePasterCommand(Command):
563 class BasePasterCommand(Command):
559 """
564 """
560 Abstract Base Class for paster commands.
565 Abstract Base Class for paster commands.
561
566
562 The celery commands are somewhat aggressive about loading
567 The celery commands are somewhat aggressive about loading
563 celery.conf, and since our module sets the `CELERY_LOADER`
568 celery.conf, and since our module sets the `CELERY_LOADER`
564 environment variable to our loader, we have to bootstrap a bit and
569 environment variable to our loader, we have to bootstrap a bit and
565 make sure we've had a chance to load the pylons config off of the
570 make sure we've had a chance to load the pylons config off of the
566 command line, otherwise everything fails.
571 command line, otherwise everything fails.
567 """
572 """
568 min_args = 1
573 min_args = 1
569 min_args_error = "Please provide a paster config file as an argument."
574 min_args_error = "Please provide a paster config file as an argument."
570 takes_config_file = 1
575 takes_config_file = 1
571 requires_config_file = True
576 requires_config_file = True
572
577
573 def notify_msg(self, msg, log=False):
578 def notify_msg(self, msg, log=False):
574 """Make a notification to user, additionally if logger is passed
579 """Make a notification to user, additionally if logger is passed
575 it logs this action using given logger
580 it logs this action using given logger
576
581
577 :param msg: message that will be printed to user
582 :param msg: message that will be printed to user
578 :param log: logging instance, to use to additionally log this message
583 :param log: logging instance, to use to additionally log this message
579
584
580 """
585 """
581 if log and isinstance(log, logging):
586 if log and isinstance(log, logging):
582 log(msg)
587 log(msg)
583
588
584 def run(self, args):
589 def run(self, args):
585 """
590 """
586 Overrides Command.run
591 Overrides Command.run
587
592
588 Checks for a config file argument and loads it.
593 Checks for a config file argument and loads it.
589 """
594 """
590 if len(args) < self.min_args:
595 if len(args) < self.min_args:
591 raise BadCommand(
596 raise BadCommand(
592 self.min_args_error % {'min_args': self.min_args,
597 self.min_args_error % {'min_args': self.min_args,
593 'actual_args': len(args)})
598 'actual_args': len(args)})
594
599
595 # Decrement because we're going to lob off the first argument.
600 # Decrement because we're going to lob off the first argument.
596 # @@ This is hacky
601 # @@ This is hacky
597 self.min_args -= 1
602 self.min_args -= 1
598 self.bootstrap_config(args[0])
603 self.bootstrap_config(args[0])
599 self.update_parser()
604 self.update_parser()
600 return super(BasePasterCommand, self).run(args[1:])
605 return super(BasePasterCommand, self).run(args[1:])
601
606
602 def update_parser(self):
607 def update_parser(self):
603 """
608 """
604 Abstract method. Allows for the class's parser to be updated
609 Abstract method. Allows for the class's parser to be updated
605 before the superclass's `run` method is called. Necessary to
610 before the superclass's `run` method is called. Necessary to
606 allow options/arguments to be passed through to the underlying
611 allow options/arguments to be passed through to the underlying
607 celery command.
612 celery command.
608 """
613 """
609 raise NotImplementedError("Abstract Method.")
614 raise NotImplementedError("Abstract Method.")
610
615
611 def bootstrap_config(self, conf):
616 def bootstrap_config(self, conf):
612 """
617 """
613 Loads the pylons configuration.
618 Loads the pylons configuration.
614 """
619 """
615 from pylons import config as pylonsconfig
620 from pylons import config as pylonsconfig
616
621
617 path_to_ini_file = os.path.realpath(conf)
622 path_to_ini_file = os.path.realpath(conf)
618 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
623 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
619 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
624 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,81 +1,81
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 import os
10 import os
11 from os.path import join as jn
11 from os.path import join as jn
12
12
13 from unittest import TestCase
13 from unittest import TestCase
14
14
15 from paste.deploy import loadapp
15 from paste.deploy import loadapp
16 from paste.script.appinstall import SetupCommand
16 from paste.script.appinstall import SetupCommand
17 from pylons import config, url
17 from pylons import config, url
18 from routes.util import URLGenerator
18 from routes.util import URLGenerator
19 from webtest import TestApp
19 from webtest import TestApp
20
20
21 from rhodecode.model import meta
21 from rhodecode.model import meta
22 import logging
22 import logging
23
23
24
24
25 log = logging.getLogger(__name__)
25 log = logging.getLogger(__name__)
26
26
27 import pylons.test
27 import pylons.test
28
28
29 __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
29 __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
30 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK', ]
30 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK', ]
31
31
32 # Invoke websetup with the current config file
32 # Invoke websetup with the current config file
33 #SetupCommand('setup-app').run([config_file])
33 #SetupCommand('setup-app').run([config_file])
34
34
35 ##RUNNING DESIRED TESTS
35 ##RUNNING DESIRED TESTS
36 #nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
36 #nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
37
37
38 environ = {}
38 environ = {}
39
39
40 #SOME GLOBALS FOR TESTS
40 #SOME GLOBALS FOR TESTS
41 TESTS_TMP_PATH = jn('/', 'tmp')
41 from tempfile import _RandomNameSequence
42
42 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
43 HG_REPO = 'vcs_test_hg'
43 HG_REPO = 'vcs_test_hg'
44 GIT_REPO = 'vcs_test_git'
44 GIT_REPO = 'vcs_test_git'
45
45
46 NEW_HG_REPO = 'vcs_test_hg_new'
46 NEW_HG_REPO = 'vcs_test_hg_new'
47 NEW_GIT_REPO = 'vcs_test_git_new'
47 NEW_GIT_REPO = 'vcs_test_git_new'
48
48
49 HG_FORK = 'vcs_test_hg_fork'
49 HG_FORK = 'vcs_test_hg_fork'
50 GIT_FORK = 'vcs_test_git_fork'
50 GIT_FORK = 'vcs_test_git_fork'
51
51
52 class TestController(TestCase):
52 class TestController(TestCase):
53
53
54 def __init__(self, *args, **kwargs):
54 def __init__(self, *args, **kwargs):
55 wsgiapp = pylons.test.pylonsapp
55 wsgiapp = pylons.test.pylonsapp
56 config = wsgiapp.config
56 config = wsgiapp.config
57
57
58 self.app = TestApp(wsgiapp)
58 self.app = TestApp(wsgiapp)
59 url._push_object(URLGenerator(config['routes.map'], environ))
59 url._push_object(URLGenerator(config['routes.map'], environ))
60 self.sa = meta.Session
60 self.sa = meta.Session
61 self.index_location = config['app_conf']['index_dir']
61 self.index_location = config['app_conf']['index_dir']
62 TestCase.__init__(self, *args, **kwargs)
62 TestCase.__init__(self, *args, **kwargs)
63
63
64 def log_user(self, username='test_admin', password='test12'):
64 def log_user(self, username='test_admin', password='test12'):
65 response = self.app.post(url(controller='login', action='index'),
65 response = self.app.post(url(controller='login', action='index'),
66 {'username':username,
66 {'username':username,
67 'password':password})
67 'password':password})
68
68
69 if 'invalid user name' in response.body:
69 if 'invalid user name' in response.body:
70 self.fail('could not login using %s %s' % (username, password))
70 self.fail('could not login using %s %s' % (username, password))
71
71
72 self.assertEqual(response.status, '302 Found')
72 self.assertEqual(response.status, '302 Found')
73 self.assertEqual(response.session['rhodecode_user'].username, username)
73 self.assertEqual(response.session['rhodecode_user'].username, username)
74 return response.follow()
74 return response.follow()
75
75
76
76
77
77
78 def checkSessionFlash(self, response, msg):
78 def checkSessionFlash(self, response, msg):
79 self.assertTrue('flash' in response.session)
79 self.assertTrue('flash' in response.session)
80 self.assertTrue(msg in response.session['flash'][0][1])
80 self.assertTrue(msg in response.session['flash'][0][1])
81
81
General Comments 0
You need to be logged in to leave comments. Login now