summaryrefslogtreecommitdiff
path: root/git/test/lib/helper.py
diff options
context:
space:
mode:
Diffstat (limited to 'git/test/lib/helper.py')
-rw-r--r--git/test/lib/helper.py139
1 files changed, 77 insertions, 62 deletions
diff --git a/git/test/lib/helper.py b/git/test/lib/helper.py
index bb17745c..db6eb121 100644
--- a/git/test/lib/helper.py
+++ b/git/test/lib/helper.py
@@ -16,36 +16,37 @@ import warnings
from nose import SkipTest
from base import (
- maketemp,
- rorepo_dir
- )
+ maketemp,
+ rorepo_dir
+)
__all__ = (
- 'StringProcessAdapter', 'GlobalsItemDeletorMetaCls', 'InheritedTestMethodsOverrideWrapperMetaClsAutoMixin',
- 'with_rw_repo', 'with_rw_and_rw_remote_repo', 'TestBase', 'TestCase', 'needs_module_or_skip'
- )
+ 'StringProcessAdapter', 'GlobalsItemDeletorMetaCls', 'InheritedTestMethodsOverrideWrapperMetaClsAutoMixin',
+ 'with_rw_repo', 'with_rw_and_rw_remote_repo', 'TestBase', 'TestCase', 'needs_module_or_skip'
+)
-
-#{ Adapters
-
+#{ Adapters
+
class StringProcessAdapter(object):
+
"""Allows to use strings as Process object as returned by SubProcess.Popen.
Its tailored to work with the test system only"""
-
+
def __init__(self, input_string):
self.stdout = cStringIO.StringIO(input_string)
self.stderr = cStringIO.StringIO()
-
+
def wait(self):
return 0
-
+
poll = wait
-
+
#} END adapters
-#{ Decorators
+#{ Decorators
+
def _rmtree_onerror(osremove, fullpath, exec_info):
"""
@@ -54,35 +55,37 @@ def _rmtree_onerror(osremove, fullpath, exec_info):
"""
if os.name != 'nt' or osremove is not os.remove:
raise
-
+
os.chmod(fullpath, 0777)
os.remove(fullpath)
+
def with_rw_repo(working_tree_ref, bare=False):
"""
Same as with_bare_repo, but clones the rorepo as non-bare repository, checking
out the working tree at the given working_tree_ref.
-
+
This repository type is more costly due to the working copy checkout.
-
+
To make working with relative paths easier, the cwd will be set to the working
dir of the repository.
"""
assert isinstance(working_tree_ref, basestring), "Decorator requires ref name for working tree checkout"
+
def argument_passer(func):
def repo_creator(self):
prefix = 'non_'
if bare:
prefix = ''
- #END handle prefix
+ # END handle prefix
repo_dir = maketemp("%sbare_%s" % (prefix, func.__name__))
rw_repo = self.rorepo.clone(repo_dir, shared=True, bare=bare, n=True)
-
+
rw_repo.head.commit = rw_repo.commit(working_tree_ref)
if not bare:
rw_repo.head.reference.checkout()
# END handle checkout
-
+
prev_cwd = os.getcwd()
os.chdir(rw_repo.working_dir)
try:
@@ -104,7 +107,8 @@ def with_rw_repo(working_tree_ref, bare=False):
return repo_creator
# END argument passer
return argument_passer
-
+
+
def with_rw_and_rw_remote_repo(working_tree_ref):
"""
Same as with_rw_repo, but also provides a writable remote repository from which the
@@ -112,36 +116,38 @@ def with_rw_and_rw_remote_repo(working_tree_ref):
run the remote_repo.
The remote repository was cloned as bare repository from the rorepo, wheras
the rw repo has a working tree and was cloned from the remote repository.
-
+
remote_repo has two remotes: origin and daemon_origin. One uses a local url,
the other uses a server url. The daemon setup must be done on system level
and should be an inetd service that serves tempdir.gettempdir() and all
directories in it.
-
+
The following scetch demonstrates this::
rorepo ---<bare clone>---> rw_remote_repo ---<clone>---> rw_repo
-
+
The test case needs to support the following signature::
def case(self, rw_repo, rw_remote_repo)
-
+
This setup allows you to test push and pull scenarios and hooks nicely.
-
+
See working dir info in with_rw_repo
"""
assert isinstance(working_tree_ref, basestring), "Decorator requires ref name for working tree checkout"
+
def argument_passer(func):
def remote_repo_creator(self):
remote_repo_dir = maketemp("remote_repo_%s" % func.__name__)
repo_dir = maketemp("remote_clone_non_bare_repo")
-
+
rw_remote_repo = self.rorepo.clone(remote_repo_dir, shared=True, bare=True)
- rw_repo = rw_remote_repo.clone(repo_dir, shared=True, bare=False, n=True) # recursive alternates info ?
+ # recursive alternates info ?
+ rw_repo = rw_remote_repo.clone(repo_dir, shared=True, bare=False, n=True)
rw_repo.head.commit = working_tree_ref
rw_repo.head.reference.checkout()
-
+
# prepare for git-daemon
rw_remote_repo.daemon_export = True
-
+
# this thing is just annoying !
crw = rw_remote_repo.config_writer()
section = "daemon"
@@ -152,28 +158,30 @@ def with_rw_and_rw_remote_repo(working_tree_ref):
crw.set(section, "receivepack", True)
# release lock
del(crw)
-
- # initialize the remote - first do it as local remote and pull, then
+
+ # initialize the remote - first do it as local remote and pull, then
# we change the url to point to the daemon. The daemon should be started
# by the user, not by us
d_remote = Remote.create(rw_repo, "daemon_origin", remote_repo_dir)
d_remote.fetch()
remote_repo_url = "git://localhost%s" % remote_repo_dir
-
+
d_remote.config_writer.set('url', remote_repo_url)
-
+
# try to list remotes to diagnoes whether the server is up
try:
rw_repo.git.ls_remote(d_remote)
- except GitCommandError,e:
+ except GitCommandError, e:
print str(e)
if os.name == 'nt':
- raise AssertionError('git-daemon needs to run this test, but windows does not have one. Otherwise, run: git-daemon "%s"' % os.path.dirname(_mktemp()))
+ raise AssertionError(
+ 'git-daemon needs to run this test, but windows does not have one. Otherwise, run: git-daemon "%s"' % os.path.dirname(_mktemp()))
else:
- raise AssertionError('Please start a git-daemon to run this test, execute: git-daemon "%s"' % os.path.dirname(_mktemp()))
+ raise AssertionError(
+ 'Please start a git-daemon to run this test, execute: git-daemon "%s"' % os.path.dirname(_mktemp()))
# END make assertion
- #END catch ls remote error
-
+ # END catch ls remote error
+
# adjust working dir
prev_cwd = os.getcwd()
os.chdir(rw_repo.working_dir)
@@ -191,9 +199,10 @@ def with_rw_and_rw_remote_repo(working_tree_ref):
return remote_repo_creator
# END remote repo creator
# END argument parsser
-
+
return argument_passer
-
+
+
def needs_module_or_skip(module):
"""Decorator to be used for test cases only.
Print a warning if the given module could not be imported, and skip the test.
@@ -207,25 +216,28 @@ def needs_module_or_skip(module):
msg = "Module %r is required to run this test - skipping" % module
warnings.warn(msg)
raise SkipTest(msg)
- #END check import
+ # END check import
return func(self, *args, **kwargs)
- #END wrapper
+ # END wrapper
wrapper.__name__ = func.__name__
return wrapper
- #END argpasser
+ # END argpasser
return argpasser
-
+
#} END decorators
#{ Meta Classes
+
+
class GlobalsItemDeletorMetaCls(type):
+
"""Utiltiy to prevent the RepoBase to be picked up by nose as the metacls
will delete the instance from the globals"""
#{ Configuration
# Set this to a string name of the module to delete
ModuleToDelete = None
#} END configuration
-
+
def __new__(metacls, name, bases, clsdict):
assert metacls.ModuleToDelete is not None, "Invalid metaclass configuration"
new_type = super(GlobalsItemDeletorMetaCls, metacls).__new__(metacls, name, bases, clsdict)
@@ -235,22 +247,23 @@ class GlobalsItemDeletorMetaCls(type):
delattr(mod, metacls.ModuleToDelete)
except AttributeError:
pass
- #END skip case that people import our base without actually using it
- #END handle deletion
+ # END skip case that people import our base without actually using it
+ # END handle deletion
return new_type
-
-
+
+
class InheritedTestMethodsOverrideWrapperMetaClsAutoMixin(object):
+
"""Automatically picks up the actual metaclass of the the type to be created,
that is the one inherited by one of the bases, and patch up its __new__ to use
the InheritedTestMethodsOverrideWrapperInstanceDecorator with our configured decorator"""
-
+
#{ Configuration
# decorator function to use when wrapping the inherited methods. Put it into a list as first member
# to hide it from being created as class method
decorator = []
#}END configuration
-
+
@classmethod
def _find_metacls(metacls, bases):
"""emulate pythons lookup"""
@@ -259,9 +272,9 @@ class InheritedTestMethodsOverrideWrapperMetaClsAutoMixin(object):
if hasattr(base, mcls_attr):
return getattr(base, mcls_attr)
return metacls._find_metacls(base.__bases__)
- #END for each base
+ # END for each base
raise AssertionError("base class had not metaclass attached")
-
+
@classmethod
def _patch_methods_recursive(metacls, bases, clsdict):
"""depth-first patching of methods"""
@@ -270,33 +283,35 @@ class InheritedTestMethodsOverrideWrapperMetaClsAutoMixin(object):
for name, item in base.__dict__.iteritems():
if not name.startswith('test_'):
continue
- #END skip non-tests
+ # END skip non-tests
clsdict[name] = metacls.decorator[0](item)
- #END for each item
- #END for each base
-
+ # END for each item
+ # END for each base
+
def __new__(metacls, name, bases, clsdict):
assert metacls.decorator, "'decorator' member needs to be set in subclass"
base_metacls = metacls._find_metacls(bases)
metacls._patch_methods_recursive(bases, clsdict)
return base_metacls.__new__(base_metacls, name, bases, clsdict)
-
+
#} END meta classes
-
+
+
class TestBase(TestCase):
+
"""
Base Class providing default functionality to all tests such as:
- Utility functions provided by the TestCase base of the unittest method such as::
self.fail("todo")
self.failUnlessRaises(...)
"""
-
+
@classmethod
def setUp(cls):
"""This method is only called to provide the most basic functionality
Subclasses may just override it or implement it differently"""
cls.rorepo = Repo(rorepo_dir())
-
+
def _make_file(self, rela_path, data, repo=None):
"""
Create a file at the given path relative to our repository, filled