~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: 2010-04-01 00:40:31 UTC
  • mfrom: (4081.2.5 bug513322-first)
  • Revision ID: pqm@pqm.ubuntu.com-20100401004031-pc7s84z6ahqunmy2
(mbp, for gagern) show first apparent author in gnu changelog

Show diffs side-by-side

added added

removed removed

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