13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
"""Tests of the bzr serve command."""
25
from bzrlib import errors
30
revision as _mod_revision,
26
33
from bzrlib.branch import Branch
27
34
from bzrlib.bzrdir import BzrDir
28
35
from bzrlib.errors import ParamikoNotPresent
36
from bzrlib.smart import client, medium
37
from bzrlib.smart.server import SmartTCPServer
29
38
from bzrlib.tests import TestCaseWithTransport, TestSkipped
30
from bzrlib.transport import get_transport, smart
39
from bzrlib.trace import mutter
40
from bzrlib.transport import get_transport, remote
33
43
class TestBzrServe(TestCaseWithTransport):
39
49
process.stdin.close()
40
50
# Hide stdin from the subprocess module, so it won't fail to close it.
41
51
process.stdin = None
42
result = self.finish_bzr_subprocess(process, retcode=0)
52
result = self.finish_bzr_subprocess(process)
43
53
self.assertEqual('', result[0])
44
54
self.assertEqual('', result[1])
46
56
def assertServerFinishesCleanly(self, process):
47
57
"""Shutdown the bzr serve instance process looking for errors."""
48
58
# Shutdown the server
51
61
self.assertEqual('', result[0])
52
62
self.assertEqual('bzr: interrupted\n', result[1])
64
def make_read_requests(self, branch):
65
"""Do some read only requests."""
68
branch.repository.all_revision_ids()
69
self.assertEqual(_mod_revision.NULL_REVISION,
70
_mod_revision.ensure_null(branch.last_revision()))
54
74
def start_server_inet(self, extra_options=()):
55
75
"""Start a bzr server subprocess using the --inet option.
64
84
# Connect to the server
65
85
# We use this url because while this is no valid URL to connect to this
66
86
# server instance, the transport needs a URL.
67
medium = smart.SmartSimplePipesClientMedium(
68
process.stdout, process.stdin)
69
transport = smart.SmartTransport('bzr://localhost/', medium=medium)
87
url = 'bzr://localhost/'
88
client_medium = medium.SmartSimplePipesClientMedium(
89
process.stdout, process.stdin, url)
90
transport = remote.RemoteTransport(url, medium=client_medium)
70
91
return process, transport
72
93
def start_server_port(self, extra_options=()):
80
101
args = ['serve', '--port', 'localhost:0']
81
102
args.extend(extra_options)
82
103
process = self.start_bzr_subprocess(args, skip_if_plan_to_signal=True)
83
port_line = process.stdout.readline()
104
port_line = process.stderr.readline()
84
105
prefix = 'listening on port: '
85
106
self.assertStartsWith(port_line, prefix)
86
107
port = int(port_line[len(prefix):])
101
122
# We get a working branch
102
123
branch = BzrDir.open_from_transport(transport).open_branch()
103
branch.repository.get_revision_graph()
104
self.assertEqual(None, branch.last_revision())
124
self.make_read_requests(branch)
105
125
self.assertInetServerShutsdownCleanly(process)
107
127
def test_bzr_serve_port_readonly(self):
120
140
# Connect to the server
121
141
branch = Branch.open(url)
123
# We get a working branch
124
branch.repository.get_revision_graph()
125
self.assertEqual(None, branch.last_revision())
127
self.assertServerFinishesCleanly(process)
129
def test_bzr_serve_no_args(self):
130
"""'bzr serve' with no arguments or options should not traceback."""
131
out, err = self.run_bzr_error(
132
['bzr serve requires one of --inet or --port'], 'serve')
142
self.make_read_requests(branch)
143
self.assertServerFinishesCleanly(process)
145
def test_bzr_serve_supports_protocol(self):
147
self.make_branch('.')
149
process, url = self.start_server_port(['--allow-writes',
152
# Connect to the server
153
branch = Branch.open(url)
154
self.make_read_requests(branch)
155
self.assertServerFinishesCleanly(process)
134
157
def test_bzr_connect_to_bzr_ssh(self):
135
158
"""User acceptance that get_transport of a bzr+ssh:// behaves correctly.
160
183
proc = subprocess.Popen(
161
184
command, shell=True, stdin=subprocess.PIPE,
162
185
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
164
187
# XXX: horribly inefficient, not to mention ugly.
165
188
# Start a thread for each of stdin/out/err, and relay bytes from
166
189
# the subprocess to channel and vice versa.
193
216
# Access the branch via a bzr+ssh URL. The BZR_REMOTE_PATH environment
194
217
# variable is used to tell bzr what command to run on the remote end.
195
path_to_branch = os.path.abspath('a_branch')
218
path_to_branch = osutils.abspath('a_branch')
197
220
orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
198
os.environ['BZR_REMOTE_PATH'] = self.get_bzr_path()
221
bzr_remote_path = self.get_bzr_path()
222
if sys.platform == 'win32':
223
bzr_remote_path = sys.executable + ' ' + self.get_bzr_path()
224
os.environ['BZR_REMOTE_PATH'] = bzr_remote_path
226
if sys.platform == 'win32':
227
path_to_branch = os.path.splitdrive(path_to_branch)[1]
200
228
branch = Branch.open(
201
229
'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch))
203
branch.repository.get_revision_graph()
204
self.assertEqual(None, branch.last_revision())
230
self.make_read_requests(branch)
205
231
# Check we can perform write operations
206
232
branch.bzrdir.root_transport.mkdir('foo')
215
241
self.assertEqual(
216
242
['%s serve --inet --directory=/ --allow-writes'
217
% self.get_bzr_path()],
218
244
self.command_executed)
247
class TestCmdServeChrooting(TestCaseWithTransport):
249
def test_serve_tcp(self):
250
"""'bzr serve' wraps the given --directory in a ChrootServer.
252
So requests that search up through the parent directories (like
253
find_repositoryV3) will give "not found" responses, rather than
254
InvalidURLJoin or jail break errors.
256
t = self.get_transport()
257
t.mkdir('server-root')
258
self.run_bzr_serve_then_func(
259
['--port', '0', '--directory', t.local_abspath('server-root'),
261
self.when_server_started)
262
# The when_server_started method issued a find_repositoryV3 that should
263
# fail with 'norepository' because there are no repositories inside the
265
self.assertEqual(('norepository',), self.client_resp)
267
def run_bzr_serve_then_func(self, serve_args, func, *func_args,
269
"""Run 'bzr serve', and run the given func in a thread once the server
272
When 'func' terminates, the server will be terminated too.
275
def on_server_start(backing_urls, tcp_server):
276
t = threading.Thread(
277
target=on_server_start_thread, args=(tcp_server,))
279
def on_server_start_thread(tcp_server):
282
self.tcp_server = tcp_server
284
func(*func_args, **func_kwargs)
286
# Log errors to make some test failures a little less
288
mutter('func broke: %r', e)
290
# Then stop the server
291
mutter('interrupting...')
292
thread.interrupt_main()
293
SmartTCPServer.hooks.install_named_hook(
294
'server_started_ex', on_server_start,
295
'run_bzr_serve_then_func hook')
298
self.run_bzr(['serve'] + list(serve_args))
299
except KeyboardInterrupt:
302
def when_server_started(self):
303
# Connect to the TCP server and issue some requests and see what comes
305
client_medium = medium.SmartTCPClientMedium(
306
'127.0.0.1', self.tcp_server.port,
307
'bzr://localhost:%d/' % (self.tcp_server.port,))
308
smart_client = client._SmartClient(client_medium)
309
resp = smart_client.call('mkdir', 'foo', '')
310
resp = smart_client.call('BzrDirFormat.initialize', 'foo/')
312
resp = smart_client.call('BzrDir.find_repositoryV3', 'foo/')
313
except errors.ErrorFromSmartServer, e:
315
self.client_resp = resp
316
client_medium.disconnect()