/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to tests/test_commit.py

Commit messages never contain config options

Show diffs side-by-side

added added

removed removed

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