~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_shelf_ui.py

(jelmer) Use the absolute_import feature everywhere in bzrlib,
 and add a source test to make sure it's used everywhere. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008 Canonical Ltd
 
1
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
from cStringIO import StringIO
19
19
import os
20
20
import sys
 
21
from textwrap import dedent
21
22
 
22
23
from bzrlib import (
23
24
    errors,
25
26
    revision,
26
27
    tests,
27
28
)
 
29
from bzrlib.tests import script
 
30
from bzrlib.tests import (
 
31
    features,
 
32
    )
28
33
 
29
34
 
30
35
class ExpectShelver(shelf_ui.Shelver):
39
44
        self.expected = []
40
45
        self.diff_writer = StringIO()
41
46
 
42
 
    def expect(self, prompt, response):
43
 
        self.expected.append((prompt, response))
 
47
    def expect(self, message, response):
 
48
        self.expected.append((message, response))
44
49
 
45
 
    def prompt(self, message):
 
50
    def prompt(self, message, choices, default):
46
51
        try:
47
 
            prompt, response = self.expected.pop(0)
 
52
            expected_message, response = self.expected.pop(0)
48
53
        except IndexError:
49
54
            raise AssertionError('Unexpected prompt: %s' % message)
50
 
        if prompt != message:
 
55
        if message != expected_message:
51
56
            raise AssertionError('Wrong prompt: %s' % message)
 
57
        if choices != '&yes\n&No\n&finish\n&quit':
 
58
            raise AssertionError('Wrong choices: %s' % choices)
52
59
        return response
53
60
 
54
61
 
61
68
LINES_AY = 'a\nb\nc\nd\ne\nf\ng\nh\ni\ny\n'
62
69
 
63
70
 
64
 
class TestShelver(tests.TestCaseWithTransport):
 
71
class ShelfTestCase(tests.TestCaseWithTransport):
65
72
 
66
73
    def create_shelvable_tree(self):
67
74
        tree = self.make_branch_and_tree('tree')
71
78
        self.build_tree_contents([('tree/foo', LINES_ZY)])
72
79
        return tree
73
80
 
 
81
 
 
82
class TestShelver(ShelfTestCase):
 
83
 
74
84
    def test_unexpected_prompt_failure(self):
75
85
        tree = self.create_shelvable_tree()
76
86
        tree.lock_tree_write()
78
88
        shelver = ExpectShelver(tree, tree.basis_tree())
79
89
        self.addCleanup(shelver.finalize)
80
90
        e = self.assertRaises(AssertionError, shelver.run)
81
 
        self.assertEqual('Unexpected prompt: Shelve? [yNfq?]', str(e))
 
91
        self.assertEqual('Unexpected prompt: Shelve?', str(e))
82
92
 
83
93
    def test_wrong_prompt_failure(self):
84
94
        tree = self.create_shelvable_tree()
86
96
        self.addCleanup(tree.unlock)
87
97
        shelver = ExpectShelver(tree, tree.basis_tree())
88
98
        self.addCleanup(shelver.finalize)
89
 
        shelver.expect('foo', 'y')
 
99
        shelver.expect('foo', 0)
90
100
        e = self.assertRaises(AssertionError, shelver.run)
91
 
        self.assertEqual('Wrong prompt: Shelve? [yNfq?]', str(e))
 
101
        self.assertEqual('Wrong prompt: Shelve?', str(e))
92
102
 
93
103
    def test_shelve_not_diff(self):
94
104
        tree = self.create_shelvable_tree()
96
106
        self.addCleanup(tree.unlock)
97
107
        shelver = ExpectShelver(tree, tree.basis_tree())
98
108
        self.addCleanup(shelver.finalize)
99
 
        shelver.expect('Shelve? [yNfq?]', 'n')
100
 
        shelver.expect('Shelve? [yNfq?]', 'n')
 
109
        shelver.expect('Shelve?', 1)
 
110
        shelver.expect('Shelve?', 1)
101
111
        # No final shelving prompt because no changes were selected
102
112
        shelver.run()
103
113
        self.assertFileEqual(LINES_ZY, 'tree/foo')
108
118
        self.addCleanup(tree.unlock)
109
119
        shelver = ExpectShelver(tree, tree.basis_tree())
110
120
        self.addCleanup(shelver.finalize)
111
 
        shelver.expect('Shelve? [yNfq?]', 'y')
112
 
        shelver.expect('Shelve? [yNfq?]', 'y')
113
 
        shelver.expect('Shelve 2 change(s)? [yNfq?]', 'n')
 
121
        shelver.expect('Shelve?', 0)
 
122
        shelver.expect('Shelve?', 0)
 
123
        shelver.expect('Shelve 2 change(s)?', 1)
114
124
        shelver.run()
115
125
        self.assertFileEqual(LINES_ZY, 'tree/foo')
116
126
 
120
130
        self.addCleanup(tree.unlock)
121
131
        shelver = ExpectShelver(tree, tree.basis_tree())
122
132
        self.addCleanup(shelver.finalize)
123
 
        shelver.expect('Shelve? [yNfq?]', 'y')
124
 
        shelver.expect('Shelve? [yNfq?]', 'y')
125
 
        shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')
 
133
        shelver.expect('Shelve?', 0)
 
134
        shelver.expect('Shelve?', 0)
 
135
        shelver.expect('Shelve 2 change(s)?', 0)
126
136
        shelver.run()
127
137
        self.assertFileEqual(LINES_AJ, 'tree/foo')
128
138
 
132
142
        self.addCleanup(tree.unlock)
133
143
        shelver = ExpectShelver(tree, tree.basis_tree())
134
144
        self.addCleanup(shelver.finalize)
135
 
        shelver.expect('Shelve? [yNfq?]', 'y')
136
 
        shelver.expect('Shelve? [yNfq?]', 'n')
137
 
        shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
 
145
        shelver.expect('Shelve?', 0)
 
146
        shelver.expect('Shelve?', 1)
 
147
        shelver.expect('Shelve 1 change(s)?', 0)
138
148
        shelver.run()
139
149
        self.assertFileEqual(LINES_AY, 'tree/foo')
140
150
 
145
155
        self.addCleanup(tree.unlock)
146
156
        shelver = ExpectShelver(tree, tree.basis_tree())
147
157
        self.addCleanup(shelver.finalize)
148
 
        shelver.expect('Shelve binary changes? [yNfq?]', 'y')
149
 
        shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
 
158
        shelver.expect('Shelve binary changes?', 0)
 
159
        shelver.expect('Shelve 1 change(s)?', 0)
150
160
        shelver.run()
151
161
        self.assertFileEqual(LINES_AJ, 'tree/foo')
152
162
 
157
167
        self.addCleanup(tree.unlock)
158
168
        shelver = ExpectShelver(tree, tree.basis_tree())
159
169
        self.addCleanup(shelver.finalize)
160
 
        shelver.expect('Shelve renaming "foo" => "bar"? [yNfq?]', 'y')
161
 
        shelver.expect('Shelve? [yNfq?]', 'y')
162
 
        shelver.expect('Shelve? [yNfq?]', 'y')
163
 
        shelver.expect('Shelve 3 change(s)? [yNfq?]', 'y')
 
170
        shelver.expect('Shelve renaming "foo" => "bar"?', 0)
 
171
        shelver.expect('Shelve?', 0)
 
172
        shelver.expect('Shelve?', 0)
 
173
        shelver.expect('Shelve 3 change(s)?', 0)
164
174
        shelver.run()
165
175
        self.assertFileEqual(LINES_AJ, 'tree/foo')
166
176
 
171
181
        self.addCleanup(tree.unlock)
172
182
        shelver = ExpectShelver(tree, tree.basis_tree())
173
183
        self.addCleanup(shelver.finalize)
174
 
        shelver.expect('Shelve removing file "foo"? [yNfq?]', 'y')
175
 
        shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
 
184
        shelver.expect('Shelve removing file "foo"?', 0)
 
185
        shelver.expect('Shelve 1 change(s)?', 0)
176
186
        shelver.run()
177
187
        self.assertFileEqual(LINES_AJ, 'tree/foo')
178
188
 
185
195
        self.addCleanup(tree.unlock)
186
196
        shelver = ExpectShelver(tree, tree.basis_tree())
187
197
        self.addCleanup(shelver.finalize)
188
 
        shelver.expect('Shelve adding file "foo"? [yNfq?]', 'y')
189
 
        shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
 
198
        shelver.expect('Shelve adding file "foo"?', 0)
 
199
        shelver.expect('Shelve 1 change(s)?', 0)
190
200
        shelver.run()
191
 
        self.failIfExists('tree/foo')
 
201
        self.assertPathDoesNotExist('tree/foo')
192
202
 
193
203
    def test_shelve_kind_change(self):
194
204
        tree = self.create_shelvable_tree()
198
208
        self.addCleanup(tree.unlock)
199
209
        shelver = ExpectShelver(tree, tree.basis_tree())
200
210
        self.addCleanup(shelver.finalize)
201
 
        shelver.expect('Shelve changing "foo" from file to directory? [yNfq?]',
202
 
                       'y')
203
 
        shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
 
211
        shelver.expect('Shelve changing "foo" from file to directory?',
 
212
                       0)
 
213
        shelver.expect('Shelve 1 change(s)?', 0)
204
214
 
205
215
    def test_shelve_modify_target(self):
206
 
        self.requireFeature(tests.SymlinkFeature)
 
216
        self.requireFeature(features.SymlinkFeature)
207
217
        tree = self.create_shelvable_tree()
208
218
        os.symlink('bar', 'tree/baz')
209
219
        tree.add('baz', 'baz-id')
215
225
        shelver = ExpectShelver(tree, tree.basis_tree())
216
226
        self.addCleanup(shelver.finalize)
217
227
        shelver.expect('Shelve changing target of "baz" from "bar" to '
218
 
                '"vax"? [yNfq?]', 'y')
219
 
        shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
 
228
                '"vax"?', 0)
 
229
        shelver.expect('Shelve 1 change(s)?', 0)
220
230
        shelver.run()
221
231
        self.assertEqual('bar', os.readlink('tree/baz'))
222
232
 
226
236
        self.addCleanup(tree.unlock)
227
237
        shelver = ExpectShelver(tree, tree.basis_tree())
228
238
        self.addCleanup(shelver.finalize)
229
 
        shelver.expect('Shelve? [yNfq?]', 'f')
230
 
        shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')
 
239
        shelver.expect('Shelve?', 2)
 
240
        shelver.expect('Shelve 2 change(s)?', 0)
231
241
        shelver.run()
232
242
        self.assertFileEqual(LINES_AJ, 'tree/foo')
233
243
 
237
247
        self.addCleanup(tree.unlock)
238
248
        shelver = ExpectShelver(tree, tree.basis_tree())
239
249
        self.addCleanup(shelver.finalize)
240
 
        shelver.expect('Shelve? [yNfq?]', 'q')
 
250
        shelver.expect('Shelve?', 3)
241
251
        self.assertRaises(errors.UserAbort, shelver.run)
242
252
        self.assertFileEqual(LINES_ZY, 'tree/foo')
243
253
 
259
269
        self.addCleanup(tree.unlock)
260
270
        shelver = ExpectShelver(tree, tree.basis_tree(), file_list=['bar'])
261
271
        self.addCleanup(shelver.finalize)
262
 
        shelver.expect('Shelve adding file "bar"? [yNfq?]', 'y')
263
 
        shelver.expect('Shelve 1 change(s)? [yNfq?]', 'y')
264
 
        shelver.run()
265
 
 
266
 
    def test_shelve_help(self):
267
 
        tree = self.create_shelvable_tree()
268
 
        tree.lock_tree_write()
269
 
        self.addCleanup(tree.unlock)
270
 
        shelver = ExpectShelver(tree, tree.basis_tree())
271
 
        self.addCleanup(shelver.finalize)
272
 
        shelver.expect('Shelve? [yNfq?]', '?')
273
 
        shelver.expect('Shelve? [(y)es, (N)o, (f)inish, or (q)uit]', 'f')
274
 
        shelver.expect('Shelve 2 change(s)? [yNfq?]', 'y')
 
272
        shelver.expect('Shelve adding file "bar"?', 0)
 
273
        shelver.expect('Shelve 1 change(s)?', 0)
275
274
        shelver.run()
276
275
 
277
276
    def test_shelve_destroy(self):
297
296
        finally:
298
297
            tree.unlock()
299
298
 
300
 
    def test_shelve_old_root_deleted(self):
 
299
    def test_shelve_old_root_preserved(self):
301
300
        tree1 = self.make_branch_and_tree('tree1')
302
301
        tree1.commit('add root')
 
302
        tree1_root_id = tree1.get_root_id()
303
303
        tree2 = self.make_branch_and_tree('tree2')
304
304
        rev2 = tree2.commit('add root')
 
305
        self.assertNotEquals(tree1_root_id, tree2.get_root_id())
305
306
        tree1.merge_from_branch(tree2.branch,
306
307
                                from_revision=revision.NULL_REVISION)
307
 
        tree1.commit('Replaced root entry')
 
308
        tree1.commit('merging in tree2')
 
309
        self.assertEquals(tree1_root_id, tree1.get_root_id())
308
310
        # This is essentially assertNotRaises(InconsistentDelta)
309
 
        self.expectFailure('Cannot shelve replacing a root entry',
310
 
                           self.assertRaises, AssertionError,
311
 
                           self.assertRaises, errors.InconsistentDelta,
312
 
                           self.shelve_all, tree1, rev2)
 
311
        # With testtools 0.9.9, it can be rewritten as:
 
312
        # with ExpectedException(AssertionError,
 
313
        #                        'InconsistentDelta not raised'):
 
314
        #     with ExpectedException(errors.InconsistentDelta, ''):
 
315
        #         self.shelve_all(tree1, rev2)
 
316
        e = self.assertRaises(AssertionError, self.assertRaises,
 
317
                              errors.InconsistentDelta, self.shelve_all, tree1,
 
318
                              rev2)
 
319
        self.assertContainsRe('InconsistentDelta not raised', str(e))
313
320
 
314
321
    def test_shelve_split(self):
315
322
        outer_tree = self.make_branch_and_tree('outer')
325
332
                           outer_tree, rev2)
326
333
 
327
334
 
328
 
class TestApplyReporter(TestShelver):
 
335
class TestApplyReporter(ShelfTestCase):
329
336
 
330
337
    def test_shelve_not_diff(self):
331
338
        tree = self.create_shelvable_tree()
334
341
        shelver = ExpectShelver(tree, tree.basis_tree(),
335
342
                                reporter=shelf_ui.ApplyReporter())
336
343
        self.addCleanup(shelver.finalize)
337
 
        shelver.expect('Apply change? [yNfq?]', 'n')
338
 
        shelver.expect('Apply change? [yNfq?]', 'n')
 
344
        shelver.expect('Apply change?', 1)
 
345
        shelver.expect('Apply change?', 1)
339
346
        # No final shelving prompt because no changes were selected
340
347
        shelver.run()
341
348
        self.assertFileEqual(LINES_ZY, 'tree/foo')
347
354
        shelver = ExpectShelver(tree, tree.basis_tree(),
348
355
                                reporter=shelf_ui.ApplyReporter())
349
356
        self.addCleanup(shelver.finalize)
350
 
        shelver.expect('Apply change? [yNfq?]', 'y')
351
 
        shelver.expect('Apply change? [yNfq?]', 'y')
352
 
        shelver.expect('Apply 2 change(s)? [yNfq?]', 'n')
 
357
        shelver.expect('Apply change?', 0)
 
358
        shelver.expect('Apply change?', 0)
 
359
        shelver.expect('Apply 2 change(s)?', 1)
353
360
        shelver.run()
354
361
        self.assertFileEqual(LINES_ZY, 'tree/foo')
355
362
 
360
367
        shelver = ExpectShelver(tree, tree.basis_tree(),
361
368
                                reporter=shelf_ui.ApplyReporter())
362
369
        self.addCleanup(shelver.finalize)
363
 
        shelver.expect('Apply change? [yNfq?]', 'y')
364
 
        shelver.expect('Apply change? [yNfq?]', 'y')
365
 
        shelver.expect('Apply 2 change(s)? [yNfq?]', 'y')
 
370
        shelver.expect('Apply change?', 0)
 
371
        shelver.expect('Apply change?', 0)
 
372
        shelver.expect('Apply 2 change(s)?', 0)
366
373
        shelver.run()
367
374
        self.assertFileEqual(LINES_AJ, 'tree/foo')
368
375
 
374
381
        shelver = ExpectShelver(tree, tree.basis_tree(),
375
382
                                reporter=shelf_ui.ApplyReporter())
376
383
        self.addCleanup(shelver.finalize)
377
 
        shelver.expect('Apply binary changes? [yNfq?]', 'y')
378
 
        shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
 
384
        shelver.expect('Apply binary changes?', 0)
 
385
        shelver.expect('Apply 1 change(s)?', 0)
379
386
        shelver.run()
380
387
        self.assertFileEqual(LINES_AJ, 'tree/foo')
381
388
 
387
394
        shelver = ExpectShelver(tree, tree.basis_tree(),
388
395
                                reporter=shelf_ui.ApplyReporter())
389
396
        self.addCleanup(shelver.finalize)
390
 
        shelver.expect('Rename "bar" => "foo"? [yNfq?]', 'y')
391
 
        shelver.expect('Apply change? [yNfq?]', 'y')
392
 
        shelver.expect('Apply change? [yNfq?]', 'y')
393
 
        shelver.expect('Apply 3 change(s)? [yNfq?]', 'y')
 
397
        shelver.expect('Rename "bar" => "foo"?', 0)
 
398
        shelver.expect('Apply change?', 0)
 
399
        shelver.expect('Apply change?', 0)
 
400
        shelver.expect('Apply 3 change(s)?', 0)
394
401
        shelver.run()
395
402
        self.assertFileEqual(LINES_AJ, 'tree/foo')
396
403
 
402
409
        shelver = ExpectShelver(tree, tree.basis_tree(),
403
410
                                reporter=shelf_ui.ApplyReporter())
404
411
        self.addCleanup(shelver.finalize)
405
 
        shelver.expect('Add file "foo"? [yNfq?]', 'y')
406
 
        shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
 
412
        shelver.expect('Add file "foo"?', 0)
 
413
        shelver.expect('Apply 1 change(s)?', 0)
407
414
        shelver.run()
408
415
        self.assertFileEqual(LINES_AJ, 'tree/foo')
409
416
 
417
424
        shelver = ExpectShelver(tree, tree.basis_tree(),
418
425
                                reporter=shelf_ui.ApplyReporter())
419
426
        self.addCleanup(shelver.finalize)
420
 
        shelver.expect('Delete file "foo"? [yNfq?]', 'y')
421
 
        shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
 
427
        shelver.expect('Delete file "foo"?', 0)
 
428
        shelver.expect('Apply 1 change(s)?', 0)
422
429
        shelver.run()
423
 
        self.failIfExists('tree/foo')
 
430
        self.assertPathDoesNotExist('tree/foo')
424
431
 
425
432
    def test_shelve_kind_change(self):
426
433
        tree = self.create_shelvable_tree()
431
438
        shelver = ExpectShelver(tree, tree.basis_tree(),
432
439
                               reporter=shelf_ui.ApplyReporter())
433
440
        self.addCleanup(shelver.finalize)
434
 
        shelver.expect('Change "foo" from directory to a file? [yNfq?]', 'y')
435
 
        shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
 
441
        shelver.expect('Change "foo" from directory to a file?', 0)
 
442
        shelver.expect('Apply 1 change(s)?', 0)
436
443
 
437
444
    def test_shelve_modify_target(self):
438
 
        self.requireFeature(tests.SymlinkFeature)
 
445
        self.requireFeature(features.SymlinkFeature)
439
446
        tree = self.create_shelvable_tree()
440
447
        os.symlink('bar', 'tree/baz')
441
448
        tree.add('baz', 'baz-id')
447
454
        shelver = ExpectShelver(tree, tree.basis_tree(),
448
455
                                reporter=shelf_ui.ApplyReporter())
449
456
        self.addCleanup(shelver.finalize)
450
 
        shelver.expect('Change target of "baz" from "vax" to "bar"? [yNfq?]',
451
 
                       'y')
452
 
        shelver.expect('Apply 1 change(s)? [yNfq?]', 'y')
 
457
        shelver.expect('Change target of "baz" from "vax" to "bar"?',
 
458
                       0)
 
459
        shelver.expect('Apply 1 change(s)?', 0)
453
460
        shelver.run()
454
461
        self.assertEqual('bar', os.readlink('tree/baz'))
455
462
 
503
510
        self.assertFileEqual(LINES_AJ, 'tree/foo')
504
511
        self.assertEqual(1, tree.get_shelf_manager().last_shelf())
505
512
 
 
513
    def test_unshelve_args_preview(self):
 
514
        tree = self.create_tree_with_shelf()
 
515
        write_diff_to = StringIO()
 
516
        unshelver = shelf_ui.Unshelver.from_args(
 
517
            directory='tree', action='preview', write_diff_to=write_diff_to)
 
518
        try:
 
519
            unshelver.run()
 
520
        finally:
 
521
            unshelver.tree.unlock()
 
522
        # The changes were not unshelved.
 
523
        self.assertFileEqual(LINES_AJ, 'tree/foo')
 
524
        self.assertEqual(1, tree.get_shelf_manager().last_shelf())
 
525
 
 
526
        # But the diff was written to write_diff_to.
 
527
        diff = write_diff_to.getvalue()
 
528
        expected = dedent("""\
 
529
            @@ -1,4 +1,4 @@
 
530
            -a
 
531
            +z
 
532
             b
 
533
             c
 
534
             d
 
535
            @@ -7,4 +7,4 @@
 
536
             g
 
537
             h
 
538
             i
 
539
            -j
 
540
            +y
 
541
 
 
542
            """)
 
543
        self.assertEqualDiff(expected, diff[-len(expected):])
 
544
 
506
545
    def test_unshelve_args_delete_only(self):
507
546
        tree = self.make_branch_and_tree('tree')
508
547
        manager = tree.get_shelf_manager()
530
569
        self.assertRaises(errors.InvalidShelfId,
531
570
            shelf_ui.Unshelver.from_args, directory='tree',
532
571
            action='delete-only', shelf_id='foo')
 
572
 
 
573
 
 
574
class TestUnshelveScripts(TestUnshelver, 
 
575
                          script.TestCaseWithTransportAndScript): 
 
576
 
 
577
    def test_unshelve_messages_keep(self):
 
578
        self.create_tree_with_shelf()
 
579
        self.run_script("""
 
580
$ cd tree
 
581
$ bzr unshelve --keep
 
582
2>Using changes with id "1".
 
583
2> M  foo
 
584
2>All changes applied successfully.
 
585
""")
 
586
 
 
587
    def test_unshelve_messages_delete(self):
 
588
        self.create_tree_with_shelf()
 
589
        self.run_script("""
 
590
$ cd tree
 
591
$ bzr unshelve --delete-only
 
592
2>Deleted changes with id "1".
 
593
""")
 
594
 
 
595
    def test_unshelve_messages_apply(self):
 
596
        self.create_tree_with_shelf()
 
597
        self.run_script("""
 
598
$ cd tree
 
599
$ bzr unshelve --apply
 
600
2>Using changes with id "1".
 
601
2> M  foo
 
602
2>All changes applied successfully.
 
603
2>Deleted changes with id "1".
 
604
""")
 
605
 
 
606
    def test_unshelve_messages_dry_run(self):
 
607
        self.create_tree_with_shelf()
 
608
        self.run_script("""
 
609
$ cd tree
 
610
$ bzr unshelve --dry-run
 
611
2>Using changes with id "1".
 
612
2> M  foo
 
613
""")