Hi.
We've mentioned several times already that it'd be good to extend the
test suite with tests that get created to reproduce a certain bug, in
order to ensure those bugs are not reintroduced again.
I know Lele wanted these tests to be in python, not in shell, which is
reasonable. As I mentioned to him a couple times, the best way seems to
express those repositories on which tailor fails as a series of
changesets (as opposed to, say, a series of svn commands).
So what I've always had in mind was something like:
TEST_WHATEVER_CHANGESETS = [
Changeset(rev(), date(), author(), log(), entries=[
ChangesetEntry('dir/file1'),
ChangesetEntry('dir/file2'),
],
Changeset(),
...
]
TEST_WHATEVER_VCS = [ 'bzr', 'hg' ]
Today I finally found some time and energy to spend on this, and I'd
like to hear comments about this approach. Basically, it breaks down to:
- it is oriented to test target backends acting on certain source repo
- such source repos are specified as a series of Changesets, with a
couple wrappers to make their creation more straightforward
- a mock source backend is introduced, that instead of being driven by
an underlying VCS, it's driven by that series of Changesets, propagating
the proper actions to the filesystem
See the attached diff for details, or the following bzr branch:
http://chistera.yi.org/~adeodato/code/tailor/tmp/tailor.mock/
Cheers,
--
Adeodato Simó dato at net.com.org.es
Debian Developer adeodato at debian.org
Listening to: The Rolling Stones - Mixed Emotions
=== added file 'vcpx/repository/mock.py'
--- vcpx/repository/mock.py 1970-01-01 00:00:00 +0000
+++ vcpx/repository/mock.py 2006-07-16 05:12:22 +0000
@@ -0,0 +1,121 @@
+# -*- mode: python; coding: utf-8 -*-
+# :Progetto: vcpx -- mock source backend
+# :Creato: Sun Jul 16 02:50:04 CEST 2006
+# :Autore: Adeodato Simó <[EMAIL PROTECTED]>
+# :Licenza: GNU General Public License
+#
+
+"""
+This module implements a mock source backend to be used in tests.
+"""
+
+__docformat__ = 'reStructuredText'
+
+import os
+from shutil import rmtree
+from datetime import datetime, timedelta
+
+from vcpx.tzinfo import UTC
+from vcpx.repository import Repository
+from vcpx.source import UpdatableSourceWorkingDir
+from vcpx.changes import (Changeset as OldChangeset,
+ ChangesetEntry as OldChangesetEntry)
+
+###
+
+class MockRepository(Repository):
+ def create(self):
+ if not os.path.isdir(self.basedir):
+ os.makedirs(self.basedir)
+
+###
+
+class MockWorkingDir(UpdatableSourceWorkingDir):
+ def __init__(self, *args, **kwargs):
+ super(MockWorkingDir, self).__init__(*args, **kwargs)
+ self.rev_offset = 0
+
+ def _getUpstreamChangesets(self, sincerev):
+ return self.CHANGESETS[sincerev-self.rev_offset:]
+
+ def _applyChangeset(self, changeset):
+ for e in changeset.entries:
+ e.apply(self.repository.basedir)
+
+ return []
+
+ def _checkoutUpstreamRevision(self, revision):
+ if revision == 'INITIAL':
+ cset = self.CHANGESETS[0]
+ self.rev_offset = cset.revision - 1
+ self._applyChangeset(cset)
+ return cset
+ else:
+ raise "Don't know what to do!"
+
+###
+
+class Changeset(OldChangeset):
+ def __init__(self, log, entries):
+ super(Changeset, self).__init__(Changeset.Rev.next(),
Changeset.Date.next(), None, log, entries)
+
+ def Rev():
+ initial = 0
+
+ while True:
+ initial += 1
+ yield initial
+
+ def Date():
+ initial = datetime.now(UTC)
+ step = timedelta(seconds=1)
+
+ while True:
+ initial += step
+ yield initial
+
+ Rev = Rev()
+ Date = Date()
+
+###
+
+class ChangesetEntry(OldChangesetEntry):
+ def __init__(self, action, name, old_name=None, new_contents=None):
+ super(ChangesetEntry, self).__init__(name)
+
+ self.isdir = False
+ self.old_name = old_name
+ self.action_kind = action
+ self.new_contents = new_contents
+
+ if self.name[-1] == '/':
+ self.isdir = True
+ self.name = self.name[:-1]
+
+ def apply(self, where):
+ name = os.path.join(where, self.name)
+ if self.action_kind == self.ADDED:
+ if self.isdir:
+ os.makedirs(name)
+ else:
+ dirname = os.path.dirname(name)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+ f = file(name, 'w')
+ f.close()
+ elif self.action_kind == self.DELETED:
+ if os.path.exists(name):
+ if self.isdir:
+ rmtree(name)
+ else:
+ os.unlink(name)
+ elif self.action_kind == self.RENAMED:
+ old_name = os.path.join(where, self.old_name)
+ if os.path.exists(oldname):
+ os.rename(old_name, name)
+ elif self.action_kind == self.UPDATED:
+ f = file(name, 'w')
+ f.write(self.new_contents)
+ f.close()
+ else:
+ raise "Unkown ChangesetEntry.action_kind: %s" %
(str(self.action_kind),)
=== added file 'vcpx/tests/fixed_bugs.py'
--- vcpx/tests/fixed_bugs.py 1970-01-01 00:00:00 +0000
+++ vcpx/tests/fixed_bugs.py 2006-07-16 05:08:17 +0000
@@ -0,0 +1,93 @@
+from os.path import exists, join
+from unittest import TestCase
+from cStringIO import StringIO
+
+from vcpx.config import Config
+from vcpx.tailor import Tailorizer
+from vcpx.repository.mock import Changeset, ChangesetEntry as Entry
+
+class FixedBugs(TestCase):
+ """Ensure already fixed bugs don't get reintroduced"""
+
+ TESTDIR = '/tmp/tailor-tests/fixed-bugs'
+
+ ALL_TARGET_VCS = [ 'arx', 'bzr', 'cdv', 'cg', 'cvs', 'cvsps', 'darcs',
'git', 'hg', 'monotone', 'svn' ]
+
+ CONFIG = """\
+[%(test_name)s]
+# verbose = Yes
+source = mock:source
+target = %(vcs)s:target
+root-directory = %(test_dir)s
+state-file = state
+
+[mock:source]
+%(subdir)s.source
+
+[%(vcs)s:target]
+%(subdir)s
+module = /
+repository = file://%(test_dir)s/repo
+"""
+
+ def setUp(self):
+ from os import makedirs
+ from shutil import rmtree
+ from atexit import register
+
+ self.test_name = self.id().split('.')[-1]
+ self.test_dir = join(self.TESTDIR, self.test_name)
+
+ if exists(self.test_dir):
+ rmtree(self.test_dir)
+ makedirs(self.test_dir)
+ # XXX register(rmtree, self.test_dir)
+
+ # defaults
+ self.TARGET_VCS = []
+ self.CHANGESETS = []
+ self.SHARED_BASEDIRS = False
+
+ def run_tailor(self):
+ test_name = self.test_name
+
+ for vcs in self.TARGET_VCS:
+ subdir = self.SHARED_BASEDIRS and '#' or 'subdir = %s' % vcs
+ test_dir = join(self.test_dir, vcs)
+ config = Config(StringIO(self.CONFIG % vars()), {})
+ project = Tailorizer(test_name, config)
+ project.workingDir().source.CHANGESETS = self.CHANGESETS
+ project()
+
+ ###
+
+ def testTicket64(self):
+ """#64: support add('foo/bar/baz') even if 'foo' was not previously
added"""
+ self.TARGET_VCS = [ 'bzr', 'darcs', 'hg' ]
+ self.CHANGESETS = [
+ Changeset("Dummy first commit",
+ [ Entry(Entry.ADDED, 'dummy.txt'), ]),
+ Changeset("Add a/b/c",
+ [ Entry(Entry.ADDED, 'a/b/'),
+ Entry(Entry.ADDED, 'a/b/c'),
+ ]),
+ ]
+ self.run_tailor()
+
+ def testTicket64_2(self):
+ """#64 (2): support update('foo2/bar') even if 'foo2' is added in the
same changeset"""
+ self.TARGET_VCS = [ 'bzr', 'darcs', 'hg' ] # XXX bzr 0.8 fails :-?
+ self.CHANGESETS = [
+ Changeset("Dummy first commit",
+ [ Entry(Entry.ADDED, 'dummy.txt'), ]),
+ Changeset("Add a/b/c",
+ [ Entry(Entry.ADDED, 'a/b/c'),
+ ]),
+ Changeset("Add (cp) a2 and modify a2/b/c",
+ [ Entry(Entry.ADDED, 'a2/b/c'),
+ Entry(Entry.UPDATED, 'a2/b/c', new_contents='foo')
+ ]),
+ ]
+ self.run_tailor()
+
+ ###
=== modified file 'vcpx/tests/__init__.py'
--- vcpx/tests/__init__.py 2006-07-13 12:05:48 +0000
+++ vcpx/tests/__init__.py 2006-07-16 01:20:25 +0000
@@ -16,6 +16,7 @@
from config import *
from statefile import *
from tailor import *
+from fixed_bugs import *
class TailorTest(TestProgram):
"""A command-line program that runs a set of tests; this is primarily
_______________________________________________
Tailor mailing list
[email protected]
http://lists.zooko.com/mailman/listinfo/tailor