/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

  • Committer: Jelmer Vernooij
  • Date: 2012-03-16 17:28:20 UTC
  • mto: This revision was merged to the branch mainline in revision 793.
  • Revision ID: jelmer@samba.org-20120316172820-ohpb9bvgo63k2n8u
use register-lazy

Show diffs side-by-side

added added

removed removed

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