~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_revisionspec.py

  • Committer: Vincent Ladeuil
  • Date: 2013-08-09 15:09:23 UTC
  • mto: This revision was merged to the branch mainline in revision 6587.
  • Revision ID: v.ladeuil+lp@free.fr-20130809150923-y71dusyorep0hbkt
Fix various typos in docstrings. Rename 'buffer' to 'buf' since it's now a python builtin function.

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