/b-gtk/fix-viz

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