Rev 417: Import bzrsvnserve code. in file:///home/jelmer/bzr-svn/native/

Jelmer Vernooij jelmer at samba.org
Mon Mar 26 04:09:31 BST 2007


At file:///home/jelmer/bzr-svn/native/

------------------------------------------------------------
revno: 417
revision-id: jelmer at samba.org-20070326030927-gx1sztmko43pepo0
parent: jelmer at samba.org-20070202201250-i5eq4yp3uikabjuz
parent: jelmer at samba.org-20070122185242-0bl6sfrx3zl79ow0
committer: Jelmer Vernooij <jelmer at samba.org>
branch nick: native
timestamp: Mon 2007-03-26 05:09:27 +0200
message:
  Import bzrsvnserve code.
added:
  server/                        server-20070326030801-7aa9p7z8zbejqs8d-1
  server/__init__.py             __init__.py-20061015145107-e1erl7tzb3zk831k-1
  server/marshall.py             marshall.py-20070122110546-ggdrkocyus0pjsqd-1
  server/svnserver.py            svnserver.py-20061015150253-0jjovnw1ax00rjlb-1
  server/tests/                  tests-20070122110405-ydhtxk45zyerq0oh-1
  server/tests/__init__.py       __init__.py-20070122111740-cibkqznxtxxbyvjt-1
  server/tests/test_marshall.py  test_marshall.py-20070122110633-944cvow1y1hg6krp-1
  server/tests/test_server.py    test_server.py-20070122114402-zcwhr4cxc67jvakf-1
    ------------------------------------------------------------
    revno: 0.3.23
    merged: jelmer at samba.org-20070122185242-0bl6sfrx3zl79ow0
    parent: jelmer at samba.org-20070122162053-ut129mvs7joysh8s
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 19:52:42 +0100
    message:
      Add some more stubs, badly needs tests.
    ------------------------------------------------------------
    revno: 0.3.22
    merged: jelmer at samba.org-20070122162053-ut129mvs7joysh8s
    parent: jelmer at samba.org-20070122161906-lcam7eccas1zq1po
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 17:20:53 +0100
    message:
      Lock (matters /a lot/)
    ------------------------------------------------------------
    revno: 0.3.21
    merged: jelmer at samba.org-20070122161906-lcam7eccas1zq1po
    parent: jelmer at samba.org-20070122155732-vfmbqrbf71u7c8of
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 17:19:06 +0100
    message:
      Test for get_log
    ------------------------------------------------------------
    revno: 0.3.20
    merged: jelmer at samba.org-20070122155732-vfmbqrbf71u7c8of
    parent: jelmer at samba.org-20070122154725-49pirhkzi6pu42lx
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:57:32 +0100
    message:
      Remove optimizations again..
    ------------------------------------------------------------
    revno: 0.3.19
    merged: jelmer at samba.org-20070122154725-49pirhkzi6pu42lx
    parent: jelmer at samba.org-20070122154117-zmsnnakil7fjoeih
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:47:25 +0100
    message:
      Fix revision handling
    ------------------------------------------------------------
    revno: 0.3.18
    merged: jelmer at samba.org-20070122154117-zmsnnakil7fjoeih
    parent: jelmer at samba.org-20070122154000-ub6ifk1ng4ca3azz
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:41:17 +0100
    message:
      Typo
    ------------------------------------------------------------
    revno: 0.3.17
    merged: jelmer at samba.org-20070122154000-ub6ifk1ng4ca3azz
    parent: jelmer at samba.org-20070122153846-bw0hexrt5t91sks5
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:40:00 +0100
    message:
      don't fetch all revisions at once
    ------------------------------------------------------------
    revno: 0.3.16
    merged: jelmer at samba.org-20070122153846-bw0hexrt5t91sks5
    parent: jelmer at samba.org-20070122152711-agvhumgzk6qvev1m
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:38:46 +0100
    message:
      use standard function for time conversions
    ------------------------------------------------------------
    revno: 0.3.15
    merged: jelmer at samba.org-20070122152711-agvhumgzk6qvev1m
    parent: jelmer at samba.org-20070122152124-obc3p0x76zptffjp
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:27:11 +0100
    message:
      Fetch multiple revisions at once
    ------------------------------------------------------------
    revno: 0.3.14
    merged: jelmer at samba.org-20070122152124-obc3p0x76zptffjp
    parent: jelmer at samba.org-20070122151242-1lnsn2vmyjehnukd
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:21:24 +0100
    message:
      Two trivial fixes
    ------------------------------------------------------------
    revno: 0.3.13
    merged: jelmer at samba.org-20070122151242-1lnsn2vmyjehnukd
    parent: jelmer at samba.org-20070122144913-r38748l092dto74c
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 16:12:42 +0100
    message:
      Very simple implementation of log
    ------------------------------------------------------------
    revno: 0.3.12
    merged: jelmer at samba.org-20070122144913-r38748l092dto74c
    parent: jelmer at samba.org-20070122142330-jg1h0cci78pnm5zc
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 15:49:13 +0100
    message:
      Get log works now (returns empty log for now)
    ------------------------------------------------------------
    revno: 0.3.11
    merged: jelmer at samba.org-20070122142330-jg1h0cci78pnm5zc
    parent: jelmer at samba.org-20070122135806-vrx4xj1uq5d8v3x1
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 15:23:30 +0100
    message:
      get_uuid and get_latest_rev work now.
    ------------------------------------------------------------
    revno: 0.3.10
    merged: jelmer at samba.org-20070122135806-vrx4xj1uq5d8v3x1
    parent: jelmer at samba.org-20070122131330-os8xa31ka1vyfjml
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 14:58:06 +0100
    message:
      Use python-svn for testing server. Currently still fails with 'Malformed network data' error
    ------------------------------------------------------------
    revno: 0.3.9
    merged: jelmer at samba.org-20070122131330-os8xa31ka1vyfjml
    parent: jelmer at samba.org-20070122125840-sib4iysnuz238g2k
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 14:13:30 +0100
    message:
      Trivial support for listening on a TCP/IP port.
    ------------------------------------------------------------
    revno: 0.3.8
    merged: jelmer at samba.org-20070122125840-sib4iysnuz238g2k
    parent: jelmer at samba.org-20070122114454-s6kqpgg5k3fl0dtf
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 13:58:40 +0100
    message:
      Add tests for server code.
    ------------------------------------------------------------
    revno: 0.3.7
    merged: jelmer at samba.org-20070122114454-s6kqpgg5k3fl0dtf
    parent: jelmer at samba.org-20070122113853-7a2zddf0rsphskmc
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 12:44:54 +0100
    message:
      remove duplicate code
    ------------------------------------------------------------
    revno: 0.3.6
    merged: jelmer at samba.org-20070122113853-7a2zddf0rsphskmc
    parent: jelmer at samba.org-20061015183108-4386822a387571fa
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Mon 2007-01-22 12:38:53 +0100
    message:
      Add testsuite for marshall/unmarshall code.
    ------------------------------------------------------------
    revno: 0.3.5
    merged: jelmer at samba.org-20061015183108-4386822a387571fa
    parent: jelmer at samba.org-20061015181131-af775e7692f74f36
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Sun 2006-10-15 20:31:08 +0200
    message:
      Add parameters for inetd, port and directory to serve.
    ------------------------------------------------------------
    revno: 0.3.4
    merged: jelmer at samba.org-20061015181131-af775e7692f74f36
    parent: jelmer at samba.org-20061015162538-d5032b56d1f63e09
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Sun 2006-10-15 20:11:31 +0200
    message:
      Fix literals support.
    ------------------------------------------------------------
    revno: 0.3.3
    merged: jelmer at samba.org-20061015162538-d5032b56d1f63e09
    parent: jelmer at samba.org-20061015154426-d819257c123de417
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Sun 2006-10-15 18:25:38 +0200
    message:
      Gets past the first few roundtrips now.
    ------------------------------------------------------------
    revno: 0.3.2
    merged: jelmer at samba.org-20061015154426-d819257c123de417
    parent: jelmer at samba.org-20061015150308-249217e986bdc16e
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Sun 2006-10-15 17:44:26 +0200
    message:
      Add marshall/unmarshall functions.
    ------------------------------------------------------------
    revno: 0.3.1
    merged: jelmer at samba.org-20061015150308-249217e986bdc16e
    committer: Jelmer Vernooij <jelmer at samba.org>
    branch nick: bzrsvnserve
    timestamp: Sun 2006-10-15 17:03:08 +0200
    message:
      Initial work on a implementation of the svnserve protocol.
=== added directory 'server'
=== added file 'server/__init__.py'
--- a/server/__init__.py	1970-01-01 00:00:00 +0000
+++ b/server/__init__.py	2007-03-26 03:09:27 +0000
@@ -0,0 +1,93 @@
+# 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
+
+from bzrlib.commands import Command, register_command, Option
+from bzrlib.trace import info
+import os
+import sys
+import threading
+
+class cmd_svnserve(Command):
+    """Provide access to a Bazaar branch using the Subversion ra_svn protocol.
+    """
+    takes_options = [
+        Option('inet',
+               help='serve on stdin/out for use from inetd or sshd'),
+        Option('port',
+               help='listen for connections on nominated port of the form '
+                    '[hostname:]portnumber. Passing 0 as the port number will '
+                    'result in a dynamically allocated port.',
+               type=str),
+        Option('directory',
+               help='serve contents of directory',
+               type=unicode)
+    ]
+
+    def run(self, inet=None, port=None, directory=None):
+        from svnserver import SVNServer
+
+        if directory is None:
+            directory = os.getcwd()
+
+        if inet:
+            def send_fn(data):
+                sys.stdout.write(data)
+                sys.stdout.flush()
+            server = SVNServer(directory, sys.stdin.read, send_fn)
+            server.serve()
+        else:
+            if port is None:
+                port = 3690
+            else:
+                port = int(port)
+
+            import socket
+            server_sock = socket.socket()
+            server_sock.bind(('0.0.0.0', port))
+            server_sock.listen(5)
+            def handle_new_client(sock):
+                def handle_connection():
+                    server.serve()
+                    sock.close()
+                server = SVNServer(directory, lambda: sock.recv(1024), sock.send)
+                server_thread = threading.Thread(None, handle_connection, name='svn-smart-server')
+                server_thread.setDaemon(True)
+                server_thread.start()
+                
+            while True:
+                sock, _ = server_sock.accept()
+                handle_new_client(sock)
+
+
+register_command(cmd_svnserve)
+
+def test_suite():
+    from unittest import TestSuite
+    import tests
+    result = TestSuite()
+    result.addTest(tests.test_suite())
+    return result
+
+sys.path.append(os.path.dirname(__file__))
+
+try:
+    import uuid
+except ImportError:
+    from bzrlib.errors import BzrError
+    from bzrlib.trace import warning
+    warning('uuid module not available, disabling svnserve plugin')
+    raise BzrError('uuid module not available')
+

=== added file 'server/marshall.py'
--- a/server/marshall.py	1970-01-01 00:00:00 +0000
+++ b/server/marshall.py	2007-03-26 03:09:27 +0000
@@ -0,0 +1,110 @@
+# 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 bzrlib.errors import BzrError
+
+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(BzrError):
+    def __init__(self, msg):
+        BzrError.__init__(self, msg)
+
+
+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])

=== added file 'server/svnserver.py'
--- a/server/svnserver.py	1970-01-01 00:00:00 +0000
+++ b/server/svnserver.py	2007-03-26 03:09:27 +0000
@@ -0,0 +1,217 @@
+# 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
+
+from bzrlib.branch import Branch
+from bzrlib.errors import BzrError
+from bzrlib.trace import mutter
+
+import copy
+import os
+import time
+
+from marshall import marshall, unmarshall, literal, MarshallError
+
+SVN_MAJOR_VERSION = 1
+SVN_MINOR_VERSION = 2
+
+SVN_NODE_NONE = 0
+SVN_NODE_FILE = 1
+SVN_NODE_DIR = 2
+
+class SVNServer:
+    def __init__(self, rootdir, recv_fn, send_fn):
+        self.rootdir = rootdir
+        self.recv_fn = recv_fn
+        self.send_fn = send_fn
+        self.inbuffer = ""
+        self._stop = False
+
+    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.branch.revno())
+
+    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):
+        import pdb
+        pdb.set_trace()
+        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 get_branch_uuid(self):
+        config = self.branch.get_config()
+        uuid = config.get_user_option('svn_uuid')
+        if uuid is None:
+            import uuid
+            uuid = uuid.uuid4()
+            config.set_user_option('svn_uuid', uuid)
+        return str(uuid)
+
+    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
+        mutter("client supports:")
+        mutter("  version %r" % version)
+        mutter("  capabilities %r " % capabilities)
+        self.send_mechs()
+
+        (mech, args) = self.recv_msg()
+        # TODO: Proper authentication
+        self.send_success()
+
+        import bzrlib.urlutils as urlutils
+        (rooturl, location) = urlutils.split(url)
+
+        self.branch, branch_path = Branch.open_containing(os.path.join(self.rootdir, location))
+        self.send_success(self.get_branch_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)
+                mutter('in: %r' % ret)
+                return ret
+            except MarshallError, e:
+                mutter('ERROR: %r' % e)
+
+    def send_msg(self, data):
+        mutter('out: %r' % data)
+        self.send_fn(marshall(data))

=== added directory 'server/tests'
=== added file 'server/tests/__init__.py'
--- a/server/tests/__init__.py	1970-01-01 00:00:00 +0000
+++ b/server/tests/__init__.py	2007-01-22 12:58:40 +0000
@@ -0,0 +1,28 @@
+# Copyright (C) 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
+
+def test_suite():
+    from unittest import TestSuite, TestLoader
+    from bzrlib.tests import TestUtil
+    loader = TestUtil.TestLoader()
+    suite = TestSuite()
+    testmod_names = [
+            'test_marshall',
+            'test_server'
+            ]
+    suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i) for i in testmod_names]))
+
+    return suite

=== added file 'server/tests/test_marshall.py'
--- a/server/tests/test_marshall.py	1970-01-01 00:00:00 +0000
+++ b/server/tests/test_marshall.py	2007-01-22 13:58:06 +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 bzrlib.tests import TestCase
+from 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 ")
+

=== added file 'server/tests/test_server.py'
--- a/server/tests/test_server.py	1970-01-01 00:00:00 +0000
+++ b/server/tests/test_server.py	2007-01-22 18:52:42 +0000
@@ -0,0 +1,146 @@
+# 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 bzrlib.bzrdir import BzrDir
+from bzrlib.tests import TestCaseInTempDir
+from bzrlib.trace import mutter
+from bzrlib.workingtree import WorkingTree
+
+from marshall import literal, MarshallError, marshall, unmarshall
+from svnserver import SVNServer
+
+from cStringIO import StringIO
+import os
+import socket
+import threading
+import uuid
+
+class DryTestServer(TestCaseInTempDir):
+    def setUp(self):
+        super(DryTestServer, self).setUp()
+        self.branch_path = os.path.join(self.test_dir, 'a')
+        self.branch = BzrDir.create_branch_convenience(self.branch_path)
+        self.outstr = StringIO()
+        self.server = SVNServer(self.branch_path, None, self.outstr.write)
+
+    def test_send_greeting(self):
+        self.server.send_greeting()
+        self.outstr.seek(0)
+        self.assertEqual("( success ( 1 2 ( ANONYMOUS ) ( ) ) ) ",
+                         self.outstr.read())
+
+    def test_unknown_cmd(self):
+        self.server.send_unknown("foobar")
+        self.outstr.seek(0)
+        msg = unmarshall(self.outstr.read())[1]
+        self.assertEqual("failure", msg[0])
+        self.assertEqual(210001, msg[1][0][0])
+        self.assertEqual("Unknown command 'foobar'", msg[1][0][1])
+
+    def test_get_uuid(self):
+        self.server.branch = self.branch
+        self.branch.get_config().set_user_option('svn_uuid', 
+            '769e3eee-b09d-4023-bf1b-773dcd4bdb2a')
+        self.assertEqual('769e3eee-b09d-4023-bf1b-773dcd4bdb2a', 
+                         self.server.get_branch_uuid())
+
+    def test_get_uuid_implicit(self):
+        self.server.branch = self.branch
+        uuid.UUID(self.server.get_branch_uuid())
+
+import svn.client, svn.ra, svn.delta
+from svn.core import Pool
+
+class NativeTestServer(TestCaseInTempDir):
+    def setUp(self):
+        super(NativeTestServer, self).setUp()
+        self.branch_path = os.path.join(self.test_dir, 'a')
+        self.branch = BzrDir.create_branch_convenience(self.branch_path)
+        self.outstr = StringIO()
+        self.pool = Pool()
+        self.client_ctx = svn.client.create_context(self.pool)
+        self.server = SVNServer(self.branch_path, None, self.outstr.write)
+        self.branch.get_config().set_user_option('svn_uuid', 
+                '769e3eee-b09d-4023-bf1b-773dcd4bdb2a')
+        server_sock = socket.socket()
+        server_sock.listen(1)
+        addr = server_sock.getsockname()
+        def handle_connection():
+            sock, client_addr = server_sock.accept()
+            self.server = SVNServer(self.branch_path, lambda: sock.recv(1024), sock.send)
+            self.server.serve()
+        self.server_thread = threading.Thread(None, handle_connection, name='svn-smart-server')
+        self.server_thread.setDaemon(True)
+        self.server_thread.start()
+        self.url = "svn://%s:%d/" % addr
+        self.ra = svn.client.open_ra_session(self.url,
+                                            self.client_ctx, self.pool)
+
+    def tearDown(self):
+        super(NativeTestServer, self).tearDown()
+        self.server.close()
+
+    def test_get_uuid(self):
+        self.assertEqual('769e3eee-b09d-4023-bf1b-773dcd4bdb2a', svn.ra.get_uuid(self.ra))
+
+    def test_get_latest_rev(self):
+        self.assertEqual(0, svn.ra.get_latest_revnum(self.ra))
+
+    def test_get_log_empty(self):
+        def rcvr(orig_paths, rev, author, date, message, pool):
+            pass
+        svn.ra.get_log(self.ra, ["/"], 0, 0, 0, True, True, rcvr, self.pool)
+
+    def test_get_log_onrev(self):
+        self.num = 0
+        def rcvr(orig_paths, rev, author, date, message, pool):
+            self.num += 1
+            self.assertEqual(1, rev)
+            self.assertEqual("someone", author)
+            self.assertEqual("a commit message", message)
+        file('a/file', 'w').write("data")
+        wt = WorkingTree.open('a')
+        wt.add('file')
+        wt.commit("a commit message", committer='someone')
+        svn.ra.get_log(self.ra, ["/"], 0, 1, 0, False, False, rcvr, self.pool)
+
+    def test_checkout(self):
+        file('a/file', 'w').write("data")
+        wt = WorkingTree.open('a')
+        wt.add('file')
+        wt.commit("a commit message", committer='someone')
+        class Editor():
+            pass
+        editor = Editor()
+        edit, edit_baton = svn.delta.make_editor(editor, self.pool)
+        reporter, report_baton = svn.ra.do_update(self.ra, 1, self.url, 
+                                                  True, edit, edit_baton)
+        svn.ra.reporter2_invoke_set_path(reporter, report_baton, "",
+                                         0, True, None, self.pool)
+        svn.ra.reporter2_invoke_finish_report(reporter, report_baton, 
+                                              self.pool)
+
+    def test_check_path(self):
+        self.assertTrue(svn.ra.check_path(self.ra, '', 0))
+
+    def test_check_path_future(self):
+        self.assertTrue(svn.ra.check_path(self.ra, '', 1))
+
+    def test_reparent(self):
+        svn.ra.reparent(self.ra, 'someurl')
+
+    def test_stat(self):
+        svn.ra.stat(self.ra, '', 1)




More information about the bazaar-commits mailing list