/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_revisionspec.py

  • Committer: Matthew Fuller
  • Date: 2009-07-25 15:21:25 UTC
  • mto: (4772.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4773.
  • Revision ID: fullermd@over-yonder.net-20090725152125-2mzil0setr85g0no
Adjust documentation in the user guide a bit to correspond to DWIM
revspecs.

This is untested, as I don't have the tools to build the docs.  It's
also really not as clean as it could be, and there are probably much
better ways to put this info in here.  But it's something.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005, 2006, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
import datetime
 
18
import os
 
19
import time
 
20
 
 
21
from bzrlib import (
 
22
    branch,
 
23
    bzrdir,
 
24
    errors,
 
25
    repository,
 
26
    revision as _mod_revision,
 
27
    )
 
28
from bzrlib.tests import TestCase, TestCaseWithTransport
 
29
from bzrlib.revisionspec import (
 
30
    RevisionSpec,
 
31
    RevisionSpec_revno,
 
32
    RevisionSpec_tag,
 
33
    )
 
34
 
 
35
 
 
36
def spec_in_history(spec, branch):
 
37
    """A simple helper to change a revision spec into a branch search"""
 
38
    return RevisionSpec.from_string(spec).in_history(branch)
 
39
 
 
40
 
 
41
# Basic class, which just creates a really basic set of revisions
 
42
class TestRevisionSpec(TestCaseWithTransport):
 
43
 
 
44
    def setUp(self):
 
45
        super(TestRevisionSpec, self).setUp()
 
46
        # this sets up a revision graph:
 
47
        # r1: []             1
 
48
        # alt_r2: [r1]       1.1.1
 
49
        # r2: [r1, alt_r2]   2
 
50
 
 
51
        self.tree = self.make_branch_and_tree('tree')
 
52
        self.build_tree(['tree/a'])
 
53
        self.tree.lock_write()
 
54
        self.addCleanup(self.tree.unlock)
 
55
        self.tree.add(['a'])
 
56
        self.tree.commit('a', rev_id='r1')
 
57
 
 
58
        self.tree2 = self.tree.bzrdir.sprout('tree2').open_workingtree()
 
59
        self.tree2.commit('alt', rev_id='alt_r2')
 
60
 
 
61
        self.tree.merge_from_branch(self.tree2.branch)
 
62
        self.tree.commit('second', rev_id='r2')
 
63
 
 
64
    def get_in_history(self, revision_spec):
 
65
        return spec_in_history(revision_spec, self.tree.branch)
 
66
 
 
67
    def assertInHistoryIs(self, exp_revno, exp_revision_id, revision_spec):
 
68
        rev_info = self.get_in_history(revision_spec)
 
69
        self.assertEqual(exp_revno, rev_info.revno,
 
70
                         'Revision spec: %r returned wrong revno: %r != %r'
 
71
                         % (revision_spec, exp_revno, rev_info.revno))
 
72
        self.assertEqual(exp_revision_id, rev_info.rev_id,
 
73
                         'Revision spec: %r returned wrong revision id:'
 
74
                         ' %r != %r'
 
75
                         % (revision_spec, exp_revision_id, rev_info.rev_id))
 
76
 
 
77
    def assertInvalid(self, revision_spec, extra='',
 
78
                      invalid_as_revision_id=True):
 
79
        try:
 
80
            self.get_in_history(revision_spec)
 
81
        except errors.InvalidRevisionSpec, e:
 
82
            self.assertEqual(revision_spec, e.spec)
 
83
            self.assertEqual(extra, e.extra)
 
84
        else:
 
85
            self.fail('Expected InvalidRevisionSpec to be raised for'
 
86
                      ' %r.in_history' % (revision_spec,))
 
87
        if invalid_as_revision_id:
 
88
            try:
 
89
                spec = RevisionSpec.from_string(revision_spec)
 
90
                spec.as_revision_id(self.tree.branch)
 
91
            except errors.InvalidRevisionSpec, e:
 
92
                self.assertEqual(revision_spec, e.spec)
 
93
                self.assertEqual(extra, e.extra)
 
94
            else:
 
95
                self.fail('Expected InvalidRevisionSpec to be raised for'
 
96
                          ' %r.as_revision_id' % (revision_spec,))
 
97
 
 
98
    def assertAsRevisionId(self, revision_id, revision_spec):
 
99
        """Calling as_revision_id() should return the specified id."""
 
100
        spec = RevisionSpec.from_string(revision_spec)
 
101
        self.assertEqual(revision_id,
 
102
                         spec.as_revision_id(self.tree.branch))
 
103
 
 
104
    def get_as_tree(self, revision_spec, tree=None):
 
105
        if tree is None:
 
106
            tree = self.tree
 
107
        spec = RevisionSpec.from_string(revision_spec)
 
108
        return spec.as_tree(tree.branch)
 
109
 
 
110
 
 
111
class RevisionSpecMatchOnTrap(RevisionSpec):
 
112
 
 
113
    def _match_on(self, branch, revs):
 
114
        self.last_call = (branch, revs)
 
115
        return super(RevisionSpecMatchOnTrap, self)._match_on(branch, revs)
 
116
 
 
117
 
 
118
class TestRevisionSpecBase(TestRevisionSpec):
 
119
 
 
120
    def test_wants_revision_history(self):
 
121
        # If wants_revision_history = True, then _match_on should get the
 
122
        # branch revision history
 
123
        spec = RevisionSpecMatchOnTrap('foo', _internal=True)
 
124
        spec.in_history(self.tree.branch)
 
125
 
 
126
        self.assertEqual((self.tree.branch, ['r1' ,'r2']),
 
127
                         spec.last_call)
 
128
 
 
129
    def test_wants_no_revision_history(self):
 
130
        # If wants_revision_history = False, then _match_on should get None for
 
131
        # the branch revision history
 
132
        spec = RevisionSpecMatchOnTrap('foo', _internal=True)
 
133
        spec.wants_revision_history = False
 
134
        spec.in_history(self.tree.branch)
 
135
 
 
136
        self.assertEqual((self.tree.branch, None), spec.last_call)
 
137
 
 
138
 
 
139
 
 
140
class TestOddRevisionSpec(TestRevisionSpec):
 
141
    """Test things that aren't normally thought of as revision specs"""
 
142
 
 
143
    def test_none(self):
 
144
        self.assertInHistoryIs(None, None, None)
 
145
 
 
146
    def test_object(self):
 
147
        self.assertRaises(TypeError, RevisionSpec.from_string, object())
 
148
 
 
149
    def test_unregistered_spec(self):
 
150
        self.assertRaises(errors.NoSuchRevisionSpec,
 
151
                          RevisionSpec.from_string, 'foo')
 
152
        self.assertRaises(errors.NoSuchRevisionSpec,
 
153
                          RevisionSpec.from_string, '123a')
 
154
 
 
155
 
 
156
class TestRevisionSpec_dwim(TestRevisionSpec):
 
157
 
 
158
    # Don't need to test revno's explicitly since TRS_revno already
 
159
    # covers that well for us
 
160
 
 
161
    def test_dwim_spec_revid(self):
 
162
        self.assertInHistoryIs(2, 'r2', 'r2')
 
163
 
 
164
    def test_dwim_spec_tag(self):
 
165
        self.tree.branch.tags.set_tag('footag', 'r1')
 
166
        self.assertAsRevisionId('r1', 'footag')
 
167
        self.tree.branch.tags.delete_tag('footag')
 
168
        self.assertRaises(errors.InvalidRevisionSpec,
 
169
                          self.get_in_history, 'footag')
 
170
 
 
171
    def test_dwim_spec_date(self):
 
172
        self.assertAsRevisionId('r1', 'today')
 
173
 
 
174
    def test_dwim_spec_branch(self):
 
175
        self.assertInHistoryIs(None, 'alt_r2', 'tree2')
 
176
 
 
177
    def test_dwim_spec_nonexistent(self):
 
178
        self.assertInvalid('revid:r3', invalid_as_revision_id=False)
 
179
 
 
180
 
 
181
class TestRevnoFromString(TestCase):
 
182
 
 
183
    def test_from_string_dotted_decimal(self):
 
184
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '-1.1')
 
185
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '.1')
 
186
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1..1')
 
187
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1.2..1')
 
188
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1.')
 
189
        self.assertIsInstance(RevisionSpec.from_string('1.1'), RevisionSpec_revno)
 
190
        self.assertIsInstance(RevisionSpec.from_string('1.1.3'), RevisionSpec_revno)
 
191
 
 
192
 
 
193
class TestRevisionSpec_revno(TestRevisionSpec):
 
194
 
 
195
    def test_positive_int(self):
 
196
        self.assertInHistoryIs(0, 'null:', '0')
 
197
        self.assertInHistoryIs(1, 'r1', '1')
 
198
        self.assertInHistoryIs(2, 'r2', '2')
 
199
        self.assertInvalid('3')
 
200
 
 
201
    def test_dotted_decimal(self):
 
202
        self.assertInHistoryIs(None, 'alt_r2', '1.1.1')
 
203
        self.assertInvalid('1.1.123')
 
204
 
 
205
    def test_negative_int(self):
 
206
        self.assertInHistoryIs(2, 'r2', '-1')
 
207
        self.assertInHistoryIs(1, 'r1', '-2')
 
208
 
 
209
        self.assertInHistoryIs(1, 'r1', '-3')
 
210
        self.assertInHistoryIs(1, 'r1', '-4')
 
211
        self.assertInHistoryIs(1, 'r1', '-100')
 
212
 
 
213
    def test_positive(self):
 
214
        self.assertInHistoryIs(0, 'null:', 'revno:0')
 
215
        self.assertInHistoryIs(1, 'r1', 'revno:1')
 
216
        self.assertInHistoryIs(2, 'r2', 'revno:2')
 
217
 
 
218
        self.assertInvalid('revno:3')
 
219
 
 
220
    def test_negative(self):
 
221
        self.assertInHistoryIs(2, 'r2', 'revno:-1')
 
222
        self.assertInHistoryIs(1, 'r1', 'revno:-2')
 
223
 
 
224
        self.assertInHistoryIs(1, 'r1', 'revno:-3')
 
225
        self.assertInHistoryIs(1, 'r1', 'revno:-4')
 
226
 
 
227
    def test_invalid_number(self):
 
228
        # Get the right exception text
 
229
        try:
 
230
            int('X')
 
231
        except ValueError, e:
 
232
            pass
 
233
        self.assertInvalid('revno:X', extra='\n' + str(e))
 
234
 
 
235
    def test_missing_number_and_branch(self):
 
236
        self.assertInvalid('revno::',
 
237
                           extra='\ncannot have an empty revno and no branch')
 
238
 
 
239
    def test_invalid_number_with_branch(self):
 
240
        try:
 
241
            int('X')
 
242
        except ValueError, e:
 
243
            pass
 
244
        self.assertInvalid('revno:X:tree2', extra='\n' + str(e))
 
245
 
 
246
    def test_non_exact_branch(self):
 
247
        # It seems better to require an exact path to the branch
 
248
        # Branch.open() rather than using Branch.open_containing()
 
249
        spec = RevisionSpec.from_string('revno:2:tree2/a')
 
250
        self.assertRaises(errors.NotBranchError,
 
251
                          spec.in_history, self.tree.branch)
 
252
 
 
253
    def test_with_branch(self):
 
254
        # Passing a URL overrides the supplied branch path
 
255
        revinfo = self.get_in_history('revno:2:tree2')
 
256
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
257
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
258
        self.assertEqual(2, revinfo.revno)
 
259
        self.assertEqual('alt_r2', revinfo.rev_id)
 
260
 
 
261
    def test_int_with_branch(self):
 
262
        revinfo = self.get_in_history('2:tree2')
 
263
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
264
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
265
        self.assertEqual(2, revinfo.revno)
 
266
        self.assertEqual('alt_r2', revinfo.rev_id)
 
267
 
 
268
    def test_with_url(self):
 
269
        url = self.get_url() + '/tree2'
 
270
        revinfo = self.get_in_history('revno:2:%s' % (url,))
 
271
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
272
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
273
        self.assertEqual(2, revinfo.revno)
 
274
        self.assertEqual('alt_r2', revinfo.rev_id)
 
275
 
 
276
    def test_negative_with_url(self):
 
277
        url = self.get_url() + '/tree2'
 
278
        revinfo = self.get_in_history('revno:-1:%s' % (url,))
 
279
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
280
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
281
        self.assertEqual(2, revinfo.revno)
 
282
        self.assertEqual('alt_r2', revinfo.rev_id)
 
283
 
 
284
    def test_different_history_lengths(self):
 
285
        # Make sure we use the revisions and offsets in the supplied branch
 
286
        # not the ones in the original branch.
 
287
        self.tree2.commit('three', rev_id='r3')
 
288
        self.assertInHistoryIs(3, 'r3', 'revno:3:tree2')
 
289
        self.assertInHistoryIs(3, 'r3', 'revno:-1:tree2')
 
290
 
 
291
    def test_invalid_branch(self):
 
292
        self.assertRaises(errors.NotBranchError,
 
293
                          self.get_in_history, 'revno:-1:tree3')
 
294
 
 
295
    def test_invalid_revno_in_branch(self):
 
296
        self.tree.commit('three', rev_id='r3')
 
297
        self.assertInvalid('revno:3:tree2')
 
298
 
 
299
    def test_revno_n_path(self):
 
300
        """Old revno:N:path tests"""
 
301
        wta = self.make_branch_and_tree('a')
 
302
        ba = wta.branch
 
303
 
 
304
        wta.commit('Commit one', rev_id='a@r-0-1')
 
305
        wta.commit('Commit two', rev_id='a@r-0-2')
 
306
        wta.commit('Commit three', rev_id='a@r-0-3')
 
307
 
 
308
        wtb = self.make_branch_and_tree('b')
 
309
        bb = wtb.branch
 
310
 
 
311
        wtb.commit('Commit one', rev_id='b@r-0-1')
 
312
        wtb.commit('Commit two', rev_id='b@r-0-2')
 
313
        wtb.commit('Commit three', rev_id='b@r-0-3')
 
314
 
 
315
 
 
316
        self.assertEqual((1, 'a@r-0-1'),
 
317
                         spec_in_history('revno:1:a/', ba))
 
318
        # The argument of in_history should be ignored since it is
 
319
        # redundant with the path in the spec.
 
320
        self.assertEqual((1, 'a@r-0-1'),
 
321
                         spec_in_history('revno:1:a/', None))
 
322
        self.assertEqual((1, 'a@r-0-1'),
 
323
                         spec_in_history('revno:1:a/', bb))
 
324
        self.assertEqual((2, 'b@r-0-2'),
 
325
                         spec_in_history('revno:2:b/', None))
 
326
 
 
327
    def test_as_revision_id(self):
 
328
        self.assertAsRevisionId('null:', '0')
 
329
        self.assertAsRevisionId('r1', '1')
 
330
        self.assertAsRevisionId('r2', '2')
 
331
        self.assertAsRevisionId('r1', '-2')
 
332
        self.assertAsRevisionId('r2', '-1')
 
333
        self.assertAsRevisionId('alt_r2', '1.1.1')
 
334
 
 
335
    def test_as_tree(self):
 
336
        tree = self.get_as_tree('0')
 
337
        self.assertEquals(_mod_revision.NULL_REVISION, tree.get_revision_id())
 
338
        tree = self.get_as_tree('1')
 
339
        self.assertEquals('r1', tree.get_revision_id())
 
340
        tree = self.get_as_tree('2')
 
341
        self.assertEquals('r2', tree.get_revision_id())
 
342
        tree = self.get_as_tree('-2')
 
343
        self.assertEquals('r1', tree.get_revision_id())
 
344
        tree = self.get_as_tree('-1')
 
345
        self.assertEquals('r2', tree.get_revision_id())
 
346
        tree = self.get_as_tree('1.1.1')
 
347
        self.assertEquals('alt_r2', tree.get_revision_id())
 
348
 
 
349
 
 
350
class TestRevisionSpec_revid(TestRevisionSpec):
 
351
 
 
352
    def test_in_history(self):
 
353
        # We should be able to access revisions that are directly
 
354
        # in the history.
 
355
        self.assertInHistoryIs(1, 'r1', 'revid:r1')
 
356
        self.assertInHistoryIs(2, 'r2', 'revid:r2')
 
357
 
 
358
    def test_missing(self):
 
359
        self.assertInvalid('revid:r3', invalid_as_revision_id=False)
 
360
 
 
361
    def test_merged(self):
 
362
        """We can reach revisions in the ancestry"""
 
363
        self.assertInHistoryIs(None, 'alt_r2', 'revid:alt_r2')
 
364
 
 
365
    def test_not_here(self):
 
366
        self.tree2.commit('alt third', rev_id='alt_r3')
 
367
        # It exists in tree2, but not in tree
 
368
        self.assertInvalid('revid:alt_r3', invalid_as_revision_id=False)
 
369
 
 
370
    def test_in_repository(self):
 
371
        """We can get any revision id in the repository"""
 
372
        # XXX: This may change in the future, but for now, it is true
 
373
        self.tree2.commit('alt third', rev_id='alt_r3')
 
374
        self.tree.branch.repository.fetch(self.tree2.branch.repository,
 
375
                                          revision_id='alt_r3')
 
376
        self.assertInHistoryIs(None, 'alt_r3', 'revid:alt_r3')
 
377
 
 
378
    def test_unicode(self):
 
379
        """We correctly convert a unicode ui string to an encoded revid."""
 
380
        revision_id = u'\N{SNOWMAN}'.encode('utf-8')
 
381
        self.tree.commit('unicode', rev_id=revision_id)
 
382
        self.assertInHistoryIs(3, revision_id, u'revid:\N{SNOWMAN}')
 
383
        self.assertInHistoryIs(3, revision_id, 'revid:' + revision_id)
 
384
 
 
385
    def test_as_revision_id(self):
 
386
        self.assertAsRevisionId('r1', 'revid:r1')
 
387
        self.assertAsRevisionId('r2', 'revid:r2')
 
388
        self.assertAsRevisionId('alt_r2', 'revid:alt_r2')
 
389
 
 
390
 
 
391
class TestRevisionSpec_last(TestRevisionSpec):
 
392
 
 
393
    def test_positive(self):
 
394
        self.assertInHistoryIs(2, 'r2', 'last:1')
 
395
        self.assertInHistoryIs(1, 'r1', 'last:2')
 
396
        self.assertInHistoryIs(0, 'null:', 'last:3')
 
397
 
 
398
    def test_empty(self):
 
399
        self.assertInHistoryIs(2, 'r2', 'last:')
 
400
 
 
401
    def test_negative(self):
 
402
        self.assertInvalid('last:-1',
 
403
                           extra='\nyou must supply a positive value')
 
404
 
 
405
    def test_missing(self):
 
406
        self.assertInvalid('last:4')
 
407
 
 
408
    def test_no_history(self):
 
409
        tree = self.make_branch_and_tree('tree3')
 
410
 
 
411
        self.assertRaises(errors.NoCommits,
 
412
                          spec_in_history, 'last:', tree.branch)
 
413
 
 
414
    def test_not_a_number(self):
 
415
        try:
 
416
            int('Y')
 
417
        except ValueError, e:
 
418
            pass
 
419
        self.assertInvalid('last:Y', extra='\n' + str(e))
 
420
 
 
421
    def test_as_revision_id(self):
 
422
        self.assertAsRevisionId('r2', 'last:1')
 
423
        self.assertAsRevisionId('r1', 'last:2')
 
424
 
 
425
 
 
426
class TestRevisionSpec_before(TestRevisionSpec):
 
427
 
 
428
    def test_int(self):
 
429
        self.assertInHistoryIs(1, 'r1', 'before:2')
 
430
        self.assertInHistoryIs(1, 'r1', 'before:-1')
 
431
 
 
432
    def test_before_one(self):
 
433
        self.assertInHistoryIs(0, 'null:', 'before:1')
 
434
 
 
435
    def test_before_none(self):
 
436
        self.assertInvalid('before:0',
 
437
                           extra='\ncannot go before the null: revision')
 
438
 
 
439
    def test_revid(self):
 
440
        self.assertInHistoryIs(1, 'r1', 'before:revid:r2')
 
441
 
 
442
    def test_last(self):
 
443
        self.assertInHistoryIs(1, 'r1', 'before:last:1')
 
444
 
 
445
    def test_alt_revid(self):
 
446
        # This will grab the left-most ancestor for alternate histories
 
447
        self.assertInHistoryIs(1, 'r1', 'before:revid:alt_r2')
 
448
 
 
449
    def test_alt_no_parents(self):
 
450
        new_tree = self.make_branch_and_tree('new_tree')
 
451
        new_tree.commit('first', rev_id='new_r1')
 
452
        self.tree.branch.repository.fetch(new_tree.branch.repository,
 
453
                                          revision_id='new_r1')
 
454
        self.assertInHistoryIs(0, 'null:', 'before:revid:new_r1')
 
455
 
 
456
    def test_as_revision_id(self):
 
457
        self.assertAsRevisionId('r1', 'before:revid:r2')
 
458
        self.assertAsRevisionId('r1', 'before:2')
 
459
        self.assertAsRevisionId('r1', 'before:1.1.1')
 
460
        self.assertAsRevisionId('r1', 'before:revid:alt_r2')
 
461
 
 
462
 
 
463
class TestRevisionSpec_tag(TestRevisionSpec):
 
464
 
 
465
    def make_branch_and_tree(self, relpath):
 
466
        # override format as the default one may not support tags
 
467
        return TestRevisionSpec.make_branch_and_tree(
 
468
            self, relpath, format='dirstate-tags')
 
469
 
 
470
    def test_from_string_tag(self):
 
471
        spec = RevisionSpec.from_string('tag:bzr-0.14')
 
472
        self.assertIsInstance(spec, RevisionSpec_tag)
 
473
        self.assertEqual(spec.spec, 'bzr-0.14')
 
474
 
 
475
    def test_lookup_tag(self):
 
476
        self.tree.branch.tags.set_tag('bzr-0.14', 'r1')
 
477
        self.assertInHistoryIs(1, 'r1', 'tag:bzr-0.14')
 
478
        self.tree.branch.tags.set_tag('null_rev', 'null:')
 
479
        self.assertInHistoryIs(0, 'null:', 'tag:null_rev')
 
480
 
 
481
    def test_failed_lookup(self):
 
482
        # tags that don't exist give a specific message: arguably we should
 
483
        # just give InvalidRevisionSpec but I think this is more helpful
 
484
        self.assertRaises(errors.NoSuchTag,
 
485
            self.get_in_history,
 
486
            'tag:some-random-tag')
 
487
 
 
488
    def test_as_revision_id(self):
 
489
        self.tree.branch.tags.set_tag('my-tag', 'r2')
 
490
        self.tree.branch.tags.set_tag('null_rev', 'null:')
 
491
        self.assertAsRevisionId('r2', 'tag:my-tag')
 
492
        self.assertAsRevisionId('null:', 'tag:null_rev')
 
493
        self.assertAsRevisionId('r1', 'before:tag:my-tag')
 
494
 
 
495
 
 
496
class TestRevisionSpec_date(TestRevisionSpec):
 
497
 
 
498
    def setUp(self):
 
499
        super(TestRevisionSpec, self).setUp()
 
500
 
 
501
        new_tree = self.make_branch_and_tree('new_tree')
 
502
        new_tree.commit('Commit one', rev_id='new_r1',
 
503
                        timestamp=time.time() - 60*60*24)
 
504
        new_tree.commit('Commit two', rev_id='new_r2')
 
505
        new_tree.commit('Commit three', rev_id='new_r3')
 
506
 
 
507
        self.tree = new_tree
 
508
 
 
509
    def test_tomorrow(self):
 
510
        self.assertInvalid('date:tomorrow')
 
511
 
 
512
    def test_today(self):
 
513
        self.assertInHistoryIs(2, 'new_r2', 'date:today')
 
514
        self.assertInHistoryIs(1, 'new_r1', 'before:date:today')
 
515
 
 
516
    def test_yesterday(self):
 
517
        self.assertInHistoryIs(1, 'new_r1', 'date:yesterday')
 
518
 
 
519
    def test_invalid(self):
 
520
        self.assertInvalid('date:foobar', extra='\ninvalid date')
 
521
        # You must have '-' between year/month/day
 
522
        self.assertInvalid('date:20040404', extra='\ninvalid date')
 
523
        # Need 2 digits for each date piece
 
524
        self.assertInvalid('date:2004-4-4', extra='\ninvalid date')
 
525
 
 
526
    def test_day(self):
 
527
        now = datetime.datetime.now()
 
528
        self.assertInHistoryIs(2, 'new_r2',
 
529
            'date:%04d-%02d-%02d' % (now.year, now.month, now.day))
 
530
 
 
531
    def test_as_revision_id(self):
 
532
        self.assertAsRevisionId('new_r2', 'date:today')
 
533
 
 
534
 
 
535
class TestRevisionSpec_ancestor(TestRevisionSpec):
 
536
 
 
537
    def test_non_exact_branch(self):
 
538
        # It seems better to require an exact path to the branch
 
539
        # Branch.open() rather than using Branch.open_containing()
 
540
        self.assertRaises(errors.NotBranchError,
 
541
                          self.get_in_history, 'ancestor:tree2/a')
 
542
 
 
543
    def test_simple(self):
 
544
        # Common ancestor of trees is 'alt_r2'
 
545
        self.assertInHistoryIs(None, 'alt_r2', 'ancestor:tree2')
 
546
 
 
547
        # Going the other way, we get a valid revno
 
548
        tmp = self.tree
 
549
        self.tree = self.tree2
 
550
        self.tree2 = tmp
 
551
        self.assertInHistoryIs(2, 'alt_r2', 'ancestor:tree')
 
552
 
 
553
    def test_self(self):
 
554
        self.assertInHistoryIs(2, 'r2', 'ancestor:tree')
 
555
 
 
556
    def test_unrelated(self):
 
557
        new_tree = self.make_branch_and_tree('new_tree')
 
558
 
 
559
        new_tree.commit('Commit one', rev_id='new_r1')
 
560
        new_tree.commit('Commit two', rev_id='new_r2')
 
561
        new_tree.commit('Commit three', rev_id='new_r3')
 
562
 
 
563
        # With no common ancestor, we should raise another user error
 
564
        self.assertRaises(errors.NoCommonAncestor,
 
565
                          self.get_in_history, 'ancestor:new_tree')
 
566
 
 
567
    def test_no_commits(self):
 
568
        new_tree = self.make_branch_and_tree('new_tree')
 
569
        self.assertRaises(errors.NoCommits,
 
570
                          spec_in_history, 'ancestor:new_tree',
 
571
                                           self.tree.branch)
 
572
 
 
573
        self.assertRaises(errors.NoCommits,
 
574
                          spec_in_history, 'ancestor:tree',
 
575
                                           new_tree.branch)
 
576
 
 
577
    def test_as_revision_id(self):
 
578
        self.assertAsRevisionId('alt_r2', 'ancestor:tree2')
 
579
 
 
580
    def test_default(self):
 
581
        # We don't have a parent to default to
 
582
        self.assertRaises(errors.NotBranchError, self.get_in_history,
 
583
                          'ancestor:')
 
584
 
 
585
        # Create a branch with a parent to default to
 
586
        tree3 = self.tree.bzrdir.sprout('tree3').open_workingtree()
 
587
        tree3.commit('foo', rev_id='r3')
 
588
        self.tree = tree3
 
589
        self.assertInHistoryIs(2, 'r2', 'ancestor:')
 
590
 
 
591
 
 
592
class TestRevisionSpec_branch(TestRevisionSpec):
 
593
 
 
594
    def test_non_exact_branch(self):
 
595
        # It seems better to require an exact path to the branch
 
596
        # Branch.open() rather than using Branch.open_containing()
 
597
        self.assertRaises(errors.NotBranchError,
 
598
                          self.get_in_history, 'branch:tree2/a')
 
599
 
 
600
    def test_simple(self):
 
601
        self.assertInHistoryIs(None, 'alt_r2', 'branch:tree2')
 
602
 
 
603
    def test_self(self):
 
604
        self.assertInHistoryIs(2, 'r2', 'branch:tree')
 
605
 
 
606
    def test_unrelated(self):
 
607
        new_tree = self.make_branch_and_tree('new_tree')
 
608
 
 
609
        new_tree.commit('Commit one', rev_id='new_r1')
 
610
        new_tree.commit('Commit two', rev_id='new_r2')
 
611
        new_tree.commit('Commit three', rev_id='new_r3')
 
612
 
 
613
        self.assertInHistoryIs(None, 'new_r3', 'branch:new_tree')
 
614
 
 
615
        # XXX: Right now, we use fetch() to make sure the remote revisions
 
616
        # have been pulled into the local branch. We may change that
 
617
        # behavior in the future.
 
618
        self.failUnless(self.tree.branch.repository.has_revision('new_r3'))
 
619
 
 
620
    def test_no_commits(self):
 
621
        new_tree = self.make_branch_and_tree('new_tree')
 
622
        self.assertRaises(errors.NoCommits,
 
623
                          self.get_in_history, 'branch:new_tree')
 
624
        self.assertRaises(errors.NoCommits,
 
625
                          self.get_as_tree, 'branch:new_tree')
 
626
 
 
627
    def test_as_revision_id(self):
 
628
        self.assertAsRevisionId('alt_r2', 'branch:tree2')
 
629
 
 
630
    def test_as_tree(self):
 
631
        tree = self.get_as_tree('branch:tree', self.tree2)
 
632
        self.assertEquals('r2', tree.get_revision_id())
 
633
        self.assertFalse(self.tree2.branch.repository.has_revision('r2'))
 
634
 
 
635
 
 
636
class TestRevisionSpec_submit(TestRevisionSpec):
 
637
 
 
638
    def test_submit_branch(self):
 
639
        # Common ancestor of trees is 'alt_r2'
 
640
        self.assertRaises(errors.NoSubmitBranch, self.get_in_history,
 
641
                          'submit:')
 
642
        self.tree.branch.set_parent('../tree2')
 
643
        self.assertInHistoryIs(None, 'alt_r2', 'submit:')
 
644
        self.tree.branch.set_parent('bogus')
 
645
        self.assertRaises(errors.NotBranchError, self.get_in_history,
 
646
            'submit:')
 
647
        # submit branch overrides parent branch
 
648
        self.tree.branch.set_submit_branch('tree2')
 
649
        self.assertInHistoryIs(None, 'alt_r2', 'submit:')
 
650
 
 
651
    def test_as_revision_id(self):
 
652
        self.tree.branch.set_submit_branch('tree2')
 
653
        self.assertAsRevisionId('alt_r2', 'branch:tree2')