diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | test/git/test_base.py | 26 | ||||
-rw-r--r-- | test/testlib/__init__.py | 1 | ||||
-rw-r--r-- | test/testlib/helper.py | 98 |
4 files changed, 122 insertions, 9 deletions
@@ -138,6 +138,12 @@ Remote * Repo.remotes lists all remotes * Repo.remote returns a remote of the specified name if it exists +Test Framework +-------------- +* Added support for common TestCase base class that provides additional functionality + to receive repositories tests can also write to. This way, more aspects can be + tested under real-world ( un-mocked ) conditions. + Tree ---- * former 'name' member renamed to path as it suits the actual data better diff --git a/test/git/test_base.py b/test/git/test_base.py index b93e61c1..9f4cfa05 100644 --- a/test/git/test_base.py +++ b/test/git/test_base.py @@ -14,16 +14,13 @@ from itertools import chain from git.objects.utils import get_object_type_by_name import tempfile -class TestBase(object): +class TestBase(TestBase): type_tuples = ( ("blob", "8741fc1d09d61f02ffd8cded15ff603eff1ec070"), ("tree", "3a6a5e3eeed3723c09f1ef0399f81ed6b8d82e79"), ("commit", "4251bd59fb8e11e40c40548cba38180a9536118c"), ("tag", "e56a60e8e9cd333cfba0140a77cd12b0d9398f10") ) - def setup(self): - self.repo = Repo(GIT_REPO) - def test_base_object(self): # test interface of base object classes types = (Blob, Tree, Commit, TagObject) @@ -33,7 +30,7 @@ class TestBase(object): num_objs = 0 num_index_objs = 0 for obj_type, (typename, hexsha) in zip(types, self.type_tuples): - item = obj_type(self.repo,hexsha) + item = obj_type(self.rorepo,hexsha) num_objs += 1 assert item.id == hexsha assert item.type == typename @@ -74,7 +71,7 @@ class TestBase(object): # tag refs can point to tag objects or to commits s = set() ref_count = 0 - for ref in chain(self.repo.tags, self.repo.heads): + for ref in chain(self.rorepo.tags, self.rorepo.heads): ref_count += 1 assert isinstance(ref, refs.Reference) assert str(ref) == ref.name @@ -88,7 +85,7 @@ class TestBase(object): def test_heads(self): # see how it dynmically updates its object - for head in self.repo.heads: + for head in self.rorepo.heads: head.name head.path prev_object = head.object @@ -106,4 +103,17 @@ class TestBase(object): def test_object_resolution(self): # objects must be resolved to shas so they compare equal - assert self.repo.head.object == self.repo.active_branch.object + assert self.rorepo.head.object == self.rorepo.active_branch.object + + @with_bare_rw_repo + def test_with_bare_rw_repo(self, bare_rw_repo): + assert bare_rw_repo.config_reader("repository").getboolean("core", "bare") + + @with_rw_repo + def test_with_rw_repo(self, rw_repo): + assert not rw_repo.config_reader("repository").getboolean("core", "bare") + + @with_rw_and_rw_remote_repo + def test_with_rw_remote_and_rw_repo(self, rw_repo, rw_remote_repo): + assert not rw_repo.config_reader("repository").getboolean("core", "bare") + assert rw_remote_repo.config_reader("repository").getboolean("core", "bare") diff --git a/test/testlib/__init__.py b/test/testlib/__init__.py index f364171b..2133eb8c 100644 --- a/test/testlib/__init__.py +++ b/test/testlib/__init__.py @@ -8,7 +8,6 @@ import inspect from mock import * from asserts import * from helper import * -from unittest import TestCase __all__ = [ name for name, obj in locals().items() if not (name.startswith('_') or inspect.ismodule(obj)) ] diff --git a/test/testlib/helper.py b/test/testlib/helper.py index b34c9303..bc314cb2 100644 --- a/test/testlib/helper.py +++ b/test/testlib/helper.py @@ -5,6 +5,8 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import os +from git import Repo +from unittest import TestCase GIT_REPO = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) @@ -58,3 +60,99 @@ class ListProcessAdapter(object): return 0 poll = wait + + +def with_bare_rw_repo(func): + """ + Decorator providing a specially made read-write repository to the test case + decorated with it. The test case requires the following signature:: + def case(self, rw_repo) + + The rwrepo will be a bare clone or the types rorepo. Once the method finishes, + it will be removed completely. + + Use this if you want to make purely index based adjustments, change refs, create + heads, generally operations that do not need a working tree. + """ + def bare_repo_creator(self): + rw_repo = None + try: + return func(self, rw_repo) + finally: + pass + # END cleanup + # END bare repo creator + bare_repo_creator.__name__ = func.__name__ + return bare_repo_creator + +def with_rw_repo(func, working_tree_ref='0.1.6'): + """ + 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. + """ + def repo_creator(self): + rw_repo = None + try: + return func(self, rw_repo) + finally: + pass + # END cleanup + # END bare repo creator + repo_creator.__name__ = func.__name__ + return repo_creator + +def with_rw_and_rw_remote_repo(func): + """ + Same as with_rw_repo, but also provides a writable remote repository from which the + rw_repo has been forked. 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. + + 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. + """ + def remote_repo_creator(self): + rw_repo = None + rw_remote_repo = None + try: + return func(self, rw_repo, rw_remote_repo) + finally: + pass + # END cleanup + # END bare repo creator + remote_repo_creator.__name__ = func.__name__ + return remote_repo_creator + + +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(...) + + - Class level repository which is considered read-only as it is shared among + all test cases in your type. + Access it using:: + self.rorepo # 'ro' stands for read-only + + The rorepo is in fact your current project's git repo. If you refer to specific + shas for your objects, be sure you choose some that are part of the immutable portion + of the project history ( to assure tests don't fail for others ). + """ + + @classmethod + def setUpAll(cls): + """ + Dynamically add a read-only repository to our actual type. This way + each test type has its own repository + """ + cls.rorepo = Repo(GIT_REPO) + |