Rev 1930: Merge new subvertpy. in file:///data/jelmer/bzr-svn/trunk/

Jelmer Vernooij jelmer at samba.org
Mon Oct 6 16:00:32 BST 2008


At file:///data/jelmer/bzr-svn/trunk/

------------------------------------------------------------
revno: 1930
revision-id: jelmer at samba.org-20081006150030-l1ux7ua7diu4ia4d
parent: jelmer at samba.org-20081002211828-68ls9ejnr9h2xmyy
parent: jelmer at samba.org-20081006145521-053crzkfoxq8vbkj
committer: Jelmer Vernooij <jelmer at samba.org>
branch nick: trunk
timestamp: Mon 2008-10-06 17:00:30 +0200
message:
  Merge new subvertpy.
added:
  subvertpy/TODO                 todo-20081005190627-4brj2368nxwmb0bp-1
  subvertpy/subvertpy/marshall.py marshall.py-20081006140850-jzf2dbt55rwefdqm-1
  subvertpy/subvertpy/server.py  server.py-20081006143330-f9p0l7eih6izgoct-1
  subvertpy/subvertpy/tests/test_marshall.py test_marshall.py-20081006140936-tuh5e2o2httgadkd-1
modified:
  subvertpy/AUTHORS              authors-20081002184818-kozkud4dvhmbez8a-1
  subvertpy/Makefile             makefile-20081002202532-xkdtg6p584inuoya-1
  subvertpy/subvertpy/__init__.py core.pyx-20080313210413-17k59slolpfe5kdq-1
  subvertpy/subvertpy/properties.py util.py-20080502170127-o220e9py99vt69s6-1
  subvertpy/subvertpy/tests/__init__.py __init__.py-20081002194117-jwf9payoee8eqz7g-1
  subvertpy/subvertpy/tests/test_client.py test_client.py-20080603021344-lmgexez0yoyp9b1s-1
  subvertpy/subvertpy/tests/test_ra.py test_ra.py-20080313141743-uzsm7ejitrlqone5-1
    ------------------------------------------------------------
    revno: 1925.1.13
    revision-id: jelmer at samba.org-20081006145521-053crzkfoxq8vbkj
    parent: jelmer at samba.org-20081006145319-vhmi2ifk7wc8vyyo
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: trunk
    timestamp: Mon 2008-10-06 16:55:21 +0200
    message:
      Fix import.
    modified:
      subvertpy/properties.py        util.py-20080502170127-o220e9py99vt69s6-1
    ------------------------------------------------------------
    revno: 1925.1.12
    revision-id: jelmer at samba.org-20081006145319-vhmi2ifk7wc8vyyo
    parent: jelmer at samba.org-20081006145212-3x3na427vstryaat
    parent: jelmer at samba.org-20081005190804-m8cww5j1jh71ub1x
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: trunk
    timestamp: Mon 2008-10-06 16:53:19 +0200
    message:
      merge trunk.
    added:
      TODO                           todo-20081005190627-4brj2368nxwmb0bp-1
    modified:
      AUTHORS                        authors-20081002184818-kozkud4dvhmbez8a-1
      subvertpy/tests/__init__.py    __init__.py-20081002194117-jwf9payoee8eqz7g-1
      subvertpy/tests/test_ra.py     test_ra.py-20080313141743-uzsm7ejitrlqone5-1
        ------------------------------------------------------------
        revno: 1925.3.4
        revision-id: jelmer at samba.org-20081005190804-m8cww5j1jh71ub1x
        parent: jelmer at samba.org-20081005190632-5ddvwfrd2juxvkh5
        parent: jelmer at samba.org-20081003153217-6jhl30rncdbki1h2
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: trunk
        timestamp: Sun 2008-10-05 21:08:04 +0200
        message:
          Merge warning changes.
        modified:
          subvertpy/__init__.py          core.pyx-20080313210413-17k59slolpfe5kdq-1
        ------------------------------------------------------------
        revno: 1925.3.3
        revision-id: jelmer at samba.org-20081005190632-5ddvwfrd2juxvkh5
        parent: jelmer at samba.org-20081005190257-isn3kpdmjj22ifee
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: trunk
        timestamp: Sun 2008-10-05 21:06:32 +0200
        message:
          Add TODO file.
        added:
          TODO                           todo-20081005190627-4brj2368nxwmb0bp-1
        ------------------------------------------------------------
        revno: 1925.3.2
        revision-id: jelmer at samba.org-20081005190257-isn3kpdmjj22ifee
        parent: jelmer at samba.org-20081005190032-h7bohnbgfymrnsc1
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: trunk
        timestamp: Sun 2008-10-05 21:02:57 +0200
        message:
          Add Alexey.
        modified:
          AUTHORS                        authors-20081002184818-kozkud4dvhmbez8a-1
        ------------------------------------------------------------
        revno: 1925.3.1
        revision-id: jelmer at samba.org-20081005190032-h7bohnbgfymrnsc1
        parent: jelmer at samba.org-20081002211756-l2qo3remar7o21sz
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: trunk
        timestamp: Sun 2008-10-05 21:00:32 +0200
        message:
          Add test for ra.get_file()
        modified:
          subvertpy/tests/__init__.py    __init__.py-20081002194117-jwf9payoee8eqz7g-1
          subvertpy/tests/test_client.py test_client.py-20080603021344-lmgexez0yoyp9b1s-1
          subvertpy/tests/test_ra.py     test_ra.py-20080313141743-uzsm7ejitrlqone5-1
    ------------------------------------------------------------
    revno: 1925.1.11
    revision-id: jelmer at samba.org-20081006145212-3x3na427vstryaat
    parent: jelmer at samba.org-20081003153217-6jhl30rncdbki1h2
    parent: jelmer at samba.org-20081006145153-bw7mize9cjrp4vny
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: trunk
    timestamp: Mon 2008-10-06 16:52:12 +0200
    message:
      Merge initial work on server support.
    added:
      subvertpy/marshall.py          marshall.py-20081006140850-jzf2dbt55rwefdqm-1
      subvertpy/server.py            server.py-20081006143330-f9p0l7eih6izgoct-1
      subvertpy/tests/test_marshall.py test_marshall.py-20081006140936-tuh5e2o2httgadkd-1
    modified:
      Makefile                       makefile-20081002202532-xkdtg6p584inuoya-1
      subvertpy/properties.py        util.py-20080502170127-o220e9py99vt69s6-1
      subvertpy/tests/test_client.py test_client.py-20080603021344-lmgexez0yoyp9b1s-1
      subvertpy/tests/test_ra.py     test_ra.py-20080313141743-uzsm7ejitrlqone5-1
        ------------------------------------------------------------
        revno: 1925.2.5
        revision-id: jelmer at samba.org-20081006145153-bw7mize9cjrp4vny
        parent: jelmer at samba.org-20081006143538-1ebbepd0el66bn6u
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: native
        timestamp: Mon 2008-10-06 16:51:53 +0200
        message:
          Make server abstract.
        modified:
          subvertpy/properties.py        util.py-20080502170127-o220e9py99vt69s6-1
          subvertpy/server.py            server.py-20081006143330-f9p0l7eih6izgoct-1
          subvertpy/tests/test_client.py test_client.py-20080603021344-lmgexez0yoyp9b1s-1
          subvertpy/tests/test_ra.py     test_ra.py-20080313141743-uzsm7ejitrlqone5-1
        ------------------------------------------------------------
        revno: 1925.2.4
        revision-id: jelmer at samba.org-20081006143538-1ebbepd0el66bn6u
        parent: jelmer at samba.org-20081006142732-15824ke4snnijbxw
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: native
        timestamp: Mon 2008-10-06 16:35:38 +0200
        message:
          Import initial work on server support.
        added:
          subvertpy/server.py            server.py-20081006143330-f9p0l7eih6izgoct-1
        ------------------------------------------------------------
        revno: 1925.2.3
        revision-id: jelmer at samba.org-20081006142732-15824ke4snnijbxw
        parent: jelmer at samba.org-20081006142530-kkrskonz776yxmn7
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: native
        timestamp: Mon 2008-10-06 16:27:32 +0200
        message:
          Add build-inplace.
        modified:
          Makefile                       makefile-20081002202532-xkdtg6p584inuoya-1
        ------------------------------------------------------------
        revno: 1925.2.2
        revision-id: jelmer at samba.org-20081006142530-kkrskonz776yxmn7
        parent: jelmer at samba.org-20081006141031-1mlz9enp8w6nd6xa
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: native
        timestamp: Mon 2008-10-06 16:25:30 +0200
        message:
          Fix marshall tests.
        modified:
          subvertpy/tests/test_marshall.py test_marshall.py-20081006140936-tuh5e2o2httgadkd-1
        ------------------------------------------------------------
        revno: 1925.2.1
        revision-id: jelmer at samba.org-20081006141031-1mlz9enp8w6nd6xa
        parent: jelmer at samba.org-20081003153217-6jhl30rncdbki1h2
        committer: Jelmer Vernooij <jelmer at samba.org>
        branch nick: native
        timestamp: Mon 2008-10-06 16:10:31 +0200
        message:
          Import marshall code.
        added:
          subvertpy/marshall.py          marshall.py-20081006140850-jzf2dbt55rwefdqm-1
          subvertpy/tests/test_marshall.py test_marshall.py-20081006140936-tuh5e2o2httgadkd-1
    ------------------------------------------------------------
    revno: 1925.1.10
    revision-id: jelmer at samba.org-20081003153217-6jhl30rncdbki1h2
    parent: jelmer at samba.org-20081002211756-l2qo3remar7o21sz
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: trunk
    timestamp: Fri 2008-10-03 17:32:17 +0200
    message:
      Warn about subvertpy rather than bzr-svn.
    modified:
      subvertpy/__init__.py          core.pyx-20080313210413-17k59slolpfe5kdq-1
=== modified file 'subvertpy/AUTHORS'
--- a/subvertpy/AUTHORS	2008-10-02 19:20:17 +0000
+++ b/subvertpy/AUTHORS	2008-10-05 19:02:57 +0000
@@ -2,6 +2,7 @@
 Jelmer Vernooij <jelmer at samba.org>
 
 Windows support:
+Alexey Borzenkov
 Mark Hammond
 Adrian Wilkins
 

=== modified file 'subvertpy/Makefile'
--- a/subvertpy/Makefile	2008-10-02 20:25:33 +0000
+++ b/subvertpy/Makefile	2008-10-06 14:27:32 +0000
@@ -2,11 +2,14 @@
 SETUP = $(PYTHON) setup.py
 TRIAL = trial
 
-all: build
+all: build build-inplace
 
 build::
 	$(SETUP) build
 
+build-inplace::
+	$(SETUP) build_ext --inplace
+
 install::
 	$(SETUP) install
 

=== added file 'subvertpy/TODO'
--- a/subvertpy/TODO	1970-01-01 00:00:00 +0000
+++ b/subvertpy/TODO	2008-10-05 19:06:32 +0000
@@ -0,0 +1,2 @@
+- Remove last dependencies on bzrlib in the testsuite
+- Add more docstrings

=== modified file 'subvertpy/subvertpy/__init__.py'
--- a/subvertpy/subvertpy/__init__.py	2008-10-02 20:45:22 +0000
+++ b/subvertpy/subvertpy/__init__.py	2008-10-03 15:32:17 +0000
@@ -76,8 +76,8 @@
     import client, ra, repos, wc
     for x in client, ra, repos, wc:
         if not _check_mtime(x):
-            warn("bzr-svn extensions are outdated and need to be rebuilt")
+            warn("subvertpy extensions are outdated and need to be rebuilt")
             break
 except ImportError:
-    raise ImportError("Unable to load bzr-svn extensions - did you build it?")
+    raise ImportError("Unable to load subvertpy extensions - did you build it?")
 

=== added file 'subvertpy/subvertpy/marshall.py'
--- a/subvertpy/subvertpy/marshall.py	1970-01-01 00:00:00 +0000
+++ b/subvertpy/subvertpy/marshall.py	2008-10-06 14:10:31 +0000
@@ -0,0 +1,107 @@
+# Copyright (C) 2006-2007 Jelmer Vernooij <jelmer at samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+class literal:
+    def __init__(self, txt):
+        self.txt = txt
+
+    def __str__(self):
+        return self.txt
+
+    def __repr__(self):
+        return self.txt
+
+# 1. Syntactic structure
+# ----------------------
+# 
+# The Subversion protocol is specified in terms of the following
+# syntactic elements, specified using ABNF [RFC 2234]:
+# 
+#   item   = word / number / string / list
+#   word   = ALPHA *(ALPHA / DIGIT / "-") space
+#   number = 1*DIGIT space
+#   string = 1*DIGIT ":" *OCTET space
+#          ; digits give the byte count of the *OCTET portion
+#   list   = "(" space *item ")" space
+#   space  = 1*(SP / LF)
+# 
+
+class MarshallError(Exception):
+    """A Marshall error."""
+
+
+def marshall(x):
+    if isinstance(x, int):
+        return "%d " % x
+    elif isinstance(x, list) or isinstance(x, tuple):
+        return "( " + "".join(map(marshall, x)) + ") "
+    elif isinstance(x, literal):
+        return "%s " % x
+    elif isinstance(x, basestring):
+        return "%d:%s " % (len(x), x)
+    raise MarshallError("Unable to marshall type %s" % x)
+
+
+def unmarshall(x):
+    whitespace = ['\n', ' ']
+    if len(x) == 0:
+        raise MarshallError("Not enough data")
+    if x[0] == "(" and x[1] == " ": # list follows
+        x = x[2:]
+        ret = []
+        try:
+            while x[0] != ")":
+                (x, n) = unmarshall(x)
+                ret.append(n)
+        except IndexError:
+            raise MarshallError("List not terminated")
+        
+        if not x[1] in whitespace:
+            raise MarshallError("Expected space, got %c" % x[1])
+
+        return (x[2:], ret)
+    elif x[0].isdigit():
+        num = ""
+        # Check if this is a string or a number
+        while x[0].isdigit():
+            num += x[0]
+            x = x[1:]
+        num = int(num)
+
+        if x[0] in whitespace:
+            return (x[1:], num)
+        elif x[0] == ":":
+            if len(x) < num:
+                raise MarshallError("Expected string of length %r" % num)
+            return (x[num+2:], x[1:num+1])
+        else:
+            raise MarshallError("Expected whitespace or ':', got '%c" % x[0])
+    elif x[0].isalpha():
+        ret = ""
+        # Parse literal
+        try:
+            while x[0].isalpha() or x[0].isdigit() or x[0] == '-':
+                ret += x[0]
+                x = x[1:]
+        except IndexError:
+            raise MarshallError("Expected literal")
+
+        if not x[0] in whitespace:
+            raise MarshallError("Expected whitespace, got %c" % x[0])
+
+        return (x[1:], ret)
+    else:
+        raise MarshallError("Unexpected character '%c'" % x[0])

=== modified file 'subvertpy/subvertpy/properties.py'
--- a/subvertpy/subvertpy/properties.py	2008-10-02 20:22:45 +0000
+++ b/subvertpy/subvertpy/properties.py	2008-10-06 14:55:21 +0000
@@ -13,8 +13,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import bisect
-from bzrlib import urlutils
+import bisect, urllib
 
 
 class InvalidExternalsDescription(Exception):
@@ -100,7 +99,7 @@
             raise NotImplementedError("Relative to the scheme externals not yet supported")
         if relurl.startswith("^/"):
             raise NotImplementedError("Relative to the repository root externals not yet supported")
-        ret[path] = (revno, "%s/%s" % (base_url, relurl))
+        ret[path] = (revno, urllib.basejoin(base_url, relurl))
     return ret
 
 

=== added file 'subvertpy/subvertpy/server.py'
--- a/subvertpy/subvertpy/server.py	1970-01-01 00:00:00 +0000
+++ b/subvertpy/subvertpy/server.py	2008-10-06 14:51:53 +0000
@@ -0,0 +1,221 @@
+# Copyright (C) 2006 Jelmer Vernooij <jelmer at samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import copy
+import os
+import time
+
+from subvertpy import SVN_NODE_NONE, SVN_NODE_FILE, SVN_NODE_DIR
+from subvertpy.marshall import marshall, unmarshall, literal, MarshallError
+
+
+class ServerBackend:
+
+    def open_repository(self, location):
+        raise NotImplementedError(self.open_repository)
+
+
+class ServerRepositoryBackend:
+    
+    def get_uuid(self):
+        raise NotImplementedError(self.get_uuid)
+
+    def get_latest_revnum(self):
+        raise NotImplementedError(self.get_latest_revnum)
+
+
+
+SVN_MAJOR_VERSION = 1
+SVN_MINOR_VERSION = 2
+
+class SVNServer:
+    def __init__(self, backend, recv_fn, send_fn, logf=None):
+        self.backend = backend
+        self.recv_fn = recv_fn
+        self.send_fn = send_fn
+        self.inbuffer = ""
+        self._stop = False
+        self._logf = logf
+
+    def send_greeting(self):
+        self.send_success(
+            SVN_MAJOR_VERSION, SVN_MINOR_VERSION, [literal("ANONYMOUS")], [])
+
+    def send_mechs(self):
+        self.send_success([literal("ANONYMOUS")], "")
+
+    def send_failure(self, *contents):
+        self.send_msg([literal("failure"), list(contents)])
+
+    def send_success(self, *contents):
+        self.send_msg([literal("success"), list(contents)])
+
+    def send_unknown(self, cmd):
+        self.send_failure([210001, "Unknown command '%s'" % cmd, __file__, \
+                          52])
+
+    def get_latest_rev(self):
+        self.send_success([], "")
+        self.send_success(self.repo_backend.get_latest_revnum())
+
+    def check_path(self, path, revnum):
+        return SVN_NODE_DIR
+
+    def log(self, target_path, start_rev, end_rev, changed_paths, 
+            strict_node, limit=None):
+        def send_revision(revno, rev):
+            self.send_msg([[], revno, [rev.committer], 
+              [time.strftime("%Y-%m-%dT%H:%M:%S.00000Z", time.gmtime(rev.timestamp))],
+                          [rev.message]])
+        self.send_success([], "")
+        revno = start_rev[0]
+        i = 0
+        self.branch.repository.lock_read()
+        try:
+            # FIXME: check whether start_rev and end_rev actually exist
+            while revno != end_rev[0]:
+                #TODO: Honor target_path, strict_node, changed_paths
+                if end_rev[0] > revno:
+                    revno+=1
+                else:
+                    revno-=1
+                if limit != 0 and i == limit:
+                    break
+                if revno != 0:
+                    send_revision(revno, self.branch.repository.get_revision(self.branch.get_rev_id(revno)))
+        finally:
+            self.branch.repository.unlock()
+
+        self.send_msg(literal("done"))
+        self.send_success()
+
+    def reparent(self, parent):
+        self.send_success([], "")
+        self.send_success()
+
+    def stat(self, path, revnum):
+        self.send_success([], "")
+        self.send_success()
+
+    def update(self, rev, target, recurse):
+        self.send_success([], "")
+        while True:
+            msg = self.recv_msg()
+            assert msg[0] in ["set-path", "finish-report"]
+            if msg[0] == "finish-report":
+                break
+
+        self.send_success([], "")
+        self.send_msg(["target-rev", rev])
+        tree = self.branch.repository.revision_tree(
+                self.branch.get_rev_id(rev[0]))
+        path2id = {}
+        id2path = {}
+        self.send_msg(["open-root", [rev, tree.inventory.root.file_id]])
+        def send_children(self, id):
+            for child in tree.inventory[id].children:
+                if tree.inventory[child].kind in ('symlink', 'file'):
+                    self.send_msg(["add-file", [tree.inventory.id2path(child),
+                                                id, child]])
+                    # FIXME
+                    self.send_msg(["close-file", [child]])
+                else:
+                    self.send_msg(["add-dir", [tree.inventory.id2path(child),
+                                                id, child]])
+                    send_children(child)
+                    self.send_msg(["close-dir", [child]])
+        #send_children(tree.inventory.root.file_id)
+        self.send_msg(["close-dir", [tree.inventory.root.file_id]])
+        self.send_msg(["close-edit", []])
+        #msg = self.recv_msg()
+        #self.send_msg(msg)
+
+
+    commands = {
+            "get-latest-rev": get_latest_rev,
+            "log": log,
+            "update": update,
+            "check-path": check_path,
+            "reparent": reparent,
+            "stat": stat,
+            # FIXME: get-dated-rev
+            # FIXME: rev-proplist
+            # FIXME: rev-prop
+            # FIXME: get-file
+            # FIXME: get-dir
+            # FIXME: check-path
+            # FIXME: switch
+            # FIXME: status
+            # FIXME: diff
+            # FIXME: get-locations
+            # FIXME: get-file-revs
+            # FIXME: replay
+    }
+
+    def send_auth_request(self):
+        pass
+
+    def serve(self):
+        self.send_greeting()
+        (version, capabilities, url) = self.recv_msg()
+        self.capabilities = capabilities
+        self.version = version
+        self.url = url
+        self.mutter("client supports:")
+        self.mutter("  version %r" % version)
+        self.mutter("  capabilities %r " % capabilities)
+        self.send_mechs()
+
+        (mech, args) = self.recv_msg()
+        # TODO: Proper authentication
+        self.send_success()
+
+        import urllib
+        (rooturl, location) = urllib.splithost(url)
+
+        self.repo_backend, self.relpath = self.backend.open_repository(location)
+        self.send_success(self.repo_backend.get_uuid(), url)
+
+        # Expect:
+        while not self._stop:
+            ( cmd, args ) = self.recv_msg()
+            if not self.commands.has_key(cmd):
+                self.send_unknown(cmd)
+                return
+            else:
+                self.commands[cmd](self, *args)
+
+    def close(self):
+        self._stop = True
+
+    def recv_msg(self):
+        # FIXME: Blocking read?
+        while True:
+            try:
+                self.inbuffer += self.recv_fn()
+                (self.inbuffer, ret) = unmarshall(self.inbuffer)
+                self.mutter('in: %r' % ret)
+                return ret
+            except MarshallError, e:
+                self.mutter('ERROR: %r' % e)
+
+    def send_msg(self, data):
+        self.mutter('out: %r' % data)
+        self.send_fn(marshall(data))
+
+    def mutter(self, text):
+        if self._logf is not None:
+            self._logf.write(text)

=== modified file 'subvertpy/subvertpy/tests/__init__.py'
--- a/subvertpy/subvertpy/tests/__init__.py	2008-10-02 19:44:15 +0000
+++ b/subvertpy/subvertpy/tests/__init__.py	2008-10-05 19:00:32 +0000
@@ -16,3 +16,266 @@
 
 """Tests for subvertpy."""
 
+from cStringIO import StringIO
+
+# FIXME: Remove dependency on bzrlib
+from bzrlib import osutils, urlutils
+from bzrlib.tests import TestCaseInTempDir
+
+import os, sys
+
+from subvertpy import delta, ra, repos, delta, client, properties
+from subvertpy.ra import Auth, RemoteAccess
+
+
+class TestFileEditor(object):
+    def __init__(self, file):
+        self.file = file
+        self.is_closed = False
+
+    def change_prop(self, name, value):
+        self.file.change_prop(name, value)
+
+    def modify(self, contents=None):
+        if contents is None:
+            contents = osutils.rand_chars(100)
+        txdelta = self.file.apply_textdelta()
+        delta.send_stream(StringIO(contents), txdelta)
+
+    def close(self):
+        assert not self.is_closed
+        self.is_closed = True
+        self.file.close()
+
+
+class TestDirEditor(object):
+    def __init__(self, dir, baseurl, revnum):
+        self.dir = dir
+        self.baseurl = baseurl
+        self.revnum = revnum
+        self.is_closed = False
+        self.children = []
+
+    def close_children(self):
+        for c in reversed(self.children):
+            if not c.is_closed:
+                c.close()
+
+    def close(self):
+        assert not self.is_closed
+        self.is_closed = True
+        self.close_children()
+        self.dir.close()
+
+    def change_prop(self, name, value):
+        self.dir.change_prop(name, value)
+
+    def open_dir(self, path):
+        self.close_children()
+        child = TestDirEditor(self.dir.open_directory(path, -1), self.baseurl, self.revnum)
+        self.children.append(child)
+        return child
+
+    def open_file(self, path):
+        self.close_children()
+        child = TestFileEditor(self.dir.open_file(path, -1))
+        self.children.append(child)
+        return child
+
+    def add_dir(self, path, copyfrom_path=None, copyfrom_rev=-1):
+        self.close_children()
+        if copyfrom_path is not None:
+            copyfrom_path = urlutils.join(self.baseurl, copyfrom_path)
+        if copyfrom_path is not None and copyfrom_rev == -1:
+            copyfrom_rev = self.revnum
+        assert (copyfrom_path is None and copyfrom_rev == -1) or \
+               (copyfrom_path is not None and copyfrom_rev > -1)
+        child = TestDirEditor(self.dir.add_directory(path, copyfrom_path, copyfrom_rev), self.baseurl, self.revnum)
+        self.children.append(child)
+        return child
+
+    def add_file(self, path, copyfrom_path=None, copyfrom_rev=-1):
+        self.close_children()
+        if copyfrom_path is not None:
+            copyfrom_path = urlutils.join(self.baseurl, copyfrom_path)
+        if copyfrom_path is not None and copyfrom_rev == -1:
+            copyfrom_rev = self.revnum
+        child = TestFileEditor(self.dir.add_file(path, copyfrom_path, copyfrom_rev))
+        self.children.append(child)
+        return child
+
+    def delete(self, path):
+        self.dir.delete_entry(path)
+
+
+class TestCommitEditor(TestDirEditor):
+    def __init__(self, editor, baseurl, revnum):
+        self.editor = editor
+        TestDirEditor.__init__(self, self.editor.open_root(), baseurl, revnum)
+
+    def close(self):
+        TestDirEditor.close(self)
+        self.editor.close()
+
+
+class SubversionTestCase(TestCaseInTempDir):
+    """A test case that provides the ability to build Subversion 
+    repositories."""
+
+    def setUp(self):
+        super(SubversionTestCase, self).setUp()
+        self.client_ctx = client.Client()
+        self.client_ctx.auth = Auth([ra.get_simple_provider(), 
+                                     ra.get_username_provider(),
+                                     ra.get_ssl_client_cert_file_provider(),
+                                     ra.get_ssl_client_cert_pw_file_provider(),
+                                     ra.get_ssl_server_trust_file_provider()])
+        self.client_ctx.log_msg_func = self.log_message_func
+        #self.client_ctx.notify_func = lambda err: mutter("Error: %s" % err)
+
+    def log_message_func(self, items):
+        return self.next_message
+
+    def make_repository(self, relpath, allow_revprop_changes=True):
+        """Create a repository.
+
+        :return: Handle to the repository.
+        """
+        abspath = os.path.join(self.test_dir, relpath)
+
+        repos.create(abspath)
+
+        if allow_revprop_changes:
+            if sys.platform == 'win32':
+                revprop_hook = os.path.join(abspath, "hooks", "pre-revprop-change.bat")
+                open(revprop_hook, 'w').write("exit 0\n")
+            else:
+                revprop_hook = os.path.join(abspath, "hooks", "pre-revprop-change")
+                open(revprop_hook, 'w').write("#!/bin/sh\n")
+                os.chmod(revprop_hook, os.stat(revprop_hook).st_mode | 0111)
+
+        return urlutils.local_path_to_url(abspath)
+
+
+    def make_checkout(self, repos_url, relpath):
+        self.client_ctx.checkout(repos_url, relpath, "HEAD") 
+
+    def client_set_prop(self, path, name, value):
+        if value is None:
+            value = ""
+        self.client_ctx.propset(name, value, path, False, True)
+
+    def client_get_prop(self, path, name, revnum=None, recursive=False):
+        if revnum is None:
+            rev = "WORKING"
+        else:
+            rev = revnum
+        ret = self.client_ctx.propget(name, path, rev, rev, recursive)
+        if recursive:
+            return ret
+        else:
+            return ret.values()[0]
+
+    def client_get_revprop(self, url, revnum, name):
+        r = ra.RemoteAccess(url)
+        return r.rev_proplist(revnum)[name]
+
+    def client_set_revprop(self, url, revnum, name, value):
+        r = ra.RemoteAccess(url)
+        r.change_rev_prop(revnum, name, value)
+        
+    def client_commit(self, dir, message=None, recursive=True):
+        """Commit current changes in specified working copy.
+        
+        :param relpath: List of paths to commit.
+        """
+        olddir = os.path.abspath('.')
+        self.next_message = message
+        os.chdir(dir)
+        info = self.client_ctx.commit(["."], recursive, False)
+        os.chdir(olddir)
+        assert info is not None
+        return info
+
+    def client_add(self, relpath, recursive=True):
+        """Add specified files to working copy.
+        
+        :param relpath: Path to the files to add.
+        """
+        self.client_ctx.add(relpath, recursive, False, False)
+
+    def client_log(self, url, start_revnum, stop_revnum):
+        r = ra.RemoteAccess(url)
+        assert isinstance(url, str)
+        ret = {}
+        def rcvr(orig_paths, rev, revprops, has_children=None):
+            ret[rev] = (orig_paths, revprops.get(properties.PROP_REVISION_AUTHOR), revprops.get(properties.PROP_REVISION_DATE), revprops.get(properties.PROP_REVISION_LOG))
+        r.get_log(rcvr, [""], start_revnum, stop_revnum, 0, True, True, 
+                  revprops=[properties.PROP_REVISION_AUTHOR, properties.PROP_REVISION_DATE, properties.PROP_REVISION_LOG])
+        return ret
+
+    def client_delete(self, relpath):
+        """Remove specified files from working copy.
+
+        :param relpath: Path to the files to remove.
+        """
+        self.client_ctx.delete([relpath], True)
+
+    def client_copy(self, oldpath, newpath, revnum=None):
+        """Copy file in working copy.
+
+        :param oldpath: Relative path to original file.
+        :param newpath: Relative path to new file.
+        """
+        if revnum is None:
+            rev = "HEAD"
+        else:
+            rev = revnum
+        self.client_ctx.copy(oldpath, newpath, rev)
+
+    def client_update(self, path):
+        self.client_ctx.update([path], "HEAD", True)
+
+    def build_tree(self, files):
+        """Create a directory tree.
+        
+        :param files: Dictionary with filenames as keys, contents as 
+            values. None as value indicates a directory.
+        """
+        for f in files:
+            if files[f] is None:
+                try:
+                    os.makedirs(f)
+                except OSError:
+                    pass
+            else:
+                try:
+                    os.makedirs(os.path.dirname(f))
+                except OSError:
+                    pass
+                open(f, 'w').write(files[f])
+
+    def make_client(self, repospath, clientpath, allow_revprop_changes=True):
+        """Create a repository and a checkout. Return the checkout.
+
+        :param relpath: Optional relpath to check out if not the full 
+            repository.
+        :param clientpath: Path to checkout
+        :return: Repository URL.
+        """
+        repos_url = self.make_repository(repospath, 
+            allow_revprop_changes=allow_revprop_changes)
+        self.make_checkout(repos_url, clientpath)
+        return repos_url
+
+    def open_fs(self, relpath):
+        """Open a fs.
+
+        :return: FS.
+        """
+        return repos.Repository(relpath).fs()
+
+    def get_commit_editor(self, url, message="Test commit"):
+        ra = RemoteAccess(url.encode("utf-8"))
+        revnum = ra.get_latest_revnum()
+        return TestCommitEditor(ra.get_commit_editor({"svn:log": message}), ra.url, revnum)

=== modified file 'subvertpy/subvertpy/tests/test_client.py'
--- a/subvertpy/subvertpy/tests/test_client.py	2008-10-02 21:16:21 +0000
+++ b/subvertpy/subvertpy/tests/test_client.py	2008-10-06 14:53:19 +0000
@@ -16,7 +16,7 @@
 """Subversion client library tests."""
 
 from subvertpy import client
-from bzrlib.plugins.svn.tests import SubversionTestCase
+from subvertpy.tests import SubversionTestCase
 
 class TestClient(SubversionTestCase):
     def setUp(self):

=== added file 'subvertpy/subvertpy/tests/test_marshall.py'
--- a/subvertpy/subvertpy/tests/test_marshall.py	1970-01-01 00:00:00 +0000
+++ b/subvertpy/subvertpy/tests/test_marshall.py	2008-10-06 14:25:30 +0000
@@ -0,0 +1,87 @@
+# Copyright (C) 2006-2007 Jelmer Vernooij <jelmer at samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from unittest import TestCase
+from subvertpy.marshall import literal, MarshallError, marshall, unmarshall
+
+class TestMarshalling(TestCase):
+    def test_literal_txt(self):
+        l = literal("foo")
+        self.assertEqual("foo", l.txt)
+
+    def test_literal_str(self):
+        l = literal("foo bar")
+        self.assertEqual("foo bar", l.__str__())
+
+    def test_literal_rep(self):
+        l = literal("foo bar")
+        self.assertEqual("foo bar", l.__repr__())
+
+    def test_marshall_error(self):
+        e = MarshallError("bla bla")
+        self.assertEqual("bla bla", e.__str__())
+    
+    def test_marshall_int(self):
+        self.assertEqual("1 ", marshall(1))
+
+    def test_marshall_list(self):
+        self.assertEqual("( 1 2 3 4 ) ", marshall([1,2,3,4]))
+    
+    def test_marshall_list_mixed(self):
+        self.assertEqual("( 1 3 4 3:str ) ", marshall([1,3,4,"str"]))
+
+    def test_marshall_literal(self):
+        self.assertEqual("foo ", marshall(literal("foo")))
+
+    def test_marshall_string(self):
+        self.assertEqual("3:foo ", marshall("foo"))
+
+    def test_marshall_raises(self):
+        self.assertRaises(MarshallError, marshall, dict())
+
+    def test_marshall_list_nested(self):
+        self.assertEqual("( ( ( 3 ) 4 ) ) ", marshall([[[3], 4]]))
+
+    def test_marshall_string_space(self):
+        self.assertEqual("5:bla l ", marshall("bla l"))
+
+    def test_unmarshall_string(self):
+        self.assertEqual(('', "bla l"), unmarshall("5:bla l"))
+
+    def test_unmarshall_list(self):
+        self.assertEqual(('', [4,5]), unmarshall("( 4 5 ) "))
+
+    def test_unmarshall_int(self):
+        self.assertEqual(('', 2), unmarshall("2 "))
+
+    def test_unmarshall_literal(self):
+        self.assertEqual(('', literal("x")), unmarshall("x "))
+
+    def test_unmarshall_empty(self):
+        self.assertRaises(MarshallError, unmarshall, "")
+
+    def test_unmarshall_nospace(self):
+        self.assertRaises(MarshallError, unmarshall, "nospace")
+
+    def test_unmarshall_toolong(self):
+        self.assertRaises(MarshallError, unmarshall, "43432432:bla")
+
+    def test_unmarshall_literal(self):
+        self.assertRaises(MarshallError, unmarshall, ":-3213")
+
+    def test_unmarshall_open_list(self):
+        self.assertRaises(MarshallError, unmarshall, "( 3 4 ")
+

=== modified file 'subvertpy/subvertpy/tests/test_ra.py'
--- a/subvertpy/subvertpy/tests/test_ra.py	2008-10-02 21:16:21 +0000
+++ b/subvertpy/subvertpy/tests/test_ra.py	2008-10-06 14:53:19 +0000
@@ -15,9 +15,10 @@
 
 """Subversion ra library tests."""
 
+from cStringIO import StringIO
 from unittest import TestCase
 from subvertpy import ra, SubversionException
-from bzrlib.plugins.svn.tests import SubversionTestCase
+from subvertpy.tests import SubversionTestCase
 
 class VersionTest(TestCase):
     def test_version_length(self):
@@ -175,6 +176,18 @@
         self.assertEquals("/bar", rets[0][0])
         self.assertEquals("/bar", rets[1][0])
 
+    def test_get_file(self):
+        cb = self.commit_editor()
+        cb.add_file("bar").modify("a")
+        cb.close()
+
+        stream = StringIO()
+
+        self.ra.get_file("bar", stream, 1)
+
+        stream.seek(0)
+        self.assertEquals("a", stream.read())
+
     def test_get_locations_root(self):
         self.assertEquals({0: "/"}, self.ra.get_locations("", 0, [0]))
 




More information about the bazaar-commits mailing list