/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
1
# Copyright (C) 2006 by Canonical Ltd
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
23
import threading
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
24
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
25
from bzrlib import errors
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
26
from bzrlib.branch import Branch
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
27
from bzrlib.bzrdir import BzrDir
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
28
from bzrlib.errors import ParamikoNotPresent
29
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`.
30
from bzrlib.transport import get_transport, smart
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
31
32
33
class DoesNotCloseStdOutClient(smart.SmartStreamClient):
34
    """A client that doesn't close stdout upon disconnect().
35
    
36
    We wish to let stdout remain open so that we can see if the server writes
37
    anything to stdout during its shutdown.
38
    """
39
40
    def disconnect(self):
41
        if self._connected:
42
            self._connected = False
43
            # The client's out is the server's in.
44
            self._out.close()
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
45
46
47
class TestBzrServe(TestCaseWithTransport):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
48
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
49
    def assertInetServerShutsdownCleanly(self, client, process):
50
        """Shutdown the server process looking for errors."""
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
51
        # Disconnect the client forcefully JUST IN CASE because of __del__'s use
52
        # in the smart module.
53
        client.disconnect()
54
55
        # Shutdown the server: the client should have disconnected cleanly and
56
        # closed stdin, so the server process should shut itself down.
57
        self.assertTrue(process.stdin.closed)
58
        # Hide stdin from the subprocess module, so it won't fail to close it.
59
        process.stdin = None
60
        result = self.finish_bzr_subprocess(process, retcode=0)
61
        self.assertEqual('', result[0])
62
        self.assertEqual('', result[1])
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
63
    
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
64
    def assertServerFinishesCleanly(self, process):
65
        """Shutdown the bzr serve instance process looking for errors."""
66
        # Shutdown the server
67
        result = self.finish_bzr_subprocess(process, retcode=3,
68
                                            send_signal=signal.SIGINT)
69
        self.assertEqual('', result[0])
70
        self.assertEqual('bzr: interrupted\n', result[1])
71
72
    def start_server_inet(self, extra_options=()):
73
        """Start a bzr server subprocess using the --inet option.
74
75
        :param extra_options: extra options to give the server.
76
        :return: a tuple with the bzr process handle for passing to
77
            finish_bzr_subprocess, a client for the server, and a transport.
78
        """
79
        # Serve from the current directory
80
        process = self.start_bzr_subprocess(['serve', '--inet'])
81
82
        # Connect to the server
83
        # We use this url because while this is no valid URL to connect to this
84
        # server instance, the transport needs a URL.
85
        client = DoesNotCloseStdOutClient(
86
            lambda: (process.stdout, process.stdin))
87
        transport = smart.SmartTransport('bzr://localhost/', client=client)
88
        return process, client, transport
89
90
    def start_server_port(self, extra_options=()):
91
        """Start a bzr server subprocess.
92
93
        :param extra_options: extra options to give the server.
94
        :return: a tuple with the bzr process handle for passing to
95
            finish_bzr_subprocess, and the base url for the server.
96
        """
97
        # Serve from the current directory
98
        args = ['serve', '--port', 'localhost:0']
99
        args.extend(extra_options)
100
        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
101
        port_line = process.stdout.readline()
102
        prefix = 'listening on port: '
103
        self.assertStartsWith(port_line, prefix)
104
        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`.
105
        return process,'bzr://localhost:%d/' % port
106
107
    def test_bzr_serve_inet_readonly(self):
108
        """bzr server should provide a read only filesystem by default."""
109
        process, client, transport = self.start_server_inet()
110
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
111
        # finish with the transport
112
        del transport
113
        self.assertInetServerShutsdownCleanly(client, process)
114
115
    def test_bzr_serve_inet_readwrite(self):
116
        # Make a branch
117
        self.make_branch('.')
118
119
        process, client, transport = self.start_server_inet(['--allow-writes'])
120
121
        # We get a working branch
122
        branch = BzrDir.open_from_transport(transport).open_branch()
123
        branch.repository.get_revision_graph()
124
        self.assertEqual(None, branch.last_revision())
125
126
        # finish with the transport
127
        del transport
128
129
        self.assertInetServerShutsdownCleanly(client, process)
130
131
    def test_bzr_serve_port_readonly(self):
132
        """bzr server should provide a read only filesystem by default."""
133
        process, url = self.start_server_port()
134
        transport = get_transport(url)
135
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
136
        self.assertServerFinishesCleanly(process)
137
138
    def test_bzr_serve_port_readwrite(self):
139
        # Make a branch
140
        self.make_branch('.')
141
142
        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
143
144
        # Connect to the server
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
145
        branch = Branch.open(url)
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
146
147
        # We get a working branch
148
        branch.repository.get_revision_graph()
149
        self.assertEqual(None, branch.last_revision())
150
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
151
        self.assertServerFinishesCleanly(process)
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
152
1910.19.8 by Andrew Bennetts
Give a sensible error if insufficient options are passed to 'bzr serve'.
153
    def test_bzr_serve_no_args(self):
154
        """'bzr serve' with no arguments or options should not traceback."""
155
        out, err = self.run_bzr_error(
156
            ['bzr serve requires one of --inet or --port'], 'serve')
157
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
158
    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.
159
        """User acceptance that get_transport of a bzr+ssh:// behaves correctly.
160
161
        bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
162
        """
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
163
        try:
164
            from bzrlib.transport.sftp import SFTPServer
165
        except ParamikoNotPresent:
166
            raise TestSkipped('Paramiko not installed')
167
        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
168
        
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
169
        # Make a branch
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
170
        self.make_branch('a_branch')
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
171
172
        # Start an SSH server
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
173
        self.command_executed = []
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
174
        # XXX: This is horrible -- we define a really dumb SSH server that
175
        # executes commands, and manage the hooking up of stdin/out/err to the
176
        # SSH channel ourselves.  Surely this has already been implemented
177
        # elsewhere?
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
178
        class StubSSHServer(StubServer):
179
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
180
            test = self
181
182
            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
183
                self.test.command_executed.append(command)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
184
                proc = subprocess.Popen(
185
                    command, shell=True, stdin=subprocess.PIPE,
186
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
187
                
188
                # 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
189
                # Start a thread for each of stdin/out/err, and relay bytes from
190
                # the subprocess to channel and vice versa.
191
                def ferry_bytes(read, write, close):
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
192
                    while True:
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
193
                        bytes = read(1)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
194
                        if bytes == '':
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
195
                            close()
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
196
                            break
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
197
                        write(bytes)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
198
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
199
                file_functions = [
200
                    (channel.recv, proc.stdin.write, proc.stdin.close),
201
                    (proc.stdout.read, channel.sendall, channel.close),
202
                    (proc.stderr.read, channel.sendall_stderr, channel.close)]
203
                for read, write, close in file_functions:
204
                    t = threading.Thread(
205
                        target=ferry_bytes, args=(read, write, close))
206
                    t.start()
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
207
208
                return True
209
2018.1.2 by Andrew Bennetts
Tidy imports, skip test if paramiko isn't installed.
210
        ssh_server = SFTPServer(StubSSHServer)
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
211
        # XXX: We *don't* want to override the default SSH vendor, so we set
212
        # _vendor to what _get_ssh_vendor returns.
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
213
        ssh_server.setUp()
214
        self.addCleanup(ssh_server.tearDown)
215
        port = ssh_server._listener.port
216
217
        # Access the branch via a bzr+ssh URL.  The BZR_REMOTE_PATH environment
218
        # variable is used to tell bzr what command to run on the remote end.
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
219
        path_to_branch = os.path.abspath('a_branch')
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
220
        
2018.1.7 by Andrew Bennetts
Use bzrlib rather than a bzr subprocess in test_bzr_connect_to_bzr_ssh.
221
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
222
        os.environ['BZR_REMOTE_PATH'] = self.get_bzr_path()
223
        try:
224
            branch = Branch.open(
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
225
                '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.
226
            
227
            branch.repository.get_revision_graph()
228
            self.assertEqual(None, branch.last_revision())
2018.1.10 by Andrew Bennetts
Merge --allow-writes from Robert
229
            # Check we can perform write operations
230
            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.
231
        finally:
232
            # Restore the BZR_REMOTE_PATH environment variable back to its
233
            # original state.
234
            if orig_bzr_remote_path is None:
235
                del os.environ['BZR_REMOTE_PATH']
236
            else:
237
                os.environ['BZR_REMOTE_PATH'] = orig_bzr_remote_path
238
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
239
        self.assertEqual(
2018.1.11 by Andrew Bennetts
Improvements to test_bzr_connect_to_bzr_ssh based on review comments
240
            ['%s serve --inet --directory=/ --allow-writes'
241
             % self.get_bzr_path()],
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
242
            self.command_executed)
243