Changeset - 6db421a8cd9a
[Not reviewed]
default
0 4 0
Branko Majic (branko) - 10 years ago 2015-09-03 23:04:13
branko@majic.rs
tests: fix generation of a unique temporary directory path for VCS testing

The old code was complicated and failed if directories were created quickly in
succession.

Re-implement vcs get_new_dir without get_normalized_path and VCSTestError.

The top level get_new_dir was unused.
4 files changed with 37 insertions and 72 deletions:
0 comments (0 inline, 0 general)
kallithea/tests/__init__.py
Show inline comments
 
@@ -47,49 +47,49 @@ import pylons
 
import pylons.test
 
from pylons import config, url
 
from pylons.i18n.translation import _get_translator
 
from pylons.util import ContextObj
 

	
 
from routes.util import URLGenerator
 
from webtest import TestApp
 
from nose.plugins.skip import SkipTest
 

	
 
from kallithea.lib.compat import unittest
 
from kallithea import is_windows
 
from kallithea.model.db import Notification, User, UserNotification
 
from kallithea.model.meta import Session
 
from kallithea.tests.parameterized import parameterized
 
from kallithea.lib.utils2 import safe_str
 

	
 

	
 
os.environ['TZ'] = 'UTC'
 
if not is_windows:
 
    time.tzset()
 

	
 
log = logging.getLogger(__name__)
 

	
 
__all__ = [
 
    'parameterized', 'environ', 'url', 'get_new_dir', 'TestController',
 
    'parameterized', 'environ', 'url', 'TestController',
 
    'SkipTest', 'ldap_lib_installed', 'pam_lib_installed', 'BaseTestCase', 'init_stack',
 
    'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
 
    'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS',
 
    'TEST_USER_ADMIN_EMAIL', 'TEST_USER_REGULAR_LOGIN', 'TEST_USER_REGULAR_PASS',
 
    'TEST_USER_REGULAR_EMAIL', 'TEST_USER_REGULAR2_LOGIN',
 
    'TEST_USER_REGULAR2_PASS', 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO',
 
    'TEST_HG_REPO_CLONE', 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO',
 
    'TEST_GIT_REPO_CLONE', 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO',
 
    'GIT_REMOTE_REPO', 'SCM_TESTS',
 
]
 

	
 
# Invoke websetup with the current config file
 
# SetupCommand('setup-app').run([config_file])
 

	
 
environ = {}
 

	
 
#SOME GLOBALS FOR TESTS
 

	
 
TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
 
TEST_USER_ADMIN_LOGIN = 'test_admin'
 
TEST_USER_ADMIN_PASS = 'test12'
 
TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
 

	
 
TEST_USER_REGULAR_LOGIN = 'test_regular'
 
@@ -130,62 +130,48 @@ TEST_DIR = tempfile.gettempdir()
 
TEST_REPO_PREFIX = 'vcs-test'
 

	
 
# cached repos if any !
 
# comment out to get some other repos from bb or github
 
GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
 
HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
 

	
 
#skip ldap tests if LDAP lib is not installed
 
ldap_lib_installed = False
 
try:
 
    import ldap
 
    ldap.API_VERSION
 
    ldap_lib_installed = True
 
except ImportError:
 
    # means that python-ldap is not installed
 
    pass
 

	
 
try:
 
    import pam
 
    pam.PAM_TEXT_INFO
 
    pam_lib_installed = True
 
except ImportError:
 
    pam_lib_installed = False
 

	
 
def get_new_dir(title):
 
    """
 
    Returns always new directory path.
 
    """
 
    from kallithea.tests.vcs.utils import get_normalized_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)
 

	
 

	
 
import logging
 

	
 
class NullHandler(logging.Handler):
 
    def emit(self, record):
 
        pass
 

	
 
def init_stack(config=None):
 
    if not config:
 
        config = pylons.test.pylonsapp.config
 
    url._push_object(URLGenerator(config['routes.map'], environ))
 
    pylons.app_globals._push_object(config['pylons.app_globals'])
 
    pylons.config._push_object(config)
 
    pylons.tmpl_context._push_object(ContextObj())
 
    # Initialize a translator for tests that utilize i18n
 
    translator = _get_translator(pylons.config.get('lang'))
 
    pylons.translator._push_object(translator)
 
    h = NullHandler()
 
    logging.getLogger("kallithea").addHandler(h)
 

	
 

	
 
class BaseTestCase(unittest.TestCase):
 
    def __init__(self, *args, **kwargs):
 
        self.wsgiapp = pylons.test.pylonsapp
 
        init_stack(self.wsgiapp.config)
kallithea/tests/vcs/__init__.py
Show inline comments
 
"""
 
Unit tests for vcs_ library.
 

	
 
In order to run tests we need to prepare our environment first. Tests would be
 
run for each engine listed at ``conf.SCM_TESTS`` - keys are aliases from
 
``vcs.backends.BACKENDS``.
 

	
 
For each SCM we run tests for, we need some repository. We would use
 
repositories location from system environment variables or test suite defaults
 
- see ``conf`` module for more detail. We simply try to check if repository at
 
certain location exists, if not we would try to fetch them. At ``test_vcs`` or
 
``test_common`` we run unit tests common for each repository type and for
 
example specific mercurial tests are located at ``test_hg`` module.
 

	
 
Oh, and tests are run with ``unittest.collector`` wrapped by ``collector``
 
function at ``tests/__init__.py``.
 

	
 
.. _vcs: http://bitbucket.org/marcinkuzminski/vcs
 
.. _unittest: http://pypi.python.org/pypi/unittest
 

	
 
"""
 
from kallithea.lib.vcs.utils.compat import unittest
 
from kallithea.tests.vcs.conf import *
 
from kallithea.tests.vcs.utils import VCSTestError, SCMFetcher
 
from kallithea.tests.vcs.utils import SCMFetcher
 

	
 
from kallithea.tests import *
 

	
 

	
 
def setup_package():
 
    """
 
    Prepares whole package for tests which mainly means it would try to fetch
 
    test repositories or use already existing ones.
 
    """
 
    fetchers = {
 
        'hg': {
 
            'alias': 'hg',
 
            'test_repo_path': TEST_HG_REPO,
 
            'remote_repo': HG_REMOTE_REPO,
 
            'clone_cmd': 'hg clone --insecure',
 
        },
 
        'git': {
 
            'alias': 'git',
 
            'test_repo_path': TEST_GIT_REPO,
 
            'remote_repo': GIT_REMOTE_REPO,
 
            'clone_cmd': 'git clone --bare',
 
        },
 
    }
 
    try:
 
        for scm, fetcher_info in fetchers.items():
 
            fetcher = SCMFetcher(**fetcher_info)
 
            fetcher.setup()
 
    except VCSTestError as err:
 
        raise RuntimeError(str(err))
 

	
 
    for scm, fetcher_info in fetchers.items():
 
        fetcher = SCMFetcher(**fetcher_info)
 
        fetcher.setup()
 

	
 

	
 
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()
kallithea/tests/vcs/conf.py
Show inline comments
 
"""
 
Unit tests configuration module for vcs.
 
"""
 
import os
 
import time
 
import hashlib
 
import tempfile
 
import datetime
 
import shutil
 
from utils import get_normalized_path
 
import uuid
 
from os.path import join as jn
 

	
 
__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):
 
def get_new_dir(title=None):
 
    """
 
    Returns always new directory path.
 
    Calculates a path for a new, non-existant, unique sub-directory in TEST_DIR.
 

	
 
    Resulting directory name will have format:
 

	
 
    prefix-[title-]hexuuid
 

	
 
    Prefix is equal to value of variable TEST_REPO_PREFIX. The "hexuuid" is a
 
    hexadecimal value of a randomly generated UUID. Title will be added if
 
    specified.
 

	
 
    Args:
 
        title: Custom title to include as part of the resulting sub-directory
 
            name. Can be useful for debugging to identify destination. Defaults
 
            to None.
 

	
 
    Returns:
 
        Path to the new directory as a string.
 
    """
 
    name = TEST_REPO_PREFIX
 

	
 
    if title:
 
        name = '-'.join((name, title))
 
    hex = hashlib.sha1(str(time.time())).hexdigest()
 
    name = '-'.join((name, hex))
 
        name = "%s-%s" % (TEST_REPO_PREFIX, title)
 
    else:
 
        name = TEST_REPO_PREFIX
 

	
 
    path = os.path.join(TEST_DIR, name)
 
    return get_normalized_path(path)
 

	
 
    # Generate new hexes until we get a unique name (just in case).
 
    hex_uuid = uuid.uuid4().hex
 
    while os.path.exists("%s-%s" % (path, hex_uuid)):
 
        hex_uuid = uuid.uuid4().hex
 

	
 
    return "%s-%s" % (path, hex_uuid)
 

	
 

	
 
PACKAGE_DIR = os.path.abspath(os.path.join(
 
    os.path.dirname(__file__), '..'))
 
_dest = jn(TEST_TMP_PATH, 'aconfig')
 
shutil.copy(jn(THIS, 'aconfig'), _dest)
 
TEST_USER_CONFIG_FILE = _dest
 

	
 
#overide default configurations with kallithea ones
 
from kallithea.tests import *
kallithea/tests/vcs/utils.py
Show inline comments
 
"""
 
Utilities for tests only. These are not or should not be used normally -
 
functions here are crafted as we don't want to use ``vcs`` to verify tests.
 
"""
 
import os
 
import re
 
import sys
 

	
 
from subprocess import Popen
 

	
 

	
 
class VCSTestError(Exception):
 
    pass
 

	
 

	
 
def run_command(cmd, args):
 
    """
 
    Runs command on the system with given ``args``.
 
    """
 
    command = ' '.join((cmd, args))
 
    p = Popen(command, shell=True)
 
    status = os.waitpid(p.pid, 0)[1]
 
    return status
 

	
 

	
 
def eprint(msg):
 
    """
 
    Prints given ``msg`` into sys.stderr as nose test runner hides all output
 
    from sys.stdout by default and if we want to pipe stream somewhere we don't
 
    need those verbose messages anyway.
 
    Appends line break.
 
    """
 
    sys.stderr.write(msg)
 
    sys.stderr.write('\n')
 

	
 

	
 
class SCMFetcher(object):
 

	
 
    def __init__(self, alias, test_repo_path, remote_repo, clone_cmd):
 
        """
 
        :param clone_cmd: command which would clone remote repository; pass
 
          only first bits - remote path and destination would be appended
 
          using ``remote_repo`` and ``test_repo_path``
 
        """
 
        self.alias = alias
 
        self.test_repo_path = test_repo_path
 
        self.remote_repo = remote_repo
 
        self.clone_cmd = clone_cmd
 

	
 
    def setup(self):
 
        if not os.path.isdir(self.test_repo_path):
 
            self.fetch_repo()
 

	
 
    def fetch_repo(self):
 
        """
 
        Tries to fetch repository from remote path.
 
        """
 
        remote = self.remote_repo
 
        eprint("Fetching repository %s into %s" % (remote, self.test_repo_path))
 
        run_command(self.clone_cmd,  '%s %s' % (remote, self.test_repo_path))
 

	
 

	
 
def get_normalized_path(path):
 
    """
 
    If given path exists, new path would be generated and returned. Otherwise
 
    same whats given is returned. Assumes that there would be no more than
 
    10000 same named files.
 
    """
 
    if os.path.exists(path):
 
        dir, basename = os.path.split(path)
 
        splitted_name = basename.split('.')
 
        if len(splitted_name) > 1:
 
            ext = splitted_name[-1]
 
        else:
 
            ext = None
 
        name = '.'.join(splitted_name[:-1])
 
        matcher = re.compile(r'^.*-(\d{5})$')
 
        start = 0
 
        m = matcher.match(name)
 
        if not m:
 
            # Haven't append number yet so return first
 
            newname = '%s-00000' % name
 
            newpath = os.path.join(dir, newname)
 
            if ext:
 
                newpath = '.'.join((newpath, ext))
 
            return get_normalized_path(newpath)
 
        else:
 
            start = int(m.group(1)[-5:]) + 1
 
            for x in xrange(start, 10000):
 
                newname = name[:-5] + str(x).rjust(5, '0')
 
                newpath = os.path.join(dir, newname)
 
                if ext:
 
                    newpath = '.'.join((newpath, ext))
 
                if not os.path.exists(newpath):
 
                    return newpath
 
        raise VCSTestError("Couldn't compute new path for %s" % path)
 
    return path
0 comments (0 inline, 0 general)