Mercurial > kallithea
changeset 3797:d7488551578e beta
synced vcs with upstream
- moved subprocessio module to VCS
- many small changes to make embedded vcs as similar to to external lib
- use only absolute imports
- patch vcs config during load pylons env
line wrap: on
line diff
--- a/rhodecode/__init__.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/__init__.py Sat Apr 27 11:24:25 2013 +0200 @@ -27,6 +27,19 @@ import platform VERSION = (1, 7, 0, 'dev') +BACKENDS = { + 'hg': 'Mercurial repository', + 'git': 'Git repository', +} + +CELERY_ON = False +CELERY_EAGER = False + +# link to config for pylons +CONFIG = {} + +# Linked module for extensions +EXTENSIONS = {} try: from rhodecode.lib import get_current_revision @@ -45,23 +58,5 @@ __author__ = 'Marcin Kuzminski' __url__ = 'http://rhodecode.org' -PLATFORM_WIN = ('Windows') -PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS') #depracated - -is_windows = __platform__ in PLATFORM_WIN +is_windows = __platform__ in ('Windows') is_unix = not is_windows - - -BACKENDS = { - 'hg': 'Mercurial repository', - 'git': 'Git repository', -} - -CELERY_ON = False -CELERY_EAGER = False - -# link to config for pylons -CONFIG = {} - -# Linked module for extensions -EXTENSIONS = {}
--- a/rhodecode/config/environment.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/config/environment.py Sat Apr 27 11:24:25 2013 +0200 @@ -18,7 +18,7 @@ from rhodecode.lib import helpers from rhodecode.lib.auth import set_available_permissions from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config,\ - load_rcextensions, check_git_version + load_rcextensions, check_git_version, set_vcs_config from rhodecode.lib.utils2 import engine_from_config, str2bool from rhodecode.lib.db_manage import DbManage from rhodecode.model import init_model @@ -114,4 +114,6 @@ # store config reference into our module to skip import magic of # pylons rhodecode.CONFIG.update(config) + + set_vcs_config(rhodecode.CONFIG) return config
--- a/rhodecode/lib/__init__.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/__init__.py Sat Apr 27 11:24:25 2013 +0200 @@ -12,7 +12,8 @@ try: from rhodecode.lib.vcs import get_repo from rhodecode.lib.vcs.utils.helpers import get_scm - repopath = os.path.join(os.path.dirname(__file__), '..', '..') + repopath = os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', '..')) scm = get_scm(repopath)[0] repo = get_repo(path=repopath, alias=scm) wk_dir = repo.workdir
--- a/rhodecode/lib/compat.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/compat.py Sat Apr 27 11:24:25 2013 +0200 @@ -25,13 +25,12 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -from rhodecode import __platform__, PLATFORM_WIN, __py_version__ +from rhodecode import __py_version__, is_windows #============================================================================== # json #============================================================================== from rhodecode.lib.ext_json import json -import array #============================================================================== @@ -366,7 +365,7 @@ #============================================================================== # kill FUNCTIONS #============================================================================== -if __platform__ in PLATFORM_WIN: +if is_windows: import ctypes def kill(pid, sig): @@ -419,6 +418,7 @@ if __py_version__ >= (2, 6): _bytearray = bytearray else: + import array # no idea if this is correct but all integration tests are passing # i think we never use bytearray anyway _bytearray = array @@ -548,173 +548,3 @@ memo[id(self)] = result result.__init__(deepcopy(tuple(self), memo)) return result - - -#============================================================================== -# threading.Event -#============================================================================== - -if __py_version__ >= (2, 6): - from threading import Event, Thread -else: - from threading import _Verbose, Condition, Lock, Thread, _time, \ - _allocate_lock, RLock, _sleep - - def Condition(*args, **kwargs): - return _Condition(*args, **kwargs) - - class _Condition(_Verbose): - - def __init__(self, lock=None, verbose=None): - _Verbose.__init__(self, verbose) - if lock is None: - lock = RLock() - self.__lock = lock - # Export the lock's acquire() and release() methods - self.acquire = lock.acquire - self.release = lock.release - # If the lock defines _release_save() and/or _acquire_restore(), - # these override the default implementations (which just call - # release() and acquire() on the lock). Ditto for _is_owned(). - try: - self._release_save = lock._release_save - except AttributeError: - pass - try: - self._acquire_restore = lock._acquire_restore - except AttributeError: - pass - try: - self._is_owned = lock._is_owned - except AttributeError: - pass - self.__waiters = [] - - def __enter__(self): - return self.__lock.__enter__() - - def __exit__(self, *args): - return self.__lock.__exit__(*args) - - def __repr__(self): - return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters)) - - def _release_save(self): - self.__lock.release() # No state to save - - def _acquire_restore(self, x): - self.__lock.acquire() # Ignore saved state - - def _is_owned(self): - # Return True if lock is owned by current_thread. - # This method is called only if __lock doesn't have _is_owned(). - if self.__lock.acquire(0): - self.__lock.release() - return False - else: - return True - - def wait(self, timeout=None): - if not self._is_owned(): - raise RuntimeError("cannot wait on un-acquired lock") - waiter = _allocate_lock() - waiter.acquire() - self.__waiters.append(waiter) - saved_state = self._release_save() - try: # restore state no matter what (e.g., KeyboardInterrupt) - if timeout is None: - waiter.acquire() - if __debug__: - self._note("%s.wait(): got it", self) - else: - # Balancing act: We can't afford a pure busy loop, so we - # have to sleep; but if we sleep the whole timeout time, - # we'll be unresponsive. The scheme here sleeps very - # little at first, longer as time goes on, but never longer - # than 20 times per second (or the timeout time remaining). - endtime = _time() + timeout - delay = 0.0005 # 500 us -> initial delay of 1 ms - while True: - gotit = waiter.acquire(0) - if gotit: - break - remaining = endtime - _time() - if remaining <= 0: - break - delay = min(delay * 2, remaining, .05) - _sleep(delay) - if not gotit: - if __debug__: - self._note("%s.wait(%s): timed out", self, timeout) - try: - self.__waiters.remove(waiter) - except ValueError: - pass - else: - if __debug__: - self._note("%s.wait(%s): got it", self, timeout) - finally: - self._acquire_restore(saved_state) - - def notify(self, n=1): - if not self._is_owned(): - raise RuntimeError("cannot notify on un-acquired lock") - __waiters = self.__waiters - waiters = __waiters[:n] - if not waiters: - if __debug__: - self._note("%s.notify(): no waiters", self) - return - self._note("%s.notify(): notifying %d waiter%s", self, n, - n != 1 and "s" or "") - for waiter in waiters: - waiter.release() - try: - __waiters.remove(waiter) - except ValueError: - pass - - def notifyAll(self): - self.notify(len(self.__waiters)) - - notify_all = notifyAll - - def Event(*args, **kwargs): - return _Event(*args, **kwargs) - - class _Event(_Verbose): - - # After Tim Peters' event class (without is_posted()) - - def __init__(self, verbose=None): - _Verbose.__init__(self, verbose) - self.__cond = Condition(Lock()) - self.__flag = False - - def isSet(self): - return self.__flag - - is_set = isSet - - def set(self): - self.__cond.acquire() - try: - self.__flag = True - self.__cond.notify_all() - finally: - self.__cond.release() - - def clear(self): - self.__cond.acquire() - try: - self.__flag = False - finally: - self.__cond.release() - - def wait(self, timeout=None): - self.__cond.acquire() - try: - if not self.__flag: - self.__cond.wait(timeout) - finally: - self.__cond.release()
--- a/rhodecode/lib/middleware/pygrack.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/middleware/pygrack.py Sat Apr 27 11:24:25 2013 +0200 @@ -7,7 +7,7 @@ from webob import Request, Response, exc import rhodecode -from rhodecode.lib import subprocessio +from rhodecode.lib.vcs import subprocessio log = logging.getLogger(__name__)
--- a/rhodecode/lib/subprocessio.py Sat Apr 27 11:45:27 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,415 +0,0 @@ -''' -Module provides a class allowing to wrap communication over subprocess.Popen -input, output, error streams into a meaningfull, non-blocking, concurrent -stream processor exposing the output data as an iterator fitting to be a -return value passed by a WSGI applicaiton to a WSGI server per PEP 3333. - -Copyright (c) 2011 Daniel Dotsenko <dotsa@hotmail.com> - -This file is part of git_http_backend.py Project. - -git_http_backend.py Project is free software: you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public License as -published by the Free Software Foundation, either version 2.1 of the License, -or (at your option) any later version. - -git_http_backend.py Project is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with git_http_backend.py Project. -If not, see <http://www.gnu.org/licenses/>. -''' -import os -import subprocess -from rhodecode.lib.compat import deque, Event, Thread, _bytes, _bytearray - - -class StreamFeeder(Thread): - """ - Normal writing into pipe-like is blocking once the buffer is filled. - This thread allows a thread to seep data from a file-like into a pipe - without blocking the main thread. - We close inpipe once the end of the source stream is reached. - """ - def __init__(self, source): - super(StreamFeeder, self).__init__() - self.daemon = True - filelike = False - self.bytes = _bytes() - if type(source) in (type(''), _bytes, _bytearray): # string-like - self.bytes = _bytes(source) - else: # can be either file pointer or file-like - if type(source) in (int, long): # file pointer it is - ## converting file descriptor (int) stdin into file-like - try: - source = os.fdopen(source, 'rb', 16384) - except Exception: - pass - # let's see if source is file-like by now - try: - filelike = source.read - except Exception: - pass - if not filelike and not self.bytes: - raise TypeError("StreamFeeder's source object must be a readable " - "file-like, a file descriptor, or a string-like.") - self.source = source - self.readiface, self.writeiface = os.pipe() - - def run(self): - t = self.writeiface - if self.bytes: - os.write(t, self.bytes) - else: - s = self.source - b = s.read(4096) - while b: - os.write(t, b) - b = s.read(4096) - os.close(t) - - @property - def output(self): - return self.readiface - - -class InputStreamChunker(Thread): - def __init__(self, source, target, buffer_size, chunk_size): - - super(InputStreamChunker, self).__init__() - - self.daemon = True # die die die. - - self.source = source - self.target = target - self.chunk_count_max = int(buffer_size / chunk_size) + 1 - self.chunk_size = chunk_size - - self.data_added = Event() - self.data_added.clear() - - self.keep_reading = Event() - self.keep_reading.set() - - self.EOF = Event() - self.EOF.clear() - - self.go = Event() - self.go.set() - - def stop(self): - self.go.clear() - self.EOF.set() - try: - # this is not proper, but is done to force the reader thread let - # go of the input because, if successful, .close() will send EOF - # down the pipe. - self.source.close() - except: - pass - - def run(self): - s = self.source - t = self.target - cs = self.chunk_size - ccm = self.chunk_count_max - kr = self.keep_reading - da = self.data_added - go = self.go - - try: - b = s.read(cs) - except ValueError: - b = '' - - while b and go.is_set(): - if len(t) > ccm: - kr.clear() - kr.wait(2) -# # this only works on 2.7.x and up -# if not kr.wait(10): -# raise Exception("Timed out while waiting for input to be read.") - # instead we'll use this - if len(t) > ccm + 3: - raise IOError("Timed out while waiting for input from subprocess.") - t.append(b) - da.set() - b = s.read(cs) - self.EOF.set() - da.set() # for cases when done but there was no input. - - -class BufferedGenerator(): - ''' - Class behaves as a non-blocking, buffered pipe reader. - Reads chunks of data (through a thread) - from a blocking pipe, and attaches these to an array (Deque) of chunks. - Reading is halted in the thread when max chunks is internally buffered. - The .next() may operate in blocking or non-blocking fashion by yielding - '' if no data is ready - to be sent or by not returning until there is some data to send - When we get EOF from underlying source pipe we raise the marker to raise - StopIteration after the last chunk of data is yielded. - ''' - - def __init__(self, source, buffer_size=65536, chunk_size=4096, - starting_values=[], bottomless=False): - - if bottomless: - maxlen = int(buffer_size / chunk_size) - else: - maxlen = None - - self.data = deque(starting_values, maxlen) - - self.worker = InputStreamChunker(source, self.data, buffer_size, - chunk_size) - if starting_values: - self.worker.data_added.set() - self.worker.start() - - #################### - # Generator's methods - #################### - - def __iter__(self): - return self - - def next(self): - while not len(self.data) and not self.worker.EOF.is_set(): - self.worker.data_added.clear() - self.worker.data_added.wait(0.2) - if len(self.data): - self.worker.keep_reading.set() - return _bytes(self.data.popleft()) - elif self.worker.EOF.is_set(): - raise StopIteration - - def throw(self, type, value=None, traceback=None): - if not self.worker.EOF.is_set(): - raise type(value) - - def start(self): - self.worker.start() - - def stop(self): - self.worker.stop() - - def close(self): - try: - self.worker.stop() - self.throw(GeneratorExit) - except (GeneratorExit, StopIteration): - pass - - def __del__(self): - self.close() - - #################### - # Threaded reader's infrastructure. - #################### - @property - def input(self): - return self.worker.w - - @property - def data_added_event(self): - return self.worker.data_added - - @property - def data_added(self): - return self.worker.data_added.is_set() - - @property - def reading_paused(self): - return not self.worker.keep_reading.is_set() - - @property - def done_reading_event(self): - ''' - Done_reding does not mean that the iterator's buffer is empty. - Iterator might have done reading from underlying source, but the read - chunks might still be available for serving through .next() method. - - @return An Event class instance. - ''' - return self.worker.EOF - - @property - def done_reading(self): - ''' - Done_reding does not mean that the iterator's buffer is empty. - Iterator might have done reading from underlying source, but the read - chunks might still be available for serving through .next() method. - - @return An Bool value. - ''' - return self.worker.EOF.is_set() - - @property - def length(self): - ''' - returns int. - - This is the lenght of the que of chunks, not the length of - the combined contents in those chunks. - - __len__() cannot be meaningfully implemented because this - reader is just flying throuh a bottomless pit content and - can only know the lenght of what it already saw. - - If __len__() on WSGI server per PEP 3333 returns a value, - the responce's length will be set to that. In order not to - confuse WSGI PEP3333 servers, we will not implement __len__ - at all. - ''' - return len(self.data) - - def prepend(self, x): - self.data.appendleft(x) - - def append(self, x): - self.data.append(x) - - def extend(self, o): - self.data.extend(o) - - def __getitem__(self, i): - return self.data[i] - - -class SubprocessIOChunker(object): - ''' - Processor class wrapping handling of subprocess IO. - - In a way, this is a "communicate()" replacement with a twist. - - - We are multithreaded. Writing in and reading out, err are all sep threads. - - We support concurrent (in and out) stream processing. - - The output is not a stream. It's a queue of read string (bytes, not unicode) - chunks. The object behaves as an iterable. You can "for chunk in obj:" us. - - We are non-blocking in more respects than communicate() - (reading from subprocess out pauses when internal buffer is full, but - does not block the parent calling code. On the flip side, reading from - slow-yielding subprocess may block the iteration until data shows up. This - does not block the parallel inpipe reading occurring parallel thread.) - - The purpose of the object is to allow us to wrap subprocess interactions into - and interable that can be passed to a WSGI server as the application's return - value. Because of stream-processing-ability, WSGI does not have to read ALL - of the subprocess's output and buffer it, before handing it to WSGI server for - HTTP response. Instead, the class initializer reads just a bit of the stream - to figure out if error ocurred or likely to occur and if not, just hands the - further iteration over subprocess output to the server for completion of HTTP - response. - - The real or perceived subprocess error is trapped and raised as one of - EnvironmentError family of exceptions - - Example usage: - # try: - # answer = SubprocessIOChunker( - # cmd, - # input, - # buffer_size = 65536, - # chunk_size = 4096 - # ) - # except (EnvironmentError) as e: - # print str(e) - # raise e - # - # return answer - - - ''' - def __init__(self, cmd, inputstream=None, buffer_size=65536, - chunk_size=4096, starting_values=[], **kwargs): - ''' - Initializes SubprocessIOChunker - - :param cmd: A Subprocess.Popen style "cmd". Can be string or array of strings - :param inputstream: (Default: None) A file-like, string, or file pointer. - :param buffer_size: (Default: 65536) A size of total buffer per stream in bytes. - :param chunk_size: (Default: 4096) A max size of a chunk. Actual chunk may be smaller. - :param starting_values: (Default: []) An array of strings to put in front of output que. - ''' - - if inputstream: - input_streamer = StreamFeeder(inputstream) - input_streamer.start() - inputstream = input_streamer.output - - if isinstance(cmd, (list, tuple)): - cmd = ' '.join(cmd) - - _shell = kwargs.get('shell') or True - kwargs['shell'] = _shell - _p = subprocess.Popen(cmd, - bufsize=-1, - stdin=inputstream, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - **kwargs - ) - - bg_out = BufferedGenerator(_p.stdout, buffer_size, chunk_size, starting_values) - bg_err = BufferedGenerator(_p.stderr, 16000, 1, bottomless=True) - - while not bg_out.done_reading and not bg_out.reading_paused and not bg_err.length: - # doing this until we reach either end of file, or end of buffer. - bg_out.data_added_event.wait(1) - bg_out.data_added_event.clear() - - # at this point it's still ambiguous if we are done reading or just full buffer. - # Either way, if error (returned by ended process, or implied based on - # presence of stuff in stderr output) we error out. - # Else, we are happy. - _returncode = _p.poll() - if _returncode or (_returncode == None and bg_err.length): - try: - _p.terminate() - except: - pass - bg_out.stop() - bg_err.stop() - err = '%s' % ''.join(bg_err) - if err: - raise EnvironmentError("Subprocess exited due to an error:\n" + err) - raise EnvironmentError("Subprocess exited with non 0 ret code:%s" % _returncode) - - self.process = _p - self.output = bg_out - self.error = bg_err - - def __iter__(self): - return self - - def next(self): - if self.process.poll(): - err = '%s' % ''.join(self.error) - raise EnvironmentError("Subprocess exited due to an error:\n" + err) - return self.output.next() - - def throw(self, type, value=None, traceback=None): - if self.output.length or not self.output.done_reading: - raise type(value) - - def close(self): - try: - self.process.terminate() - except: - pass - try: - self.output.close() - except: - pass - try: - self.error.close() - except: - pass - - def __del__(self): - self.close()
--- a/rhodecode/lib/utils.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/utils.py Sat Apr 27 11:24:25 2013 +0200 @@ -374,6 +374,26 @@ config[k] = v +def set_vcs_config(config): + """ + Patch VCS config with some RhodeCode specific stuff + + :param config: rhodecode.CONFIG + """ + import rhodecode + from rhodecode.lib.vcs import conf + from rhodecode.lib.utils2 import aslist + conf.settings.BACKENDS = { + 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository', + 'git': 'rhodecode.lib.vcs.backends.git.GitRepository', + } + + conf.settings.GIT_EXECUTABLE_PATH = config.get('git_path', 'git') + conf.settings.GIT_REV_FILTER = config.get('git_rev_filter', '--all').strip() + conf.settings.DEFAULT_ENCODINGS = aslist(config.get('default_encoding', + 'utf8'), sep=',') + + def map_groups(path): """ Given a full path to a repository, create all nested groups that this @@ -739,6 +759,7 @@ """ from rhodecode import BACKENDS from rhodecode.lib.vcs.backends.git.repository import GitRepository + from rhodecode.lib.vcs.conf import settings from distutils.version import StrictVersion stdout, stderr = GitRepository._run_git_command('--version', _bare=True, @@ -760,7 +781,8 @@ to_old_git = True if 'git' in BACKENDS: - log.debug('GIT version detected: %s' % stdout) + log.debug('GIT executable: "%s" version detected: %s' + % (settings.GIT_EXECUTABLE_PATH, stdout)) if stderr: log.warning('Unable to detect git version, org error was: %r' % stderr) elif to_old_git:
--- a/rhodecode/lib/vcs/__init__.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/__init__.py Sat Apr 27 11:24:25 2013 +0200 @@ -10,13 +10,14 @@ :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak. """ -VERSION = (0, 3, 0, 'dev') +VERSION = (0, 4, 0, 'dev') __version__ = '.'.join((str(each) for each in VERSION[:4])) __all__ = [ 'get_version', 'get_repo', 'get_backend', - 'VCSError', 'RepositoryError', 'ChangesetError'] + 'VCSError', 'RepositoryError', 'ChangesetError' +] import sys from rhodecode.lib.vcs.backends import get_repo, get_backend @@ -29,6 +30,7 @@ """ return '.'.join((str(each) for each in VERSION[:3])) + def main(argv=None): if argv is None: argv = sys.argv
--- a/rhodecode/lib/vcs/backends/base.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/backends/base.py Sat Apr 27 11:24:25 2013 +0200 @@ -10,16 +10,18 @@ """ import datetime -from itertools import chain +import itertools + from rhodecode.lib.vcs.utils import author_name, author_email from rhodecode.lib.vcs.utils.lazy import LazyProperty from rhodecode.lib.vcs.utils.helpers import get_dict_for_attrs from rhodecode.lib.vcs.conf import settings -from rhodecode.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, \ - NodeAlreadyAddedError, NodeAlreadyChangedError, NodeAlreadyExistsError, \ - NodeAlreadyRemovedError, NodeDoesNotExistError, NodeNotChangedError, \ - RepositoryError +from rhodecode.lib.vcs.exceptions import ( + ChangesetError, EmptyRepositoryError, NodeAlreadyAddedError, + NodeAlreadyChangedError, NodeAlreadyExistsError, NodeAlreadyRemovedError, + NodeDoesNotExistError, NodeNotChangedError, RepositoryError +) class BaseRepository(object): @@ -851,7 +853,7 @@ Returns generator of paths from nodes marked as added, changed or removed. """ - for node in chain(self.added, self.changed, self.removed): + for node in itertools.chain(self.added, self.changed, self.removed): yield node.path def get_paths(self):
--- a/rhodecode/lib/vcs/backends/git/changeset.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/backends/git/changeset.py Sat Apr 27 11:24:25 2013 +0200 @@ -2,22 +2,21 @@ from itertools import chain from dulwich import objects from subprocess import Popen, PIPE -import rhodecode + from rhodecode.lib.vcs.conf import settings -from rhodecode.lib.vcs.exceptions import RepositoryError -from rhodecode.lib.vcs.exceptions import ChangesetError -from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError -from rhodecode.lib.vcs.exceptions import VCSError -from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError -from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset -from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, \ - RemovedFileNode, SubModuleNode, ChangedFileNodesGenerator,\ - AddedFileNodesGenerator, RemovedFileNodesGenerator -from rhodecode.lib.vcs.utils import safe_unicode -from rhodecode.lib.vcs.utils import date_fromtimestamp +from rhodecode.lib.vcs.exceptions import ( + RepositoryError, ChangesetError, NodeDoesNotExistError, VCSError, + ChangesetDoesNotExistError, ImproperArchiveTypeError +) +from rhodecode.lib.vcs.nodes import ( + FileNode, DirNode, NodeKind, RootNode, RemovedFileNode, SubModuleNode, + ChangedFileNodesGenerator, AddedFileNodesGenerator, RemovedFileNodesGenerator +) +from rhodecode.lib.vcs.utils import ( + safe_unicode, safe_str, safe_int, date_fromtimestamp +) from rhodecode.lib.vcs.utils.lazy import LazyProperty -from rhodecode.lib.utils2 import safe_int, safe_str class GitChangeset(BaseChangeset): @@ -187,8 +186,7 @@ """ Returns list of children changesets. """ - rev_filter = _git_path = rhodecode.CONFIG.get('git_rev_filter', - '--all').strip() + rev_filter = _git_path = settings.GIT_REV_FILTER so, se = self.repository.run_git_command( "rev-list %s --children | grep '^%s'" % (rev_filter, self.raw_id) ) @@ -373,7 +371,7 @@ frmt = 'zip' else: frmt = 'tar' - _git_path = rhodecode.CONFIG.get('git_path', 'git') + _git_path = settings.GIT_EXECUTABLE_PATH cmd = '%s archive --format=%s --prefix=%s/ %s' % (_git_path, frmt, prefix, self.raw_id) if kind == 'tgz': @@ -472,8 +470,8 @@ """ Get's a fast accessible file changes for given changeset """ - a, m, d = self._changes_cache - return list(a.union(m).union(d)) + added, modified, deleted = self._changes_cache + return list(added.union(modified).union(deleted)) @LazyProperty def _diff_name_status(self): @@ -516,11 +514,11 @@ :param status: one of: *added*, *modified* or *deleted* """ - a, m, d = self._changes_cache + added, modified, deleted = self._changes_cache return sorted({ - 'added': list(a), - 'modified': list(m), - 'deleted': list(d)}[status] + 'added': list(added), + 'modified': list(modified), + 'deleted': list(deleted)}[status] ) @LazyProperty
--- a/rhodecode/lib/vcs/backends/git/inmemory.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/backends/git/inmemory.py Sat Apr 27 11:24:25 2013 +0200 @@ -150,7 +150,6 @@ ref = 'refs/heads/%s' % branch repo.refs[ref] = commit.id - repo.refs.set_symbolic_ref('HEAD', ref) # Update vcs repository object & recreate dulwich repo self.repository.revisions.append(commit.id)
--- a/rhodecode/lib/vcs/backends/git/repository.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/backends/git/repository.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ - vcs.backends.git - ~~~~~~~~~~~~~~~~ + vcs.backends.git.repository + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Git backend implementation. + Git repository implementation. :created_on: Apr 8, 2010 :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak. @@ -12,34 +12,36 @@ import os import re import time -import posixpath -import logging -import traceback import urllib import urllib2 -from dulwich.repo import Repo, NotGitRepository +import logging +import posixpath +import string + from dulwich.objects import Tag -from string import Template +from dulwich.repo import Repo, NotGitRepository -import rhodecode +from rhodecode.lib.vcs import subprocessio from rhodecode.lib.vcs.backends.base import BaseRepository, CollectionGenerator -from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError -from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError -from rhodecode.lib.vcs.exceptions import EmptyRepositoryError -from rhodecode.lib.vcs.exceptions import RepositoryError -from rhodecode.lib.vcs.exceptions import TagAlreadyExistError -from rhodecode.lib.vcs.exceptions import TagDoesNotExistError +from rhodecode.lib.vcs.conf import settings + +from rhodecode.lib.vcs.exceptions import ( + BranchDoesNotExistError, ChangesetDoesNotExistError, EmptyRepositoryError, + RepositoryError, TagAlreadyExistError, TagDoesNotExistError +) from rhodecode.lib.vcs.utils import safe_unicode, makedate, date_fromtimestamp -from rhodecode.lib.vcs.utils.lazy import LazyProperty, ThreadLocalLazyProperty +from rhodecode.lib.vcs.utils.lazy import LazyProperty from rhodecode.lib.vcs.utils.ordered_dict import OrderedDict -from rhodecode.lib.vcs.utils.paths import abspath -from rhodecode.lib.vcs.utils.paths import get_user_home -from .workdir import GitWorkdir +from rhodecode.lib.vcs.utils.paths import abspath, get_user_home + +from rhodecode.lib.vcs.utils.hgcompat import ( + hg_url, httpbasicauthhandler, httpdigestauthhandler +) + from .changeset import GitChangeset +from .config import ConfigFile from .inmemory import GitInMemoryChangeset -from .config import ConfigFile -from rhodecode.lib import subprocessio - +from .workdir import GitWorkdir log = logging.getLogger(__name__) @@ -115,7 +117,7 @@ del gitenv['GIT_DIR'] gitenv['GIT_CONFIG_NOGLOBAL'] = '1' - _git_path = rhodecode.CONFIG.get('git_path', 'git') + _git_path = settings.GIT_EXECUTABLE_PATH cmd = [_git_path] + _copts + cmd if _str_cmd: cmd = ' '.join(cmd) @@ -153,11 +155,6 @@ On failures it'll raise urllib2.HTTPError """ - from mercurial.util import url as Url - - # those authnadlers are patched for python 2.6.5 bug an - # infinit looping when given invalid resources - from mercurial.url import httpbasicauthhandler, httpdigestauthhandler # check first if it's not an local url if os.path.isdir(url) or url.startswith('file:'): @@ -167,7 +164,7 @@ url = url[url.find('+') + 1:] handlers = [] - test_uri, authinfo = Url(url).authinfo() + test_uri, authinfo = hg_url(url).authinfo() if not test_uri.endswith('info/refs'): test_uri = test_uri.rstrip('/') + '/info/refs' if authinfo: @@ -224,8 +221,8 @@ self._repo.head() except KeyError: return [] - rev_filter = _git_path = rhodecode.CONFIG.get('git_rev_filter', - '--all').strip() + + rev_filter = _git_path = settings.GIT_REV_FILTER cmd = 'rev-list %s --reverse --date-order' % (rev_filter) try: so, se = self.run_git_command(cmd) @@ -502,11 +499,10 @@ cmd_template += ' $branch_name' cmd_params['branch_name'] = branch_name else: - rev_filter = _git_path = rhodecode.CONFIG.get('git_rev_filter', - '--all').strip() + rev_filter = _git_path = settings.GIT_REV_FILTER cmd_template += ' %s' % (rev_filter) - cmd = Template(cmd_template).safe_substitute(**cmd_params) + cmd = string.Template(cmd_template).safe_substitute(**cmd_params) revs = self.run_git_command(cmd)[0].splitlines() start_pos = 0 end_pos = len(revs)
--- a/rhodecode/lib/vcs/backends/hg/changeset.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/backends/hg/changeset.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,14 +1,16 @@ import os import posixpath +from rhodecode.lib.vcs.conf import settings from rhodecode.lib.vcs.backends.base import BaseChangeset -from rhodecode.lib.vcs.conf import settings -from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError, \ - ChangesetError, ImproperArchiveTypeError, NodeDoesNotExistError, VCSError -from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, \ - ChangedFileNodesGenerator, DirNode, FileNode, NodeKind, \ - RemovedFileNodesGenerator, RootNode, SubModuleNode - +from rhodecode.lib.vcs.exceptions import ( + ChangesetDoesNotExistError, ChangesetError, ImproperArchiveTypeError, + NodeDoesNotExistError, VCSError +) +from rhodecode.lib.vcs.nodes import ( + AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode, + NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode +) from rhodecode.lib.vcs.utils import safe_str, safe_unicode, date_fromtimestamp from rhodecode.lib.vcs.utils.lazy import LazyProperty from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
--- a/rhodecode/lib/vcs/backends/hg/repository.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/backends/hg/repository.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,26 +1,46 @@ +# -*- coding: utf-8 -*- +""" + vcs.backends.hg.repository + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Mercurial repository implementation. + + :created_on: Apr 8, 2010 + :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak. +""" + import os import time -import datetime import urllib import urllib2 +import logging +import datetime + from rhodecode.lib.vcs.backends.base import BaseRepository, CollectionGenerator -from .workdir import MercurialWorkdir -from .changeset import MercurialChangeset -from .inmemory import MercurialInMemoryChangeset +from rhodecode.lib.vcs.conf import settings -from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError, \ - ChangesetDoesNotExistError, EmptyRepositoryError, RepositoryError, \ - VCSError, TagAlreadyExistError, TagDoesNotExistError -from rhodecode.lib.vcs.utils import author_email, author_name, date_fromtimestamp, \ - makedate, safe_unicode +from rhodecode.lib.vcs.exceptions import ( + BranchDoesNotExistError, ChangesetDoesNotExistError, EmptyRepositoryError, + RepositoryError, VCSError, TagAlreadyExistError, TagDoesNotExistError +) +from rhodecode.lib.vcs.utils import ( + author_email, author_name, date_fromtimestamp, makedate, safe_unicode +) from rhodecode.lib.vcs.utils.lazy import LazyProperty from rhodecode.lib.vcs.utils.ordered_dict import OrderedDict from rhodecode.lib.vcs.utils.paths import abspath +from rhodecode.lib.vcs.utils.hgcompat import ( + ui, nullid, match, patch, diffopts, clone, get_contact, pull, + localrepository, RepoLookupError, Abort, RepoError, hex, scmutil, hg_url, + httpbasicauthhandler, httpdigestauthhandler +) -from rhodecode.lib.vcs.utils.hgcompat import ui, nullid, match, patch, \ - diffopts, clone, get_contact, pull, localrepository, RepoLookupError, \ - Abort, RepoError, hex, scmutil +from .changeset import MercurialChangeset +from .inmemory import MercurialInMemoryChangeset +from .workdir import MercurialWorkdir + +log = logging.getLogger(__name__) class MercurialRepository(BaseRepository): @@ -272,12 +292,6 @@ is valid or True if it's a local path """ - from mercurial.util import url as Url - - # those authnadlers are patched for python 2.6.5 bug an - # infinit looping when given invalid resources - from mercurial.url import httpbasicauthhandler, httpdigestauthhandler - # check first if it's not an local url if os.path.isdir(url) or url.startswith('file:'): return True @@ -286,7 +300,7 @@ url = url[url.find('+') + 1:] handlers = [] - test_uri, authinfo = Url(url).authinfo() + test_uri, authinfo = hg_url(url).authinfo() if authinfo: #create a password manager @@ -486,8 +500,10 @@ revisions = scmutil.revrange(self._repo, filter_) else: revisions = self.revisions - revs = reversed(revisions[start_pos:end_pos]) if reverse else \ - revisions[start_pos:end_pos] + + revs = revisions[start_pos:end_pos] + if reverse: + revs = reversed(revs) return CollectionGenerator(self, revs)
--- a/rhodecode/lib/vcs/conf/settings.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/conf/settings.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,5 +1,6 @@ import os import tempfile +from rhodecode.lib.vcs.utils import aslist from rhodecode.lib.vcs.utils.paths import get_user_home abspath = lambda * p: os.path.abspath(os.path.join(*p)) @@ -15,9 +16,17 @@ if os.path.isdir(VCSRC_PATH): VCSRC_PATH = os.path.join(VCSRC_PATH, '__init__.py') +# list of default encoding used in safe_unicode/safe_str methods +DEFAULT_ENCODINGS = aslist('utf8') + +# path to git executable runned by run_git_command function +GIT_EXECUTABLE_PATH = 'git' +# can be also --branches --tags +GIT_REV_FILTER = '--all' + BACKENDS = { - 'hg': 'vcs.backends.hg.MercurialRepository', - 'git': 'vcs.backends.git.GitRepository', + 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository', + 'git': 'rhodecode.lib.vcs.backends.git.GitRepository', } ARCHIVE_SPECS = { @@ -26,8 +35,3 @@ 'tgz': ('application/x-gzip', '.tar.gz'), 'zip': ('application/zip', '.zip'), } - -BACKENDS = { - 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository', - 'git': 'rhodecode.lib.vcs.backends.git.GitRepository', -}
--- a/rhodecode/lib/vcs/nodes.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/nodes.py Sat Apr 27 11:24:25 2013 +0200 @@ -15,11 +15,10 @@ from pygments import lexers +from rhodecode.lib.vcs.backends.base import EmptyChangeset +from rhodecode.lib.vcs.exceptions import NodeError, RemovedFileNodeError from rhodecode.lib.vcs.utils.lazy import LazyProperty from rhodecode.lib.vcs.utils import safe_unicode -from rhodecode.lib.vcs.exceptions import NodeError -from rhodecode.lib.vcs.exceptions import RemovedFileNodeError -from rhodecode.lib.vcs.backends.base import EmptyChangeset class NodeKind: @@ -353,7 +352,6 @@ @LazyProperty def mimetype_main(self): - return ['', ''] return self.mimetype.split('/')[0] @LazyProperty
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/lib/vcs/subprocessio.py Sat Apr 27 11:24:25 2013 +0200 @@ -0,0 +1,415 @@ +''' +Module provides a class allowing to wrap communication over subprocess.Popen +input, output, error streams into a meaningfull, non-blocking, concurrent +stream processor exposing the output data as an iterator fitting to be a +return value passed by a WSGI applicaiton to a WSGI server per PEP 3333. + +Copyright (c) 2011 Daniel Dotsenko <dotsa@hotmail.com> + +This file is part of git_http_backend.py Project. + +git_http_backend.py Project is free software: you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 2.1 of the License, +or (at your option) any later version. + +git_http_backend.py Project is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with git_http_backend.py Project. +If not, see <http://www.gnu.org/licenses/>. +''' +import os +import subprocess +from rhodecode.lib.vcs.utils.compat import deque, Event, Thread, _bytes, _bytearray + + +class StreamFeeder(Thread): + """ + Normal writing into pipe-like is blocking once the buffer is filled. + This thread allows a thread to seep data from a file-like into a pipe + without blocking the main thread. + We close inpipe once the end of the source stream is reached. + """ + def __init__(self, source): + super(StreamFeeder, self).__init__() + self.daemon = True + filelike = False + self.bytes = _bytes() + if type(source) in (type(''), _bytes, _bytearray): # string-like + self.bytes = _bytes(source) + else: # can be either file pointer or file-like + if type(source) in (int, long): # file pointer it is + ## converting file descriptor (int) stdin into file-like + try: + source = os.fdopen(source, 'rb', 16384) + except Exception: + pass + # let's see if source is file-like by now + try: + filelike = source.read + except Exception: + pass + if not filelike and not self.bytes: + raise TypeError("StreamFeeder's source object must be a readable " + "file-like, a file descriptor, or a string-like.") + self.source = source + self.readiface, self.writeiface = os.pipe() + + def run(self): + t = self.writeiface + if self.bytes: + os.write(t, self.bytes) + else: + s = self.source + b = s.read(4096) + while b: + os.write(t, b) + b = s.read(4096) + os.close(t) + + @property + def output(self): + return self.readiface + + +class InputStreamChunker(Thread): + def __init__(self, source, target, buffer_size, chunk_size): + + super(InputStreamChunker, self).__init__() + + self.daemon = True # die die die. + + self.source = source + self.target = target + self.chunk_count_max = int(buffer_size / chunk_size) + 1 + self.chunk_size = chunk_size + + self.data_added = Event() + self.data_added.clear() + + self.keep_reading = Event() + self.keep_reading.set() + + self.EOF = Event() + self.EOF.clear() + + self.go = Event() + self.go.set() + + def stop(self): + self.go.clear() + self.EOF.set() + try: + # this is not proper, but is done to force the reader thread let + # go of the input because, if successful, .close() will send EOF + # down the pipe. + self.source.close() + except: + pass + + def run(self): + s = self.source + t = self.target + cs = self.chunk_size + ccm = self.chunk_count_max + kr = self.keep_reading + da = self.data_added + go = self.go + + try: + b = s.read(cs) + except ValueError: + b = '' + + while b and go.is_set(): + if len(t) > ccm: + kr.clear() + kr.wait(2) +# # this only works on 2.7.x and up +# if not kr.wait(10): +# raise Exception("Timed out while waiting for input to be read.") + # instead we'll use this + if len(t) > ccm + 3: + raise IOError("Timed out while waiting for input from subprocess.") + t.append(b) + da.set() + b = s.read(cs) + self.EOF.set() + da.set() # for cases when done but there was no input. + + +class BufferedGenerator(): + ''' + Class behaves as a non-blocking, buffered pipe reader. + Reads chunks of data (through a thread) + from a blocking pipe, and attaches these to an array (Deque) of chunks. + Reading is halted in the thread when max chunks is internally buffered. + The .next() may operate in blocking or non-blocking fashion by yielding + '' if no data is ready + to be sent or by not returning until there is some data to send + When we get EOF from underlying source pipe we raise the marker to raise + StopIteration after the last chunk of data is yielded. + ''' + + def __init__(self, source, buffer_size=65536, chunk_size=4096, + starting_values=[], bottomless=False): + + if bottomless: + maxlen = int(buffer_size / chunk_size) + else: + maxlen = None + + self.data = deque(starting_values, maxlen) + + self.worker = InputStreamChunker(source, self.data, buffer_size, + chunk_size) + if starting_values: + self.worker.data_added.set() + self.worker.start() + + #################### + # Generator's methods + #################### + + def __iter__(self): + return self + + def next(self): + while not len(self.data) and not self.worker.EOF.is_set(): + self.worker.data_added.clear() + self.worker.data_added.wait(0.2) + if len(self.data): + self.worker.keep_reading.set() + return _bytes(self.data.popleft()) + elif self.worker.EOF.is_set(): + raise StopIteration + + def throw(self, type, value=None, traceback=None): + if not self.worker.EOF.is_set(): + raise type(value) + + def start(self): + self.worker.start() + + def stop(self): + self.worker.stop() + + def close(self): + try: + self.worker.stop() + self.throw(GeneratorExit) + except (GeneratorExit, StopIteration): + pass + + def __del__(self): + self.close() + + #################### + # Threaded reader's infrastructure. + #################### + @property + def input(self): + return self.worker.w + + @property + def data_added_event(self): + return self.worker.data_added + + @property + def data_added(self): + return self.worker.data_added.is_set() + + @property + def reading_paused(self): + return not self.worker.keep_reading.is_set() + + @property + def done_reading_event(self): + ''' + Done_reding does not mean that the iterator's buffer is empty. + Iterator might have done reading from underlying source, but the read + chunks might still be available for serving through .next() method. + + @return An Event class instance. + ''' + return self.worker.EOF + + @property + def done_reading(self): + ''' + Done_reding does not mean that the iterator's buffer is empty. + Iterator might have done reading from underlying source, but the read + chunks might still be available for serving through .next() method. + + @return An Bool value. + ''' + return self.worker.EOF.is_set() + + @property + def length(self): + ''' + returns int. + + This is the lenght of the que of chunks, not the length of + the combined contents in those chunks. + + __len__() cannot be meaningfully implemented because this + reader is just flying throuh a bottomless pit content and + can only know the lenght of what it already saw. + + If __len__() on WSGI server per PEP 3333 returns a value, + the responce's length will be set to that. In order not to + confuse WSGI PEP3333 servers, we will not implement __len__ + at all. + ''' + return len(self.data) + + def prepend(self, x): + self.data.appendleft(x) + + def append(self, x): + self.data.append(x) + + def extend(self, o): + self.data.extend(o) + + def __getitem__(self, i): + return self.data[i] + + +class SubprocessIOChunker(object): + ''' + Processor class wrapping handling of subprocess IO. + + In a way, this is a "communicate()" replacement with a twist. + + - We are multithreaded. Writing in and reading out, err are all sep threads. + - We support concurrent (in and out) stream processing. + - The output is not a stream. It's a queue of read string (bytes, not unicode) + chunks. The object behaves as an iterable. You can "for chunk in obj:" us. + - We are non-blocking in more respects than communicate() + (reading from subprocess out pauses when internal buffer is full, but + does not block the parent calling code. On the flip side, reading from + slow-yielding subprocess may block the iteration until data shows up. This + does not block the parallel inpipe reading occurring parallel thread.) + + The purpose of the object is to allow us to wrap subprocess interactions into + and interable that can be passed to a WSGI server as the application's return + value. Because of stream-processing-ability, WSGI does not have to read ALL + of the subprocess's output and buffer it, before handing it to WSGI server for + HTTP response. Instead, the class initializer reads just a bit of the stream + to figure out if error ocurred or likely to occur and if not, just hands the + further iteration over subprocess output to the server for completion of HTTP + response. + + The real or perceived subprocess error is trapped and raised as one of + EnvironmentError family of exceptions + + Example usage: + # try: + # answer = SubprocessIOChunker( + # cmd, + # input, + # buffer_size = 65536, + # chunk_size = 4096 + # ) + # except (EnvironmentError) as e: + # print str(e) + # raise e + # + # return answer + + + ''' + def __init__(self, cmd, inputstream=None, buffer_size=65536, + chunk_size=4096, starting_values=[], **kwargs): + ''' + Initializes SubprocessIOChunker + + :param cmd: A Subprocess.Popen style "cmd". Can be string or array of strings + :param inputstream: (Default: None) A file-like, string, or file pointer. + :param buffer_size: (Default: 65536) A size of total buffer per stream in bytes. + :param chunk_size: (Default: 4096) A max size of a chunk. Actual chunk may be smaller. + :param starting_values: (Default: []) An array of strings to put in front of output que. + ''' + + if inputstream: + input_streamer = StreamFeeder(inputstream) + input_streamer.start() + inputstream = input_streamer.output + + if isinstance(cmd, (list, tuple)): + cmd = ' '.join(cmd) + + _shell = kwargs.get('shell') or True + kwargs['shell'] = _shell + _p = subprocess.Popen(cmd, + bufsize=-1, + stdin=inputstream, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + **kwargs + ) + + bg_out = BufferedGenerator(_p.stdout, buffer_size, chunk_size, starting_values) + bg_err = BufferedGenerator(_p.stderr, 16000, 1, bottomless=True) + + while not bg_out.done_reading and not bg_out.reading_paused and not bg_err.length: + # doing this until we reach either end of file, or end of buffer. + bg_out.data_added_event.wait(1) + bg_out.data_added_event.clear() + + # at this point it's still ambiguous if we are done reading or just full buffer. + # Either way, if error (returned by ended process, or implied based on + # presence of stuff in stderr output) we error out. + # Else, we are happy. + _returncode = _p.poll() + if _returncode or (_returncode == None and bg_err.length): + try: + _p.terminate() + except: + pass + bg_out.stop() + bg_err.stop() + err = '%s' % ''.join(bg_err) + if err: + raise EnvironmentError("Subprocess exited due to an error:\n" + err) + raise EnvironmentError("Subprocess exited with non 0 ret code:%s" % _returncode) + + self.process = _p + self.output = bg_out + self.error = bg_err + + def __iter__(self): + return self + + def next(self): + if self.process.poll(): + err = '%s' % ''.join(self.error) + raise EnvironmentError("Subprocess exited due to an error:\n" + err) + return self.output.next() + + def throw(self, type, value=None, traceback=None): + if self.output.length or not self.output.done_reading: + raise type(value) + + def close(self): + try: + self.process.terminate() + except: + pass + try: + self.output.close() + except: + pass + try: + self.error.close() + except: + pass + + def __del__(self): + self.close()
--- a/rhodecode/lib/vcs/utils/__init__.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/utils/__init__.py Sat Apr 27 11:24:25 2013 +0200 @@ -16,6 +16,27 @@ return time.mktime(lt), tz +def aslist(obj, sep=None, strip=True): + """ + Returns given string separated by sep as list + + :param obj: + :param sep: + :param strip: + """ + if isinstance(obj, (basestring)): + lst = obj.split(sep) + if strip: + lst = [v.strip() for v in lst] + return lst + elif isinstance(obj, (list, tuple)): + return obj + elif obj is None: + return [] + else: + return [obj] + + def date_fromtimestamp(unixts, tzoffset=0): """ Makes a local datetime object out of unix timestamp @@ -27,6 +48,23 @@ return datetime.datetime.fromtimestamp(float(unixts)) +def safe_int(val, default=None): + """ + Returns int() of val if val is not convertable to int use default + instead + + :param val: + :param default: + """ + + try: + val = int(val) + except (ValueError, TypeError): + val = default + + return val + + def safe_unicode(str_, from_encoding=None): """ safe unicode function. Does few trick to turn str_ into unicode @@ -38,21 +76,26 @@ :rtype: unicode :returns: unicode object """ - from rhodecode.lib.utils2 import safe_unicode - return safe_unicode(str_, from_encoding) - if isinstance(str_, unicode): return str_ + if not from_encoding: + from rhodecode.lib.vcs.conf import settings + from_encoding = settings.DEFAULT_ENCODINGS + + if not isinstance(from_encoding, (list, tuple)): + from_encoding = [from_encoding] + try: return unicode(str_) except UnicodeDecodeError: pass - try: - return unicode(str_, from_encoding) - except UnicodeDecodeError: - pass + for enc in from_encoding: + try: + return unicode(str_, enc) + except UnicodeDecodeError: + pass try: import chardet @@ -61,7 +104,7 @@ raise Exception() return str_.decode(encoding) except (ImportError, UnicodeDecodeError, Exception): - return unicode(str_, from_encoding, 'replace') + return unicode(str_, from_encoding[0], 'replace') def safe_str(unicode_, to_encoding=None): @@ -75,16 +118,26 @@ :rtype: str :returns: str object """ - from rhodecode.lib.utils2 import safe_str - return safe_str(unicode_, to_encoding) + + # if it's not basestr cast to str + if not isinstance(unicode_, basestring): + return str(unicode_) if isinstance(unicode_, str): return unicode_ - try: - return unicode_.encode(to_encoding) - except UnicodeEncodeError: - pass + if not to_encoding: + from rhodecode.lib.vcs.conf import settings + to_encoding = settings.DEFAULT_ENCODINGS + + if not isinstance(to_encoding, (list, tuple)): + to_encoding = [to_encoding] + + for enc in to_encoding: + try: + return unicode_.encode(enc) + except UnicodeEncodeError: + pass try: import chardet @@ -94,7 +147,7 @@ return unicode_.encode(encoding) except (ImportError, UnicodeEncodeError): - return unicode_.encode(to_encoding, 'replace') + return unicode_.encode(to_encoding[0], 'replace') return safe_str
--- a/rhodecode/lib/vcs/utils/annotate.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/utils/annotate.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,9 +1,10 @@ -from rhodecode.lib.vcs.exceptions import VCSError -from rhodecode.lib.vcs.nodes import FileNode +import StringIO + from pygments.formatters import HtmlFormatter from pygments import highlight -import StringIO +from rhodecode.lib.vcs.exceptions import VCSError +from rhodecode.lib.vcs.nodes import FileNode def annotate_highlight(filenode, annotate_from_changeset_func=None,
--- a/rhodecode/lib/vcs/utils/compat.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/utils/compat.py Sat Apr 27 11:24:25 2013 +0200 @@ -5,9 +5,314 @@ versions. """ import sys - +import array if sys.version_info >= (2, 7): unittest = __import__('unittest') else: unittest = __import__('unittest2') + + +if sys.version_info >= (2, 6): + _bytes = bytes +else: + # in py2.6 bytes is a synonim for str + _bytes = str + +if sys.version_info >= (2, 6): + _bytearray = bytearray +else: + # no idea if this is correct but all integration tests are passing + # i think we never use bytearray anyway + _bytearray = array + +if sys.version_info >= (2, 6): + from collections import deque +else: + #need to implement our own deque with maxlen + class deque(object): + + def __init__(self, iterable=(), maxlen= -1): + if not hasattr(self, 'data'): + self.left = self.right = 0 + self.data = {} + self.maxlen = maxlen or -1 + self.extend(iterable) + + def append(self, x): + self.data[self.right] = x + self.right += 1 + if self.maxlen != -1 and len(self) > self.maxlen: + self.popleft() + + def appendleft(self, x): + self.left -= 1 + self.data[self.left] = x + if self.maxlen != -1 and len(self) > self.maxlen: + self.pop() + + def pop(self): + if self.left == self.right: + raise IndexError('cannot pop from empty deque') + self.right -= 1 + elem = self.data[self.right] + del self.data[self.right] + return elem + + def popleft(self): + if self.left == self.right: + raise IndexError('cannot pop from empty deque') + elem = self.data[self.left] + del self.data[self.left] + self.left += 1 + return elem + + def clear(self): + self.data.clear() + self.left = self.right = 0 + + def extend(self, iterable): + for elem in iterable: + self.append(elem) + + def extendleft(self, iterable): + for elem in iterable: + self.appendleft(elem) + + def rotate(self, n=1): + if self: + n %= len(self) + for i in xrange(n): + self.appendleft(self.pop()) + + def __getitem__(self, i): + if i < 0: + i += len(self) + try: + return self.data[i + self.left] + except KeyError: + raise IndexError + + def __setitem__(self, i, value): + if i < 0: + i += len(self) + try: + self.data[i + self.left] = value + except KeyError: + raise IndexError + + def __delitem__(self, i): + size = len(self) + if not (-size <= i < size): + raise IndexError + data = self.data + if i < 0: + i += size + for j in xrange(self.left + i, self.right - 1): + data[j] = data[j + 1] + self.pop() + + def __len__(self): + return self.right - self.left + + def __cmp__(self, other): + if type(self) != type(other): + return cmp(type(self), type(other)) + return cmp(list(self), list(other)) + + def __repr__(self, _track=[]): + if id(self) in _track: + return '...' + _track.append(id(self)) + r = 'deque(%r, maxlen=%s)' % (list(self), self.maxlen) + _track.remove(id(self)) + return r + + def __getstate__(self): + return (tuple(self),) + + def __setstate__(self, s): + self.__init__(s[0]) + + def __hash__(self): + raise TypeError + + def __copy__(self): + return self.__class__(self) + + def __deepcopy__(self, memo={}): + from copy import deepcopy + result = self.__class__() + memo[id(self)] = result + result.__init__(deepcopy(tuple(self), memo)) + return result + + +#============================================================================== +# threading.Event +#============================================================================== + +if sys.version_info >= (2, 6): + from threading import Event, Thread +else: + from threading import _Verbose, Lock, Thread, _time, \ + _allocate_lock, RLock, _sleep + + def Condition(*args, **kwargs): + return _Condition(*args, **kwargs) + + class _Condition(_Verbose): + + def __init__(self, lock=None, verbose=None): + _Verbose.__init__(self, verbose) + if lock is None: + lock = RLock() + self.__lock = lock + # Export the lock's acquire() and release() methods + self.acquire = lock.acquire + self.release = lock.release + # If the lock defines _release_save() and/or _acquire_restore(), + # these override the default implementations (which just call + # release() and acquire() on the lock). Ditto for _is_owned(). + try: + self._release_save = lock._release_save + except AttributeError: + pass + try: + self._acquire_restore = lock._acquire_restore + except AttributeError: + pass + try: + self._is_owned = lock._is_owned + except AttributeError: + pass + self.__waiters = [] + + def __enter__(self): + return self.__lock.__enter__() + + def __exit__(self, *args): + return self.__lock.__exit__(*args) + + def __repr__(self): + return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters)) + + def _release_save(self): + self.__lock.release() # No state to save + + def _acquire_restore(self, x): + self.__lock.acquire() # Ignore saved state + + def _is_owned(self): + # Return True if lock is owned by current_thread. + # This method is called only if __lock doesn't have _is_owned(). + if self.__lock.acquire(0): + self.__lock.release() + return False + else: + return True + + def wait(self, timeout=None): + if not self._is_owned(): + raise RuntimeError("cannot wait on un-acquired lock") + waiter = _allocate_lock() + waiter.acquire() + self.__waiters.append(waiter) + saved_state = self._release_save() + try: # restore state no matter what (e.g., KeyboardInterrupt) + if timeout is None: + waiter.acquire() + if __debug__: + self._note("%s.wait(): got it", self) + else: + # Balancing act: We can't afford a pure busy loop, so we + # have to sleep; but if we sleep the whole timeout time, + # we'll be unresponsive. The scheme here sleeps very + # little at first, longer as time goes on, but never longer + # than 20 times per second (or the timeout time remaining). + endtime = _time() + timeout + delay = 0.0005 # 500 us -> initial delay of 1 ms + while True: + gotit = waiter.acquire(0) + if gotit: + break + remaining = endtime - _time() + if remaining <= 0: + break + delay = min(delay * 2, remaining, .05) + _sleep(delay) + if not gotit: + if __debug__: + self._note("%s.wait(%s): timed out", self, timeout) + try: + self.__waiters.remove(waiter) + except ValueError: + pass + else: + if __debug__: + self._note("%s.wait(%s): got it", self, timeout) + finally: + self._acquire_restore(saved_state) + + def notify(self, n=1): + if not self._is_owned(): + raise RuntimeError("cannot notify on un-acquired lock") + __waiters = self.__waiters + waiters = __waiters[:n] + if not waiters: + if __debug__: + self._note("%s.notify(): no waiters", self) + return + self._note("%s.notify(): notifying %d waiter%s", self, n, + n != 1 and "s" or "") + for waiter in waiters: + waiter.release() + try: + __waiters.remove(waiter) + except ValueError: + pass + + def notifyAll(self): + self.notify(len(self.__waiters)) + + notify_all = notifyAll + + def Event(*args, **kwargs): + return _Event(*args, **kwargs) + + class _Event(_Verbose): + + # After Tim Peters' event class (without is_posted()) + + def __init__(self, verbose=None): + _Verbose.__init__(self, verbose) + self.__cond = Condition(Lock()) + self.__flag = False + + def isSet(self): + return self.__flag + + is_set = isSet + + def set(self): + self.__cond.acquire() + try: + self.__flag = True + self.__cond.notify_all() + finally: + self.__cond.release() + + def clear(self): + self.__cond.acquire() + try: + self.__flag = False + finally: + self.__cond.release() + + def wait(self, timeout=None): + self.__cond.acquire() + try: + if not self.__flag: + self.__cond.wait(timeout) + finally: + self.__cond.release()
--- a/rhodecode/lib/vcs/utils/diffs.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/utils/diffs.py Sat Apr 27 11:24:25 2013 +0200 @@ -5,11 +5,9 @@ import re import difflib import logging +import itertools from difflib import unified_diff -from itertools import tee, imap - -from mercurial.match import match from rhodecode.lib.vcs.exceptions import VCSError from rhodecode.lib.vcs.nodes import FileNode, NodeError @@ -65,7 +63,7 @@ new_raw_id = getattr(filenode_new.changeset, 'raw_id', '0' * 40) repo = filenode_new.changeset.repository - vcs_gitdiff = repo._get_diff(old_raw_id, new_raw_id, filenode_new.path, + vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path, ignore_whitespace) return vcs_gitdiff @@ -97,10 +95,11 @@ elif self.__format == 'gitdiff': udiff_copy = self.copy_iterator() - self.lines = imap(self.escaper, self._parse_gitdiff(udiff_copy)) + self.lines = itertools.imap(self.escaper, + self._parse_gitdiff(udiff_copy)) else: udiff_copy = self.copy_iterator() - self.lines = imap(self.escaper, udiff_copy) + self.lines = itertools.imap(self.escaper, udiff_copy) # Select a differ. if differ == 'difflib': @@ -117,7 +116,7 @@ an original as it's needed for repeating operations on this instance of DiffProcessor """ - self.__udiff, iterator_copy = tee(self.__udiff) + self.__udiff, iterator_copy = itertools.tee(self.__udiff) return iterator_copy def _extract_rev(self, line1, line2):
--- a/rhodecode/lib/vcs/utils/helpers.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/utils/helpers.py Sat Apr 27 11:24:25 2013 +0200 @@ -4,10 +4,11 @@ from __future__ import division import re +import os import time import datetime -import os.path from subprocess import Popen, PIPE + from rhodecode.lib.vcs.exceptions import VCSError from rhodecode.lib.vcs.exceptions import RepositoryError from rhodecode.lib.vcs.utils.paths import abspath @@ -163,6 +164,7 @@ return result raise ValueError("IDs not recognized") + def parse_datetime(text): """ Parses given text and returns ``datetime.datetime`` instance or raises
--- a/rhodecode/lib/vcs/utils/hgcompat.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/utils/hgcompat.py Sat Apr 27 11:24:25 2013 +0200 @@ -16,3 +16,9 @@ from mercurial import localrepo from mercurial import scmutil from mercurial.discovery import findcommonoutgoing + +from mercurial.util import url as hg_url + +# those authnadlers are patched for python 2.6.5 bug an +# infinit looping when given invalid resources +from mercurial.url import httpbasicauthhandler, httpdigestauthhandler
--- a/rhodecode/lib/vcs/utils/progressbar.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/lib/vcs/utils/progressbar.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,7 +1,8 @@ # encoding: UTF-8 import sys import datetime -from string import Template +import string + from rhodecode.lib.vcs.utils.filesize import filesizeformat from rhodecode.lib.vcs.utils.helpers import get_total_seconds @@ -63,7 +64,7 @@ def get_template(self): separator = self.get_separator() elements = self.get_elements() - return Template(separator.join((('$%s' % e) for e in elements))) + return string.Template(separator.join((('$%s' % e) for e in elements))) def get_total_time(self, current_time=None): if current_time is None:
--- a/rhodecode/tests/__init__.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/__init__.py Sat Apr 27 11:24:25 2013 +0200 @@ -30,7 +30,12 @@ from paste.deploy import loadapp from paste.script.appinstall import SetupCommand + +import pylons +import pylons.test from pylons import config, url +from pylons.i18n.translation import _get_translator + from routes.util import URLGenerator from webtest import TestApp from nose.plugins.skip import SkipTest @@ -39,8 +44,6 @@ from rhodecode.model.meta import Session from rhodecode.model.db import User from rhodecode.tests.nose_parametrized import parameterized - -import pylons.test from rhodecode.lib.utils2 import safe_unicode, safe_str @@ -149,7 +152,13 @@ self.app = TestApp(wsgiapp) url._push_object(URLGenerator(config['routes.map'], environ)) - self.Session = Session + pylons.app_globals._push_object(config['pylons.app_globals']) + pylons.config._push_object(config) + + # Initialize a translator for tests that utilize i18n + translator = _get_translator(pylons.config.get('lang')) + pylons.translator._push_object(translator) + self.index_location = config['app_conf']['index_dir'] TestCase.__init__(self, *args, **kwargs)
--- a/rhodecode/tests/functional/test_admin_notifications.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_admin_notifications.py Sat Apr 27 11:24:25 2013 +0200 @@ -3,6 +3,7 @@ from rhodecode.model.user import UserModel from rhodecode.model.notification import NotificationModel +from rhodecode.model.meta import Session class TestNotificationsController(TestController): @@ -10,8 +11,8 @@ def tearDown(self): for n in Notification.query().all(): inst = Notification.get(n.notification_id) - self.Session().delete(inst) - self.Session().commit() + Session().delete(inst) + Session().commit() def test_index(self): self.log_user() @@ -29,7 +30,7 @@ NotificationModel().create(created_by=u1, subject=u'test_notification_1', body=u'notification_1', recipients=[cur_user]) - self.Session().commit() + Session().commit() response = self.app.get(url('notifications')) response.mustcontain(u'test_notification_1') @@ -67,7 +68,7 @@ subject=u'test', body=u'hi there', recipients=[cur_user, u1, u2]) - self.Session().commit() + Session().commit() u1 = User.get(u1.user_id) u2 = User.get(u2.user_id)
--- a/rhodecode/tests/functional/test_admin_repos.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_admin_repos.py Sat Apr 27 11:24:25 2013 +0200 @@ -47,7 +47,7 @@ % (repo_name, repo_name)) #test if the repo was created in the database - new_repo = self.Session().query(Repository)\ + new_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name).one() self.assertEqual(new_repo.repo_name, repo_name) @@ -80,7 +80,7 @@ u'Created repository <a href="/%s">%s</a>' % (urllib.quote(repo_name), repo_name_unicode)) #test if the repo was created in the database - new_repo = self.Session().query(Repository)\ + new_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name_unicode).one() self.assertEqual(new_repo.repo_name, repo_name_unicode) @@ -105,7 +105,7 @@ gr = ReposGroupModel().create(group_name=group_name, group_description='test', owner=TEST_USER_ADMIN_LOGIN) - self.Session().commit() + Session().commit() repo_name = 'ingroup' repo_name_full = RepoGroup.url_sep().join([group_name, repo_name]) @@ -120,7 +120,7 @@ 'Created repository <a href="/%s">%s</a>' % (repo_name_full, repo_name)) #test if the repo was created in the database - new_repo = self.Session().query(Repository)\ + new_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name_full).one() self.assertEqual(new_repo.repo_name, repo_name_full) @@ -136,12 +136,12 @@ vcs.get_repo(os.path.join(TESTS_TMP_PATH, repo_name_full)) except Exception: ReposGroupModel().delete(group_name) - self.Session().commit() + Session().commit() self.fail('no repo %s in filesystem' % repo_name) RepoModel().delete(repo_name_full) ReposGroupModel().delete(group_name) - self.Session().commit() + Session().commit() def test_create_git(self): self.log_user() @@ -158,7 +158,7 @@ % (repo_name, repo_name)) #test if the repo was created in the database - new_repo = self.Session().query(Repository)\ + new_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name).one() self.assertEqual(new_repo.repo_name, repo_name) @@ -194,7 +194,7 @@ % (urllib.quote(repo_name), repo_name_unicode)) #test if the repo was created in the database - new_repo = self.Session().query(Repository)\ + new_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name_unicode).one() self.assertEqual(new_repo.repo_name, repo_name_unicode) @@ -232,7 +232,7 @@ 'Created repository <a href="/%s">%s</a>' % (repo_name, repo_name)) #test if the repo was created in the database - new_repo = self.Session().query(Repository)\ + new_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name).one() self.assertEqual(new_repo.repo_name, repo_name) @@ -256,7 +256,7 @@ response.follow() #check if repo was deleted from db - deleted_repo = self.Session().query(Repository)\ + deleted_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name).scalar() self.assertEqual(deleted_repo, None) @@ -279,7 +279,7 @@ 'Created repository <a href="/%s">%s</a>' % (repo_name, repo_name)) #test if the repo was created in the database - new_repo = self.Session().query(Repository)\ + new_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name).one() self.assertEqual(new_repo.repo_name, repo_name) @@ -303,7 +303,7 @@ response.follow() #check if repo was deleted from db - deleted_repo = self.Session().query(Repository)\ + deleted_repo = Session().query(Repository)\ .filter(Repository.repo_name == repo_name).scalar() self.assertEqual(deleted_repo, None)
--- a/rhodecode/tests/functional/test_admin_settings.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_admin_settings.py Sat Apr 27 11:24:25 2013 +0200 @@ -6,6 +6,7 @@ from rhodecode.lib import helpers as h from rhodecode.model.user import UserModel from rhodecode.model.scm import ScmModel +from rhodecode.model.meta import Session class TestAdminSettingsController(TestController): @@ -135,7 +136,7 @@ uname = 'testme' usr = UserModel().create_or_update(username=uname, password='qweqwe', email='testme@rhodecod.org') - self.Session().commit() + Session().commit() params = usr.get_api_data() user_id = usr.user_id self.log_user(username=uname, password='qweqwe')
--- a/rhodecode/tests/functional/test_admin_users.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_admin_users.py Sat Apr 27 11:24:25 2013 +0200 @@ -39,7 +39,7 @@ self.checkSessionFlash(response, '''Created user %s''' % (username)) - new_user = self.Session.query(User).\ + new_user = Session().query(User).\ filter(User.username == username).one() self.assertEqual(new_user.username, username) @@ -74,7 +74,7 @@ response.mustcontain("""<span class="error-message">An email address must contain a single @</span>""") def get_user(): - self.Session.query(User).filter(User.username == username).one() + Session().query(User).filter(User.username == username).one() self.assertRaises(NoResultFound, get_user), 'found user in database' @@ -100,7 +100,7 @@ uname = 'testme' usr = UserModel().create_or_update(username=uname, password='qweqwe', email='testme@rhodecod.org') - self.Session().commit() + Session().commit() params = usr.get_api_data() params.update({name: expected}) params.update({'password_confirmation': ''}) @@ -146,7 +146,7 @@ response = response.follow() - new_user = self.Session.query(User)\ + new_user = Session().query(User)\ .filter(User.username == username).one() response = self.app.delete(url('user', id=new_user.user_id))
--- a/rhodecode/tests/functional/test_admin_users_groups.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_admin_users_groups.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,5 +1,6 @@ from rhodecode.tests import * from rhodecode.model.db import UserGroup, UserGroupToPerm, Permission +from rhodecode.model.meta import Session TEST_USER_GROUP = 'admins_test' @@ -48,13 +49,13 @@ self.checkSessionFlash(response, 'Created user group %s' % users_group_name) - gr = self.Session.query(UserGroup)\ + gr = Session().query(UserGroup)\ .filter(UserGroup.users_group_name == users_group_name).one() response = self.app.delete(url('users_group', id=gr.users_group_id)) - gr = self.Session.query(UserGroup)\ + gr = Session().query(UserGroup)\ .filter(UserGroup.users_group_name == users_group_name).scalar() @@ -119,7 +120,7 @@ ugid = ug.users_group_id response = self.app.delete(url('users_group', id=ug.users_group_id)) response = response.follow() - gr = self.Session.query(UserGroup)\ + gr = Session().query(UserGroup)\ .filter(UserGroup.users_group_name == users_group_name).scalar() @@ -192,7 +193,7 @@ ugid = ug.users_group_id response = self.app.delete(url('users_group', id=ug.users_group_id)) response = response.follow() - gr = self.Session.query(UserGroup)\ + gr = Session().query(UserGroup)\ .filter(UserGroup.users_group_name == users_group_name).scalar()
--- a/rhodecode/tests/functional/test_branches.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_branches.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,11 +1,20 @@ from rhodecode.tests import * + class TestBranchesController(TestController): - def test_index(self): + def test_index_hg(self): self.log_user() response = self.app.get(url(controller='branches', action='index', repo_name=HG_REPO)) - response.mustcontain("""<a href="/%s/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/">default</a>""" % HG_REPO) - response.mustcontain("""<a href="/%s/files/97e8b885c04894463c51898e14387d80c30ed1ee/">git</a>""" % HG_REPO) - response.mustcontain("""<a href="/%s/files/2e6a2bf9356ca56df08807f4ad86d480da72a8f4/">web</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/">default</a>""" % HG_REPO) + + # closed branches + response.mustcontain("""<a href="/%s/changeset/95ca6417ec0de6ac3bd19b336d7b608f27b88711">git [closed]</a><""" % HG_REPO) + response.mustcontain("""<a href="/%s/changeset/0dd5fd7b37a4eea4dd9b662af63cee743b4ccce2">web [closed]</a>""" % HG_REPO) + + def test_index_git(self): + self.log_user() + response = self.app.get(url(controller='branches', + action='index', repo_name=GIT_REPO)) + response.mustcontain("""<a href="/%s/files/5f2c6ee195929b0be80749243c18121c9864a3b3/">master</a>""" % GIT_REPO)
--- a/rhodecode/tests/functional/test_changelog.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_changelog.py Sat Apr 27 11:24:25 2013 +0200 @@ -11,23 +11,15 @@ response.mustcontain('''id="chg_20" class="container tablerow1"''') response.mustcontain( """<input class="changeset_range" """ - """id="5e204e7583b9c8e7b93a020bd036564b1e731dae" """ - """name="5e204e7583b9c8e7b93a020bd036564b1e731dae" """ + """id="7b22a518347bb9bc19679f6af07cd0a61bfe16e7" """ + """name="7b22a518347bb9bc19679f6af07cd0a61bfe16e7" """ """type="checkbox" value="1" />""" ) - + #rev 640: code garden response.mustcontain( - """<span class="changeset_hash">r154:5e204e7583b9</span>""" + """<span class="changeset_hash">r640:0a4e54a44604</span>""" ) - - response.mustcontain("""Small update at simplevcs app""") - -# response.mustcontain( -# """<div id="changed_total_5e204e7583b9c8e7b93a020bd036564b1e731dae" """ -# """style="float:right;" class="changed_total tooltip" """ -# """title="Affected number of files, click to show """ -# """more details">3</div>""" -# ) + response.mustcontain("""code garden""") def test_index_pagination_hg(self): self.log_user() @@ -48,29 +40,15 @@ # Test response after pagination... response.mustcontain( """<input class="changeset_range" """ - """id="46ad32a4f974e45472a898c6b0acb600320579b1" """ - """name="46ad32a4f974e45472a898c6b0acb600320579b1" """ + """id="22baf968d547386b9516965ce89d189665003a31" """ + """name="22baf968d547386b9516965ce89d189665003a31" """ """type="checkbox" value="1" />""" ) response.mustcontain( - """<span class="changeset_hash">r64:46ad32a4f974</span>""" + """<span class="changeset_hash">r539:22baf968d547</span>""" ) -# response.mustcontain( -# """<div id="changed_total_46ad32a4f974e45472a898c6b0acb600320579b1" """ -# """style="float:right;" class="changed_total tooltip" """ -# """title="Affected number of files, click to show """ -# """more details">21</div>""" -# ) -# -# response.mustcontain( -# """<a href="/%s/changeset/""" -# """46ad32a4f974e45472a898c6b0acb600320579b1" """ -# """title="Merge with 2e6a2bf9356ca56df08807f4ad86d480da72a8f4">""" -# """46ad32a4f974</a>""" % HG_REPO -# ) - def test_index_git(self): self.log_user() response = self.app.get(url(controller='changelog', action='index',
--- a/rhodecode/tests/functional/test_changeset_comments.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_changeset_comments.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,27 +1,28 @@ from rhodecode.tests import * from rhodecode.model.db import ChangesetComment, Notification, User, \ UserNotification +from rhodecode.model.meta import Session class TestChangeSetCommentsController(TestController): def setUp(self): for x in ChangesetComment.query().all(): - self.Session.delete(x) - self.Session.commit() + Session().delete(x) + Session().commit() for x in Notification.query().all(): - self.Session.delete(x) - self.Session.commit() + Session().delete(x) + Session().commit() def tearDown(self): for x in ChangesetComment.query().all(): - self.Session.delete(x) - self.Session.commit() + Session().delete(x) + Session().commit() for x in Notification.query().all(): - self.Session.delete(x) - self.Session.commit() + Session().delete(x) + Session().commit() def test_create(self): self.log_user()
--- a/rhodecode/tests/functional/test_compare_local.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_compare_local.py Sat Apr 27 11:24:25 2013 +0200 @@ -10,16 +10,17 @@ def test_compare_tag_hg(self): self.log_user() - tag1 = '0.1.2' - tag2 = '0.1.3' + tag1 = 'v0.1.2' + tag2 = 'v0.1.3' response = self.app.get(url(controller='compare', action='index', repo_name=HG_REPO, org_ref_type="tag", org_ref=tag1, other_ref_type="tag", other_ref=tag2, - )) + ), status=200) response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, tag1, HG_REPO, tag2)) + ## outgoing changesets between tags response.mustcontain('''<a href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO) response.mustcontain('''<a href="/%s/changeset/70d4cef8a37657ee4cf5aabb3bd9f68879769816">r115:70d4cef8a376</a>''' % HG_REPO) @@ -54,7 +55,7 @@ org_ref=tag1, other_ref_type="tag", other_ref=tag2, - )) + ), status=200) response.mustcontain('%s@%s -> %s@%s' % (GIT_REPO, tag1, GIT_REPO, tag2)) ## outgoing changesets between tags
--- a/rhodecode/tests/functional/test_files.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_files.py Sat Apr 27 11:24:25 2013 +0200 @@ -25,11 +25,19 @@ revision='tip', f_path='/')) # Test response... - response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/docs">docs</a>') - response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/tests">tests</a>') - response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/vcs">vcs</a>') - response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/.hgignore">.hgignore</a>') - response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/MANIFEST.in">MANIFEST.in</a>') + response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/docs">docs</a>') + response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/vcs">vcs</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.gitignore">.gitignore</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.hgignore">.hgignore</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.hgtags">.hgtags</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/.travis.yml">.travis.yml</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/MANIFEST.in">MANIFEST.in</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/README.rst">README.rst</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/run_test_and_report.sh">run_test_and_report.sh</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/setup.cfg">setup.cfg</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/setup.py">setup.py</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/test_and_report.sh">test_and_report.sh</a>') + response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/tox.ini">tox.ini</a>') def test_index_revision(self): self.log_user() @@ -79,7 +87,7 @@ self.log_user() response = self.app.get(url(controller='files', action='index', repo_name=HG_REPO, - revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc', + revision='8911406ad776fdd3d0b9932a2e89677e57405a48', f_path='vcs/nodes.py')) response.mustcontain("""<div class="commit">Partially implemented <a class="issue-tracker-link" href="https://myissueserver.com/vcs_test_hg/issue/16">#16</a>. filecontent/commit message/author/node name are safe_unicode now. @@ -100,7 +108,41 @@ extra_environ={'HTTP_X_PARTIAL_XHR': '1'},) #test or history response.mustcontain("""<optgroup label="Changesets"> -<option selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option> +<option value="dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6">r648:dbec37a0d5ca (default)</option> +<option value="1d20ed9eda9482d46ff0a6af5812550218b3ff15">r639:1d20ed9eda94 (default)</option> +<option value="0173395e822797f098799ed95c1a81b6a547a9ad">r547:0173395e8227 (default)</option> +<option value="afbb45ade933a8182f1d8ec5d4d1bb2de2572043">r546:afbb45ade933 (default)</option> +<option value="6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7">r502:6f093e30cac3 (default)</option> +<option value="c7e2212dd2ae975d1d06534a3d7e317165c06960">r476:c7e2212dd2ae (default)</option> +<option value="45477506df79f701bf69419aac3e1f0fed3c5bcf">r472:45477506df79 (default)</option> +<option value="5fc76cb25d11e07c60de040f78b8cd265ff10d53">r469:5fc76cb25d11 (default)</option> +<option value="b073433cf8994969ee5cd7cce84cbe587bb880b2">r468:b073433cf899 (default)</option> +<option value="7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96">r467:7a74dbfcacd1 (default)</option> +<option value="71ee52cc4d629096bdbee036325975dac2af4501">r465:71ee52cc4d62 (default)</option> +<option value="a5b217d26c5f111e72bae4de672b084ee0fbf75c">r452:a5b217d26c5f (default)</option> +<option value="47aedd538bf616eedcb0e7d630ea476df0e159c7">r450:47aedd538bf6 (default)</option> +<option value="8e4915fa32d727dcbf09746f637a5f82e539511e">r432:8e4915fa32d7 (default)</option> +<option value="25213a5fbb048dff8ba65d21e466a835536e5b70">r356:25213a5fbb04 (default)</option> +<option value="23debcedddc1c23c14be33e713e7786d4a9de471">r351:23debcedddc1 (default)</option> +<option value="61e25b2a90a19e7fffd75dea1e4c7e20df526bbe">r342:61e25b2a90a1 (default)</option> +<option value="fb95b340e0d03fa51f33c56c991c08077c99303e">r318:fb95b340e0d0 (webvcs)</option> +<option value="bda35e0e564fbbc5cd26fe0a37fb647a254c99fe">r303:bda35e0e564f (default)</option> +<option value="97ff74896d7dbf3115a337a421d44b55154acc89">r302:97ff74896d7d (default)</option> +<option value="cec3473c3fdb9599c98067182a075b49bde570f9">r293:cec3473c3fdb (default)</option> +<option value="0e86c43eef866a013a587666a877c879899599bb">r289:0e86c43eef86 (default)</option> +<option value="91a27c312808100cf20a602f78befbbff9d89bfd">r288:91a27c312808 (default)</option> +<option value="400e36a1670a57d11e3edcb5b07bf82c30006d0b">r287:400e36a1670a (default)</option> +<option value="014fb17dfc95b0995e838c565376bf9a993e230a">r261:014fb17dfc95 (default)</option> +<option value="cca7aebbc4d6125798446b11e69dc8847834a982">r260:cca7aebbc4d6 (default)</option> +<option value="14cdb2957c011a5feba36f50d960d9832ba0f0c1">r258:14cdb2957c01 (workdir)</option> +<option value="34df20118ed74b5987d22a579e8a60e903da5bf8">r245:34df20118ed7 (default)</option> +<option value="0375d9042a64a1ac1641528f0f0668f9a339e86d">r233:0375d9042a64 (workdir)</option> +<option value="94aa45fc1806c04d4ba640933edf682c22478453">r222:94aa45fc1806 (workdir)</option> +<option value="7ed99bc738818879941e3ce20243f8856a7cfc84">r188:7ed99bc73881 (default)</option> +<option value="1e85975528bcebe853732a9e5fb8dbf4461f6bb2">r184:1e85975528bc (default)</option> +<option value="ed30beddde7bbddb26042625be19bcd11576c1dd">r183:ed30beddde7b (default)</option> +<option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">r177:a6664e18181c (default)</option> +<option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option> <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option> <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option> <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option> @@ -135,16 +177,23 @@ <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option> </optgroup> <optgroup label="Branches"> -<option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option> -<option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option> -<option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option> +<option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">default</option> +<option value="4f7e2131323e0749a740c0a56ab68ae9269c562a">stable</option> </optgroup> <optgroup label="Tags"> -<option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option> -<option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option> -<option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option> -<option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option> -<option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option> +<option value="2c96c02def9a7c997f33047761a53943e6254396">v0.2.0</option> +<option value="8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9">v0.1.9</option> +<option value="ecb25ba9c96faf1e65a0bc3fd914918420a2f116">v0.1.8</option> +<option value="f67633a2894edaf28513706d558205fa93df9209">v0.1.7</option> +<option value="02b38c0eb6f982174750c0e309ff9faddc0c7e12">v0.1.6</option> +<option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">v0.1.5</option> +<option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">v0.1.4</option> +<option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">v0.1.3</option> +<option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">v0.1.2</option> +<option value="fef5bfe1dc17611d5fb59a7f6f95c55c3606f933">v0.1.11</option> +<option value="92831aebf2f8dd4879e897024b89d09af214df1c">v0.1.10</option> +<option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">v0.1.1</option> +<option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">tip</option> </optgroup> """) @@ -167,8 +216,42 @@ annotate=True), extra_environ={'HTTP_X_PARTIAL_XHR': '1'}) - response.mustcontain("""<optgroup label="Changesets"> -<option selected="selected" value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option> + response.mustcontain(""" +<option value="dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6">r648:dbec37a0d5ca (default)</option> +<option value="1d20ed9eda9482d46ff0a6af5812550218b3ff15">r639:1d20ed9eda94 (default)</option> +<option value="0173395e822797f098799ed95c1a81b6a547a9ad">r547:0173395e8227 (default)</option> +<option value="afbb45ade933a8182f1d8ec5d4d1bb2de2572043">r546:afbb45ade933 (default)</option> +<option value="6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7">r502:6f093e30cac3 (default)</option> +<option value="c7e2212dd2ae975d1d06534a3d7e317165c06960">r476:c7e2212dd2ae (default)</option> +<option value="45477506df79f701bf69419aac3e1f0fed3c5bcf">r472:45477506df79 (default)</option> +<option value="5fc76cb25d11e07c60de040f78b8cd265ff10d53">r469:5fc76cb25d11 (default)</option> +<option value="b073433cf8994969ee5cd7cce84cbe587bb880b2">r468:b073433cf899 (default)</option> +<option value="7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96">r467:7a74dbfcacd1 (default)</option> +<option value="71ee52cc4d629096bdbee036325975dac2af4501">r465:71ee52cc4d62 (default)</option> +<option value="a5b217d26c5f111e72bae4de672b084ee0fbf75c">r452:a5b217d26c5f (default)</option> +<option value="47aedd538bf616eedcb0e7d630ea476df0e159c7">r450:47aedd538bf6 (default)</option> +<option value="8e4915fa32d727dcbf09746f637a5f82e539511e">r432:8e4915fa32d7 (default)</option> +<option value="25213a5fbb048dff8ba65d21e466a835536e5b70">r356:25213a5fbb04 (default)</option> +<option value="23debcedddc1c23c14be33e713e7786d4a9de471">r351:23debcedddc1 (default)</option> +<option value="61e25b2a90a19e7fffd75dea1e4c7e20df526bbe">r342:61e25b2a90a1 (default)</option> +<option value="fb95b340e0d03fa51f33c56c991c08077c99303e">r318:fb95b340e0d0 (webvcs)</option> +<option value="bda35e0e564fbbc5cd26fe0a37fb647a254c99fe">r303:bda35e0e564f (default)</option> +<option value="97ff74896d7dbf3115a337a421d44b55154acc89">r302:97ff74896d7d (default)</option> +<option value="cec3473c3fdb9599c98067182a075b49bde570f9">r293:cec3473c3fdb (default)</option> +<option value="0e86c43eef866a013a587666a877c879899599bb">r289:0e86c43eef86 (default)</option> +<option value="91a27c312808100cf20a602f78befbbff9d89bfd">r288:91a27c312808 (default)</option> +<option value="400e36a1670a57d11e3edcb5b07bf82c30006d0b">r287:400e36a1670a (default)</option> +<option value="014fb17dfc95b0995e838c565376bf9a993e230a">r261:014fb17dfc95 (default)</option> +<option value="cca7aebbc4d6125798446b11e69dc8847834a982">r260:cca7aebbc4d6 (default)</option> +<option value="14cdb2957c011a5feba36f50d960d9832ba0f0c1">r258:14cdb2957c01 (workdir)</option> +<option value="34df20118ed74b5987d22a579e8a60e903da5bf8">r245:34df20118ed7 (default)</option> +<option value="0375d9042a64a1ac1641528f0f0668f9a339e86d">r233:0375d9042a64 (workdir)</option> +<option value="94aa45fc1806c04d4ba640933edf682c22478453">r222:94aa45fc1806 (workdir)</option> +<option value="7ed99bc738818879941e3ce20243f8856a7cfc84">r188:7ed99bc73881 (default)</option> +<option value="1e85975528bcebe853732a9e5fb8dbf4461f6bb2">r184:1e85975528bc (default)</option> +<option value="ed30beddde7bbddb26042625be19bcd11576c1dd">r183:ed30beddde7b (default)</option> +<option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">r177:a6664e18181c (default)</option> +<option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option> <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option> <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option> <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option> @@ -203,16 +286,23 @@ <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option> </optgroup> <optgroup label="Branches"> -<option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option> -<option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option> -<option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option> +<option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">default</option> +<option value="4f7e2131323e0749a740c0a56ab68ae9269c562a">stable</option> </optgroup> <optgroup label="Tags"> -<option value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option> -<option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option> -<option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option> -<option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option> -<option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option> +<option value="2c96c02def9a7c997f33047761a53943e6254396">v0.2.0</option> +<option value="8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9">v0.1.9</option> +<option value="ecb25ba9c96faf1e65a0bc3fd914918420a2f116">v0.1.8</option> +<option value="f67633a2894edaf28513706d558205fa93df9209">v0.1.7</option> +<option value="02b38c0eb6f982174750c0e309ff9faddc0c7e12">v0.1.6</option> +<option value="a6664e18181c6fc81b751a8d01474e7e1a3fe7fc">v0.1.5</option> +<option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">v0.1.4</option> +<option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">v0.1.3</option> +<option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">v0.1.2</option> +<option value="fef5bfe1dc17611d5fb59a7f6f95c55c3606f933">v0.1.11</option> +<option value="92831aebf2f8dd4879e897024b89d09af214df1c">v0.1.10</option> +<option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">v0.1.1</option> +<option value="96507bd11ecc815ebc6270fdf6db110928c09c1e">tip</option> </optgroup>""") def test_file_annotation_git(self):
--- a/rhodecode/tests/functional/test_home.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_home.py Sat Apr 27 11:24:25 2013 +0200 @@ -29,11 +29,11 @@ r"""alt=\"Public repository\" src=\"/images/icons/lock_""" r"""open.png\"/>""") - response.mustcontain( -r"""<a title=\"Marcin Kuzminski &lt;marcin@python-works.com&gt;:\n\n""" -r"""merge\" class=\"tooltip\" href=\"/vcs_test_hg/changeset/27cd5cce30c96924232""" -r"""dffcd24178a07ffeb5dfc\">r173:27cd5cce30c9</a>""" -) + response.mustcontain("""fixes issue with having custom format for git-log""") + response.mustcontain("""/%s/changeset/5f2c6ee195929b0be80749243c18121c9864a3b3""" % GIT_REPO) + + response.mustcontain("""disable security checks on hg clone for travis""") + response.mustcontain("""/%s/changeset/96507bd11ecc815ebc6270fdf6db110928c09c1e""" % HG_REPO) def test_repo_summary_with_anonymous_access_disabled(self): anon = User.get_default_user()
--- a/rhodecode/tests/functional/test_journal.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_journal.py Sat Apr 27 11:24:25 2013 +0200 @@ -14,10 +14,10 @@ def test_stop_following_repository(self): session = self.log_user() -# usr = self.Session.query(User).filter(User.username == 'test_admin').one() -# repo = self.Session.query(Repository).filter(Repository.repo_name == HG_REPO).one() +# usr = Session().query(User).filter(User.username == 'test_admin').one() +# repo = Session().query(Repository).filter(Repository.repo_name == HG_REPO).one() # -# followings = self.Session.query(UserFollowing)\ +# followings = Session().query(UserFollowing)\ # .filter(UserFollowing.user == usr)\ # .filter(UserFollowing.follows_repository == repo).all() #
--- a/rhodecode/tests/functional/test_login.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_login.py Sat Apr 27 11:24:25 2013 +0200 @@ -5,15 +5,16 @@ from rhodecode.lib.auth import check_password from rhodecode.lib import helpers as h from rhodecode.model import validators +from rhodecode.model.meta import Session class TestLoginController(TestController): def tearDown(self): for n in Notification.query().all(): - self.Session().delete(n) + Session().delete(n) - self.Session().commit() + Session().commit() self.assertEqual(Notification.query().all(), []) def test_index(self): @@ -216,7 +217,7 @@ self.assertEqual(response.status, '302 Found') self.checkSessionFlash(response, 'You have successfully registered into RhodeCode') - ret = self.Session().query(User).filter(User.username == 'test_regular4').one() + ret = Session().query(User).filter(User.username == 'test_regular4').one() self.assertEqual(ret.username, username) self.assertEqual(check_password(password, ret.password), True) self.assertEqual(ret.email, email) @@ -254,8 +255,8 @@ new.name = name new.lastname = lastname new.api_key = generate_api_key(username) - self.Session().add(new) - self.Session().commit() + Session().add(new) + Session().commit() response = self.app.post(url(controller='login', action='password_reset'),
--- a/rhodecode/tests/functional/test_search.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_search.py Sat Apr 27 11:24:25 2013 +0200 @@ -25,14 +25,14 @@ self.log_user() response = self.app.get(url(controller='search', action='index'), {'q': 'def repo'}) - response.mustcontain('39 results') + response.mustcontain('58 results') def test_repo_search(self): self.log_user() response = self.app.get(url(controller='search', action='index'), {'q': 'repository:%s def test' % HG_REPO}) - response.mustcontain('4 results') + response.mustcontain('18 results') def test_search_last(self): self.log_user() @@ -67,7 +67,7 @@ {'q': 'changed:tests/utils.py', 'type': 'commit'}) - response.mustcontain('20 results') + response.mustcontain('29 results') def test_search_commit_changed_files_get_commit(self): self.log_user() @@ -75,7 +75,7 @@ {'q': 'changed:vcs/utils/lazy.py', 'type': 'commit'}) - response.mustcontain('7 results') + response.mustcontain('11 results') response.mustcontain('36e0fc9d2808c5022a24f49d6658330383ed8666') response.mustcontain('af182745859d779f17336241a0815d15166ae1ee') response.mustcontain('17438a11f72b93f56d0e08e7d1fa79a378578a82')
--- a/rhodecode/tests/functional/test_summary.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_summary.py Sat Apr 27 11:24:25 2013 +0200 @@ -36,13 +36,16 @@ response = self.app.get(url(controller='summary', action='index', repo_name=HG_REPO)) response.mustcontain( - """var data = [["py", {"count": 42, "desc": ["Python"]}], """ - """["rst", {"count": 11, "desc": ["Rst"]}], """ + """var data = [["py", {"count": 68, "desc": ["Python"]}], """ + """["rst", {"count": 16, "desc": ["Rst"]}], """ + """["css", {"count": 2, "desc": ["Css"]}], """ """["sh", {"count": 2, "desc": ["Bash"]}], """ - """["makefile", {"count": 1, "desc": ["Makefile", "Makefile"]}],""" - """ ["cfg", {"count": 1, "desc": ["Ini"]}], """ - """["css", {"count": 1, "desc": ["Css"]}], """ - """["bat", {"count": 1, "desc": ["Batch"]}]];""" + """["yml", {"count": 1, "desc": ["Yaml"]}], """ + """["makefile", {"count": 1, "desc": ["Makefile", "Makefile"]}], """ + """["js", {"count": 1, "desc": ["Javascript"]}], """ + """["cfg", {"count": 1, "desc": ["Ini"]}], """ + """["ini", {"count": 1, "desc": ["Ini"]}], """ + """["html", {"count": 1, "desc": ["EvoqueHtml", "Html"]}]];""" ) # clone url... @@ -118,5 +121,5 @@ def _enable_stats(self): r = Repository.get_by_repo_name(HG_REPO) r.enable_statistics = True - self.Session.add(r) - self.Session.commit() + Session().add(r) + Session().commit()
--- a/rhodecode/tests/functional/test_tags.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/functional/test_tags.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,12 +1,40 @@ from rhodecode.tests import * + class TestTagsController(TestController): - def test_index(self): + def test_index_hg(self): self.log_user() response = self.app.get(url(controller='tags', action='index', repo_name=HG_REPO)) - response.mustcontain("""<a href="/%s/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/">tip</a>""" % HG_REPO) - response.mustcontain("""<a href="/%s/files/fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200/">0.1.4</a>""" % HG_REPO) - response.mustcontain("""<a href="/%s/files/17544fbfcd33ffb439e2b728b5d526b1ef30bfcf/">0.1.3</a>""" % HG_REPO) - response.mustcontain("""<a href="/%s/files/a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720/">0.1.2</a>""" % HG_REPO) - response.mustcontain("""<a href="/%s/files/eb3a60fc964309c1a318b8dfe26aa2d1586c85ae/">0.1.1</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/96507bd11ecc815ebc6270fdf6db110928c09c1e/">tip</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/2c96c02def9a7c997f33047761a53943e6254396/">v0.2.0</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/fef5bfe1dc17611d5fb59a7f6f95c55c3606f933/">v0.1.11</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/92831aebf2f8dd4879e897024b89d09af214df1c/">v0.1.10</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9/">v0.1.9</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/ecb25ba9c96faf1e65a0bc3fd914918420a2f116/">v0.1.8</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/f67633a2894edaf28513706d558205fa93df9209/">v0.1.7</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/02b38c0eb6f982174750c0e309ff9faddc0c7e12/">v0.1.6</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/a6664e18181c6fc81b751a8d01474e7e1a3fe7fc/">v0.1.5</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200/">v0.1.4</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/17544fbfcd33ffb439e2b728b5d526b1ef30bfcf/">v0.1.3</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720/">v0.1.2</a>""" % HG_REPO) + response.mustcontain("""<a href="/%s/files/eb3a60fc964309c1a318b8dfe26aa2d1586c85ae/">v0.1.1</a>""" % HG_REPO) + + def test_index_git(self): + self.log_user() + response = self.app.get(url(controller='tags', action='index', repo_name=GIT_REPO)) + + response.mustcontain("""<a href="/%s/files/137fea89f304a42321d40488091ee2ed419a3686/">v0.2.2</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/5051d0fa344d4408a2659d9a0348eb2d41868ecf/">v0.2.1</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/599ba911aa24d2981225f3966eb659dfae9e9f30/">v0.2.0</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/c60f01b77c42dce653d6b1d3b04689862c261929/">v0.1.11</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/10cddef6b794696066fb346434014f0a56810218/">v0.1.10</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/341d28f0eec5ddf0b6b77871e13c2bbd6bec685c/">v0.1.9</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/74ebce002c088b8a5ecf40073db09375515ecd68/">v0.1.8</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/4d78bf73b5c22c82b68f902f138f7881b4fffa2c/">v0.1.7</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/0205cb3f44223fb3099d12a77a69c81b798772d9/">v0.1.6</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/6c0ce52b229aa978889e91b38777f800e85f330b/">v0.1.5</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/7d735150934cd7645ac3051903add952390324a5/">v0.1.4</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/5a3a8fb005554692b16e21dee62bf02667d8dc3e/">v0.1.3</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/0ba5f8a4660034ff25c0cac2a5baabf5d2791d63/">v0.1.2</a>""" % GIT_REPO) + response.mustcontain("""<a href="/%s/files/e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0/">v0.1.1</a>""" % GIT_REPO)
--- a/rhodecode/tests/vcs/__init__.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/__init__.py Sat Apr 27 11:24:25 2013 +0200 @@ -19,10 +19,10 @@ .. _unittest: http://pypi.python.org/pypi/unittest """ -import os -from rhodecode.lib import vcs from rhodecode.lib.vcs.utils.compat import unittest -from utils import VCSTestError, SCMFetcher +from rhodecode.tests.vcs.conf import * +from rhodecode.tests.vcs.utils import VCSTestError, SCMFetcher + from rhodecode.tests import * @@ -36,7 +36,7 @@ 'alias': 'hg', 'test_repo_path': TEST_HG_REPO, 'remote_repo': HG_REMOTE_REPO, - 'clone_cmd': 'hg clone', + 'clone_cmd': 'hg clone --insecure', }, 'git': { 'alias': 'git', @@ -52,5 +52,16 @@ except VCSTestError, err: raise RuntimeError(str(err)) -#start_dir = os.path.abspath(os.path.dirname(__file__)) -#unittest.defaultTestLoader.discover(start_dir) + +def collector(): + setup_package() + start_dir = os.path.abspath(os.path.dirname(__file__)) + return unittest.defaultTestLoader.discover(start_dir) + + +def main(): + collector() + unittest.main() + +#if __name__ == '__main__': +# main()
--- a/rhodecode/tests/vcs/base.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/base.py Sat Apr 27 11:24:25 2013 +0200 @@ -3,14 +3,13 @@ InMemoryChangeset class is working properly at backend class. """ import os -from rhodecode.lib import vcs import time import shutil import datetime -from rhodecode.lib.vcs.utils.compat import unittest +from rhodecode.tests.vcs.conf import SCM_TESTS, get_new_dir -from conf import SCM_TESTS, get_new_dir - +from rhodecode.lib import vcs +from rhodecode.lib.vcs.utils.compat import unittest from rhodecode.lib.vcs.nodes import FileNode @@ -68,6 +67,7 @@ cls.repo_path = get_new_dir(str(time.time())) cls.repo = Backend(cls.repo_path, create=True) cls.imc = cls.repo.in_memory_changeset + cls.default_branch = cls.repo.DEFAULT_BRANCH_NAME for commit in cls._get_commits(): for node in commit.get('added', []):
--- a/rhodecode/tests/vcs/conf.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/conf.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,64 +1,65 @@ """ Unit tests configuration module for vcs. """ - import os import time import hashlib import tempfile import datetime import shutil -from rhodecode.tests import * from utils import get_normalized_path from os.path import join as jn -TEST_TMP_PATH = TESTS_TMP_PATH -#__all__ = ( -# 'TEST_HG_REPO', 'TEST_GIT_REPO', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', -# 'SCM_TESTS', -#) -# -#SCM_TESTS = ['hg', 'git'] -#uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple()))) -# +__all__ = ( + 'TEST_HG_REPO', 'TEST_GIT_REPO', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', + 'SCM_TESTS', +) + +SCM_TESTS = ['hg', 'git'] +uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple()))) + THIS = os.path.abspath(os.path.dirname(__file__)) -# -#GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git' -# -#TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', '/tmp') -#TEST_GIT_REPO = os.environ.get('VCS_TEST_GIT_REPO', -# jn(TEST_TMP_PATH, 'vcs-git')) -#TEST_GIT_REPO_CLONE = os.environ.get('VCS_TEST_GIT_REPO_CLONE', -# jn(TEST_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)) -#TEST_GIT_REPO_PULL = os.environ.get('VCS_TEST_GIT_REPO_PULL', -# jn(TEST_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)) -# -#HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs' -#TEST_HG_REPO = os.environ.get('VCS_TEST_HG_REPO', -# jn(TEST_TMP_PATH, 'vcs-hg')) -#TEST_HG_REPO_CLONE = os.environ.get('VCS_TEST_HG_REPO_CLONE', -# jn(TEST_TMP_PATH, 'vcshgclone%s' % uniq_suffix)) -#TEST_HG_REPO_PULL = os.environ.get('VCS_TEST_HG_REPO_PULL', -# jn(TEST_TMP_PATH, 'vcshgpull%s' % uniq_suffix)) -# -#TEST_DIR = os.environ.get('VCS_TEST_ROOT', tempfile.gettempdir()) -#TEST_REPO_PREFIX = 'vcs-test' -# -# -#def get_new_dir(title): -# """ -# Returns always new directory path. -# """ -# name = TEST_REPO_PREFIX -# if title: -# name = '-'.join((name, title)) -# hex = hashlib.sha1(str(time.time())).hexdigest() -# name = '-'.join((name, hex)) -# path = os.path.join(TEST_DIR, name) -# return get_normalized_path(path) + +GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git' + +TEST_TMP_PATH = os.environ.get('VCS_TEST_ROOT', '/tmp') +TEST_GIT_REPO = os.environ.get('VCS_TEST_GIT_REPO', + jn(TEST_TMP_PATH, 'vcs-git')) +TEST_GIT_REPO_CLONE = os.environ.get('VCS_TEST_GIT_REPO_CLONE', + jn(TEST_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)) +TEST_GIT_REPO_PULL = os.environ.get('VCS_TEST_GIT_REPO_PULL', + jn(TEST_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)) + +HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs' +TEST_HG_REPO = os.environ.get('VCS_TEST_HG_REPO', + jn(TEST_TMP_PATH, 'vcs-hg')) +TEST_HG_REPO_CLONE = os.environ.get('VCS_TEST_HG_REPO_CLONE', + jn(TEST_TMP_PATH, 'vcshgclone%s' % uniq_suffix)) +TEST_HG_REPO_PULL = os.environ.get('VCS_TEST_HG_REPO_PULL', + jn(TEST_TMP_PATH, 'vcshgpull%s' % uniq_suffix)) + +TEST_DIR = os.environ.get('VCS_TEST_ROOT', tempfile.gettempdir()) +TEST_REPO_PREFIX = 'vcs-test' + + +def get_new_dir(title): + """ + Returns always new directory path. + """ + name = TEST_REPO_PREFIX + if title: + name = '-'.join((name, title)) + hex = hashlib.sha1(str(time.time())).hexdigest() + name = '-'.join((name, hex)) + path = os.path.join(TEST_DIR, name) + return get_normalized_path(path) + PACKAGE_DIR = os.path.abspath(os.path.join( os.path.dirname(__file__), '..')) -_dest = jn(TESTS_TMP_PATH,'aconfig') +_dest = jn(TEST_TMP_PATH, 'aconfig') shutil.copy(jn(THIS, 'aconfig'), _dest) TEST_USER_CONFIG_FILE = _dest + +#overide default configurations with rhodecode ones +from rhodecode.tests import *
--- a/rhodecode/tests/vcs/test_archives.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_archives.py Sat Apr 27 11:24:25 2013 +0200 @@ -6,8 +6,8 @@ import datetime import tempfile import StringIO -from base import BackendTestMixin -from conf import SCM_TESTS +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS from rhodecode.lib.vcs.exceptions import VCSError from rhodecode.lib.vcs.nodes import FileNode from rhodecode.lib.vcs.utils.compat import unittest
--- a/rhodecode/tests/vcs/test_branches.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_branches.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,13 +1,12 @@ from __future__ import with_statement +import datetime from rhodecode.lib import vcs -import datetime from rhodecode.lib.vcs.utils.compat import unittest +from rhodecode.lib.vcs.nodes import FileNode -from base import BackendTestMixin -from conf import SCM_TESTS - -from rhodecode.lib.vcs.nodes import FileNode +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS class BranchesTestCaseMixin(BackendTestMixin):
--- a/rhodecode/tests/vcs/test_changesets.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_changesets.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,15 +1,19 @@ from __future__ import with_statement +import datetime from rhodecode.lib import vcs -import datetime -from base import BackendTestMixin -from conf import SCM_TESTS +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS + from rhodecode.lib.vcs.backends.base import BaseChangeset -from rhodecode.lib.vcs.nodes import FileNode, AddedFileNodesGenerator,\ +from rhodecode.lib.vcs.nodes import ( + FileNode, AddedFileNodesGenerator, ChangedFileNodesGenerator, RemovedFileNodesGenerator -from rhodecode.lib.vcs.exceptions import BranchDoesNotExistError -from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError -from rhodecode.lib.vcs.exceptions import RepositoryError +) +from rhodecode.lib.vcs.exceptions import ( + BranchDoesNotExistError, ChangesetDoesNotExistError, + RepositoryError +) from rhodecode.lib.vcs.utils.compat import unittest
--- a/rhodecode/tests/vcs/test_filenodes_unicode_path.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_filenodes_unicode_path.py Sat Apr 27 11:24:25 2013 +0200 @@ -5,8 +5,8 @@ import datetime from rhodecode.lib.vcs.nodes import FileNode from rhodecode.lib.vcs.utils.compat import unittest -from test_inmemchangesets import BackendBaseTestCase -from conf import SCM_TESTS +from rhodecode.tests.vcs.test_inmemchangesets import BackendBaseTestCase +from rhodecode.tests.vcs.conf import SCM_TESTS class FileNodeUnicodePathTestsMixin(object):
--- a/rhodecode/tests/vcs/test_getitem.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_getitem.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,8 +1,8 @@ from __future__ import with_statement import datetime -from base import BackendTestMixin -from conf import SCM_TESTS +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS from rhodecode.lib.vcs.nodes import FileNode from rhodecode.lib.vcs.utils.compat import unittest
--- a/rhodecode/tests/vcs/test_getslice.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_getslice.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,8 +1,8 @@ from __future__ import with_statement import datetime -from base import BackendTestMixin -from conf import SCM_TESTS +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS from rhodecode.lib.vcs.nodes import FileNode from rhodecode.lib.vcs.utils.compat import unittest
--- a/rhodecode/tests/vcs/test_git.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_git.py Sat Apr 27 11:24:25 2013 +0200 @@ -8,7 +8,7 @@ from rhodecode.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState from rhodecode.lib.vcs.utils.compat import unittest from rhodecode.tests.vcs.base import BackendTestMixin -from conf import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir +from rhodecode.tests.vcs.conf import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir class GitRepositoryTest(unittest.TestCase):
--- a/rhodecode/tests/vcs/test_hg.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_hg.py Sat Apr 27 11:24:25 2013 +0200 @@ -4,7 +4,7 @@ from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialChangeset from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError from rhodecode.lib.vcs.nodes import NodeKind, NodeState -from conf import PACKAGE_DIR, TEST_HG_REPO, TEST_HG_REPO_CLONE, \ +from rhodecode.tests.vcs.conf import PACKAGE_DIR, TEST_HG_REPO, TEST_HG_REPO_CLONE, \ TEST_HG_REPO_PULL from rhodecode.lib.vcs.utils.compat import unittest @@ -156,9 +156,10 @@ #active branches self.assertTrue('default' in self.repo.branches) - self.assertTrue('git' in self.repo.branches) + self.assertTrue('stable' in self.repo.branches) # closed + self.assertTrue('git' in self.repo._get_branches(closed=True)) self.assertTrue('web' in self.repo._get_branches(closed=True)) for name, id in self.repo.branches.items():
--- a/rhodecode/tests/vcs/test_inmemchangesets.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_inmemchangesets.py Sat Apr 27 11:24:25 2013 +0200 @@ -3,10 +3,11 @@ """ from __future__ import with_statement -from rhodecode.lib import vcs import time import datetime -from conf import SCM_TESTS, get_new_dir + +from rhodecode.lib import vcs +from rhodecode.tests.vcs.conf import SCM_TESTS, get_new_dir from rhodecode.lib.vcs.exceptions import EmptyRepositoryError from rhodecode.lib.vcs.exceptions import NodeAlreadyAddedError from rhodecode.lib.vcs.exceptions import NodeAlreadyExistsError
--- a/rhodecode/tests/vcs/test_repository.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_repository.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,8 +1,8 @@ from __future__ import with_statement import datetime -from base import BackendTestMixin -from conf import SCM_TESTS -from conf import TEST_USER_CONFIG_FILE +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS +from rhodecode.tests.vcs.conf import TEST_USER_CONFIG_FILE from rhodecode.lib.vcs.nodes import FileNode from rhodecode.lib.vcs.utils.compat import unittest from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
--- a/rhodecode/tests/vcs/test_tags.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_tags.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,7 +1,7 @@ from __future__ import with_statement -from base import BackendTestMixin -from conf import SCM_TESTS +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS from rhodecode.lib.vcs.exceptions import TagAlreadyExistError from rhodecode.lib.vcs.exceptions import TagDoesNotExistError from rhodecode.lib.vcs.utils.compat import unittest
--- a/rhodecode/tests/vcs/test_utils.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_utils.py Sat Apr 27 11:24:25 2013 +0200 @@ -18,7 +18,7 @@ from rhodecode.lib.vcs.utils.paths import get_user_home from rhodecode.lib.vcs.exceptions import VCSError -from conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH +from rhodecode.tests.vcs.conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH class PathsTest(unittest.TestCase):
--- a/rhodecode/tests/vcs/test_vcs.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_vcs.py Sat Apr 27 11:24:25 2013 +0200 @@ -1,11 +1,13 @@ from __future__ import with_statement +import os +import shutil + from rhodecode.lib.vcs import VCSError, get_repo, get_backend from rhodecode.lib.vcs.backends.hg import MercurialRepository from rhodecode.lib.vcs.utils.compat import unittest -from conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH -import os -import shutil +from rhodecode.tests.vcs.conf import TEST_HG_REPO, TEST_GIT_REPO, TEST_TMP_PATH + class VCSTest(unittest.TestCase):
--- a/rhodecode/tests/vcs/test_workdirs.py Sat Apr 27 11:45:27 2013 +0200 +++ b/rhodecode/tests/vcs/test_workdirs.py Sat Apr 27 11:24:25 2013 +0200 @@ -3,8 +3,8 @@ import datetime from rhodecode.lib.vcs.nodes import FileNode from rhodecode.lib.vcs.utils.compat import unittest -from base import BackendTestMixin -from conf import SCM_TESTS +from rhodecode.tests.vcs.base import BackendTestMixin +from rhodecode.tests.vcs.conf import SCM_TESTS class WorkdirTestCaseMixin(BackendTestMixin):