144
108
msg = tree.log_message()
145
109
msg["summary"] = "Import with a symlink"
146
110
tree.import_(msg)
148
f = file(link_path, 'w')
149
f.write('Not a symlink no more!')
151
msg = tree.log_message()
152
msg["summary"] = "Turn a symlink into a file"
154
111
shutil.rmtree(os.path.join(self._tmpdir, 'tree'))
156
113
def make_empty_import(self):
157
114
self._import = 'demo@DONOTUSE/c--import--0'
158
115
os.mkdir(os.path.join(self._tmpdir, 'tree'))
159
tree = pybaz.init_tree(os.path.join(self._tmpdir, 'tree'),
161
msg = tree.log_message()
162
msg["summary"] = "I am importing now"
164
shutil.rmtree(os.path.join(self._tmpdir, 'tree'))
166
def make_missing_ancestor(self):
167
self._archivegoneroot = os.path.join(self._tmpdir, 'archivegone')
168
self._archive = make_archive('demo-gone@DONOTUSE',
169
str(self._archivegoneroot))
170
self._missing_import = 'demo-gone@DONOTUSE/c--import--0'
171
self._missing_import_bzr = revision_id(self._missing_import
173
self._missing_ancestor = 'demo@DONOTUSE/c--gone--0'
174
self._missing_ancestor_bzr = revision_id(self._missing_ancestor
176
os.mkdir(os.path.join(self._tmpdir, 'tree'))
177
tree = pybaz.init_tree(os.path.join(self._tmpdir, 'tree'),
178
self._missing_import)
179
msg = tree.log_message()
180
msg["summary"] = "I am importing now"
182
shutil.rmtree(os.path.join(self._tmpdir, 'tree'))
183
# tag into the kept archive
184
pybaz.Revision(self._missing_import + '--base-0').make_continuation(
185
pybaz.Version(self._missing_ancestor))
187
# make an import for testing history-reuse logic.
188
# note the use of a namespace layout here.
189
self._missing_import_imported = os.path.join(self._tmpdir,
191
os.mkdir(os.path.join(self._tmpdir, 'archivegone-bzr'))
192
os.mkdir(os.path.join(self._tmpdir, 'archivegone-bzr', 'c'))
193
import_version(os.path.join(self._tmpdir, 'archivegone-bzr',
195
pybaz.Version(self._missing_import))
196
# and make it inaccessible
197
pybaz.Archive('demo-gone@DONOTUSE').unregister()
199
def make_inbranch_continuation(self):
200
self._inbranch_tag = 'demo@DONOTUSE/c--inbranch-tag--0'
201
self._inbranch_tag_base = self._inbranch_tag + '--base-0'
202
self._inbranch_tag_base_bzr = revision_id(self._inbranch_tag_base)
203
pybaz.Revision('demo@DONOTUSE/c--import--0--base-0').make_continuation(
204
pybaz.Version(self._inbranch_tag))
205
self._inbranch_tag_head = self._inbranch_tag + '--patch-1'
206
self._inbranch_tag_head_bzr = revision_id(self._inbranch_tag_head)
207
pybaz.Revision(self._inbranch_tag_base).make_continuation(
208
pybaz.Version(self._inbranch_tag))
116
tree = pybaz.init_tree(os.path.join(self._tmpdir, 'tree'), self._import)
117
msg = tree.log_message()
118
msg["summary"] = "I am importing now"
120
shutil.rmtree(os.path.join(self._tmpdir, 'tree'))
211
123
def _makeResource(self):
288
195
# self._baz._empty_merged_tag_bzr
289
196
# and a merged revision from the latter of
290
197
# self._baz._empty_tag_bzr
291
branch = Branch.open('output')
292
repo = branch.repository
198
branch = find_branch('output', find_root=False)
293
199
self.assertEqual(branch.revision_history(),
294
200
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
295
201
self._baz._empty_merged_tag_bzr_base,
296
202
self._baz._empty_merged_tag_bzr])
298
204
import_version('output2', pybaz.Version(self._baz._empty_merged_tag))
299
branch2 = Branch.open('output2')
300
repo2 = branch2.repository
205
branch2 = find_branch('output2', find_root=False)
301
206
# and import what we should be merged up against for checking with.
302
207
import_version('output3', pybaz.Version(self._baz._empty_tag))
303
branch3 = Branch.open('output3')
208
branch3 = find_branch('output3', find_root=False)
305
210
self.assertEqual(branch.revision_history(), branch2.revision_history())
306
self.assertNotEqual(branch.revision_history(),
307
branch3.revision_history())
211
self.assertNotEqual(branch.revision_history(), branch3.revision_history())
308
212
# check revisions in the history.
309
rev = repo.get_revision(self._baz._empty_merged_tag_bzr_base)
310
rev2 = repo2.get_revision(self._baz._empty_merged_tag_bzr_base)
213
rev = branch.get_revision(self._baz._empty_merged_tag_bzr_base)
214
rev2 = branch2.get_revision(self._baz._empty_merged_tag_bzr_base)
311
215
# they must be the same
312
216
self.assertEqual(rev, rev2)
313
217
# and we should get some expected values:
314
218
self.assertEqual(rev.committer, "Test User<test@example.org>")
315
self.assertEqual(rev.message,
316
"tag of demo@DONOTUSE/c--import--0--base-0")
219
self.assertEqual(rev.message, "tag of demo@DONOTUSE/c--import--0--base-0")
317
220
self.assertEqual(rev.revision_id, self._baz._empty_merged_tag_bzr_base)
318
self.assertEqual(['Arch-1:demo@DONOTUSE%c--import--0--base-0'],
321
222
# check next revisions in the history.
322
rev = repo.get_revision(self._baz._empty_merged_tag_bzr)
323
rev2 = repo2.get_revision(self._baz._empty_merged_tag_bzr)
223
rev = branch.get_revision(self._baz._empty_merged_tag_bzr)
224
rev2 = branch2.get_revision(self._baz._empty_merged_tag_bzr)
324
225
# they must be the same
325
226
self.assertEqual(rev, rev2)
326
227
# and we should get some expected values:
327
228
self.assertEqual(rev.committer, "Test User<test@example.org>")
328
229
self.assertEqual(rev.message, "did a merge, yarh")
329
230
self.assertEqual(rev.revision_id, self._baz._empty_merged_tag_bzr)
330
self.assertEqual(rev.parent_ids[0],
231
self.assertEqual(rev.parents[0].revision_id,
331
232
self._baz._empty_merged_tag_bzr_base)
332
self.assertEqual(rev.parent_ids[1], self._baz._empty_tag_bzr)
233
self.assertEqual(rev.parents[1].revision_id,
234
self._baz._empty_tag_bzr)
334
236
# this tree should have nothing missing from that tree.
335
237
# FIXME there is no code for this right now.
344
246
# self._baz._empty_merged_tag_2_bzr
345
247
# and a merged revision from the latter of
346
248
# self._baz._empty_merged_tag_bzr
347
branch = Branch.open('output')
348
repo = branch.repository
249
branch = find_branch('output', find_root=False)
349
250
self.assertEqual(branch.revision_history(),
350
251
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
351
252
self._baz._empty_merged_tag_2_bzr_base,
352
253
self._baz._empty_merged_tag_2_bzr])
354
255
import_version('output2', pybaz.Version(self._baz._empty_merged_tag_2))
355
branch2 = Branch.open('output2')
356
repo2 = branch2.repository
256
branch2 = find_branch('output2', find_root=False)
357
257
# and import what we should be merged up against for checking with.
358
258
import_version('output3', pybaz.Version(self._baz._empty_merged_tag))
359
branch3 = Branch.open('output3')
259
branch3 = find_branch('output3', find_root=False)
361
261
self.assertEqual(branch.revision_history(), branch2.revision_history())
362
self.assertNotEqual(branch.revision_history(),
363
branch3.revision_history())
262
self.assertNotEqual(branch.revision_history(), branch3.revision_history())
364
263
# check revisions in the history.
365
rev = repo.get_revision(self._baz._empty_merged_tag_2_bzr_base)
366
rev2 = repo2.get_revision(self._baz._empty_merged_tag_2_bzr_base)
264
rev = branch.get_revision(self._baz._empty_merged_tag_2_bzr_base)
265
rev2 = branch2.get_revision(self._baz._empty_merged_tag_2_bzr_base)
367
266
# they must be the same
368
267
self.assertEqual(rev, rev2)
369
268
# and we should get some expected values:
370
269
self.assertEqual(rev.committer, "Test User<test@example.org>")
371
self.assertEqual(rev.message,
372
"tag of demo@DONOTUSE/c--import--0--base-0")
373
self.assertEqual(rev.revision_id,
374
self._baz._empty_merged_tag_2_bzr_base)
270
self.assertEqual(rev.message, "tag of demo@DONOTUSE/c--import--0--base-0")
271
self.assertEqual(rev.revision_id, self._baz._empty_merged_tag_2_bzr_base)
376
273
# check next revisions in the history.
377
rev = repo.get_revision(self._baz._empty_merged_tag_2_bzr)
378
rev2 = repo2.get_revision(self._baz._empty_merged_tag_2_bzr)
274
rev = branch.get_revision(self._baz._empty_merged_tag_2_bzr)
275
rev2 = branch2.get_revision(self._baz._empty_merged_tag_2_bzr)
379
276
# they must be the same
380
277
self.assertEqual(rev, rev2)
381
278
# and we should get some expected values:
382
279
self.assertEqual(rev.committer, "Test User<test@example.org>")
383
280
self.assertEqual(rev.message, "merge in a merged tree.")
384
281
self.assertEqual(rev.revision_id, self._baz._empty_merged_tag_2_bzr)
385
self.assertEqual(rev.parent_ids[0],
282
self.assertEqual(rev.parents[0].revision_id,
386
283
self._baz._empty_merged_tag_2_bzr_base)
387
self.assertEqual(rev.parent_ids[1],
284
self.assertEqual(rev.parents[1].revision_id,
388
285
self._baz._empty_merged_tag_bzr)
390
287
# this tree should have nothing missing from that tree.
416
310
self.assertEqual(rev.revision_id, self._baz._import_symlink_bzr)
418
312
# and we want the symlink alink with target 'missing-file-name'
419
inv = branch.repository.get_inventory(rev.revision_id)
313
inv = branch.get_inventory(rev.inventory_id)
420
314
self.assertEqual(inv.path2id('alink'), 'x_symlink_tag')
421
315
entry = inv['x_symlink_tag']
422
316
self.assertEqual(entry.kind, 'symlink')
423
317
self.assertEqual(entry.symlink_target, 'missing-file-name')
425
# current bzr doesn't handle type changes
426
self.assertRaises(AssertionError, import_version, 'output3',
427
pybaz.Version(self._baz._import_symlink))
429
def test_missing_ancestor(self):
430
import_version('output', pybaz.Version(self._baz._missing_ancestor))
432
# one commits, no files, revision identifiers of
433
# 'demo@DONOTUSE_c--gone--0--base-0' and
434
# a merge of demo-gone@DONOTUSE%c--import--0
435
branch = Branch.open('output')
436
self.assertEqual(branch.revision_history(),
437
[self._baz._missing_ancestor_bzr])
438
rev = branch.repository.get_revision(self._baz._missing_ancestor_bzr)
440
import_version('output2', pybaz.Version(self._baz._missing_ancestor))
441
branch2 = Branch.open('output2')
442
self.assertEqual(branch.revision_history(), branch2.revision_history())
443
rev2 = branch2.repository.get_revision(self._baz._missing_ancestor_bzr)
444
# they must be the same
445
self.assertEqual(rev, rev2)
447
# and we should get some expected values:
448
self.assertEqual(rev.committer, "Test User<test@example.org>")
449
self.assertEqual(rev.message,
450
"tag of demo-gone@DONOTUSE/c--import--0--base-0")
451
self.assertEqual(rev.revision_id, self._baz._missing_ancestor_bzr)
452
self.assertEqual(rev.parent_ids[0], self._baz._missing_import_bzr)
453
self.assertEqual(1, len(rev.parent_ids))
455
# must NOT be able to get the merged evision
456
self.assertRaises(NoSuchRevision, branch.repository.get_revision,
457
self._baz._missing_import_bzr)
459
def test_missing_ancestor_reusing_history(self):
460
import_version('output', pybaz.Version(self._baz._missing_ancestor),
461
reuse_history_from=[self._baz._missing_import_imported])
463
# one commits, no files, revision identifiers of
464
# 'demo-gone@DONOTUSE%c--import--0--base-0' and
465
# 'demo@DONOTUSE%c--gone--0--base-0'
466
branch = Branch.open('output')
467
self.assertEqual(branch.revision_history(),
468
[self._baz._missing_import_bzr,
469
self._baz._missing_ancestor_bzr])
470
rev = branch.repository.get_revision(self._baz._missing_ancestor_bzr)
472
import_version('output2', pybaz.Version(self._baz._missing_ancestor),
473
reuse_history_from=[self._baz._missing_import_imported])
474
branch2 = Branch.open('output2')
475
self.assertEqual(branch.revision_history(), branch2.revision_history())
476
rev2 = branch2.repository.get_revision(self._baz._missing_ancestor_bzr)
477
# they must be the same
478
self.assertEqual(rev, rev2)
480
# must be able to get the missing base revision
481
branch.repository.get_revision(self._baz._missing_import_bzr)
483
# and we should get some expected values:
484
self.assertEqual(rev.committer, "Test User<test@example.org>")
485
self.assertEqual(rev.message,
486
"tag of demo-gone@DONOTUSE/c--import--0--base-0")
487
self.assertEqual(rev.revision_id, self._baz._missing_ancestor_bzr)
488
self.assertEqual(rev.parent_ids[0], self._baz._missing_import_bzr)
489
self.assertEqual(1, len(rev.parent_ids))
491
def test_bad_file_id(self):
492
import_version('output', pybaz.Version(self._baz._bad_id_tag))
494
# three commits, one files, revision identifiers of
495
# 'demo@DONOTUSE_c--import--0--base-0' ,
496
# 'demo@DONOTUSE/c--bad-id--0--base-0' ,
497
# ''demo@DONOTUSE/c--bad-id--0--patch-1'
498
branch = Branch.open('output')
499
self.assertEqual(branch.revision_history(),
500
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
501
self._baz._bad_id_tag_bzr_base,
502
self._baz._bad_id_tag_bzr])
503
rev = branch.repository.get_revision(self._baz._bad_id_tag_bzr)
504
inv = branch.repository.get_inventory(self._baz._bad_id_tag_bzr)
505
self.assertEqual(inv.path2id('path'), 'x_this_id%2fneeds%25escaping')
506
self.assertEqual('path', inv.id2path('x_this_id%2fneeds%25escaping'))
508
def test_appending_revisions_already_present(self):
509
import_version('output', pybaz.Version(self._baz._bad_id_tag),
512
# three commits, one files, revision identifiers of
513
# 'demo@DONOTUSE_c--import--0--base-0' ,
514
# 'demo@DONOTUSE/c--bad-id--0--base-0' ,
515
# ''demo@DONOTUSE/c--bad-id--0--patch-1'
516
branch = Branch.open('output')
517
self.assertEqual(branch.revision_history(),
518
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
519
self._baz._bad_id_tag_bzr_base])
520
branch.set_revision_history(
521
['Arch-1:demo@DONOTUSE%c--import--0--base-0'])
523
branch = Branch.open('output')
524
self.assertEqual(branch.revision_history(),
525
['Arch-1:demo@DONOTUSE%c--import--0--base-0'])
527
import_version('output', pybaz.Version(self._baz._bad_id_tag))
528
branch = Branch.open('output')
529
self.assertEqual(branch.revision_history(),
530
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
531
self._baz._bad_id_tag_bzr_base,
532
self._baz._bad_id_tag_bzr])
533
rev = branch.repository.get_revision(self._baz._bad_id_tag_bzr)
534
inv = branch.repository.get_inventory(self._baz._bad_id_tag_bzr)
535
self.assertEqual(inv.path2id('path'), 'x_this_id%2fneeds%25escaping')
536
self.assertEqual('path', inv.id2path('x_this_id%2fneeds%25escaping'))
538
def test_appending_revisions_all_already_present(self):
539
import_version('output', pybaz.Version(self._baz._bad_id_tag))
541
# three commits, one files, revision identifiers of
542
# 'demo@DONOTUSE_c--import--0--base-0' ,
543
# 'demo@DONOTUSE/c--bad-id--0--base-0' ,
544
# ''demo@DONOTUSE/c--bad-id--0--patch-1'
545
branch = Branch.open('output')
546
self.assertEqual(branch.revision_history(),
547
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
548
self._baz._bad_id_tag_bzr_base,
549
self._baz._bad_id_tag_bzr])
550
branch.set_revision_history(
551
['Arch-1:demo@DONOTUSE%c--import--0--base-0'])
553
branch = Branch.open('output')
554
self.assertEqual(branch.revision_history(),
555
['Arch-1:demo@DONOTUSE%c--import--0--base-0'])
557
import_version('output', pybaz.Version(self._baz._bad_id_tag))
558
branch = Branch.open('output')
559
self.assertEqual(branch.revision_history(),
560
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
561
self._baz._bad_id_tag_bzr_base,
562
self._baz._bad_id_tag_bzr])
563
rev = branch.repository.get_revision(self._baz._bad_id_tag_bzr)
564
inv = branch.repository.get_inventory(self._baz._bad_id_tag_bzr)
565
self.assertEqual(inv.path2id('path'), 'x_this_id%2fneeds%25escaping')
566
self.assertEqual('path', inv.id2path('x_this_id%2fneeds%25escaping'))
568
def test_inbranch_conversion(self):
569
import_version('output', pybaz.Version(self._baz._inbranch_tag))
571
# three commits, no files, revision identifiers of
572
# 'demo@DONOTUSE_c--import--0--base-0' and
573
# self._baz._inbranch_tag_base_bzr
574
# self._baz._inbranch_tag_head_bzr
576
branch = Branch.open('output')
577
self.assertEqual(branch.revision_history(),
578
['Arch-1:demo@DONOTUSE%c--import--0--base-0',
579
self._baz._inbranch_tag_base_bzr,
580
self._baz._inbranch_tag_head_bzr])
582
import_version('output2', pybaz.Version(self._baz._inbranch_tag))
583
branch2 = Branch.open('output2')
585
self.assertEqual(branch.revision_history(), branch2.revision_history())
586
# check revisions in the history.
587
rev = branch.repository.get_revision(self._baz._inbranch_tag_base_bzr)
588
rev2 = branch2.repository.get_revision(self._baz._inbranch_tag_base_bzr)
589
# they must be the same
590
self.assertEqual(rev, rev2)
591
# and we should get some expected values:
592
self.assertEqual(rev.committer, "Test User<test@example.org>")
593
self.assertEqual(rev.message, "tag of demo@DONOTUSE/c--import--0--base-0")
594
self.assertEqual(rev.revision_id, self._baz._inbranch_tag_base_bzr)
596
# check next revisions in the history.
597
rev = branch.repository.get_revision(self._baz._inbranch_tag_head_bzr)
598
rev2 = branch2.repository.get_revision(self._baz._inbranch_tag_head_bzr)
599
# they must be the same
600
self.assertEqual(rev, rev2)
601
# and we should get some expected values:
602
self.assertEqual(rev.committer, "Test User<test@example.org>")
603
self.assertEqual(rev.message, "tag of demo@DONOTUSE/c--inbranch-tag--0--base-0")
604
self.assertEqual(rev.revision_id, self._baz._inbranch_tag_head_bzr)
605
self.assertEqual(rev.parent_ids,
606
[self._baz._inbranch_tag_base_bzr])
608
def test_no_commits_same_as_missing(self):
611
branch = bzrlib.bzrdir.BzrDir.create_branch_convenience(path)
612
import_version(path, pybaz.Version(self._baz._import))
614
# one commit, revision identifier of
615
# 'demo@DONOTUSE_c--import--0--base-0'
616
self.assertEqual(branch.revision_history(),
617
['Arch-1:demo@DONOTUSE%c--import--0--base-0'])
620
320
class TestNamespacePrevious(TestCase):
639
339
self.version['version-0'])
641
341
def test_version1_version0(self):
642
self.assertEqual(namespace_previous(self.version['versionfix-1']),
342
self.assertEqual(namespace_previous(self.version['version-1']),
643
343
self.version['version-0'])
645
def test_version3000_patch2999(self):
646
self.assertEqual(namespace_previous(self.version['versionfix-3000']),
647
self.version['versionfix-2999'])
650
class TestNamespaceMapping(TestCase):
652
def test_namespace_mapping_branch(self):
653
from bzrlib.plugins.bzrtools.baz_import import map_namespace
654
branch = pybaz.Branch('foo@example.com/c--b')
655
self.assertRaises(pybaz.errors.NamespaceError, map_namespace, branch)
656
self.assertEqual('c/b', map_namespace(branch['0']))
657
self.assertEqual('c/0.1/b', map_namespace(branch['0.1']))
659
def test_namespace_mapping_no_branch(self):
660
from bzrlib.plugins.bzrtools.baz_import import map_namespace
661
category = pybaz.Category('foo@example.com/c')
662
self.assertRaises(pybaz.errors.NamespaceError, map_namespace, category)
663
self.assertEqual('c/+trunk',
664
map_namespace(pybaz.Version("%s--0" % category)))
665
self.assertEqual('c/0.1/+trunk',
666
map_namespace(pybaz.Version('%s--0.1' % category)))
669
class TestFileIdMapping(TestCase):
671
def test_slash(self):
672
self.assertEqual('c%2fc', map_file_id('c/c'))
673
self.assertEqual('c%25c', map_file_id('c%c'))
676
class TestImport(TestCaseInTempDir):
679
TestCaseInTempDir.setUp(self)
680
self._oldhome = os.environ['HOME']
681
self._tmpdir = tempfile.mkdtemp()
682
self._homedir = os.path.join(self._tmpdir, 'home')
683
os.mkdir(self._homedir)
684
os.environ['HOME'] = self._homedir
685
self._archiveroot = os.path.join(self._tmpdir, 'archive')
686
self._archive = make_archive('demo@DONOTUSE', str(self._archiveroot))
689
os.environ['HOME'] = self._oldhome
690
shutil.rmtree(self._tmpdir)
691
TestCaseInTempDir.tearDown(self)
693
def make_import(self, namespace):
694
self._import = 'demo@DONOTUSE/%s' % namespace
695
os.mkdir(os.path.join(self._tmpdir, 'tree'))
696
tree = pybaz.init_tree(os.path.join(self._tmpdir, 'tree'),
698
msg = tree.log_message()
699
msg["summary"] = "I am importing now"
701
shutil.rmtree(os.path.join(self._tmpdir, 'tree'))
703
def test_cmd_exists(self):
704
from bzrlib.plugins.bzrtools.baz_import import cmd_baz_import
706
def test_empty_archive(self):
707
command = cmd_baz_import()
708
command.run(os.path.join(self._tmpdir, 'output'), 'demo@DONOTUSE')
709
self.failUnless(os.path.exists(os.path.join(self._tmpdir,'output')))
710
walk_len = len(list(os.walk(os.path.join(self._tmpdir,'output'))))
711
self.assertEqual(7, walk_len)
713
def test_two_branches(self):
714
self.make_import('c--0')
715
self.make_import('c1--branch--0.2')
716
command = cmd_baz_import()
717
command.run(os.path.join(self._tmpdir, 'output'), 'demo@DONOTUSE')
718
self.failUnless(os.path.exists(os.path.join(self._tmpdir,'output')))
719
self.failUnless(os.path.exists(os.path.join(self._tmpdir,'output',
721
self.failUnless(os.path.exists(os.path.join(self._tmpdir,'output',
722
'c1', '0.2','branch')))
723
walk_len = len(list(os.walk(os.path.join(self._tmpdir,'output'))))
724
self.assertEqual(20, walk_len)
726
def test_run_twice(self):
727
self.make_import('c--0')
728
command = cmd_baz_import()
729
command.run(os.path.join(self._tmpdir, 'output'), 'demo@DONOTUSE')
730
command.run(os.path.join(self._tmpdir, 'output'), 'demo@DONOTUSE')
732
def test_accepts_reuse_history(self):
733
self.make_import('c--0')
734
self.run_bzr('baz-import', os.path.join(self._tmpdir, 'output'),
735
'demo@DONOTUSE', '.', '.')
737
def test_does_not_need_reuse_history(self):
738
self.make_import('c--0')
739
self.run_bzr('baz-import', os.path.join(self._tmpdir, 'output'),