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