/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
622.1.2 by John Arbash Meinel
Add tests of RevisionView that it can handle broken file-info properties.
1
# Copyright (C) 2007, 2008 John Arbash Meinel <john@arbash-meinel.com>
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Test the Commit functionality."""
18
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
19
import os
20
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
21
import gtk
22
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
23
from bzrlib import (
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
24
    branch,
25
    revision,
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
26
    tests,
635.2.8 by Vincent Ladeuil
Start testing patch behavior.
27
    uncommit,
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
28
    )
645 by Jelmer Vernooij
Fix more bencode imports.
29
try:
646 by Jelmer Vernooij
Reorder bencode imports, prefer the new location to prevent deprecation warnings.
30
    from bzrlib import bencode
31
except ImportError:
645 by Jelmer Vernooij
Fix more bencode imports.
32
    from bzrlib.util import bencode
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
33
34
from bzrlib.plugins.gtk import commit
35
36
37
# TODO: All we need is basic ancestry code to test this, we shouldn't need a
38
# TestCaseWithTransport, just a TestCaseWithMemoryTransport or somesuch.
39
40
class TestPendingRevisions(tests.TestCaseWithTransport):
41
42
    def test_pending_revisions_none(self):
43
        tree = self.make_branch_and_tree('.')
44
        tree.commit('one')
45
46
        self.assertIs(None, commit.pending_revisions(tree))
47
48
    def test_pending_revisions_simple(self):
49
        tree = self.make_branch_and_tree('tree')
50
        rev_id1 = tree.commit('one')
51
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
52
        rev_id2 = tree2.commit('two')
53
        tree.merge_from_branch(tree2.branch)
54
        self.assertEqual([rev_id1, rev_id2], tree.get_parent_ids())
55
56
        pending_revisions = commit.pending_revisions(tree)
57
        # One primary merge
58
        self.assertEqual(1, len(pending_revisions))
59
        # Revision == rev_id2
60
        self.assertEqual(rev_id2, pending_revisions[0][0].revision_id)
61
        # No children of this revision.
62
        self.assertEqual([], pending_revisions[0][1])
63
64
    def test_pending_revisions_with_children(self):
65
        tree = self.make_branch_and_tree('tree')
66
        rev_id1 = tree.commit('one')
67
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
68
        rev_id2 = tree2.commit('two')
69
        rev_id3 = tree2.commit('three')
70
        rev_id4 = tree2.commit('four')
71
        tree.merge_from_branch(tree2.branch)
72
        self.assertEqual([rev_id1, rev_id4], tree.get_parent_ids())
73
74
        pending_revisions = commit.pending_revisions(tree)
75
        # One primary merge
76
        self.assertEqual(1, len(pending_revisions))
77
        # Revision == rev_id2
78
        self.assertEqual(rev_id4, pending_revisions[0][0].revision_id)
79
        # Two children for this revision
80
        self.assertEqual(2, len(pending_revisions[0][1]))
81
        self.assertEqual(rev_id3, pending_revisions[0][1][0].revision_id)
82
        self.assertEqual(rev_id2, pending_revisions[0][1][1].revision_id)
83
84
    def test_pending_revisions_multi_merge(self):
85
        tree = self.make_branch_and_tree('tree')
86
        rev_id1 = tree.commit('one')
87
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
88
        rev_id2 = tree2.commit('two')
89
        tree3 = tree2.bzrdir.sprout('tree3').open_workingtree()
500.1.2 by Vincent Ladeuil
Fix third failing test (thanks to jam).
90
        rev_id3 = tree2.commit('three')
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
91
        rev_id4 = tree3.commit('four')
92
        rev_id5 = tree3.commit('five')
93
        tree.merge_from_branch(tree2.branch)
94
        tree.merge_from_branch(tree3.branch)
95
        self.assertEqual([rev_id1, rev_id3, rev_id5], tree.get_parent_ids())
96
97
        pending_revisions = commit.pending_revisions(tree)
98
        # Two primary merges
99
        self.assertEqual(2, len(pending_revisions))
100
        # Revision == rev_id2
101
        self.assertEqual(rev_id3, pending_revisions[0][0].revision_id)
102
        self.assertEqual(rev_id5, pending_revisions[1][0].revision_id)
103
        # One child for the first merge
104
        self.assertEqual(1, len(pending_revisions[0][1]))
105
        self.assertEqual(rev_id2, pending_revisions[0][1][0].revision_id)
106
        # One child for the second merge
107
        self.assertEqual(1, len(pending_revisions[1][1]))
108
        self.assertEqual(rev_id4, pending_revisions[1][1][0].revision_id)
109
110
111
class Test_RevToPendingInfo(tests.TestCaseWithTransport):
112
113
    def test_basic_info(self):
114
        tree = self.make_branch_and_tree('tree')
115
        rev_id = tree.commit('Multiline\ncommit\nmessage',
116
                             committer='Joe Foo <joe@foo.com>',
117
                             timestamp=1191012408.674,
118
                             timezone=-18000
119
                             )
120
        rev = tree.branch.repository.get_revision(rev_id)
121
        rev_dict = commit.CommitDialog._rev_to_pending_info(rev)
122
        self.assertEqual({'committer':'Joe Foo',
123
                          'summary':'Multiline',
124
                          'date':'2007-09-28',
125
                          'revision_id':rev_id,
126
                         }, rev_dict)
127
128
129
class CommitDialogNoWidgets(commit.CommitDialog):
130
131
    def construct(self):
132
        pass # Don't create any widgets here
133
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
134
    def fill_in_data(self):
135
        pass # With no widgets, there are no widgets to fill out
136
137
138
class TestCommitDialogSimple(tests.TestCaseWithTransport):
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
139
140
    def test_setup_parameters_no_pending(self):
141
        tree = self.make_branch_and_tree('tree')
142
        rev_id = tree.commit('first')
143
144
        dlg = CommitDialogNoWidgets(tree)
145
        self.assertEqual(rev_id, dlg._basis_tree.get_revision_id())
146
        self.assertIs(None, dlg._pending)
147
        self.assertFalse(dlg._is_checkout)
148
149
    def test_setup_parameters_checkout(self):
150
        tree = self.make_branch_and_tree('tree')
151
        rev_id = tree.commit('first')
152
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
153
        tree2.branch.bind(tree.branch)
154
155
        dlg = CommitDialogNoWidgets(tree2)
156
        self.assertEqual(rev_id, dlg._basis_tree.get_revision_id())
157
        self.assertIs(None, dlg._pending)
158
        self.assertTrue(dlg._is_checkout)
159
160
    def test_setup_parameters_pending(self):
161
        tree = self.make_branch_and_tree('tree')
162
        rev_id1 = tree.commit('one')
163
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
164
        rev_id2 = tree2.commit('two')
165
        tree.merge_from_branch(tree2.branch)
166
167
        dlg = CommitDialogNoWidgets(tree)
168
        self.assertEqual(rev_id1, dlg._basis_tree.get_revision_id())
169
        self.assertIsNot(None, dlg._pending)
170
        self.assertEqual(1, len(dlg._pending))
171
        self.assertEqual(rev_id2, dlg._pending[0][0].revision_id)
172
173
    def test_setup_parameters_delta(self):
174
        tree = self.make_branch_and_tree('tree')
175
        self.build_tree(['tree/a'])
176
        tree.add(['a'], ['a-id'])
177
178
        dlg = CommitDialogNoWidgets(tree)
278.1.12 by John Arbash Meinel
Delay computing the delta, and clean up some of the diff view names.
179
        self.assertIs(None, dlg._delta)
180
        dlg._compute_delta()
181
278.1.5 by John Arbash Meinel
Starting to flesh out the dialog with actual windows.
182
        delta = dlg._delta
183
        self.assertEqual([], delta.modified)
184
        self.assertEqual([], delta.renamed)
185
        self.assertEqual([], delta.removed)
186
        self.assertEqual([(u'a', 'a-id', 'file')], delta.added)
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
187
188
189
class TestCommitDialog(tests.TestCaseWithTransport):
190
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
191
    def test_bound(self):
192
        tree = self.make_branch_and_tree('tree')
193
        rev_id = tree.commit('first')
194
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
195
        tree2.branch.bind(tree.branch)
196
197
        # tree is not a checkout
198
        dlg = commit.CommitDialog(tree)
199
        self.assertFalse(dlg._check_local.get_property('visible'))
200
201
        # tree2 is a checkout
202
        dlg2 = commit.CommitDialog(tree2)
203
        self.assertTrue(dlg2._check_local.get_property('visible'))
204
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
205
    def test_no_pending(self):
206
        tree = self.make_branch_and_tree('tree')
207
        rev_id1 = tree.commit('one')
208
209
        dlg = commit.CommitDialog(tree)
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
210
211
        self.assertFalse(dlg._pending_box.get_property('visible'))
212
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
213
        commit_col = dlg._treeview_files.get_column(0)
214
        self.assertEqual('Commit', commit_col.get_title())
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
215
        renderer = commit_col.get_cell_renderers()[0]
278.1.39 by John Arbash Meinel
To disable a checkbox it is set_property('activatable', False),
216
        self.assertTrue(renderer.get_property('activatable'))
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
217
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
218
        self.assertEqual('Commit all changes',
219
                         dlg._commit_all_files_radio.get_label())
220
        self.assertTrue(dlg._commit_all_files_radio.get_property('sensitive'))
221
        self.assertTrue(dlg._commit_selected_radio.get_property('sensitive'))
222
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
223
    def test_pending(self):
224
        tree = self.make_branch_and_tree('tree')
225
        rev_id1 = tree.commit('one')
226
227
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
228
        rev_id2 = tree2.commit('two',
229
                               committer='Joe Foo <joe@foo.com>',
230
                               timestamp=1191264271.05,
231
                               timezone=+7200)
232
        tree.merge_from_branch(tree2.branch)
233
234
        dlg = commit.CommitDialog(tree)
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
235
236
        self.assertTrue(dlg._pending_box.get_property('visible'))
237
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
238
        commit_col = dlg._treeview_files.get_column(0)
239
        self.assertEqual('Commit*', commit_col.get_title())
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
240
        renderer = commit_col.get_cell_renderers()[0]
278.1.39 by John Arbash Meinel
To disable a checkbox it is set_property('activatable', False),
241
        self.assertFalse(renderer.get_property('activatable'))
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
242
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
243
        values = [(r[0], r[1], r[2], r[3]) for r in dlg._pending_store]
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
244
        self.assertEqual([(rev_id2, '2007-10-01', 'Joe Foo', 'two')], values)
245
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
246
        self.assertEqual('Commit all changes*',
247
                         dlg._commit_all_files_radio.get_label())
248
        self.assertFalse(dlg._commit_all_files_radio.get_property('sensitive'))
249
        self.assertFalse(dlg._commit_selected_radio.get_property('sensitive'))
250
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
251
    def test_pending_multiple(self):
252
        tree = self.make_branch_and_tree('tree')
253
        rev_id1 = tree.commit('one')
254
255
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
256
        rev_id2 = tree2.commit('two',
257
                               committer='Joe Foo <joe@foo.com>',
258
                               timestamp=1191264271.05,
259
                               timezone=+7200)
260
        rev_id3 = tree2.commit('three',
261
                               committer='Jerry Foo <jerry@foo.com>',
262
                               timestamp=1191264278.05,
263
                               timezone=+7200)
264
        tree.merge_from_branch(tree2.branch)
265
        tree3 = tree.bzrdir.sprout('tree3').open_workingtree()
266
        rev_id4 = tree3.commit('four',
267
                               committer='Joe Foo <joe@foo.com>',
268
                               timestamp=1191264279.05,
269
                               timezone=+7200)
270
        rev_id5 = tree3.commit('five',
271
                               committer='Jerry Foo <jerry@foo.com>',
272
                               timestamp=1191372278.05,
273
                               timezone=+7200)
274
        tree.merge_from_branch(tree3.branch)
275
276
        dlg = commit.CommitDialog(tree)
277
        # TODO: assert that the pending box is set to show
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
278
        values = [(r[0], r[1], r[2], r[3]) for r in dlg._pending_store]
278.1.14 by John Arbash Meinel
Tests that we fill out the pending list correctly.
279
        self.assertEqual([(rev_id3, '2007-10-01', 'Jerry Foo', 'three'),
280
                          (rev_id2, '2007-10-01', 'Joe Foo', 'two'),
281
                          (rev_id5, '2007-10-03', 'Jerry Foo', 'five'),
282
                          (rev_id4, '2007-10-01', 'Joe Foo', 'four'),
283
                         ], values)
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
284
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
285
    def test_filelist_added(self):
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
286
        tree = self.make_branch_and_tree('tree')
287
        self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
288
        tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
289
290
        dlg = commit.CommitDialog(tree)
291
        values = [(r[0], r[1], r[2], r[3], r[4]) for r in dlg._files_store]
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
292
        self.assertEqual([(None, None, True, 'All Files', ''),
293
                          ('a-id', 'a', True, 'a', 'added'),
278.1.15 by John Arbash Meinel
Hook up the list of modified files.
294
                          ('b-id', 'b', True, 'b/', 'added'),
295
                          ('c-id', 'b/c', True, 'b/c', 'added'),
296
                         ], values)
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
297
298
    def test_filelist_renamed(self):
299
        tree = self.make_branch_and_tree('tree')
300
        self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
301
        tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
302
        rev_id1 = tree.commit('one')
303
304
        tree.rename_one('b', 'd')
305
        tree.rename_one('a', 'd/a')
306
307
        dlg = commit.CommitDialog(tree)
308
        values = [(r[0], r[1], r[2], r[3], r[4]) for r in dlg._files_store]
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
309
        self.assertEqual([(None, None, True, 'All Files', ''),
310
                          ('b-id', 'd', True, 'b/ => d/', 'renamed'),
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
311
                          ('a-id', 'd/a', True, 'a => d/a', 'renamed'),
312
                         ], values)
313
314
    def test_filelist_modified(self):
315
        tree = self.make_branch_and_tree('tree')
316
        self.build_tree(['tree/a'])
317
        tree.add(['a'], ['a-id'])
318
        rev_id1 = tree.commit('one')
319
320
        self.build_tree_contents([('tree/a', 'new contents for a\n')])
321
322
        dlg = commit.CommitDialog(tree)
323
        values = [(r[0], r[1], r[2], r[3], r[4]) for r in dlg._files_store]
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
324
        self.assertEqual([(None, None, True, 'All Files', ''),
325
                          ('a-id', 'a', True, 'a', 'modified'),
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
326
                         ], values)
327
328
    def test_filelist_renamed_and_modified(self):
329
        tree = self.make_branch_and_tree('tree')
330
        self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
331
        tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
332
        rev_id1 = tree.commit('one')
333
334
        tree.rename_one('b', 'd')
335
        tree.rename_one('a', 'd/a')
336
        self.build_tree_contents([('tree/d/a', 'new contents for a\n'),
337
                                  ('tree/d/c', 'new contents for c\n'),
338
                                 ])
339
        # 'c' is not considered renamed, because only its parent was moved, it
340
        # stayed in the same directory
341
342
        dlg = commit.CommitDialog(tree)
343
        values = [(r[0], r[1], r[2], r[3], r[4]) for r in dlg._files_store]
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
344
        self.assertEqual([(None, None, True, 'All Files', ''),
345
                          ('b-id', 'd', True, 'b/ => d/', 'renamed'),
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
346
                          ('a-id', 'd/a', True, 'a => d/a', 'renamed and modified'),
347
                          ('c-id', 'd/c', True, 'd/c', 'modified'),
348
                         ], values)
349
350
    def test_filelist_kind_changed(self):
351
        tree = self.make_branch_and_tree('tree')
352
        self.build_tree(['tree/a', 'tree/b'])
353
        tree.add(['a', 'b'], ['a-id', 'b-id'])
354
        tree.commit('one')
355
356
        os.remove('tree/a')
357
        self.build_tree(['tree/a/'])
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
358
        # XXX:  This is technically valid, and the file list handles it fine,
359
        #       but 'show_diff_trees()' does not, so we skip this part of the
360
        #       test for now.
361
        # tree.rename_one('b', 'c')
362
        # os.remove('tree/c')
363
        # self.build_tree(['tree/c/'])
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
364
365
        dlg = commit.CommitDialog(tree)
366
        values = [(r[0], r[1], r[2], r[3], r[4]) for r in dlg._files_store]
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
367
        self.assertEqual([(None, None, True, 'All Files', ''),
368
                          ('a-id', 'a', True, 'a => a/', 'kind changed'),
278.1.17 by John Arbash Meinel
Add a * reference for why you can't change the commit selection.
369
                          # ('b-id', 'c', True, 'b => c/', 'renamed and modified'),
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
370
                         ], values)
371
372
    def test_filelist_removed(self):
373
        tree = self.make_branch_and_tree('tree')
374
        self.build_tree(['tree/a', 'tree/b/'])
375
        tree.add(['a', 'b'], ['a-id', 'b-id'])
376
        tree.commit('one')
377
378
        os.remove('tree/a')
379
        tree.remove('b', force=True)
380
381
        dlg = commit.CommitDialog(tree)
382
        values = [(r[0], r[1], r[2], r[3], r[4]) for r in dlg._files_store]
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
383
        self.assertEqual([(None, None, True, 'All Files', ''),
384
                          ('a-id', 'a', True, 'a', 'removed'),
278.1.16 by John Arbash Meinel
Implement the file changes list on top of _iter_changes rather than
385
                          ('b-id', 'b', True, 'b/', 'removed'),
386
                         ], values)
278.1.35 by John Arbash Meinel
Make use of the 'selected' parameter to CommitDialog.
387
        # All Files should be selected
388
        self.assertEqual(((0,), None), dlg._treeview_files.get_cursor())
389
390
    def test_filelist_with_selected(self):
391
        tree = self.make_branch_and_tree('tree')
392
        self.build_tree(['tree/a', 'tree/b/'])
393
        tree.add(['a', 'b'], ['a-id', 'b-id'])
394
395
        dlg = commit.CommitDialog(tree, selected='a')
396
        values = [(r[0], r[1], r[2], r[3], r[4]) for r in dlg._files_store]
397
        self.assertEqual([(None, None, False, 'All Files', ''),
398
                          ('a-id', 'a', True, 'a', 'added'),
399
                          ('b-id', 'b', False, 'b/', 'added'),
400
                         ], values)
401
        # This file should also be selected in the file list, rather than the
402
        # 'All Files' selection
403
        self.assertEqual(((1,), None), dlg._treeview_files.get_cursor())
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
404
405
    def test_diff_view(self):
406
        tree = self.make_branch_and_tree('tree')
407
        self.build_tree(['tree/a', 'tree/b'])
408
        tree.add(['a', 'b'], ['a-id', 'b-id'])
409
        tree.commit('one')
410
411
        self.build_tree_contents([('tree/a', 'new contents for a\n')])
412
        tree.remove('b')
413
414
        dlg = commit.CommitDialog(tree)
415
        diff_buffer = dlg._diff_view.buffer
416
        text = diff_buffer.get_text(diff_buffer.get_start_iter(),
417
                                    diff_buffer.get_end_iter()).splitlines(True)
418
483 by Jelmer Vernooij
Fix diff test.
419
        self.assertEqual("=== modified file 'a'\n", text[0])
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
420
        self.assertContainsRe(text[1],
421
            r"--- a\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d")
483 by Jelmer Vernooij
Fix diff test.
422
        self.assertContainsRe(text[2],
423
            r"\+\+\+ a\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d")
424
        self.assertEqual('@@ -1,1 +1,1 @@\n', text[3])
425
        self.assertEqual('-contents of tree/a\n', text[4])
426
        self.assertEqual('+new contents for a\n', text[5])
427
        self.assertEqual('\n', text[6])
428
429
        self.assertEqual("=== removed file 'b'\n", text[7])
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
430
        self.assertContainsRe(text[8],
483 by Jelmer Vernooij
Fix diff test.
431
            r"--- b\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d")
432
        self.assertEqual('+++ b\t1970-01-01 00:00:00 +0000\n', text[9])
433
        self.assertEqual('@@ -1,1 +0,0 @@\n', text[10])
434
        self.assertEqual('-contents of tree/b\n', text[11])
278.1.18 by John Arbash Meinel
Start checking the diff view is correct.
435
        self.assertEqual('\n', text[12])
436
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
437
        self.assertEqual('Diff for All Files', dlg._diff_label.get_text())
278.1.19 by John Arbash Meinel
Test what happens when a specific file is selected.
438
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
439
    def test_commit_partial_toggle(self):
440
        tree = self.make_branch_and_tree('tree')
441
        self.build_tree(['tree/a', 'tree/b'])
442
        tree.add(['a', 'b'], ['a-id', 'b-id'])
443
444
        dlg = commit.CommitDialog(tree)
445
        checked_col = dlg._treeview_files.get_column(0)
446
        self.assertFalse(checked_col.get_property('visible'))
447
        self.assertTrue(dlg._commit_all_changes)
448
449
        dlg._commit_selected_radio.set_active(True)
450
        self.assertTrue(checked_col.get_property('visible'))
451
        self.assertFalse(dlg._commit_all_changes)
452
278.1.19 by John Arbash Meinel
Test what happens when a specific file is selected.
453
    def test_file_selection(self):
454
        """Several things should happen when a file has been selected."""
455
        tree = self.make_branch_and_tree('tree')
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
456
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
278.1.19 by John Arbash Meinel
Test what happens when a specific file is selected.
457
        self.build_tree(['tree/a', 'tree/b'])
458
        tree.add(['a', 'b'], ['a-id', 'b-id'])
459
460
        dlg = commit.CommitDialog(tree)
461
        diff_buffer = dlg._diff_view.buffer
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
462
        self.assertEqual('Diff for All Files', dlg._diff_label.get_text())
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
463
        self.assertEqual('File commit message',
464
                         dlg._file_message_expander.get_label())
465
        self.assertFalse(dlg._file_message_expander.get_expanded())
466
        self.assertFalse(dlg._file_message_expander.get_property('sensitive'))
278.1.19 by John Arbash Meinel
Test what happens when a specific file is selected.
467
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
468
        dlg._treeview_files.set_cursor((1,))
278.1.19 by John Arbash Meinel
Test what happens when a specific file is selected.
469
        self.assertEqual('Diff for a', dlg._diff_label.get_text())
470
        text = diff_buffer.get_text(diff_buffer.get_start_iter(),
471
                                    diff_buffer.get_end_iter()).splitlines(True)
472
        self.assertEqual("=== added file 'a'\n", text[0])
473
        self.assertContainsRe(text[1],
474
            r"--- a\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d")
475
        self.assertContainsRe(text[2],
476
            r"\+\+\+ a\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d")
477
        self.assertEqual('@@ -0,0 +1,1 @@\n', text[3])
478
        self.assertEqual('+contents of tree/a\n', text[4])
479
        self.assertEqual('\n', text[5])
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
480
        self.assertEqual('Commit message for a',
481
                         dlg._file_message_expander.get_label())
482
        self.assertTrue(dlg._file_message_expander.get_expanded())
483
        self.assertTrue(dlg._file_message_expander.get_property('sensitive'))
278.1.19 by John Arbash Meinel
Test what happens when a specific file is selected.
484
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
485
        dlg._treeview_files.set_cursor((2,))
278.1.19 by John Arbash Meinel
Test what happens when a specific file is selected.
486
        self.assertEqual('Diff for b', dlg._diff_label.get_text())
487
        text = diff_buffer.get_text(diff_buffer.get_start_iter(),
488
                                    diff_buffer.get_end_iter()).splitlines(True)
489
        self.assertEqual("=== added file 'b'\n", text[0])
490
        self.assertContainsRe(text[1],
491
            r"--- b\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d")
492
        self.assertContainsRe(text[2],
493
            r"\+\+\+ b\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d")
494
        self.assertEqual('@@ -0,0 +1,1 @@\n', text[3])
495
        self.assertEqual('+contents of tree/b\n', text[4])
496
        self.assertEqual('\n', text[5])
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
497
        self.assertEqual('Commit message for b',
498
                         dlg._file_message_expander.get_label())
499
        self.assertTrue(dlg._file_message_expander.get_expanded())
500
        self.assertTrue(dlg._file_message_expander.get_property('sensitive'))
501
502
        dlg._treeview_files.set_cursor((0,))
503
        self.assertEqual('Diff for All Files', dlg._diff_label.get_text())
504
        self.assertEqual('File commit message',
505
                         dlg._file_message_expander.get_label())
506
        self.assertFalse(dlg._file_message_expander.get_expanded())
507
        self.assertFalse(dlg._file_message_expander.get_property('sensitive'))
508
509
    def test_file_selection_message(self):
510
        """Selecting a file should bring up its commit message."""
511
        tree = self.make_branch_and_tree('tree')
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
512
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
513
        self.build_tree(['tree/a', 'tree/b/'])
514
        tree.add(['a', 'b'], ['a-id', 'b-id'])
515
516
        def get_file_text():
517
            buf = dlg._file_message_text_view.get_buffer()
518
            return buf.get_text(buf.get_start_iter(), buf.get_end_iter())
519
520
        def get_saved_text(path):
521
            """Get the saved text for a given record."""
522
            return dlg._files_store.get_value(dlg._files_store.get_iter(path), 5)
523
524
        dlg = commit.CommitDialog(tree)
525
        self.assertEqual('File commit message',
526
                         dlg._file_message_expander.get_label())
527
        self.assertFalse(dlg._file_message_expander.get_expanded())
528
        self.assertFalse(dlg._file_message_expander.get_property('sensitive'))
529
        self.assertEqual('', get_file_text())
530
531
        dlg._treeview_files.set_cursor((1,))
532
        self.assertEqual('Commit message for a',
533
                         dlg._file_message_expander.get_label())
534
        self.assertTrue(dlg._file_message_expander.get_expanded())
535
        self.assertTrue(dlg._file_message_expander.get_property('sensitive'))
536
        self.assertEqual('', get_file_text())
537
538
        self.assertEqual('', get_saved_text(1))
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
539
        dlg._set_file_commit_message('Some text\nfor a\n')
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
540
        dlg._save_current_file_message()
541
        # We should have updated the ListStore with the new file commit info
542
        self.assertEqual('Some text\nfor a\n', get_saved_text(1))
543
544
        dlg._treeview_files.set_cursor((2,))
545
        self.assertEqual('Commit message for b/',
546
                         dlg._file_message_expander.get_label())
547
        self.assertTrue(dlg._file_message_expander.get_expanded())
548
        self.assertTrue(dlg._file_message_expander.get_property('sensitive'))
549
        self.assertEqual('', get_file_text())
550
551
        self.assertEqual('', get_saved_text(2))
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
552
        dlg._set_file_commit_message('More text\nfor b\n')
278.1.21 by John Arbash Meinel
Start tracking the per-file commit messages.
553
        # Now switch back to 'a'. The message should be saved, and the buffer
554
        # should be updated with the other text
555
        dlg._treeview_files.set_cursor((1,))
556
        self.assertEqual('More text\nfor b\n', get_saved_text(2))
557
        self.assertEqual('Commit message for a',
558
                         dlg._file_message_expander.get_label())
559
        self.assertTrue(dlg._file_message_expander.get_expanded())
560
        self.assertTrue(dlg._file_message_expander.get_property('sensitive'))
561
        self.assertEqual('Some text\nfor a\n', get_file_text())
278.1.20 by John Arbash Meinel
We always select the All Files record in the files view,
562
563
    def test_toggle_all_files(self):
564
        """When checking the All Files entry, it should toggle all fields"""
565
        tree = self.make_branch_and_tree('tree')
566
        self.build_tree(['tree/a', 'tree/b/'])
567
        tree.add(['a', 'b'], ['a-id', 'b-id'])
568
569
        dlg = commit.CommitDialog(tree)
570
        self.assertEqual([(None, None, True),
571
                          ('a-id', 'a', True),
572
                          ('b-id', 'b', True),
573
                         ], [(r[0], r[1], r[2]) for r in dlg._files_store])
574
575
        # TODO: jam 20071002 I'm not sure how to exactly trigger a toggle, it
576
        #       looks like we need to call renderer.activate() and pass an
577
        #       event and widget, and lots of other stuff I'm not sure what to
578
        #       do with. So instead, we just call toggle directly, and assume
579
        #       that toggle is hooked in correctly
580
        # column = dlg._treeview_files.get_column(0)
581
        # renderer = column.get_cell_renderers()[0]
582
583
        # Toggle a single entry should set just that entry to False
584
        dlg._toggle_commit(None, 1, dlg._files_store)
585
        self.assertEqual([(None, None, True),
586
                          ('a-id', 'a', False),
587
                          ('b-id', 'b', True),
588
                         ], [(r[0], r[1], r[2]) for r in dlg._files_store])
589
590
        # Toggling the main entry should set all entries
591
        dlg._toggle_commit(None, 0, dlg._files_store)
592
        self.assertEqual([(None, None, False),
593
                          ('a-id', 'a', False),
594
                          ('b-id', 'b', False),
595
                         ], [(r[0], r[1], r[2]) for r in dlg._files_store])
596
597
        dlg._toggle_commit(None, 2, dlg._files_store)
598
        self.assertEqual([(None, None, False),
599
                          ('a-id', 'a', False),
600
                          ('b-id', 'b', True),
601
                         ], [(r[0], r[1], r[2]) for r in dlg._files_store])
602
603
        dlg._toggle_commit(None, 0, dlg._files_store)
604
        self.assertEqual([(None, None, True),
605
                          ('a-id', 'a', True),
606
                          ('b-id', 'b', True),
607
                         ], [(r[0], r[1], r[2]) for r in dlg._files_store])
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
608
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
609
    def test_specific_files(self):
610
        tree = self.make_branch_and_tree('tree')
611
        self.build_tree(['tree/a', 'tree/b/'])
612
        tree.add(['a', 'b'], ['a-id', 'b-id'])
613
614
        dlg = commit.CommitDialog(tree)
615
        self.assertEqual((['a', 'b'], []), dlg._get_specific_files())
616
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
617
        dlg._commit_selected_radio.set_active(True)
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
618
        dlg._toggle_commit(None, 0, dlg._files_store)
619
        self.assertEqual(([], []), dlg._get_specific_files())
620
621
        dlg._toggle_commit(None, 1, dlg._files_store)
622
        self.assertEqual((['a'], []), dlg._get_specific_files())
623
624
    def test_specific_files_with_messages(self):
625
        tree = self.make_branch_and_tree('tree')
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
626
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
627
        self.build_tree(['tree/a_file', 'tree/b_dir/'])
628
        tree.add(['a_file', 'b_dir'], ['1a-id', '0b-id'])
629
630
        dlg = commit.CommitDialog(tree)
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
631
        dlg._commit_selected_radio.set_active(True)
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
632
        self.assertEqual((['a_file', 'b_dir'], []), dlg._get_specific_files())
633
634
        dlg._treeview_files.set_cursor((1,))
635
        dlg._set_file_commit_message('Test\nmessage\nfor a_file\n')
636
        dlg._treeview_files.set_cursor((2,))
637
        dlg._set_file_commit_message('message\nfor b_dir\n')
638
639
        self.assertEqual((['a_file', 'b_dir'],
640
                          [{'path':'a_file', 'file_id':'1a-id',
641
                            'message':'Test\nmessage\nfor a_file\n'},
642
                           {'path':'b_dir', 'file_id':'0b-id',
643
                            'message':'message\nfor b_dir\n'},
644
                          ]), dlg._get_specific_files())
645
646
        dlg._toggle_commit(None, 1, dlg._files_store)
647
        self.assertEqual((['b_dir'],
648
                          [{'path':'b_dir', 'file_id':'0b-id',
649
                            'message':'message\nfor b_dir\n'},
650
                          ]), dlg._get_specific_files())
651
622.1.1 by John Arbash Meinel
Ensure that per-file commit messages and global commit messages get sanitized.
652
    def test_specific_files_sanitizes_messages(self):
653
        tree = self.make_branch_and_tree('tree')
654
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
655
        self.build_tree(['tree/a_file', 'tree/b_dir/'])
656
        tree.add(['a_file', 'b_dir'], ['1a-id', '0b-id'])
657
658
        dlg = commit.CommitDialog(tree)
659
        dlg._commit_selected_radio.set_active(True)
660
        self.assertEqual((['a_file', 'b_dir'], []), dlg._get_specific_files())
661
662
        dlg._treeview_files.set_cursor((1,))
663
        dlg._set_file_commit_message('Test\r\nmessage\rfor a_file\n')
664
        dlg._treeview_files.set_cursor((2,))
665
        dlg._set_file_commit_message('message\r\nfor\nb_dir\r')
666
667
        self.assertEqual((['a_file', 'b_dir'],
668
                          [{'path':'a_file', 'file_id':'1a-id',
669
                            'message':'Test\nmessage\nfor a_file\n'},
670
                           {'path':'b_dir', 'file_id':'0b-id',
671
                            'message':'message\nfor\nb_dir\n'},
672
                          ]), dlg._get_specific_files())
673
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
674
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
675
class QuestionHelpers(object):
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
676
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
677
    def _set_question_yes(self, dlg):
678
        """Set the dialog to answer YES to any questions."""
679
        self.questions = []
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
680
        def _question_yes(*args, **kwargs):
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
681
            self.questions.append(args)
682
            self.questions.append('YES')
683
            return gtk.RESPONSE_YES
684
        dlg._question_dialog = _question_yes
685
686
    def _set_question_no(self, dlg):
687
        """Set the dialog to answer NO to any questions."""
688
        self.questions = []
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
689
        def _question_no(*args, **kwargs):
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
690
            self.questions.append(args)
691
            self.questions.append('NO')
692
            return gtk.RESPONSE_NO
693
        dlg._question_dialog = _question_no
694
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
695
696
class TestCommitDialog_Commit(tests.TestCaseWithTransport, QuestionHelpers):
697
    """Tests on the actual 'commit' button being pushed."""
698
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
699
    def test_bound_commit_local(self):
700
        tree = self.make_branch_and_tree('tree')
701
        self.build_tree(['tree/a'])
702
        tree.add(['a'], ['a-id'])
703
        rev_id1 = tree.commit('one')
704
705
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
706
        self.build_tree(['tree2/b'])
707
        tree2.add(['b'], ['b-id'])
708
        tree2.branch.bind(tree.branch)
709
710
        dlg = commit.CommitDialog(tree2)
711
        # With the check box set, it should only effect the local branch
712
        dlg._check_local.set_active(True)
713
        dlg._set_global_commit_message('Commit message\n')
714
        dlg._do_commit()
715
716
        last_rev = tree2.last_revision()
717
        self.assertEqual(last_rev, dlg.committed_revision_id)
718
        self.assertEqual(rev_id1, tree.branch.last_revision())
719
622.1.1 by John Arbash Meinel
Ensure that per-file commit messages and global commit messages get sanitized.
720
    def test_commit_global_sanitizes_message(self):
721
        tree = self.make_branch_and_tree('tree')
722
        self.build_tree(['tree/a'])
723
        tree.add(['a'], ['a-id'])
724
        rev_id1 = tree.commit('one')
725
726
        self.build_tree(['tree/b'])
727
        tree.add(['b'], ['b-id'])
728
        dlg = commit.CommitDialog(tree)
729
        # With the check box set, it should only effect the local branch
730
        dlg._set_global_commit_message('Commit\r\nmessage\rfoo\n')
731
        dlg._do_commit()
732
        rev = tree.branch.repository.get_revision(tree.last_revision())
733
        self.assertEqual('Commit\nmessage\nfoo\n', rev.message)
734
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
735
    def test_bound_commit_both(self):
736
        tree = self.make_branch_and_tree('tree')
737
        self.build_tree(['tree/a'])
738
        tree.add(['a'], ['a-id'])
739
        rev_id1 = tree.commit('one')
740
741
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
742
        self.build_tree(['tree2/b'])
743
        tree2.add(['b'], ['b-id'])
744
        tree2.branch.bind(tree.branch)
745
746
        dlg = commit.CommitDialog(tree2)
747
        # With the check box set, it should only effect the local branch
748
        dlg._check_local.set_active(False)
749
        dlg._set_global_commit_message('Commit message\n')
750
        dlg._do_commit()
751
752
        last_rev = tree2.last_revision()
753
        self.assertEqual(last_rev, dlg.committed_revision_id)
754
        self.assertEqual(last_rev, tree.branch.last_revision())
755
606 by Vincent Ladeuil
Fix gtk dialogs popping up and asking for input during selftest.
756
    def test_commit_empty_message(self):
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
757
        tree = self.make_branch_and_tree('tree')
758
        self.build_tree(['tree/a', 'tree/b'])
759
        tree.add(['a'], ['a-id'])
760
        rev_id = tree.commit('one')
761
762
        tree.add(['b'], ['b-id'])
763
764
        dlg = commit.CommitDialog(tree)
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
765
        self._set_question_no(dlg)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
766
        dlg._do_commit()
767
        self.assertEqual(
768
            [('Commit with an empty message?',
769
              'You can describe your commit intent in the message.'),
770
              'NO',
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
771
            ], self.questions)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
772
        # By saying NO, nothing should be committed.
773
        self.assertEqual(rev_id, tree.last_revision())
774
        self.assertIs(None, dlg.committed_revision_id)
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
775
        self.assertTrue(dlg._global_message_text_view.get_property('is-focus'))
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
776
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
777
        self._set_question_yes(dlg)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
778
779
        dlg._do_commit()
780
        self.assertEqual(
781
            [('Commit with an empty message?',
782
              'You can describe your commit intent in the message.'),
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
783
              'YES',
784
            ], self.questions)
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
785
        committed = tree.last_revision()
786
        self.assertNotEqual(rev_id, committed)
787
        self.assertEqual(committed, dlg.committed_revision_id)
788
789
    def test_initial_commit(self):
790
        tree = self.make_branch_and_tree('tree')
791
        self.build_tree(['tree/a'])
792
        tree.add(['a'], ['a-id'])
793
794
        dlg = commit.CommitDialog(tree)
278.1.25 by John Arbash Meinel
Add the 'Only Commit Locally' checkbox, we may want to put it elsewhere, though.
795
        dlg._set_global_commit_message('Some text\n')
278.1.23 by John Arbash Meinel
Beginning to support actual commit.
796
        dlg._do_commit()
797
798
        last_rev = tree.last_revision()
799
        self.assertEqual(last_rev, dlg.committed_revision_id)
800
        rev = tree.branch.repository.get_revision(last_rev)
801
        self.assertEqual(last_rev, rev.revision_id)
802
        self.assertEqual('Some text\n', rev.message)
278.1.26 by John Arbash Meinel
Handle pointless commits and trees with unknown files.
803
804
    def test_pointless_commit(self):
805
        tree = self.make_branch_and_tree('tree')
806
        self.build_tree(['tree/a'])
807
        tree.add(['a'], ['a-id'])
808
        rev_id1 = tree.commit('one')
809
810
        dlg = commit.CommitDialog(tree)
811
        dlg._set_global_commit_message('Some text\n')
812
813
        self._set_question_no(dlg)
814
        dlg._do_commit()
815
816
        self.assertIs(None, dlg.committed_revision_id)
817
        self.assertEqual(rev_id1, tree.last_revision())
818
        self.assertEqual(
819
            [('Commit with no changes?',
820
              'There are no changes in the working tree.'
821
              ' Do you want to commit anyway?'),
822
              'NO',
823
            ], self.questions)
824
825
        self._set_question_yes(dlg)
826
        dlg._do_commit()
827
828
        rev_id2 = tree.last_revision()
829
        self.assertEqual(rev_id2, dlg.committed_revision_id)
830
        self.assertNotEqual(rev_id1, rev_id2)
831
        self.assertEqual(
832
            [('Commit with no changes?',
833
              'There are no changes in the working tree.'
834
              ' Do you want to commit anyway?'),
835
              'YES',
836
            ], self.questions)
837
838
    def test_unknowns(self):
839
        """We should check if there are unknown files."""
840
        tree = self.make_branch_and_tree('tree')
841
        rev_id1 = tree.commit('one')
842
        self.build_tree(['tree/a', 'tree/b'])
843
        tree.add(['a'], ['a-id'])
844
845
        dlg = commit.CommitDialog(tree)
846
        dlg._set_global_commit_message('Some text\n')
847
        self._set_question_no(dlg)
848
849
        dlg._do_commit()
850
851
        self.assertIs(None, dlg.committed_revision_id)
852
        self.assertEqual(rev_id1, tree.last_revision())
853
        self.assertEqual(
854
            [("Commit with unknowns?",
855
              "Unknown files exist in the working tree. Commit anyway?"),
856
              "NO",
857
            ], self.questions)
858
859
        self._set_question_yes(dlg)
860
        dlg._do_commit()
861
862
        rev_id2 = tree.last_revision()
863
        self.assertNotEqual(rev_id1, rev_id2)
864
        self.assertEqual(rev_id2, dlg.committed_revision_id)
865
        self.assertEqual(
866
            [("Commit with unknowns?",
867
              "Unknown files exist in the working tree. Commit anyway?"),
868
              "YES",
869
            ], self.questions)
870
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
871
    def test_commit_specific_files(self):
872
        tree = self.make_branch_and_tree('tree')
873
        rev_id1 = tree.commit('one')
874
        self.build_tree(['tree/a', 'tree/b'])
875
        tree.add(['a', 'b'], ['a-id', 'b-id'])
876
877
        dlg = commit.CommitDialog(tree)
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
878
        dlg._commit_selected_radio.set_active(True) # enable partial
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
879
        dlg._toggle_commit(None, 2, dlg._files_store) # unset 'b'
880
881
        dlg._set_global_commit_message('Committing just "a"\n')
882
        dlg._do_commit()
883
884
        rev_id2 = dlg.committed_revision_id
885
        self.assertIsNot(None, rev_id2)
886
        self.assertEqual(rev_id2, tree.last_revision())
887
888
        rt = tree.branch.repository.revision_tree(rev_id2)
889
        entries = [(path, ie.file_id) for path, ie in rt.iter_entries_by_dir()
890
                                       if path] # Ignore the root entry
891
        self.assertEqual([('a', 'a-id')], entries)
892
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
893
    def test_commit_partial_no_partial(self):
894
        """Ignore the checkboxes if committing all files."""
895
        tree = self.make_branch_and_tree('tree')
896
        rev_id1 = tree.commit('one')
897
        self.build_tree(['tree/a', 'tree/b'])
898
        tree.add(['a', 'b'], ['a-id', 'b-id'])
899
900
        dlg = commit.CommitDialog(tree)
901
        dlg._commit_selected_radio.set_active(True) # enable partial
902
        dlg._toggle_commit(None, 2, dlg._files_store) # unset 'b'
903
904
        # Switch back to committing all changes
905
        dlg._commit_all_files_radio.set_active(True)
906
907
        dlg._set_global_commit_message('Committing everything\n')
908
        dlg._do_commit()
909
910
        rev_id2 = dlg.committed_revision_id
911
        self.assertIsNot(None, rev_id2)
912
        self.assertEqual(rev_id2, tree.last_revision())
913
914
        rt = tree.branch.repository.revision_tree(rev_id2)
915
        entries = [(path, ie.file_id) for path, ie in rt.iter_entries_by_dir()
916
                                       if path] # Ignore the root entry
917
        self.assertEqual([('a', 'a-id'), ('b', 'b-id')], entries)
918
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
919
    def test_commit_no_messages(self):
920
        tree = self.make_branch_and_tree('tree')
921
        rev_id1 = tree.commit('one')
922
        self.build_tree(['tree/a', 'tree/b'])
923
        tree.add(['a', 'b'], ['a-id', 'b-id'])
924
925
        dlg = commit.CommitDialog(tree)
926
        dlg._set_global_commit_message('Simple commit\n')
927
        dlg._do_commit()
928
929
        rev = tree.branch.repository.get_revision(dlg.committed_revision_id)
930
        self.failIf('file-info' in rev.properties)
931
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
932
    def test_commit_disabled_messages(self):
933
        tree = self.make_branch_and_tree('tree')
934
        rev_id1 = tree.commit('one')
935
936
        self.build_tree(['tree/a', 'tree/b'])
937
        tree.add(['a', 'b'], ['a-id', 'b-id'])
938
939
        dlg = commit.CommitDialog(tree)
940
        self.assertFalse(dlg._file_message_expander.get_property('visible'))
278.1.38 by John Arbash Meinel
Add tests that when per-file messages are disabled
941
        self.assertEqual('Commit Message',
942
                         dlg._global_message_label.get_text())
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
943
944
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
945
        dlg = commit.CommitDialog(tree)
946
        self.assertTrue(dlg._file_message_expander.get_property('visible'))
278.1.38 by John Arbash Meinel
Add tests that when per-file messages are disabled
947
        self.assertEqual('Global Commit Message',
948
                         dlg._global_message_label.get_text())
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
949
950
        tree.branch.get_config().set_user_option('per_file_commits', 'on')
951
        dlg = commit.CommitDialog(tree)
952
        self.assertTrue(dlg._file_message_expander.get_property('visible'))
278.1.38 by John Arbash Meinel
Add tests that when per-file messages are disabled
953
        self.assertEqual('Global Commit Message',
954
                         dlg._global_message_label.get_text())
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
955
956
        tree.branch.get_config().set_user_option('per_file_commits', 'y')
957
        dlg = commit.CommitDialog(tree)
958
        self.assertTrue(dlg._file_message_expander.get_property('visible'))
278.1.38 by John Arbash Meinel
Add tests that when per-file messages are disabled
959
        self.assertEqual('Global Commit Message',
960
                         dlg._global_message_label.get_text())
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
961
962
        tree.branch.get_config().set_user_option('per_file_commits', 'n')
963
        dlg = commit.CommitDialog(tree)
964
        self.assertFalse(dlg._file_message_expander.get_property('visible'))
278.1.38 by John Arbash Meinel
Add tests that when per-file messages are disabled
965
        self.assertEqual('Commit Message',
966
                         dlg._global_message_label.get_text())
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
967
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
968
    def test_commit_specific_files_with_messages(self):
969
        tree = self.make_branch_and_tree('tree')
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
970
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
971
        rev_id1 = tree.commit('one')
972
        self.build_tree(['tree/a', 'tree/b'])
973
        tree.add(['a', 'b'], ['a-id', 'b-id'])
974
975
        dlg = commit.CommitDialog(tree)
278.1.43 by John Arbash Meinel
Finish connecting the 'Commit all changes' radio buttons.
976
        dlg._commit_selected_radio.set_active(True) # enable partial
278.1.27 by John Arbash Meinel
Add the ability to commit just specific files.
977
        dlg._treeview_files.set_cursor((1,))
978
        dlg._set_file_commit_message('Message for A\n')
979
        dlg._treeview_files.set_cursor((2,))
980
        dlg._set_file_commit_message('Message for B\n')
981
        dlg._toggle_commit(None, 2, dlg._files_store) # unset 'b'
982
        dlg._set_global_commit_message('Commit just "a"')
983
984
        dlg._do_commit()
985
986
        rev_id2 = dlg.committed_revision_id
987
        self.assertEqual(rev_id2, tree.last_revision())
988
        rev = tree.branch.repository.get_revision(rev_id2)
989
        self.assertEqual('Commit just "a"', rev.message)
990
        file_info = rev.properties['file-info']
991
        self.assertEqual('ld7:file_id4:a-id'
992
                           '7:message14:Message for A\n'
993
                           '4:path1:a'
994
                         'ee', file_info)
995
        self.assertEqual([{'path':'a', 'file_id':'a-id',
996
                           'message':'Message for A\n'},
997
                         ], bencode.bdecode(file_info))
278.1.28 by John Arbash Meinel
Ensure that we can set per-file messages even during a merge.
998
999
    def test_commit_messages_after_merge(self):
1000
        tree = self.make_branch_and_tree('tree')
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
1001
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
278.1.28 by John Arbash Meinel
Ensure that we can set per-file messages even during a merge.
1002
        rev_id1 = tree.commit('one')
1003
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
1004
        self.build_tree(['tree2/a', 'tree2/b'])
1005
        tree2.add(['a', 'b'], ['a-id', 'b-id'])
1006
        rev_id2 = tree2.commit('two')
1007
1008
        tree.merge_from_branch(tree2.branch)
1009
1010
        dlg = commit.CommitDialog(tree)
1011
        dlg._treeview_files.set_cursor((1,)) # 'a'
1012
        dlg._set_file_commit_message('Message for A\n')
1013
        # No message for 'B'
1014
        dlg._set_global_commit_message('Merging from "tree2"\n')
1015
1016
        dlg._do_commit()
1017
1018
        rev_id3 = dlg.committed_revision_id
1019
        self.assertEqual(rev_id3, tree.last_revision())
1020
        rev = tree.branch.repository.get_revision(rev_id3)
1021
        self.assertEqual('Merging from "tree2"\n', rev.message)
1022
        self.assertEqual([rev_id1, rev_id2], rev.parent_ids)
1023
        file_info = rev.properties['file-info']
1024
        self.assertEqual('ld7:file_id4:a-id'
1025
                           '7:message14:Message for A\n'
1026
                           '4:path1:a'
1027
                         'ee', file_info)
1028
        self.assertEqual([{'path':'a', 'file_id':'a-id',
1029
                           'message':'Message for A\n'},
1030
                         ], bencode.bdecode(file_info))
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
1031
1032
    def test_commit_unicode_messages(self):
500.1.1 by Vincent Ladeuil
Fix test failing after a feature rename in bzr.
1033
        self.requireFeature(tests.UnicodeFilenameFeature)
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
1034
1035
        tree = self.make_branch_and_tree('tree')
278.1.33 by John Arbash Meinel
Only enable the per-file dialog if 'per_file_commits' is enabled in the config.
1036
        tree.branch.get_config().set_user_option('per_file_commits', 'true')
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
1037
        self.build_tree(['tree/a', u'tree/\u03a9'])
1038
        tree.add(['a', u'\u03a9'], ['a-id', 'omega-id'])
1039
1040
        dlg = commit.CommitDialog(tree)
1041
        dlg._treeview_files.set_cursor((1,)) # 'a'
1042
        dlg._set_file_commit_message(u'Test \xfan\xecc\xf6de\n')
1043
        dlg._treeview_files.set_cursor((2,)) # omega
1044
        dlg._set_file_commit_message(u'\u03a9 is the end of all things.\n')
1045
        dlg._set_global_commit_message(u'\u03a9 and \xfan\xecc\xf6de\n')
1046
1047
        self.assertEqual(([u'a', u'\u03a9'],
1048
                          [{'path':'a', 'file_id':'a-id',
1049
                            'message':'Test \xc3\xban\xc3\xacc\xc3\xb6de\n'},
1050
                           {'path':'\xce\xa9', 'file_id':'omega-id',
1051
                            'message':'\xce\xa9 is the end of all things.\n'},
1052
                          ]), dlg._get_specific_files())
1053
1054
        dlg._do_commit()
1055
1056
        rev = tree.branch.repository.get_revision(dlg.committed_revision_id)
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
1057
        file_info = rev.properties['file-info'].encode('UTF-8')
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
1058
        value = ('ld7:file_id4:a-id'
1059
                   '7:message16:Test \xc3\xban\xc3\xacc\xc3\xb6de\n'
1060
                   '4:path1:a'
1061
                  'e'
1062
                  'd7:file_id8:omega-id'
1063
                   '7:message29:\xce\xa9 is the end of all things.\n'
1064
                   '4:path2:\xce\xa9'
1065
                  'e'
1066
                 'e')
1067
        self.assertEqual(value, file_info)
1068
        file_info_decoded = bencode.bdecode(file_info)
1069
        for d in file_info_decoded:
278.1.31 by John Arbash Meinel
We can make bencode work again by a simple decode/encode step.
1070
            d['path'] = d['path'].decode('UTF-8')
1071
            d['message'] = d['message'].decode('UTF-8')
278.1.29 by John Arbash Meinel
Start testing with Unicode data.
1072
1073
        self.assertEqual([{'path':u'a', 'file_id':'a-id',
1074
                           'message':u'Test \xfan\xecc\xf6de\n'},
1075
                          {'path':u'\u03a9', 'file_id':'omega-id',
1076
                           'message':u'\u03a9 is the end of all things.\n'},
1077
                         ], file_info_decoded)
622.1.1 by John Arbash Meinel
Ensure that per-file commit messages and global commit messages get sanitized.
1078
1079
1080
class TestSanitizeMessage(tests.TestCase):
1081
1082
    def assertSanitize(self, expected, original):
1083
        self.assertEqual(expected,
1084
                         commit._sanitize_and_decode_message(original))
1085
1086
    def test_untouched(self):
1087
        self.assertSanitize('foo\nbar\nbaz\n', 'foo\nbar\nbaz\n')
1088
1089
    def test_converts_cr_to_lf(self):
1090
        self.assertSanitize('foo\nbar\nbaz\n', 'foo\rbar\rbaz\r')
1091
1092
    def test_converts_crlf_to_lf(self):
1093
        self.assertSanitize('foo\nbar\nbaz\n', 'foo\r\nbar\r\nbaz\r\n')
1094
1095
    def test_converts_mixed_to_lf(self):
1096
        self.assertSanitize('foo\nbar\nbaz\n', 'foo\r\nbar\rbaz\n')
635.2.8 by Vincent Ladeuil
Start testing patch behavior.
1097
1098
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1099
class TestSavedCommitMessages(tests.TestCaseWithTransport):
635.2.9 by Vincent Ladeuil
Tests completed for uncommit.
1100
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
1101
    def setUp(self):
1102
        super(TestSavedCommitMessages, self).setUp()
1103
        # Install our hook
1104
        branch.Branch.hooks.install_named_hook(
1105
            'post_uncommit', commit.save_commit_messages, None)
1106
635.2.9 by Vincent Ladeuil
Tests completed for uncommit.
1107
    def _get_file_info_dict(self, rank):
1108
        file_info = [dict(path='a', file_id='a-id', message='a msg %d' % rank),
1109
                     dict(path='b', file_id='b-id', message='b msg %d' % rank)]
1110
        return file_info
1111
1112
    def _get_file_info_revprops(self, rank):
1113
        file_info_prop = self._get_file_info_dict(rank)
1114
        return {'file-info': bencode.bencode(file_info_prop).decode('UTF-8')}
1115
1116
    def _get_commit_message(self):
1117
        return self.config.get_user_option('gtk_global_commit_message')
1118
1119
    def _get_file_commit_messages(self):
1120
        return self.config.get_user_option('gtk_file_commit_messages')
1121
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1122
1123
class TestUncommitHook(TestSavedCommitMessages):
1124
1125
    def setUp(self):
1126
        super(TestUncommitHook, self).setUp()
1127
        self.tree = self.make_branch_and_tree('tree')
1128
        self.config = self.tree.branch.get_config()
1129
        self.build_tree(['tree/a', 'tree/b'])
1130
        self.tree.add(['a'], ['a-id'])
1131
        self.tree.add(['b'], ['b-id'])
635.2.12 by Vincent Ladeuil
Implement commit message saving without modifying bzrlib.
1132
        rev1 = self.tree.commit('one', rev_id='one-id',
1133
                                revprops=self._get_file_info_revprops(1))
1134
        rev2 = self.tree.commit('two', rev_id='two-id',
1135
                                revprops=self._get_file_info_revprops(2))
1136
        rev3 = self.tree.commit('three', rev_id='three-id',
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1137
                                revprops=self._get_file_info_revprops(3))
1138
635.2.9 by Vincent Ladeuil
Tests completed for uncommit.
1139
    def test_uncommit_one_by_one(self):
1140
        uncommit.uncommit(self.tree.branch, tree=self.tree)
1141
        self.assertEquals(u'three', self._get_commit_message())
1142
        self.assertEquals(u'd4:a-id7:a msg 34:b-id7:b msg 3e',
1143
                          self._get_file_commit_messages())
1144
1145
        uncommit.uncommit(self.tree.branch, tree=self.tree)
1146
        self.assertEquals(u'two\n******\nthree', self._get_commit_message())
1147
        self.assertEquals(u'd4:a-id22:a msg 2\n******\na msg 3'
1148
                          '4:b-id22:b msg 2\n******\nb msg 3e',
1149
                          self._get_file_commit_messages())
1150
1151
        uncommit.uncommit(self.tree.branch, tree=self.tree)
1152
        self.assertEquals(u'one\n******\ntwo\n******\nthree',
1153
                          self._get_commit_message())
1154
        self.assertEquals(u'd4:a-id37:a msg 1\n******\na msg 2\n******\na msg 3'
1155
                          '4:b-id37:b msg 1\n******\nb msg 2\n******\nb msg 3e',
1156
                          self._get_file_commit_messages())
1157
1158
    def test_uncommit_all_at_once(self):
1159
        uncommit.uncommit(self.tree.branch, tree=self.tree, revno=1)
1160
        self.assertEquals(u'one\n******\ntwo\n******\nthree',
1161
                          self._get_commit_message())
1162
        self.assertEquals(u'd4:a-id37:a msg 1\n******\na msg 2\n******\na msg 3'
1163
                          '4:b-id37:b msg 1\n******\nb msg 2\n******\nb msg 3e',
1164
                          self._get_file_commit_messages())
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1165
1166
1167
class TestReusingSavedCommitMessages(TestSavedCommitMessages, QuestionHelpers):
1168
1169
    def setUp(self):
1170
        super(TestReusingSavedCommitMessages, self).setUp()
1171
        self.tree = self.make_branch_and_tree('tree')
1172
        self.config = self.tree.branch.get_config()
1173
        self.config.set_user_option('per_file_commits', 'true')
1174
        self.build_tree(['tree/a', 'tree/b'])
1175
        self.tree.add(['a'], ['a-id'])
1176
        self.tree.add(['b'], ['b-id'])
1177
        rev1 = self.tree.commit('one', revprops=self._get_file_info_revprops(1))
1178
        rev2 = self.tree.commit('two', revprops=self._get_file_info_revprops(2))
1179
        uncommit.uncommit(self.tree.branch, tree=self.tree)
1180
        self.build_tree_contents([('tree/a', 'new a content\n'),
1181
                                  ('tree/b', 'new b content'),])
1182
635.2.11 by Vincent Ladeuil
Fix a leaking dialog windows appearing during the test suite.
1183
    def _get_commit_dialog(self, tree):
1184
        # Ensure we will never use a dialog that can actually prompt the user
1185
        # during the test suite. Test *can* and *should* override with the
1186
        # correct question dialog type.
1187
        dlg = commit.CommitDialog(tree)
1188
        self._set_question_no(dlg)
1189
        return dlg
1190
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1191
    def test_setup_saved_messages(self):
1192
        # Check the initial setup
1193
        self.assertEquals(u'two', self._get_commit_message())
1194
        self.assertEquals(u'd4:a-id7:a msg 24:b-id7:b msg 2e',
1195
                          self._get_file_commit_messages())
1196
1197
    def test_messages_are_reloaded(self):
635.2.11 by Vincent Ladeuil
Fix a leaking dialog windows appearing during the test suite.
1198
        dlg = self._get_commit_dialog(self.tree)
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1199
        self.assertEquals(u'two', dlg._get_global_commit_message())
1200
        self.assertEquals(([u'a', u'b'],
1201
                           [{ 'path': 'a',
1202
                             'file_id': 'a-id', 'message': 'a msg 2',},
1203
                           {'path': 'b',
1204
                            'file_id': 'b-id', 'message': 'b msg 2',}],),
1205
                          dlg._get_specific_files())
1206
1207
    def test_messages_are_consumed(self):
635.2.11 by Vincent Ladeuil
Fix a leaking dialog windows appearing during the test suite.
1208
        dlg = self._get_commit_dialog(self.tree)
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1209
        dlg._do_commit()
1210
        self.assertEquals(u'', self._get_commit_message())
1211
        self.assertEquals(u'de', self._get_file_commit_messages())
1212
1213
    def test_messages_are_saved_on_cancel_if_required(self):
635.2.11 by Vincent Ladeuil
Fix a leaking dialog windows appearing during the test suite.
1214
        dlg = self._get_commit_dialog(self.tree)
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1215
        self._set_question_yes(dlg) # Save messages
1216
        dlg._do_cancel()
1217
        self.assertEquals(u'two', self._get_commit_message())
1218
        self.assertEquals(u'd4:a-id7:a msg 24:b-id7:b msg 2e',
1219
                          self._get_file_commit_messages())
1220
1221
    def test_messages_are_cleared_on_cancel_if_required(self):
635.2.11 by Vincent Ladeuil
Fix a leaking dialog windows appearing during the test suite.
1222
        dlg = self._get_commit_dialog(self.tree)
635.2.10 by Vincent Ladeuil
Complete tests around saved commit messages.
1223
        self._set_question_no(dlg) # Don't save messages
1224
        dlg._do_cancel()
1225
        self.assertEquals(u'', self._get_commit_message())
1226
        self.assertEquals(u'de',
1227
                          self._get_file_commit_messages())