~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: 2008-09-02 03:59:07 UTC
  • mfrom: (3653.2.3 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080902035907-3b81euge0gyypozk
Remove obsolete dev formats.

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