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"
 
 
65
class CommandFailed(Exception):
 
 
70
    if isinstance(cmd, basestring):
 
 
76
            cmd.insert(0, OVERRIDE_PYTHON)
 
 
78
    logfile.write('$ %r\n' % cmd)
 
 
83
def runcmd(cmd, retcode=0):
 
 
84
    """Run one command and check the return code.
 
 
86
    Returns a tuple of (stdout,stderr) strings.
 
 
88
    If a single string is based, it is split into words.
 
 
89
    For commands that are not simple space-separated words, please
 
 
90
    pass a list instead."""
 
 
94
    actual_retcode = call(cmd, stdout=logfile, stderr=logfile)
 
 
96
    if retcode != actual_retcode:
 
 
97
        raise CommandFailed("test failed: %r returned %d, expected %d"
 
 
98
                            % (cmd, actual_retcode, retcode))
 
 
102
def backtick(cmd, retcode=0):
 
 
105
    child = Popen(cmd, stdout=PIPE, stderr=logfile)
 
 
106
    outd, errd = child.communicate()
 
 
108
    actual_retcode = child.wait()
 
 
110
    outd = outd.replace('\r', '')
 
 
112
    if retcode != actual_retcode:
 
 
113
        raise CommandFailed("test failed: %r returned %d, expected %d"
 
 
114
                            % (cmd, actual_retcode, retcode))
 
 
122
    logfile.write('* '+ msg + '\n')
 
 
127
    logfile.write('$ cd %s\n' % dirname)
 
 
132
def log_linenumber():
 
 
133
    """Log the stack frame location two things up."""
 
 
134
    stack = traceback.extract_stack()[-3]
 
 
135
    logfile.write('   at %s:%d\n' % stack[:2])
 
 
139
# prepare an empty scratch directory
 
 
140
if os.path.exists(TESTDIR):
 
 
141
    shutil.rmtree(TESTDIR)
 
 
143
start_dir = os.getcwd()
 
 
146
logfile = open(LOGFILENAME, 'wt', buffering=1)
 
 
149
    """Run a test involving creating a plugin to load,
 
 
150
    and making sure it is seen properly.
 
 
153
    f = open(os.path.join('plugin_test', 'myplug.py'), 'wb')
 
 
154
    f.write("""import bzrlib, bzrlib.commands
 
 
155
class cmd_myplug(bzrlib.commands.Command):
 
 
156
    '''Just a simple test plugin.'''
 
 
159
        print 'Hello from my plugin'
 
 
163
    os.environ['BZRPLUGINPATH'] = os.path.abspath('plugin_test')
 
 
164
    help = backtick('bzr help commands')
 
 
165
    assert help.find('myplug') != -1
 
 
166
    assert help.find('Just a simple test plugin.') != -1
 
 
169
    assert backtick('bzr myplug') == 'Hello from my plugin\n'
 
 
170
    assert backtick('bzr mplg') == 'Hello from my plugin\n'
 
 
172
    f = open(os.path.join('plugin_test', 'override.py'), 'wb')
 
 
173
    f.write("""import bzrlib, bzrlib.commands
 
 
174
class cmd_commit(bzrlib.commands.cmd_commit):
 
 
175
    '''Commit changes into a new revision.'''
 
 
176
    def run(self, *args, **kwargs):
 
 
177
        print "I'm sorry dave, you can't do that"
 
 
179
class cmd_help(bzrlib.commands.cmd_help):
 
 
180
    '''Show help on a command or other topic.'''
 
 
181
    def run(self, *args, **kwargs):
 
 
182
        print "You have been overridden"
 
 
183
        bzrlib.commands.cmd_help.run(self, *args, **kwargs)
 
 
188
    newhelp = backtick('bzr help commands')
 
 
189
    assert newhelp.startswith('You have been overridden\n')
 
 
190
    # We added a line, but the rest should work
 
 
191
    assert newhelp[25:] == help
 
 
193
    assert backtick('bzr commit -m test') == "I'm sorry dave, you can't do that\n"
 
 
195
    shutil.rmtree('plugin_test')
 
 
198
    from getopt import getopt
 
 
199
    opts, args = getopt(sys.argv[1:], 'p:')
 
 
201
    for option, value in opts:
 
 
203
            OVERRIDE_PYTHON = value
 
 
206
    mypath = os.path.abspath(sys.argv[0])
 
 
207
    print '%-30s %s' % ('running tests from', mypath)
 
 
214
        BZRPATH = os.path.join(os.path.split(mypath)[0], 'bzr')
 
 
216
    print '%-30s %s' % ('against bzr', BZRPATH)
 
 
217
    print '%-30s %s' % ('in directory', os.getcwd())
 
 
218
    print '%-30s %s' % ('with python', (OVERRIDE_PYTHON or '(default)'))
 
 
220
    print backtick('bzr version')
 
 
222
    runcmd(['mkdir', TESTDIR])
 
 
224
    test_root = os.getcwd()
 
 
226
    progress("introductory commands")
 
 
227
    runcmd("bzr version")
 
 
228
    runcmd("bzr --version")
 
 
232
    progress("internal tests")
 
 
233
    runcmd("bzr selftest")
 
 
235
    progress("user identity")
 
 
236
    # this should always identify something, if only "john@localhost"
 
 
238
    runcmd("bzr whoami --email")
 
 
239
    assert backtick("bzr whoami --email").count('@') == 1
 
 
241
    progress("invalid commands")
 
 
242
    runcmd("bzr pants", retcode=1)
 
 
243
    runcmd("bzr --pants off", retcode=1)
 
 
244
    runcmd("bzr diff --message foo", retcode=1)
 
 
246
    progress("basic branch creation")
 
 
247
    runcmd(['mkdir', 'branch1'])
 
 
251
    assert backtick('bzr root')[:-1] == os.path.join(test_root, 'branch1')
 
 
253
    progress("status of new file")
 
 
255
    f = file('test.txt', 'wt')
 
 
256
    f.write('hello world!\n')
 
 
259
    out = backtick("bzr unknowns")
 
 
260
    assert out == 'test.txt\n'
 
 
262
    out = backtick("bzr status")
 
 
263
    assert out == 'unknown:\n  test.txt\n'
 
 
265
    out = backtick("bzr status --all")
 
 
266
    assert out == "unknown:\n  test.txt\n"
 
 
268
    out = backtick("bzr status test.txt --all")
 
 
269
    assert out == "unknown:\n  test.txt\n"
 
 
271
    f = file('test2.txt', 'wt')
 
 
272
    f.write('goodbye cruel world...\n')
 
 
275
    out = backtick("bzr status test.txt")
 
 
276
    assert out == "unknown:\n  test.txt\n"
 
 
278
    out = backtick("bzr status")
 
 
279
    assert out == ("unknown:\n"
 
 
283
    os.unlink('test2.txt')
 
 
285
    progress("command aliases")
 
 
286
    out = backtick("bzr st --all")
 
 
287
    assert out == ("unknown:\n"
 
 
290
    out = backtick("bzr stat")
 
 
291
    assert out == ("unknown:\n"
 
 
294
    progress("command help")
 
 
295
    runcmd("bzr help st")
 
 
297
    runcmd("bzr help commands")
 
 
298
    runcmd("bzr help slartibartfast", 1)
 
 
300
    out = backtick("bzr help ci")
 
 
301
    out.index('aliases: ')
 
 
303
    progress("can't rename unversioned file")
 
 
304
    runcmd("bzr rename test.txt new-test.txt", 1)
 
 
306
    progress("adding a file")
 
 
308
    runcmd("bzr add test.txt")
 
 
309
    assert backtick("bzr unknowns") == ''
 
 
310
    assert backtick("bzr status --all") == ("added:\n"
 
 
313
    progress("rename newly-added file")
 
 
314
    runcmd("bzr rename test.txt hello.txt")
 
 
315
    assert os.path.exists("hello.txt")
 
 
316
    assert not os.path.exists("test.txt")
 
 
318
    assert backtick("bzr revno") == '0\n'
 
 
320
    progress("add first revision")
 
 
321
    runcmd(["bzr", "commit", "-m", 'add first revision'])
 
 
323
    progress("more complex renames")
 
 
325
    runcmd("bzr rename hello.txt sub1", 1)
 
 
326
    runcmd("bzr rename hello.txt sub1/hello.txt", 1)
 
 
327
    runcmd("bzr move hello.txt sub1", 1)
 
 
329
    runcmd("bzr add sub1")
 
 
330
    runcmd("bzr rename sub1 sub2")
 
 
331
    runcmd("bzr move hello.txt sub2")
 
 
332
    assert backtick("bzr relpath sub2/hello.txt") == os.path.join("sub2", "hello.txt\n")
 
 
334
    assert exists("sub2")
 
 
335
    assert exists("sub2/hello.txt")
 
 
336
    assert not exists("sub1")
 
 
337
    assert not exists("hello.txt")
 
 
339
    runcmd(['bzr', 'commit', '-m', 'commit with some things moved to subdirs'])
 
 
342
    runcmd('bzr add sub1')
 
 
343
    runcmd('bzr move sub2/hello.txt sub1')
 
 
344
    assert not exists('sub2/hello.txt')
 
 
345
    assert exists('sub1/hello.txt')
 
 
346
    runcmd('bzr move sub2 sub1')
 
 
347
    assert not exists('sub2')
 
 
348
    assert exists('sub1/sub2')
 
 
350
    runcmd(['bzr', 'commit', '-m', 'rename nested subdirectories'])
 
 
353
    assert backtick('bzr root')[:-1] == os.path.join(test_root, 'branch1')
 
 
354
    runcmd('bzr move ../hello.txt .')
 
 
355
    assert exists('./hello.txt')
 
 
356
    assert backtick('bzr relpath hello.txt') == os.path.join('sub1', 'sub2', 'hello.txt\n')
 
 
357
    assert backtick('bzr relpath ../../sub1/sub2/hello.txt') == os.path.join('sub1', 'sub2', 'hello.txt\n')
 
 
358
    runcmd(['bzr', 'commit', '-m', 'move to parent directory'])
 
 
360
    assert backtick('bzr relpath sub2/hello.txt') == os.path.join('sub1', 'sub2', 'hello.txt\n')
 
 
362
    runcmd('bzr move sub2/hello.txt .')
 
 
363
    assert exists('hello.txt')
 
 
365
    f = file('hello.txt', 'wt')
 
 
366
    f.write('some nice new content\n')
 
 
369
    f = file('msg.tmp', 'wt')
 
 
370
    f.write('this is my new commit\n')
 
 
373
    runcmd('bzr commit -F msg.tmp')
 
 
375
    assert backtick('bzr revno') == '5\n'
 
 
376
    runcmd('bzr export -r 5 export-5.tmp')
 
 
377
    runcmd('bzr export export.tmp')
 
 
384
    progress("file with spaces in name")
 
 
385
    mkdir('sub directory')
 
 
386
    file('sub directory/file with spaces ', 'wt').write('see how this works\n')
 
 
389
    runcmd('bzr commit -m add-spaces')
 
 
393
    runcmd('bzr log --forward')
 
 
405
    # Can't create a branch if it already exists
 
 
406
    runcmd('bzr branch branch1', retcode=1)
 
 
407
    # Can't create a branch if its parent doesn't exist
 
 
408
    runcmd('bzr branch /unlikely/to/exist', retcode=1)
 
 
409
    runcmd('bzr branch branch1 branch2')
 
 
413
    runcmd('bzr pull', retcode=1)
 
 
414
    runcmd('bzr pull ../branch2')
 
 
417
    runcmd('bzr commit -m empty')
 
 
421
    runcmd('bzr commit -m empty')
 
 
423
    runcmd('bzr commit -m empty')
 
 
424
    runcmd('bzr pull', retcode=1)
 
 
427
    progress('status after remove')
 
 
428
    mkdir('status-after-remove')
 
 
429
    # see mail from William Dodé, 2005-05-25
 
 
430
    # $ bzr init; touch a; bzr add a; bzr commit -m "add a"
 
 
431
    #     * looking for changes...
 
 
436
    #     bzr: local variable 'kind' referenced before assignment
 
 
437
    #     at /vrac/python/bazaar-ng/bzrlib/diff.py:286 in compare_trees()
 
 
438
    #     see ~/.bzr.log for debug information
 
 
439
    cd('status-after-remove')
 
 
441
    file('a', 'w').write('foo')
 
 
443
    runcmd(['bzr', 'commit', '-m', 'add a'])
 
 
444
    runcmd('bzr remove a')
 
 
449
    progress('ignore patterns')
 
 
450
    mkdir('ignorebranch')
 
 
453
    assert backtick('bzr unknowns') == ''
 
 
455
    file('foo.tmp', 'wt').write('tmp files are ignored')
 
 
456
    assert backtick('bzr unknowns') == ''
 
 
458
    file('foo.c', 'wt').write('int main() {}')
 
 
459
    assert backtick('bzr unknowns') == 'foo.c\n'
 
 
460
    runcmd('bzr add foo.c')
 
 
461
    assert backtick('bzr unknowns') == ''
 
 
463
    # 'ignore' works when creating the .bzignore file
 
 
464
    file('foo.blah', 'wt').write('blah')
 
 
465
    assert backtick('bzr unknowns') == 'foo.blah\n'
 
 
466
    runcmd('bzr ignore *.blah')
 
 
467
    assert backtick('bzr unknowns') == ''
 
 
468
    assert file('.bzrignore', 'rb').read() == '*.blah\n'
 
 
470
    # 'ignore' works when then .bzrignore file already exists
 
 
471
    file('garh', 'wt').write('garh')
 
 
472
    assert backtick('bzr unknowns') == 'garh\n'
 
 
473
    runcmd('bzr ignore garh')
 
 
474
    assert backtick('bzr unknowns') == ''
 
 
475
    assert file('.bzrignore', 'rb').read() == '*.blah\ngarh\n'
 
 
482
    progress("recursive and non-recursive add")
 
 
487
    fp = os.path.join('foo', 'test.txt')
 
 
491
    runcmd('bzr add --no-recurse foo')
 
 
492
    runcmd('bzr file-id foo')
 
 
493
    runcmd('bzr file-id ' + fp, 1)      # not versioned yet
 
 
494
    runcmd('bzr commit -m add-dir-only')
 
 
496
    runcmd('bzr file-id ' + fp, 1)      # still not versioned 
 
 
498
    runcmd('bzr add foo')
 
 
499
    runcmd('bzr file-id ' + fp)
 
 
500
    runcmd('bzr commit -m add-sub-file')
 
 
507
    # Run any function in this 
 
 
512
        if k.startswith('test_') and callable(g[k]):
 
 
513
            progress(k[5:].replace('_', ' '))
 
 
516
    progress("all tests passed!")
 
 
518
    sys.stderr.write('*' * 50 + '\n'
 
 
519
                     + 'testbzr: tests failed\n'
 
 
520
                     + 'see ' + LOGFILENAME + ' for more information\n'
 
 
522
    logfile.write('tests failed!\n')
 
 
523
    traceback.print_exc(None, logfile)
 
 
526
    sys.stdout.writelines(file(os.path.join(start_dir, LOGFILENAME), 'rt').readlines()[-50:])