From bb0ac304431e8aed686a8a817aaccd74b1ba4f24 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 15:04:29 +0200 Subject: Added frame for new Repo handling and some neat decorators, including tests that test whether the testing framework does what it should --- test/testlib/helper.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'test/testlib/helper.py') 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 ------> rw_remote_repo ------> 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) + -- cgit v1.2.3 From 9c3bbc43d097656b54c808290ce0c656d127ce47 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 15:36:41 +0200 Subject: Implemented decorators, tests pass at least --- test/testlib/helper.py | 70 +++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 26 deletions(-) (limited to 'test/testlib/helper.py') diff --git a/test/testlib/helper.py b/test/testlib/helper.py index bc314cb2..c4fccbf6 100644 --- a/test/testlib/helper.py +++ b/test/testlib/helper.py @@ -7,6 +7,8 @@ import os from git import Repo from unittest import TestCase +import tempfile +import shutil GIT_REPO = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) @@ -75,35 +77,41 @@ def with_bare_rw_repo(func): heads, generally operations that do not need a working tree. """ def bare_repo_creator(self): - rw_repo = None + repo_dir = tempfile.mktemp("bare_repo") + rw_repo = self.rorepo.clone(repo_dir, shared=True, bare=True) try: return func(self, rw_repo) finally: - pass + shutil.rmtree(repo_dir) # 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'): +def with_rw_repo(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): + def argument_passer(func): + def repo_creator(self): + repo_dir = tempfile.mktemp("non_bare_repo") + rw_repo = self.rorepo.clone(repo_dir, shared=True, bare=False, n=True) + rw_repo.git.checkout(working_tree_ref) + try: + return func(self, rw_repo) + finally: + shutil.rmtree(repo_dir) + # END cleanup + # END rw repo creator + repo_creator.__name__ = func.__name__ + return repo_creator + # END argument passer + return argument_passer + +def with_rw_and_rw_remote_repo(working_tree_ref='0.1.6'): """ 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 @@ -117,17 +125,27 @@ def with_rw_and_rw_remote_repo(func): 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 + def argument_passer(func): + def remote_repo_creator(self): + remote_repo_dir = tempfile.mktemp("remote_repo") + repo_dir = tempfile.mktemp("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 ? + rw_repo.git.checkout(working_tree_ref) + try: + return func(self, rw_repo, rw_remote_repo) + finally: + shutil.rmtree(repo_dir) + shutil.rmtree(remote_repo_dir) + # END cleanup + # END bare repo creator + remote_repo_creator.__name__ = func.__name__ + return remote_repo_creator + # END remote repo creator + # END argument parsser + + return argument_passer class TestBase(TestCase): -- cgit v1.2.3 From 3c770f8e98a0079497d3eb5bc31e7260cc70cc63 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 22 Oct 2009 16:06:10 +0200 Subject: Fixed decorator issue that would cause a function to be passed even though there is a default argument. This feels inconsistent as the 'argument passer' wrapper function can be called with a function or a string as first argument depending on whether the client code was explicitly passing an argument or not. That ... sucks. Now test for that case specifically and fail with a proper assertion error. I don't like it, but what can I do ... . Remote tests adjusted to use rw repositories instead. More tests to follow, and many api methods are to be implemented now these things can be tested properly. --- test/testlib/helper.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'test/testlib/helper.py') diff --git a/test/testlib/helper.py b/test/testlib/helper.py index c4fccbf6..eef7876f 100644 --- a/test/testlib/helper.py +++ b/test/testlib/helper.py @@ -88,13 +88,14 @@ def with_bare_rw_repo(func): bare_repo_creator.__name__ = func.__name__ return bare_repo_creator -def with_rw_repo(working_tree_ref='0.1.6'): +def with_rw_repo(working_tree_ref): """ 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. """ + assert isinstance(working_tree_ref, basestring), "Decorator requires ref name for working tree checkout" def argument_passer(func): def repo_creator(self): repo_dir = tempfile.mktemp("non_bare_repo") @@ -111,7 +112,7 @@ def with_rw_repo(working_tree_ref='0.1.6'): # END argument passer return argument_passer -def with_rw_and_rw_remote_repo(working_tree_ref='0.1.6'): +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 rw_repo has been forked. The remote repository was cloned as bare repository from @@ -125,6 +126,7 @@ def with_rw_and_rw_remote_repo(working_tree_ref='0.1.6'): This setup allows you to test push and pull scenarios and hooks nicely. """ + 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 = tempfile.mktemp("remote_repo") -- cgit v1.2.3