~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test__annotator.py

  • Committer: John Arbash Meinel
  • Date: 2009-02-19 20:55:17 UTC
  • mto: (0.22.4 experimental)
  • mto: This revision was merged to the branch mainline in revision 4280.
  • Revision ID: john@arbash-meinel.com-20090219205517-drw89424koe6h1da
Play around a bit.

1) Empty texts are no-op inserted, to avoid ever trying to match against their text.
2) If we find a new file-id and the compressor is more than half full, we go
ahead and start a new compressor.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Tests for Annotators."""
18
 
 
19
 
from bzrlib import (
20
 
    annotate,
21
 
    _annotator_py,
22
 
    errors,
23
 
    knit,
24
 
    revision,
25
 
    tests,
26
 
    )
27
 
 
28
 
 
29
 
def load_tests(standard_tests, module, loader):
30
 
    """Parameterize tests for all versions of groupcompress."""
31
 
    suite, _ = tests.permute_tests_for_extension(standard_tests, loader,
32
 
        'bzrlib._annotator_py', 'bzrlib._annotator_pyx')
33
 
    return suite
34
 
 
35
 
 
36
 
class TestAnnotator(tests.TestCaseWithMemoryTransport):
37
 
 
38
 
    module = None # Set by load_tests
39
 
 
40
 
    fa_key = ('f-id', 'a-id')
41
 
    fb_key = ('f-id', 'b-id')
42
 
    fc_key = ('f-id', 'c-id')
43
 
    fd_key = ('f-id', 'd-id')
44
 
    fe_key = ('f-id', 'e-id')
45
 
    ff_key = ('f-id', 'f-id')
46
 
 
47
 
    def make_no_graph_texts(self):
48
 
        factory = knit.make_pack_factory(False, False, 2)
49
 
        self.vf = factory(self.get_transport())
50
 
        self.ann = self.module.Annotator(self.vf)
51
 
        self.vf.add_lines(self.fa_key, (), ['simple\n', 'content\n'])
52
 
        self.vf.add_lines(self.fb_key, (), ['simple\n', 'new content\n'])
53
 
 
54
 
    def make_simple_text(self):
55
 
        # TODO: all we really need is a VersionedFile instance, we'd like to
56
 
        #       avoid creating all the intermediate stuff
57
 
        factory = knit.make_pack_factory(True, True, 2)
58
 
        self.vf = factory(self.get_transport())
59
 
        # This assumes nothing special happens during __init__, which may be
60
 
        # valid
61
 
        self.ann = self.module.Annotator(self.vf)
62
 
        #  A    'simple|content|'
63
 
        #  |
64
 
        #  B    'simple|new content|'
65
 
        self.vf.add_lines(self.fa_key, [], ['simple\n', 'content\n'])
66
 
        self.vf.add_lines(self.fb_key, [self.fa_key],
67
 
                          ['simple\n', 'new content\n'])
68
 
 
69
 
    def make_merge_text(self):
70
 
        self.make_simple_text()
71
 
        #  A    'simple|content|'
72
 
        #  |\
73
 
        #  B |  'simple|new content|'
74
 
        #  | |
75
 
        #  | C  'simple|from c|content|'
76
 
        #  |/
77
 
        #  D    'simple|from c|new content|introduced in merge|'
78
 
        self.vf.add_lines(self.fc_key, [self.fa_key],
79
 
                          ['simple\n', 'from c\n', 'content\n'])
80
 
        self.vf.add_lines(self.fd_key, [self.fb_key, self.fc_key],
81
 
                          ['simple\n', 'from c\n', 'new content\n',
82
 
                           'introduced in merge\n'])
83
 
 
84
 
    def make_common_merge_text(self):
85
 
        """Both sides of the merge will have introduced a line."""
86
 
        self.make_simple_text()
87
 
        #  A    'simple|content|'
88
 
        #  |\
89
 
        #  B |  'simple|new content|'
90
 
        #  | |
91
 
        #  | C  'simple|new content|'
92
 
        #  |/
93
 
        #  D    'simple|new content|'
94
 
        self.vf.add_lines(self.fc_key, [self.fa_key],
95
 
                          ['simple\n', 'new content\n'])
96
 
        self.vf.add_lines(self.fd_key, [self.fb_key, self.fc_key],
97
 
                          ['simple\n', 'new content\n'])
98
 
 
99
 
    def make_many_way_common_merge_text(self):
100
 
        self.make_simple_text()
101
 
        #  A-.    'simple|content|'
102
 
        #  |\ \
103
 
        #  B | |  'simple|new content|'
104
 
        #  | | |
105
 
        #  | C |  'simple|new content|'
106
 
        #  |/  |
107
 
        #  D   |  'simple|new content|'
108
 
        #  |   |
109
 
        #  |   E  'simple|new content|'
110
 
        #  |  /
111
 
        #  F-'    'simple|new content|'
112
 
        self.vf.add_lines(self.fc_key, [self.fa_key],
113
 
                          ['simple\n', 'new content\n'])
114
 
        self.vf.add_lines(self.fd_key, [self.fb_key, self.fc_key],
115
 
                          ['simple\n', 'new content\n'])
116
 
        self.vf.add_lines(self.fe_key, [self.fa_key],
117
 
                          ['simple\n', 'new content\n'])
118
 
        self.vf.add_lines(self.ff_key, [self.fd_key, self.fe_key],
119
 
                          ['simple\n', 'new content\n'])
120
 
 
121
 
    def make_merge_and_restored_text(self):
122
 
        self.make_simple_text()
123
 
        #  A    'simple|content|'
124
 
        #  |\
125
 
        #  B |  'simple|new content|'
126
 
        #  | |
127
 
        #  C |  'simple|content|' # reverted to A
128
 
        #   \|
129
 
        #    D  'simple|content|'
130
 
        # c reverts back to 'a' for the new content line
131
 
        self.vf.add_lines(self.fc_key, [self.fb_key],
132
 
                          ['simple\n', 'content\n'])
133
 
        # d merges 'a' and 'c', to find both claim last modified
134
 
        self.vf.add_lines(self.fd_key, [self.fa_key, self.fc_key],
135
 
                          ['simple\n', 'content\n'])
136
 
 
137
 
    def assertAnnotateEqual(self, expected_annotation, key, exp_text=None):
138
 
        annotation, lines = self.ann.annotate(key)
139
 
        self.assertEqual(expected_annotation, annotation)
140
 
        if exp_text is None:
141
 
            record = self.vf.get_record_stream([key], 'unordered', True).next()
142
 
            exp_text = record.get_bytes_as('fulltext')
143
 
        self.assertEqualDiff(exp_text, ''.join(lines))
144
 
 
145
 
    def test_annotate_missing(self):
146
 
        self.make_simple_text()
147
 
        self.assertRaises(errors.RevisionNotPresent,
148
 
                          self.ann.annotate, ('not', 'present'))
149
 
 
150
 
    def test_annotate_simple(self):
151
 
        self.make_simple_text()
152
 
        self.assertAnnotateEqual([(self.fa_key,)]*2, self.fa_key)
153
 
        self.assertAnnotateEqual([(self.fa_key,), (self.fb_key,)], self.fb_key)
154
 
 
155
 
    def test_annotate_merge_text(self):
156
 
        self.make_merge_text()
157
 
        self.assertAnnotateEqual([(self.fa_key,), (self.fc_key,),
158
 
                                  (self.fb_key,), (self.fd_key,)],
159
 
                                 self.fd_key)
160
 
 
161
 
    def test_annotate_common_merge_text(self):
162
 
        self.make_common_merge_text()
163
 
        self.assertAnnotateEqual([(self.fa_key,), (self.fb_key, self.fc_key)],
164
 
                                 self.fd_key)
165
 
 
166
 
    def test_annotate_many_way_common_merge_text(self):
167
 
        self.make_many_way_common_merge_text()
168
 
        self.assertAnnotateEqual([(self.fa_key,),
169
 
                                  (self.fb_key, self.fc_key, self.fe_key)],
170
 
                                 self.ff_key)
171
 
 
172
 
    def test_annotate_merge_and_restored(self):
173
 
        self.make_merge_and_restored_text()
174
 
        self.assertAnnotateEqual([(self.fa_key,), (self.fa_key, self.fc_key)],
175
 
                                 self.fd_key)
176
 
 
177
 
    def test_annotate_flat_simple(self):
178
 
        self.make_simple_text()
179
 
        self.assertEqual([(self.fa_key, 'simple\n'),
180
 
                          (self.fa_key, 'content\n'),
181
 
                         ], self.ann.annotate_flat(self.fa_key))
182
 
        self.assertEqual([(self.fa_key, 'simple\n'),
183
 
                          (self.fb_key, 'new content\n'),
184
 
                         ], self.ann.annotate_flat(self.fb_key))
185
 
 
186
 
    def test_annotate_flat_merge_and_restored_text(self):
187
 
        self.make_merge_and_restored_text()
188
 
        # fc is a simple dominator of fa
189
 
        self.assertEqual([(self.fa_key, 'simple\n'),
190
 
                          (self.fc_key, 'content\n'),
191
 
                         ], self.ann.annotate_flat(self.fd_key))
192
 
 
193
 
    def test_annotate_common_merge_text(self):
194
 
        self.make_common_merge_text()
195
 
        # there is no common point, so we just pick the lexicographical lowest
196
 
        # and 'b-id' comes before 'c-id'
197
 
        self.assertEqual([(self.fa_key, 'simple\n'),
198
 
                          (self.fb_key, 'new content\n'),
199
 
                         ], self.ann.annotate_flat(self.fd_key))
200
 
 
201
 
    def test_annotate_many_way_common_merge_text(self):
202
 
        self.make_many_way_common_merge_text()
203
 
        self.assertEqual([(self.fa_key, 'simple\n'),
204
 
                         (self.fb_key, 'new content\n')],
205
 
                         self.ann.annotate_flat(self.ff_key))
206
 
 
207
 
    def test_annotate_flat_respects_break_ann_tie(self):
208
 
        tiebreaker = annotate._break_annotation_tie
209
 
        try:
210
 
            calls = []
211
 
            def custom_tiebreaker(annotated_lines):
212
 
                self.assertEqual(2, len(annotated_lines))
213
 
                left = annotated_lines[0]
214
 
                self.assertEqual(2, len(left))
215
 
                self.assertEqual('new content\n', left[1])
216
 
                right = annotated_lines[1]
217
 
                self.assertEqual(2, len(right))
218
 
                self.assertEqual('new content\n', right[1])
219
 
                calls.append((left[0], right[0]))
220
 
                # Our custom tiebreaker takes the *largest* value, rather than
221
 
                # the *smallest* value
222
 
                if left[0] < right[0]:
223
 
                    return right
224
 
                else:
225
 
                    return left
226
 
            annotate._break_annotation_tie = custom_tiebreaker
227
 
            self.make_many_way_common_merge_text()
228
 
            self.assertEqual([(self.fa_key, 'simple\n'),
229
 
                             (self.fe_key, 'new content\n')],
230
 
                             self.ann.annotate_flat(self.ff_key))
231
 
            self.assertEqual([(self.fe_key, self.fc_key),
232
 
                              (self.fe_key, self.fb_key)], calls)
233
 
        finally:
234
 
            annotate._break_annotation_tie = tiebreaker
235
 
 
236
 
 
237
 
    def test_needed_keys_simple(self):
238
 
        self.make_simple_text()
239
 
        keys, ann_keys = self.ann._get_needed_keys(self.fb_key)
240
 
        self.assertEqual([self.fa_key, self.fb_key], sorted(keys))
241
 
        self.assertEqual({self.fa_key: 1, self.fb_key: 1},
242
 
                         self.ann._num_needed_children)
243
 
        self.assertEqual(set(), ann_keys)
244
 
 
245
 
    def test_needed_keys_many(self):
246
 
        self.make_many_way_common_merge_text()
247
 
        keys, ann_keys = self.ann._get_needed_keys(self.ff_key)
248
 
        self.assertEqual([self.fa_key, self.fb_key, self.fc_key,
249
 
                          self.fd_key, self.fe_key, self.ff_key,
250
 
                         ], sorted(keys))
251
 
        self.assertEqual({self.fa_key: 3,
252
 
                          self.fb_key: 1,
253
 
                          self.fc_key: 1,
254
 
                          self.fd_key: 1,
255
 
                          self.fe_key: 1,
256
 
                          self.ff_key: 1,
257
 
                         }, self.ann._num_needed_children)
258
 
        self.assertEqual(set(), ann_keys)
259
 
 
260
 
    def test_needed_keys_with_special_text(self):
261
 
        self.make_many_way_common_merge_text()
262
 
        spec_key = ('f-id', revision.CURRENT_REVISION)
263
 
        spec_text = 'simple\nnew content\nlocally modified\n'
264
 
        self.ann.add_special_text(spec_key, [self.fd_key, self.fe_key],
265
 
                                  spec_text)
266
 
        keys, ann_keys = self.ann._get_needed_keys(spec_key)
267
 
        self.assertEqual([self.fa_key, self.fb_key, self.fc_key,
268
 
                          self.fd_key, self.fe_key,
269
 
                         ], sorted(keys))
270
 
        self.assertEqual([spec_key], sorted(ann_keys))
271
 
 
272
 
    def test_needed_keys_with_parent_texts(self):
273
 
        self.make_many_way_common_merge_text()
274
 
        # If 'D' and 'E' are already annotated, we don't need to extract all
275
 
        # the texts
276
 
        #  D   |  'simple|new content|'
277
 
        #  |   |
278
 
        #  |   E  'simple|new content|'
279
 
        #  |  /
280
 
        #  F-'    'simple|new content|'
281
 
        self.ann._parent_map[self.fd_key] = (self.fb_key, self.fc_key)
282
 
        self.ann._text_cache[self.fd_key] = ['simple\n', 'new content\n']
283
 
        self.ann._annotations_cache[self.fd_key] = [
284
 
            (self.fa_key,),
285
 
            (self.fb_key, self.fc_key),
286
 
            ]
287
 
        self.ann._parent_map[self.fe_key] = (self.fa_key,)
288
 
        self.ann._text_cache[self.fe_key] = ['simple\n', 'new content\n']
289
 
        self.ann._annotations_cache[self.fe_key] = [
290
 
            (self.fa_key,),
291
 
            (self.fe_key,),
292
 
            ]
293
 
        keys, ann_keys = self.ann._get_needed_keys(self.ff_key)
294
 
        self.assertEqual([self.ff_key], sorted(keys))
295
 
        self.assertEqual({self.fd_key: 1,
296
 
                          self.fe_key: 1,
297
 
                          self.ff_key: 1,
298
 
                         }, self.ann._num_needed_children)
299
 
        self.assertEqual([], sorted(ann_keys))
300
 
 
301
 
    def test_record_annotation_removes_texts(self):
302
 
        self.make_many_way_common_merge_text()
303
 
        # Populate the caches
304
 
        for x in self.ann._get_needed_texts(self.ff_key):
305
 
            continue
306
 
        self.assertEqual({self.fa_key: 3,
307
 
                          self.fb_key: 1,
308
 
                          self.fc_key: 1,
309
 
                          self.fd_key: 1,
310
 
                          self.fe_key: 1,
311
 
                          self.ff_key: 1,
312
 
                         }, self.ann._num_needed_children)
313
 
        self.assertEqual([self.fa_key, self.fb_key, self.fc_key,
314
 
                          self.fd_key, self.fe_key, self.ff_key,
315
 
                         ], sorted(self.ann._text_cache.keys()))
316
 
        self.ann._record_annotation(self.fa_key, [], [])
317
 
        self.ann._record_annotation(self.fb_key, [self.fa_key], [])
318
 
        self.assertEqual({self.fa_key: 2,
319
 
                          self.fb_key: 1,
320
 
                          self.fc_key: 1,
321
 
                          self.fd_key: 1,
322
 
                          self.fe_key: 1,
323
 
                          self.ff_key: 1,
324
 
                         }, self.ann._num_needed_children)
325
 
        self.assertTrue(self.fa_key in self.ann._text_cache)
326
 
        self.assertTrue(self.fa_key in self.ann._annotations_cache)
327
 
        self.ann._record_annotation(self.fc_key, [self.fa_key], [])
328
 
        self.ann._record_annotation(self.fd_key, [self.fb_key, self.fc_key], [])
329
 
        self.assertEqual({self.fa_key: 1,
330
 
                          self.fb_key: 0,
331
 
                          self.fc_key: 0,
332
 
                          self.fd_key: 1,
333
 
                          self.fe_key: 1,
334
 
                          self.ff_key: 1,
335
 
                         }, self.ann._num_needed_children)
336
 
        self.assertTrue(self.fa_key in self.ann._text_cache)
337
 
        self.assertTrue(self.fa_key in self.ann._annotations_cache)
338
 
        self.assertFalse(self.fb_key in self.ann._text_cache)
339
 
        self.assertFalse(self.fb_key in self.ann._annotations_cache)
340
 
        self.assertFalse(self.fc_key in self.ann._text_cache)
341
 
        self.assertFalse(self.fc_key in self.ann._annotations_cache)
342
 
 
343
 
    def test_annotate_special_text(self):
344
 
        # Things like WT and PreviewTree want to annotate an arbitrary text
345
 
        # ('current:') so we need a way to add that to the group of files to be
346
 
        # annotated.
347
 
        self.make_many_way_common_merge_text()
348
 
        #  A-.    'simple|content|'
349
 
        #  |\ \
350
 
        #  B | |  'simple|new content|'
351
 
        #  | | |
352
 
        #  | C |  'simple|new content|'
353
 
        #  |/  |
354
 
        #  D   |  'simple|new content|'
355
 
        #  |   |
356
 
        #  |   E  'simple|new content|'
357
 
        #  |  /
358
 
        #  SPEC   'simple|new content|locally modified|'
359
 
        spec_key = ('f-id', revision.CURRENT_REVISION)
360
 
        spec_text = 'simple\nnew content\nlocally modified\n'
361
 
        self.ann.add_special_text(spec_key, [self.fd_key, self.fe_key],
362
 
                                  spec_text)
363
 
        self.assertAnnotateEqual([(self.fa_key,),
364
 
                                  (self.fb_key, self.fc_key, self.fe_key),
365
 
                                  (spec_key,),
366
 
                                 ], spec_key,
367
 
                                 exp_text=spec_text)
368
 
 
369
 
    def test_no_graph(self):
370
 
        self.make_no_graph_texts()
371
 
        self.assertAnnotateEqual([(self.fa_key,),
372
 
                                  (self.fa_key,),
373
 
                                 ], self.fa_key)
374
 
        self.assertAnnotateEqual([(self.fb_key,),
375
 
                                  (self.fb_key,),
376
 
                                 ], self.fb_key)