~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
"""Tests of the bzr serve command."""
19
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
20
import os
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
21
import signal
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
22
import subprocess
2309.2.5 by Alexander Belchenko
test_bzr_connect_to_bzr_ssh: win32-compatibility
23
import sys
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
24
import threading
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
25
2309.2.5 by Alexander Belchenko
test_bzr_connect_to_bzr_ssh: win32-compatibility
26
from bzrlib import (
27
    errors,
28
    osutils,
29
    )
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
30
from bzrlib.branch import Branch
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
31
from bzrlib.bzrdir import BzrDir
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
32
from bzrlib.errors import ParamikoNotPresent
33
from bzrlib.tests import TestCaseWithTransport, TestSkipped
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
34
from bzrlib.transport import get_transport, smart
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
35
36
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
37
class TestBzrServe(TestCaseWithTransport):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
38
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
39
    def assertInetServerShutsdownCleanly(self, process):
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
40
        """Shutdown the server process looking for errors."""
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
41
        # Shutdown the server: the server should shut down when it cannot read
42
        # from stdin anymore.
43
        process.stdin.close()
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
44
        # Hide stdin from the subprocess module, so it won't fail to close it.
45
        process.stdin = None
46
        result = self.finish_bzr_subprocess(process, retcode=0)
47
        self.assertEqual('', result[0])
48
        self.assertEqual('', result[1])
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
49
    
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
50
    def assertServerFinishesCleanly(self, process):
51
        """Shutdown the bzr serve instance process looking for errors."""
52
        # Shutdown the server
53
        result = self.finish_bzr_subprocess(process, retcode=3,
54
                                            send_signal=signal.SIGINT)
55
        self.assertEqual('', result[0])
56
        self.assertEqual('bzr: interrupted\n', result[1])
57
58
    def start_server_inet(self, extra_options=()):
59
        """Start a bzr server subprocess using the --inet option.
60
61
        :param extra_options: extra options to give the server.
62
        :return: a tuple with the bzr process handle for passing to
63
            finish_bzr_subprocess, a client for the server, and a transport.
64
        """
65
        # Serve from the current directory
66
        process = self.start_bzr_subprocess(['serve', '--inet'])
67
68
        # Connect to the server
69
        # We use this url because while this is no valid URL to connect to this
70
        # server instance, the transport needs a URL.
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
71
        medium = smart.SmartSimplePipesClientMedium(
72
            process.stdout, process.stdin)
73
        transport = smart.SmartTransport('bzr://localhost/', medium=medium)
74
        return process, transport
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
75
76
    def start_server_port(self, extra_options=()):
77
        """Start a bzr server subprocess.
78
79
        :param extra_options: extra options to give the server.
80
        :return: a tuple with the bzr process handle for passing to
81
            finish_bzr_subprocess, and the base url for the server.
82
        """
83
        # Serve from the current directory
84
        args = ['serve', '--port', 'localhost:0']
85
        args.extend(extra_options)
86
        process = self.start_bzr_subprocess(args, skip_if_plan_to_signal=True)
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
87
        port_line = process.stdout.readline()
88
        prefix = 'listening on port: '
89
        self.assertStartsWith(port_line, prefix)
90
        port = int(port_line[len(prefix):])
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
91
        return process,'bzr://localhost:%d/' % port
92
93
    def test_bzr_serve_inet_readonly(self):
94
        """bzr server should provide a read only filesystem by default."""
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
95
        process, transport = self.start_server_inet()
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
96
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
97
        self.assertInetServerShutsdownCleanly(process)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
98
99
    def test_bzr_serve_inet_readwrite(self):
100
        # Make a branch
101
        self.make_branch('.')
102
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
103
        process, transport = self.start_server_inet(['--allow-writes'])
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
104
105
        # We get a working branch
106
        branch = BzrDir.open_from_transport(transport).open_branch()
107
        branch.repository.get_revision_graph()
108
        self.assertEqual(None, branch.last_revision())
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
109
        self.assertInetServerShutsdownCleanly(process)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
110
111
    def test_bzr_serve_port_readonly(self):
112
        """bzr server should provide a read only filesystem by default."""
113
        process, url = self.start_server_port()
114
        transport = get_transport(url)
115
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
116
        self.assertServerFinishesCleanly(process)
117
118
    def test_bzr_serve_port_readwrite(self):
119
        # Make a branch
120
        self.make_branch('.')
121
122
        process, url = self.start_server_port(['--allow-writes'])
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
123
124
        # Connect to the server
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
125
        branch = Branch.open(url)
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
126
127
        # We get a working branch
128
        branch.repository.get_revision_graph()
129
        self.assertEqual(None, branch.last_revision())
130
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
131
        self.assertServerFinishesCleanly(process)
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
132
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
133
    def test_bzr_connect_to_bzr_ssh(self):
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
134
        """User acceptance that get_transport of a bzr+ssh:// behaves correctly.
135
136
        bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
137
        """
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
138
        try:
139
            from bzrlib.transport.sftp import SFTPServer
140
        except ParamikoNotPresent:
141
            raise TestSkipped('Paramiko not installed')
142
        from bzrlib.tests.stub_sftp import StubServer
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
143
        
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
144
        # Make a branch
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
145
        self.make_branch('a_branch')
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
146
147
        # Start an SSH server
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
148
        self.command_executed = []
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
149
        # XXX: This is horrible -- we define a really dumb SSH server that
150
        # executes commands, and manage the hooking up of stdin/out/err to the
151
        # SSH channel ourselves.  Surely this has already been implemented
152
        # elsewhere?
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
153
        class StubSSHServer(StubServer):
154
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
155
            test = self
156
157
            def check_channel_exec_request(self, channel, command):
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
158
                self.test.command_executed.append(command)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
159
                proc = subprocess.Popen(
160
                    command, shell=True, stdin=subprocess.PIPE,
161
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
162
                
163
                # XXX: horribly inefficient, not to mention ugly.
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
164
                # Start a thread for each of stdin/out/err, and relay bytes from
165
                # the subprocess to channel and vice versa.
166
                def ferry_bytes(read, write, close):
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
167
                    while True:
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
168
                        bytes = read(1)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
169
                        if bytes == '':
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
170
                            close()
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
171
                            break
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
172
                        write(bytes)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
173
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
174
                file_functions = [
175
                    (channel.recv, proc.stdin.write, proc.stdin.close),
176
                    (proc.stdout.read, channel.sendall, channel.close),
177
                    (proc.stderr.read, channel.sendall_stderr, channel.close)]
178
                for read, write, close in file_functions:
179
                    t = threading.Thread(
180
                        target=ferry_bytes, args=(read, write, close))
181
                    t.start()
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
182
183
                return True
184
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
185
        ssh_server = SFTPServer(StubSSHServer)
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
186
        # XXX: We *don't* want to override the default SSH vendor, so we set
187
        # _vendor to what _get_ssh_vendor returns.
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
188
        ssh_server.setUp()
189
        self.addCleanup(ssh_server.tearDown)
190
        port = ssh_server._listener.port
191
192
        # Access the branch via a bzr+ssh URL.  The BZR_REMOTE_PATH environment
193
        # variable is used to tell bzr what command to run on the remote end.
2309.2.5 by Alexander Belchenko
test_bzr_connect_to_bzr_ssh: win32-compatibility
194
        path_to_branch = osutils.abspath('a_branch')
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
195
        
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
196
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
2309.2.5 by Alexander Belchenko
test_bzr_connect_to_bzr_ssh: win32-compatibility
197
        bzr_remote_path = self.get_bzr_path()
198
        if sys.platform == 'win32':
199
            bzr_remote_path = sys.executable + ' ' + self.get_bzr_path()
200
        os.environ['BZR_REMOTE_PATH'] = bzr_remote_path
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
201
        try:
2309.2.5 by Alexander Belchenko
test_bzr_connect_to_bzr_ssh: win32-compatibility
202
            if sys.platform == 'win32':
203
                path_to_branch = os.path.splitdrive(path_to_branch)[1]
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
204
            branch = Branch.open(
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
205
                'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch))
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
206
            
207
            branch.repository.get_revision_graph()
208
            self.assertEqual(None, branch.last_revision())
2018.1.10 by Andrew Bennetts
Merge --allow-writes from Robert
209
            # Check we can perform write operations
210
            branch.bzrdir.root_transport.mkdir('foo')
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
211
        finally:
212
            # Restore the BZR_REMOTE_PATH environment variable back to its
213
            # original state.
214
            if orig_bzr_remote_path is None:
215
                del os.environ['BZR_REMOTE_PATH']
216
            else:
217
                os.environ['BZR_REMOTE_PATH'] = orig_bzr_remote_path
218
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
219
        self.assertEqual(
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
220
            ['%s serve --inet --directory=/ --allow-writes'
2309.2.5 by Alexander Belchenko
test_bzr_connect_to_bzr_ssh: win32-compatibility
221
             % bzr_remote_path],
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
222
            self.command_executed)
223