2
# -*- coding: utf-8 -*-
 
 
4
# Copyright (C) 2005 Canonical Ltd
 
 
6
# This program is free software; you can redistribute it and/or modify
 
 
7
# it under the terms of the GNU General Public License as published by
 
 
8
# the Free Software Foundation; either version 2 of the License, or
 
 
9
# (at your option) any later version.
 
 
11
# This program is distributed in the hope that it will be useful,
 
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
14
# GNU General Public License for more details.
 
 
16
# You should have received a copy of the GNU General Public License
 
 
17
# along with this program; if not, write to the Free Software
 
 
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
21
"""External black-box test for bzr.
 
 
23
This always runs bzr as an external process to try to catch bugs
 
 
24
related to argument processing, startup, etc.
 
 
28
    testbzr [-p PYTHON] [BZR]
 
 
30
By default this tests the copy of bzr found in the same directory as
 
 
31
testbzr, or the first one found on the $PATH.  A copy of bzr may be
 
 
32
given on the command line to override this, for example when applying
 
 
33
a new test suite to an old copy of bzr or vice versa.
 
 
35
testbzr normally invokes bzr using the same version of python as it
 
 
36
would normally use to run -- that is, the system default python,
 
 
37
unless that is older than 2.3.  The -p option allows specification of
 
 
38
a different Python interpreter, such as when testing that bzr still
 
 
41
This replaces the previous test.sh which was not very portable."""
 
 
43
import sys, os, traceback
 
 
45
from os.path import exists
 
 
47
TESTDIR = "testbzr.tmp"
 
 
50
# we always invoke bzr as 'python bzr' (or e.g. 'python2.3 bzr')
 
 
51
# partly so as to cope if the bzr binary is not marked executable
 
 
52
OVERRIDE_PYTHON = 'python'
 
 
54
LOGFILENAME = 'testbzr.log'
 
 
58
    from subprocess import call, Popen, PIPE
 
 
59
except ImportError, e:
 
 
60
    sys.stderr.write("testbzr: sorry, this test suite requires modules from python2.4\n"
 
 
68
    if isinstance(cmd, basestring):
 
 
74
            cmd.insert(0, OVERRIDE_PYTHON)
 
 
76
    logfile.write('$ %r\n' % cmd)
 
 
81
def runcmd(cmd, retcode=0):
 
 
82
    """Run one command and check the return code.
 
 
84
    Returns a tuple of (stdout,stderr) strings.
 
 
86
    If a single string is based, it is split into words.
 
 
87
    For commands that are not simple space-separated words, please
 
 
88
    pass a list instead."""
 
 
92
    actual_retcode = call(cmd, stdout=logfile, stderr=logfile)
 
 
94
    if retcode != actual_retcode:
 
 
95
        raise CommandFailed("test failed: %r returned %d, expected %d"
 
 
96
                            % (cmd, actual_retcode, retcode))
 
 
100
def backtick(cmd, retcode=0):
 
 
103
    child = Popen(cmd, stdout=PIPE, stderr=logfile)
 
 
104
    outd, errd = child.communicate()
 
 
106
    actual_retcode = child.wait()
 
 
108
    outd = outd.replace('\r', '')
 
 
110
    if retcode != actual_retcode:
 
 
111
        raise CommandFailed("test failed: %r returned %d, expected %d"
 
 
112
                            % (cmd, actual_retcode, retcode))
 
 
120
    logfile.write('* '+ msg + '\n')
 
 
125
    logfile.write('$ cd %s\n' % dirname)
 
 
130
def log_linenumber():
 
 
131
    """Log the stack frame location two things up."""
 
 
132
    stack = traceback.extract_stack()[-3]
 
 
133
    logfile.write('   at %s:%d\n' % stack[:2])
 
 
137
# prepare an empty scratch directory
 
 
138
if os.path.exists(TESTDIR):
 
 
139
    shutil.rmtree(TESTDIR)
 
 
141
start_dir = os.getcwd()
 
 
144
logfile = open(LOGFILENAME, 'wt', buffering=1)
 
 
147
    """Run a test involving creating a plugin to load,
 
 
148
    and making sure it is seen properly.
 
 
151
    f = open(os.path.join('plugin_test', 'myplug.py'), 'wb')
 
 
152
    f.write("""import bzrlib, bzrlib.commands
 
 
153
class cmd_myplug(bzrlib.commands.Command):
 
 
154
    '''Just a simple test plugin.'''
 
 
157
        print 'Hello from my plugin'
 
 
161
    os.environ['BZRPLUGINPATH'] = os.path.abspath('plugin_test')
 
 
162
    help = backtick('bzr help commands')
 
 
163
    assert help.find('myplug') != -1
 
 
164
    assert help.find('Just a simple test plugin.') != -1
 
 
167
    assert backtick('bzr myplug') == 'Hello from my plugin\n'
 
 
168
    assert backtick('bzr mplg') == 'Hello from my plugin\n'
 
 
170
    f = open(os.path.join('plugin_test', 'override.py'), 'wb')
 
 
171
    f.write("""import bzrlib, bzrlib.commands
 
 
172
class cmd_commit(bzrlib.commands.cmd_commit):
 
 
173
    '''Commit changes into a new revision.'''
 
 
174
    def run(self, *args, **kwargs):
 
 
175
        print "I'm sorry dave, you can't do that"
 
 
177
class cmd_help(bzrlib.commands.cmd_help):
 
 
178
    '''Show help on a command or other topic.'''
 
 
179
    def run(self, *args, **kwargs):
 
 
180
        print "You have been overridden"
 
 
181
        bzrlib.commands.cmd_help.run(self, *args, **kwargs)
 
 
186
    newhelp = backtick('bzr help commands')
 
 
187
    assert newhelp.startswith('You have been overridden\n')
 
 
188
    # We added a line, but the rest should work
 
 
189
    assert newhelp[25:] == help
 
 
191
    assert backtick('bzr commit -m test') == "I'm sorry dave, you can't do that\n"
 
 
193
    shutil.rmtree('plugin_test')
 
 
196
    from getopt import getopt
 
 
197
    opts, args = getopt(sys.argv[1:], 'p:')
 
 
199
    for option, value in opts:
 
 
201
            OVERRIDE_PYTHON = value
 
 
204
    mypath = os.path.abspath(sys.argv[0])
 
 
205
    print '%-30s %s' % ('running tests from', mypath)
 
 
212
        BZRPATH = os.path.join(os.path.split(mypath)[0], 'bzr')
 
 
214
    print '%-30s %s' % ('against bzr', BZRPATH)
 
 
215
    print '%-30s %s' % ('in directory', os.getcwd())
 
 
216
    print '%-30s %s' % ('with python', (OVERRIDE_PYTHON or '(default)'))
 
 
218
    print backtick('bzr version')
 
 
220
    runcmd(['mkdir', TESTDIR])
 
 
222
    test_root = os.getcwd()
 
 
224
    progress("introductory commands")
 
 
225
    runcmd("bzr version")
 
 
226
    runcmd("bzr --version")
 
 
230
    progress("internal tests")
 
 
231
    runcmd("bzr selftest")
 
 
233
    progress("user identity")
 
 
234
    # this should always identify something, if only "john@localhost"
 
 
236
    runcmd("bzr whoami --email")
 
 
237
    assert backtick("bzr whoami --email").count('@') == 1
 
 
239
    progress("invalid commands")
 
 
240
    runcmd("bzr pants", retcode=1)
 
 
241
    runcmd("bzr --pants off", retcode=1)
 
 
242
    runcmd("bzr diff --message foo", retcode=1)
 
 
244
    progress("basic branch creation")
 
 
245
    runcmd(['mkdir', 'branch1'])
 
 
249
    assert backtick('bzr root')[:-1] == os.path.join(test_root, 'branch1')
 
 
251
    progress("status of new file")
 
 
253
    f = file('test.txt', 'wt')
 
 
254
    f.write('hello world!\n')
 
 
257
    out = backtick("bzr unknowns")
 
 
258
    assert out == 'test.txt\n'
 
 
260
    out = backtick("bzr status")
 
 
261
    assert out == 'unknown:\n  test.txt\n'
 
 
263
    out = backtick("bzr status --all")
 
 
264
    assert out == "unknown:\n  test.txt\n"
 
 
266
    out = backtick("bzr status test.txt --all")
 
 
267
    assert out == "unknown:\n  test.txt\n"
 
 
269
    f = file('test2.txt', 'wt')
 
 
270
    f.write('goodbye cruel world...\n')
 
 
273
    out = backtick("bzr status test.txt")
 
 
274
    assert out == "unknown:\n  test.txt\n"
 
 
276
    out = backtick("bzr status")
 
 
277
    assert out == ("unknown:\n"
 
 
281
    os.unlink('test2.txt')
 
 
283
    progress("command aliases")
 
 
284
    out = backtick("bzr st --all")
 
 
285
    assert out == ("unknown:\n"
 
 
288
    out = backtick("bzr stat")
 
 
289
    assert out == ("unknown:\n"
 
 
292
    progress("command help")
 
 
293
    runcmd("bzr help st")
 
 
295
    runcmd("bzr help commands")
 
 
296
    runcmd("bzr help slartibartfast", 1)
 
 
298
    out = backtick("bzr help ci")
 
 
299
    out.index('aliases: ')
 
 
301
    progress("can't rename unversioned file")
 
 
302
    runcmd("bzr rename test.txt new-test.txt", 1)
 
 
304
    progress("adding a file")
 
 
306
    runcmd("bzr add test.txt")
 
 
307
    assert backtick("bzr unknowns") == ''
 
 
308
    assert backtick("bzr status --all") == ("added:\n"
 
 
311
    progress("rename newly-added file")
 
 
312
    runcmd("bzr rename test.txt hello.txt")
 
 
313
    assert os.path.exists("hello.txt")
 
 
314
    assert not os.path.exists("test.txt")
 
 
316
    assert backtick("bzr revno") == '0\n'
 
 
318
    progress("add first revision")
 
 
319
    runcmd(["bzr", "commit", "-m", 'add first revision'])
 
 
321
    progress("more complex renames")
 
 
323
    runcmd("bzr rename hello.txt sub1", 1)
 
 
324
    runcmd("bzr rename hello.txt sub1/hello.txt", 1)
 
 
325
    runcmd("bzr move hello.txt sub1", 1)
 
 
327
    runcmd("bzr add sub1")
 
 
328
    runcmd("bzr rename sub1 sub2")
 
 
329
    runcmd("bzr move hello.txt sub2")
 
 
330
    assert backtick("bzr relpath sub2/hello.txt") == os.path.join("sub2", "hello.txt\n")
 
 
332
    assert exists("sub2")
 
 
333
    assert exists("sub2/hello.txt")
 
 
334
    assert not exists("sub1")
 
 
335
    assert not exists("hello.txt")
 
 
337
    runcmd(['bzr', 'commit', '-m', 'commit with some things moved to subdirs'])
 
 
340
    runcmd('bzr add sub1')
 
 
341
    runcmd('bzr move sub2/hello.txt sub1')
 
 
342
    assert not exists('sub2/hello.txt')
 
 
343
    assert exists('sub1/hello.txt')
 
 
344
    runcmd('bzr move sub2 sub1')
 
 
345
    assert not exists('sub2')
 
 
346
    assert exists('sub1/sub2')
 
 
348
    runcmd(['bzr', 'commit', '-m', 'rename nested subdirectories'])
 
 
351
    assert backtick('bzr root')[:-1] == os.path.join(test_root, 'branch1')
 
 
352
    runcmd('bzr move ../hello.txt .')
 
 
353
    assert exists('./hello.txt')
 
 
354
    assert backtick('bzr relpath hello.txt') == os.path.join('sub1', 'sub2', 'hello.txt\n')
 
 
355
    assert backtick('bzr relpath ../../sub1/sub2/hello.txt') == os.path.join('sub1', 'sub2', 'hello.txt\n')
 
 
356
    runcmd(['bzr', 'commit', '-m', 'move to parent directory'])
 
 
358
    assert backtick('bzr relpath sub2/hello.txt') == os.path.join('sub1', 'sub2', 'hello.txt\n')
 
 
360
    runcmd('bzr move sub2/hello.txt .')
 
 
361
    assert exists('hello.txt')
 
 
363
    f = file('hello.txt', 'wt')
 
 
364
    f.write('some nice new content\n')
 
 
367
    f = file('msg.tmp', 'wt')
 
 
368
    f.write('this is my new commit\n')
 
 
371
    runcmd('bzr commit -F msg.tmp')
 
 
373
    assert backtick('bzr revno') == '5\n'
 
 
374
    runcmd('bzr export -r 5 export-5.tmp')
 
 
375
    runcmd('bzr export export.tmp')
 
 
382
    progress("file with spaces in name")
 
 
383
    mkdir('sub directory')
 
 
384
    file('sub directory/file with spaces ', 'wt').write('see how this works\n')
 
 
387
    runcmd('bzr commit -m add-spaces')
 
 
391
    runcmd('bzr log --forward')
 
 
403
    # Can't create a branch if it already exists
 
 
404
    runcmd('bzr branch branch1', retcode=1)
 
 
405
    # Can't create a branch if its parent doesn't exist
 
 
406
    runcmd('bzr branch /unlikely/to/exist', retcode=1)
 
 
407
    runcmd('bzr branch branch1 branch2')
 
 
411
    runcmd('bzr pull', retcode=1)
 
 
412
    runcmd('bzr pull ../branch2')
 
 
415
    runcmd('bzr commit -m empty')
 
 
419
    runcmd('bzr commit -m empty')
 
 
421
    runcmd('bzr commit -m empty')
 
 
422
    runcmd('bzr pull', retcode=1)
 
 
425
    progress('status after remove')
 
 
426
    mkdir('status-after-remove')
 
 
427
    # see mail from William Dodé, 2005-05-25
 
 
428
    # $ bzr init; touch a; bzr add a; bzr commit -m "add a"
 
 
429
    #     * looking for changes...
 
 
434
    #     bzr: local variable 'kind' referenced before assignment
 
 
435
    #     at /vrac/python/bazaar-ng/bzrlib/diff.py:286 in compare_trees()
 
 
436
    #     see ~/.bzr.log for debug information
 
 
437
    cd('status-after-remove')
 
 
439
    file('a', 'w').write('foo')
 
 
441
    runcmd(['bzr', 'commit', '-m', 'add a'])
 
 
442
    runcmd('bzr remove a')
 
 
447
    progress('ignore patterns')
 
 
448
    mkdir('ignorebranch')
 
 
451
    assert backtick('bzr unknowns') == ''
 
 
453
    file('foo.tmp', 'wt').write('tmp files are ignored')
 
 
454
    assert backtick('bzr unknowns') == ''
 
 
456
    file('foo.c', 'wt').write('int main() {}')
 
 
457
    assert backtick('bzr unknowns') == 'foo.c\n'
 
 
458
    runcmd('bzr add foo.c')
 
 
459
    assert backtick('bzr unknowns') == ''
 
 
461
    # 'ignore' works when creating the .bzignore file
 
 
462
    file('foo.blah', 'wt').write('blah')
 
 
463
    assert backtick('bzr unknowns') == 'foo.blah\n'
 
 
464
    runcmd('bzr ignore *.blah')
 
 
465
    assert backtick('bzr unknowns') == ''
 
 
466
    assert file('.bzrignore', 'rb').read() == '*.blah\n'
 
 
468
    # 'ignore' works when then .bzrignore file already exists
 
 
469
    file('garh', 'wt').write('garh')
 
 
470
    assert backtick('bzr unknowns') == 'garh\n'
 
 
471
    runcmd('bzr ignore garh')
 
 
472
    assert backtick('bzr unknowns') == ''
 
 
473
    assert file('.bzrignore', 'rb').read() == '*.blah\ngarh\n'
 
 
480
    progress("recursive and non-recursive add")
 
 
485
    fp = os.path.join('foo', 'test.txt')
 
 
489
    runcmd('bzr add --no-recurse foo')
 
 
490
    runcmd('bzr file-id foo')
 
 
491
    runcmd('bzr file-id ' + fp, 1)      # not versioned yet
 
 
492
    runcmd('bzr commit -m add-dir-only')
 
 
494
    runcmd('bzr file-id ' + fp, 1)      # still not versioned 
 
 
496
    runcmd('bzr add foo')
 
 
497
    runcmd('bzr file-id ' + fp)
 
 
498
    runcmd('bzr commit -m add-sub-file')
 
 
505
    # Run any function in this 
 
 
510
        if k.startswith('test_') and callable(g[k]):
 
 
511
            progress(k[5:].replace('_', ' '))
 
 
514
    progress("all tests passed!")
 
 
516
    sys.stderr.write('*' * 50 + '\n'
 
 
517
                     + 'testbzr: tests failed\n'
 
 
518
                     + 'see ' + LOGFILENAME + ' for more information\n'
 
 
520
    logfile.write('tests failed!\n')
 
 
521
    traceback.print_exc(None, logfile)
 
 
524
    sys.stdout.writelines(file(os.path.join(start_dir, LOGFILENAME), 'rt').readlines()[-50:])