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