~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_revisionspec.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-02-18 13:27:08 UTC
  • mfrom: (4011.4.3 ssh-hints)
  • Revision ID: pqm@pqm.ubuntu.com-20090218132708-okubrahz9exvae9r
(Jelmer) Point out bzr+ssh:// to the user when they use ssh://.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
 
17
import datetime
17
18
import os
18
19
import time
19
 
from bzrlib.selftest import TestCaseInTempDir
20
 
from bzrlib.errors import NoCommonAncestor, NoCommits
21
 
from bzrlib.clone import copy_branch
22
 
from bzrlib.merge import merge
23
 
 
24
 
class TestRevisionNamespaces(TestCaseInTempDir):
25
 
 
26
 
    def test_revision_namespaces(self):
27
 
        """Test revision specifiers.
28
 
 
29
 
        These identify revisions by date, etc."""
30
 
        from bzrlib.errors import NoSuchRevision
31
 
        from bzrlib.branch import Branch
32
 
        from bzrlib.revisionspec import RevisionSpec
33
 
 
34
 
        b = Branch.initialize('.')
35
 
 
36
 
        b.commit('Commit one', rev_id='a@r-0-1', timestamp=time.time() - 60*60*24)
37
 
        b.commit('Commit two', rev_id='a@r-0-2')
38
 
        b.commit('Commit three', rev_id='a@r-0-3')
39
 
 
40
 
        self.assertEquals(RevisionSpec(None).in_history(b), (0, None))
41
 
        self.assertEquals(RevisionSpec(1).in_history(b), (1, 'a@r-0-1'))
42
 
        self.assertEquals(RevisionSpec('revno:1').in_history(b),
43
 
                          (1, 'a@r-0-1'))
44
 
        self.assertEquals(RevisionSpec('revid:a@r-0-1').in_history(b),
45
 
                          (1, 'a@r-0-1'))
46
 
        self.assertRaises(NoSuchRevision,
47
 
                          RevisionSpec('revid:a@r-0-0').in_history, b)
48
 
        self.assertRaises(TypeError, RevisionSpec, object)
49
 
 
50
 
        self.assertEquals(RevisionSpec('date:today').in_history(b),
51
 
                          (2, 'a@r-0-2'))
52
 
        self.assertEquals(RevisionSpec('date:yesterday').in_history(b),
53
 
                          (1, 'a@r-0-1'))
54
 
        self.assertEquals(RevisionSpec('before:date:today').in_history(b),
55
 
                          (1, 'a@r-0-1'))
56
 
 
57
 
        self.assertEquals(RevisionSpec('last:1').in_history(b),
58
 
                          (3, 'a@r-0-3'))
59
 
        self.assertEquals(RevisionSpec('-1').in_history(b), (3, 'a@r-0-3'))
60
 
#        self.assertEquals(b.get_revision_info('last:1'), (3, 'a@r-0-3'))
61
 
#        self.assertEquals(b.get_revision_info('-1'), (3, 'a@r-0-3'))
62
 
 
63
 
        self.assertEquals(RevisionSpec('ancestor:.').in_history(b).rev_id,
64
 
                          'a@r-0-3')
65
 
 
66
 
        os.mkdir('newbranch')
67
 
        b2 = Branch.initialize('newbranch')
68
 
        self.assertRaises(NoCommits, RevisionSpec('ancestor:.').in_history, b2)
69
 
 
70
 
        os.mkdir('copy')
71
 
        b3 = copy_branch(b, 'copy')
72
 
        b3.commit('Commit four', rev_id='b@r-0-4')
73
 
        self.assertEquals(RevisionSpec('ancestor:.').in_history(b3).rev_id,
74
 
                          'a@r-0-3')
75
 
        merge(['copy', -1], [None, None])
76
 
        b.commit('Commit five', rev_id='a@r-0-4')
77
 
        self.assertEquals(RevisionSpec('ancestor:copy').in_history(b).rev_id,
78
 
                          'b@r-0-4')
79
 
        self.assertEquals(RevisionSpec('ancestor:.').in_history(b3).rev_id,
80
 
                          'b@r-0-4')
 
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
 
 
157
class TestRevnoFromString(TestCase):
 
158
 
 
159
    def test_from_string_dotted_decimal(self):
 
160
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '-1.1')
 
161
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '.1')
 
162
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1..1')
 
163
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1.2..1')
 
164
        self.assertRaises(errors.NoSuchRevisionSpec, RevisionSpec.from_string, '1.')
 
165
        self.assertIsInstance(RevisionSpec.from_string('1.1'), RevisionSpec_revno)
 
166
        self.assertIsInstance(RevisionSpec.from_string('1.1.3'), RevisionSpec_revno)
 
167
 
 
168
 
 
169
class TestRevisionSpec_revno(TestRevisionSpec):
 
170
 
 
171
    def test_positive_int(self):
 
172
        self.assertInHistoryIs(0, 'null:', '0')
 
173
        self.assertInHistoryIs(1, 'r1', '1')
 
174
        self.assertInHistoryIs(2, 'r2', '2')
 
175
        self.assertInvalid('3')
 
176
 
 
177
    def test_dotted_decimal(self):
 
178
        self.assertInHistoryIs(None, 'alt_r2', '1.1.1')
 
179
        self.assertInvalid('1.1.123')
 
180
 
 
181
    def test_negative_int(self):
 
182
        self.assertInHistoryIs(2, 'r2', '-1')
 
183
        self.assertInHistoryIs(1, 'r1', '-2')
 
184
 
 
185
        self.assertInHistoryIs(1, 'r1', '-3')
 
186
        self.assertInHistoryIs(1, 'r1', '-4')
 
187
        self.assertInHistoryIs(1, 'r1', '-100')
 
188
 
 
189
    def test_positive(self):
 
190
        self.assertInHistoryIs(0, 'null:', 'revno:0')
 
191
        self.assertInHistoryIs(1, 'r1', 'revno:1')
 
192
        self.assertInHistoryIs(2, 'r2', 'revno:2')
 
193
 
 
194
        self.assertInvalid('revno:3')
 
195
 
 
196
    def test_negative(self):
 
197
        self.assertInHistoryIs(2, 'r2', 'revno:-1')
 
198
        self.assertInHistoryIs(1, 'r1', 'revno:-2')
 
199
 
 
200
        self.assertInHistoryIs(1, 'r1', 'revno:-3')
 
201
        self.assertInHistoryIs(1, 'r1', 'revno:-4')
 
202
 
 
203
    def test_invalid_number(self):
 
204
        # Get the right exception text
 
205
        try:
 
206
            int('X')
 
207
        except ValueError, e:
 
208
            pass
 
209
        self.assertInvalid('revno:X', extra='\n' + str(e))
 
210
 
 
211
    def test_missing_number_and_branch(self):
 
212
        self.assertInvalid('revno::',
 
213
                           extra='\ncannot have an empty revno and no branch')
 
214
 
 
215
    def test_invalid_number_with_branch(self):
 
216
        try:
 
217
            int('X')
 
218
        except ValueError, e:
 
219
            pass
 
220
        self.assertInvalid('revno:X:tree2', extra='\n' + str(e))
 
221
 
 
222
    def test_non_exact_branch(self):
 
223
        # It seems better to require an exact path to the branch
 
224
        # Branch.open() rather than using Branch.open_containing()
 
225
        spec = RevisionSpec.from_string('revno:2:tree2/a')
 
226
        self.assertRaises(errors.NotBranchError,
 
227
                          spec.in_history, self.tree.branch)
 
228
 
 
229
    def test_with_branch(self):
 
230
        # Passing a URL overrides the supplied branch path
 
231
        revinfo = self.get_in_history('revno:2:tree2')
 
232
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
233
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
234
        self.assertEqual(2, revinfo.revno)
 
235
        self.assertEqual('alt_r2', revinfo.rev_id)
 
236
 
 
237
    def test_int_with_branch(self):
 
238
        revinfo = self.get_in_history('2:tree2')
 
239
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
240
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
241
        self.assertEqual(2, revinfo.revno)
 
242
        self.assertEqual('alt_r2', revinfo.rev_id)
 
243
 
 
244
    def test_with_url(self):
 
245
        url = self.get_url() + '/tree2'
 
246
        revinfo = self.get_in_history('revno:2:%s' % (url,))
 
247
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
248
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
249
        self.assertEqual(2, revinfo.revno)
 
250
        self.assertEqual('alt_r2', revinfo.rev_id)
 
251
 
 
252
    def test_negative_with_url(self):
 
253
        url = self.get_url() + '/tree2'
 
254
        revinfo = self.get_in_history('revno:-1:%s' % (url,))
 
255
        self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
 
256
        self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
 
257
        self.assertEqual(2, revinfo.revno)
 
258
        self.assertEqual('alt_r2', revinfo.rev_id)
 
259
 
 
260
    def test_different_history_lengths(self):
 
261
        # Make sure we use the revisions and offsets in the supplied branch
 
262
        # not the ones in the original branch.
 
263
        self.tree2.commit('three', rev_id='r3')
 
264
        self.assertInHistoryIs(3, 'r3', 'revno:3:tree2')
 
265
        self.assertInHistoryIs(3, 'r3', 'revno:-1:tree2')
 
266
 
 
267
    def test_invalid_branch(self):
 
268
        self.assertRaises(errors.NotBranchError,
 
269
                          self.get_in_history, 'revno:-1:tree3')
 
270
 
 
271
    def test_invalid_revno_in_branch(self):
 
272
        self.tree.commit('three', rev_id='r3')
 
273
        self.assertInvalid('revno:3:tree2')
 
274
 
 
275
    def test_revno_n_path(self):
 
276
        """Old revno:N:path tests"""
 
277
        wta = self.make_branch_and_tree('a')
 
278
        ba = wta.branch
 
279
        
 
280
        wta.commit('Commit one', rev_id='a@r-0-1')
 
281
        wta.commit('Commit two', rev_id='a@r-0-2')
 
282
        wta.commit('Commit three', rev_id='a@r-0-3')
 
283
 
 
284
        wtb = self.make_branch_and_tree('b')
 
285
        bb = wtb.branch
 
286
 
 
287
        wtb.commit('Commit one', rev_id='b@r-0-1')
 
288
        wtb.commit('Commit two', rev_id='b@r-0-2')
 
289
        wtb.commit('Commit three', rev_id='b@r-0-3')
 
290
 
 
291
 
 
292
        self.assertEqual((1, 'a@r-0-1'),
 
293
                         spec_in_history('revno:1:a/', ba))
 
294
        # The argument of in_history should be ignored since it is
 
295
        # redundant with the path in the spec.
 
296
        self.assertEqual((1, 'a@r-0-1'),
 
297
                         spec_in_history('revno:1:a/', None))
 
298
        self.assertEqual((1, 'a@r-0-1'),
 
299
                         spec_in_history('revno:1:a/', bb))
 
300
        self.assertEqual((2, 'b@r-0-2'),
 
301
                         spec_in_history('revno:2:b/', None))
 
302
 
 
303
    def test_as_revision_id(self):
 
304
        self.assertAsRevisionId('null:', '0')
 
305
        self.assertAsRevisionId('r1', '1')
 
306
        self.assertAsRevisionId('r2', '2')
 
307
        self.assertAsRevisionId('r1', '-2')
 
308
        self.assertAsRevisionId('r2', '-1')
 
309
        self.assertAsRevisionId('alt_r2', '1.1.1')
 
310
 
 
311
    def test_as_tree(self):
 
312
        tree = self.get_as_tree('0')
 
313
        self.assertEquals(_mod_revision.NULL_REVISION, tree.get_revision_id())
 
314
        tree = self.get_as_tree('1')
 
315
        self.assertEquals('r1', tree.get_revision_id())
 
316
        tree = self.get_as_tree('2')
 
317
        self.assertEquals('r2', tree.get_revision_id())
 
318
        tree = self.get_as_tree('-2')
 
319
        self.assertEquals('r1', tree.get_revision_id())
 
320
        tree = self.get_as_tree('-1')
 
321
        self.assertEquals('r2', tree.get_revision_id())
 
322
        tree = self.get_as_tree('1.1.1')
 
323
        self.assertEquals('alt_r2', tree.get_revision_id())
 
324
 
 
325
 
 
326
class TestRevisionSpec_revid(TestRevisionSpec):
 
327
    
 
328
    def test_in_history(self):
 
329
        # We should be able to access revisions that are directly
 
330
        # in the history.
 
331
        self.assertInHistoryIs(1, 'r1', 'revid:r1')
 
332
        self.assertInHistoryIs(2, 'r2', 'revid:r2')
 
333
        
 
334
    def test_missing(self):
 
335
        self.assertInvalid('revid:r3', invalid_as_revision_id=False)
 
336
 
 
337
    def test_merged(self):
 
338
        """We can reach revisions in the ancestry"""
 
339
        self.assertInHistoryIs(None, 'alt_r2', 'revid:alt_r2')
 
340
 
 
341
    def test_not_here(self):
 
342
        self.tree2.commit('alt third', rev_id='alt_r3')
 
343
        # It exists in tree2, but not in tree
 
344
        self.assertInvalid('revid:alt_r3', invalid_as_revision_id=False)
 
345
 
 
346
    def test_in_repository(self):
 
347
        """We can get any revision id in the repository"""
 
348
        # XXX: This may change in the future, but for now, it is true
 
349
        self.tree2.commit('alt third', rev_id='alt_r3')
 
350
        self.tree.branch.repository.fetch(self.tree2.branch.repository,
 
351
                                          revision_id='alt_r3')
 
352
        self.assertInHistoryIs(None, 'alt_r3', 'revid:alt_r3')
 
353
 
 
354
    def test_unicode(self):
 
355
        """We correctly convert a unicode ui string to an encoded revid."""
 
356
        revision_id = u'\N{SNOWMAN}'.encode('utf-8')
 
357
        self.tree.commit('unicode', rev_id=revision_id)
 
358
        self.assertInHistoryIs(3, revision_id, u'revid:\N{SNOWMAN}')
 
359
        self.assertInHistoryIs(3, revision_id, 'revid:' + revision_id)
 
360
 
 
361
    def test_as_revision_id(self):
 
362
        self.assertAsRevisionId('r1', 'revid:r1')
 
363
        self.assertAsRevisionId('r2', 'revid:r2')
 
364
        self.assertAsRevisionId('alt_r2', 'revid:alt_r2')
 
365
 
 
366
 
 
367
class TestRevisionSpec_last(TestRevisionSpec):
 
368
 
 
369
    def test_positive(self):
 
370
        self.assertInHistoryIs(2, 'r2', 'last:1')
 
371
        self.assertInHistoryIs(1, 'r1', 'last:2')
 
372
        self.assertInHistoryIs(0, 'null:', 'last:3')
 
373
 
 
374
    def test_empty(self):
 
375
        self.assertInHistoryIs(2, 'r2', 'last:')
 
376
 
 
377
    def test_negative(self):
 
378
        self.assertInvalid('last:-1',
 
379
                           extra='\nyou must supply a positive value')
 
380
 
 
381
    def test_missing(self):
 
382
        self.assertInvalid('last:4')
 
383
 
 
384
    def test_no_history(self):
 
385
        tree = self.make_branch_and_tree('tree3')
 
386
 
 
387
        self.assertRaises(errors.NoCommits,
 
388
                          spec_in_history, 'last:', tree.branch)
 
389
 
 
390
    def test_not_a_number(self):
 
391
        try:
 
392
            int('Y')
 
393
        except ValueError, e:
 
394
            pass
 
395
        self.assertInvalid('last:Y', extra='\n' + str(e))
 
396
 
 
397
    def test_as_revision_id(self):
 
398
        self.assertAsRevisionId('r2', 'last:1')
 
399
        self.assertAsRevisionId('r1', 'last:2')
 
400
 
 
401
 
 
402
class TestRevisionSpec_before(TestRevisionSpec):
 
403
 
 
404
    def test_int(self):
 
405
        self.assertInHistoryIs(1, 'r1', 'before:2')
 
406
        self.assertInHistoryIs(1, 'r1', 'before:-1')
 
407
 
 
408
    def test_before_one(self):
 
409
        self.assertInHistoryIs(0, 'null:', 'before:1')
 
410
 
 
411
    def test_before_none(self):
 
412
        self.assertInvalid('before:0',
 
413
                           extra='\ncannot go before the null: revision')
 
414
 
 
415
    def test_revid(self):
 
416
        self.assertInHistoryIs(1, 'r1', 'before:revid:r2')
 
417
 
 
418
    def test_last(self):
 
419
        self.assertInHistoryIs(1, 'r1', 'before:last:1')
 
420
 
 
421
    def test_alt_revid(self):
 
422
        # This will grab the left-most ancestor for alternate histories
 
423
        self.assertInHistoryIs(1, 'r1', 'before:revid:alt_r2')
 
424
 
 
425
    def test_alt_no_parents(self):
 
426
        new_tree = self.make_branch_and_tree('new_tree')
 
427
        new_tree.commit('first', rev_id='new_r1')
 
428
        self.tree.branch.repository.fetch(new_tree.branch.repository,
 
429
                                          revision_id='new_r1')
 
430
        self.assertInHistoryIs(0, 'null:', 'before:revid:new_r1')
 
431
 
 
432
    def test_as_revision_id(self):
 
433
        self.assertAsRevisionId('r1', 'before:revid:r2')
 
434
        self.assertAsRevisionId('r1', 'before:2')
 
435
        self.assertAsRevisionId('r1', 'before:1.1.1')
 
436
        self.assertAsRevisionId('r1', 'before:revid:alt_r2')
 
437
 
 
438
 
 
439
class TestRevisionSpec_tag(TestRevisionSpec):
 
440
    
 
441
    def make_branch_and_tree(self, relpath):
 
442
        # override format as the default one may not support tags
 
443
        return TestRevisionSpec.make_branch_and_tree(
 
444
            self, relpath, format='dirstate-tags')
 
445
 
 
446
    def test_from_string_tag(self):
 
447
        spec = RevisionSpec.from_string('tag:bzr-0.14')
 
448
        self.assertIsInstance(spec, RevisionSpec_tag)
 
449
        self.assertEqual(spec.spec, 'bzr-0.14')
 
450
 
 
451
    def test_lookup_tag(self):
 
452
        self.tree.branch.tags.set_tag('bzr-0.14', 'r1')
 
453
        self.assertInHistoryIs(1, 'r1', 'tag:bzr-0.14')
 
454
        self.tree.branch.tags.set_tag('null_rev', 'null:')
 
455
        self.assertInHistoryIs(0, 'null:', 'tag:null_rev')
 
456
 
 
457
    def test_failed_lookup(self):
 
458
        # tags that don't exist give a specific message: arguably we should
 
459
        # just give InvalidRevisionSpec but I think this is more helpful
 
460
        self.assertRaises(errors.NoSuchTag,
 
461
            self.get_in_history,
 
462
            'tag:some-random-tag')
 
463
 
 
464
    def test_as_revision_id(self):
 
465
        self.tree.branch.tags.set_tag('my-tag', 'r2')
 
466
        self.tree.branch.tags.set_tag('null_rev', 'null:')
 
467
        self.assertAsRevisionId('r2', 'tag:my-tag')
 
468
        self.assertAsRevisionId('null:', 'tag:null_rev')
 
469
        self.assertAsRevisionId('r1', 'before:tag:my-tag')
 
470
 
 
471
 
 
472
class TestRevisionSpec_date(TestRevisionSpec):
 
473
 
 
474
    def setUp(self):
 
475
        super(TestRevisionSpec, self).setUp()
 
476
 
 
477
        new_tree = self.make_branch_and_tree('new_tree')
 
478
        new_tree.commit('Commit one', rev_id='new_r1',
 
479
                        timestamp=time.time() - 60*60*24)
 
480
        new_tree.commit('Commit two', rev_id='new_r2')
 
481
        new_tree.commit('Commit three', rev_id='new_r3')
 
482
 
 
483
        self.tree = new_tree
 
484
 
 
485
    def test_tomorrow(self):
 
486
        self.assertInvalid('date:tomorrow')
 
487
 
 
488
    def test_today(self):
 
489
        self.assertInHistoryIs(2, 'new_r2', 'date:today')
 
490
        self.assertInHistoryIs(1, 'new_r1', 'before:date:today')
 
491
 
 
492
    def test_yesterday(self):
 
493
        self.assertInHistoryIs(1, 'new_r1', 'date:yesterday')
 
494
 
 
495
    def test_invalid(self):
 
496
        self.assertInvalid('date:foobar', extra='\ninvalid date')
 
497
        # You must have '-' between year/month/day
 
498
        self.assertInvalid('date:20040404', extra='\ninvalid date')
 
499
        # Need 2 digits for each date piece
 
500
        self.assertInvalid('date:2004-4-4', extra='\ninvalid date')
 
501
 
 
502
    def test_day(self):
 
503
        now = datetime.datetime.now()
 
504
        self.assertInHistoryIs(2, 'new_r2',
 
505
            'date:%04d-%02d-%02d' % (now.year, now.month, now.day))
 
506
 
 
507
    def test_as_revision_id(self):
 
508
        self.assertAsRevisionId('new_r2', 'date:today')
 
509
 
 
510
 
 
511
class TestRevisionSpec_ancestor(TestRevisionSpec):
 
512
    
 
513
    def test_non_exact_branch(self):
 
514
        # It seems better to require an exact path to the branch
 
515
        # Branch.open() rather than using Branch.open_containing()
 
516
        self.assertRaises(errors.NotBranchError,
 
517
                          self.get_in_history, 'ancestor:tree2/a')
 
518
 
 
519
    def test_simple(self):
 
520
        # Common ancestor of trees is 'alt_r2'
 
521
        self.assertInHistoryIs(None, 'alt_r2', 'ancestor:tree2')
 
522
 
 
523
        # Going the other way, we get a valid revno
 
524
        tmp = self.tree
 
525
        self.tree = self.tree2
 
526
        self.tree2 = tmp
 
527
        self.assertInHistoryIs(2, 'alt_r2', 'ancestor:tree')
 
528
 
 
529
    def test_self(self):
 
530
        self.assertInHistoryIs(2, 'r2', 'ancestor:tree')
 
531
 
 
532
    def test_unrelated(self):
 
533
        new_tree = self.make_branch_and_tree('new_tree')
 
534
 
 
535
        new_tree.commit('Commit one', rev_id='new_r1')
 
536
        new_tree.commit('Commit two', rev_id='new_r2')
 
537
        new_tree.commit('Commit three', rev_id='new_r3')
 
538
 
 
539
        # With no common ancestor, we should raise another user error
 
540
        self.assertRaises(errors.NoCommonAncestor,
 
541
                          self.get_in_history, 'ancestor:new_tree')
 
542
 
 
543
    def test_no_commits(self):
 
544
        new_tree = self.make_branch_and_tree('new_tree')
 
545
        self.assertRaises(errors.NoCommits,
 
546
                          spec_in_history, 'ancestor:new_tree',
 
547
                                           self.tree.branch)
 
548
                        
 
549
        self.assertRaises(errors.NoCommits,
 
550
                          spec_in_history, 'ancestor:tree',
 
551
                                           new_tree.branch)
 
552
 
 
553
    def test_as_revision_id(self):
 
554
        self.assertAsRevisionId('alt_r2', 'ancestor:tree2')
 
555
 
 
556
    def test_default(self):
 
557
        # We don't have a parent to default to
 
558
        self.assertRaises(errors.NotBranchError, self.get_in_history,
 
559
                          'ancestor:')
 
560
 
 
561
        # Create a branch with a parent to default to
 
562
        tree3 = self.tree.bzrdir.sprout('tree3').open_workingtree()
 
563
        tree3.commit('foo', rev_id='r3')
 
564
        self.tree = tree3
 
565
        self.assertInHistoryIs(2, 'r2', 'ancestor:')
 
566
 
 
567
 
 
568
class TestRevisionSpec_branch(TestRevisionSpec):
 
569
    
 
570
    def test_non_exact_branch(self):
 
571
        # It seems better to require an exact path to the branch
 
572
        # Branch.open() rather than using Branch.open_containing()
 
573
        self.assertRaises(errors.NotBranchError,
 
574
                          self.get_in_history, 'branch:tree2/a')
 
575
 
 
576
    def test_simple(self):
 
577
        self.assertInHistoryIs(None, 'alt_r2', 'branch:tree2')
 
578
 
 
579
    def test_self(self):
 
580
        self.assertInHistoryIs(2, 'r2', 'branch:tree')
 
581
 
 
582
    def test_unrelated(self):
 
583
        new_tree = self.make_branch_and_tree('new_tree')
 
584
 
 
585
        new_tree.commit('Commit one', rev_id='new_r1')
 
586
        new_tree.commit('Commit two', rev_id='new_r2')
 
587
        new_tree.commit('Commit three', rev_id='new_r3')
 
588
 
 
589
        self.assertInHistoryIs(None, 'new_r3', 'branch:new_tree')
 
590
 
 
591
        # XXX: Right now, we use fetch() to make sure the remote revisions
 
592
        # have been pulled into the local branch. We may change that
 
593
        # behavior in the future.
 
594
        self.failUnless(self.tree.branch.repository.has_revision('new_r3'))
 
595
 
 
596
    def test_no_commits(self):
 
597
        new_tree = self.make_branch_and_tree('new_tree')
 
598
        self.assertRaises(errors.NoCommits,
 
599
                          self.get_in_history, 'branch:new_tree')
 
600
        self.assertRaises(errors.NoCommits,
 
601
                          self.get_as_tree, 'branch:new_tree')
 
602
 
 
603
    def test_as_revision_id(self):
 
604
        self.assertAsRevisionId('alt_r2', 'branch:tree2')
 
605
 
 
606
    def test_as_tree(self):
 
607
        tree = self.get_as_tree('branch:tree', self.tree2)
 
608
        self.assertEquals('r2', tree.get_revision_id())
 
609
        self.assertFalse(self.tree2.branch.repository.has_revision('r2'))
 
610
 
 
611
 
 
612
class TestRevisionSpec_submit(TestRevisionSpec):
 
613
 
 
614
    def test_submit_branch(self):
 
615
        # Common ancestor of trees is 'alt_r2'
 
616
        self.assertRaises(errors.NoSubmitBranch, self.get_in_history,
 
617
                          'submit:')
 
618
        self.tree.branch.set_parent('../tree2')
 
619
        self.assertInHistoryIs(None, 'alt_r2', 'submit:')
 
620
        self.tree.branch.set_parent('bogus')
 
621
        self.assertRaises(errors.NotBranchError, self.get_in_history,
 
622
            'submit:')
 
623
        # submit branch overrides parent branch
 
624
        self.tree.branch.set_submit_branch('tree2')
 
625
        self.assertInHistoryIs(None, 'alt_r2', 'submit:')
 
626
 
 
627
    def test_as_revision_id(self):
 
628
        self.tree.branch.set_submit_branch('tree2')
 
629
        self.assertAsRevisionId('alt_r2', 'branch:tree2')