~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_knit.py

  • Committer: v.ladeuil+lp at free
  • Date: 2007-05-18 18:20:31 UTC
  • mto: (2485.8.44 bzr.connection.sharing)
  • mto: This revision was merged to the branch mainline in revision 2646.
  • Revision ID: v.ladeuil+lp@free.fr-20070518182031-gbg2cgidv5l20x9p
Takes Robert comments into account.

* bzrlib/transport/ftp.py:
(FtpTransport.__init__): Write a better explanation.

* bzrlib/tests/test_init.py:
(InstrumentedTransport): Just make hooks a class attribute.
(InstrumentedTransport._get_FTP): Run hook directly in the for
loop.
(TransportHooks.run_hook, TransportHooks.uninstall_hook): Not
needed. The hooks should be cleaned up by the test itself.
(TestInit.setUp.cleanup): Resset to default hooks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 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
20
20
import difflib
21
21
import gzip
22
22
import sha
23
 
import sys
24
23
 
25
24
from bzrlib import (
26
25
    errors,
27
 
    generate_ids,
28
 
    knit,
29
26
    )
30
27
from bzrlib.errors import (
31
28
    RevisionAlreadyPresent,
41
38
    _KnitData,
42
39
    _KnitIndex,
43
40
    WeaveToKnit,
44
 
    KnitSequenceMatcher,
45
41
    )
46
42
from bzrlib.osutils import split_lines
47
 
from bzrlib.tests import TestCase, TestCaseWithTransport, Feature
 
43
from bzrlib.tests import TestCase, TestCaseWithTransport
48
44
from bzrlib.transport import TransportLogger, get_transport
49
45
from bzrlib.transport.memory import MemoryTransport
50
46
from bzrlib.weave import Weave
51
47
 
52
48
 
53
 
class _CompiledKnitFeature(Feature):
54
 
 
55
 
    def _probe(self):
56
 
        try:
57
 
            import bzrlib._knit_load_data_c
58
 
        except ImportError:
59
 
            return False
60
 
        return True
61
 
 
62
 
    def feature_name(self):
63
 
        return 'bzrlib._knit_load_data_c'
64
 
 
65
 
CompiledKnitFeature = _CompiledKnitFeature()
66
 
 
67
 
 
68
49
class KnitContentTests(TestCase):
69
50
 
70
51
    def test_constructor(self):
259
240
 
260
241
class LowLevelKnitIndexTests(TestCase):
261
242
 
262
 
    def get_knit_index(self, *args, **kwargs):
263
 
        orig = knit._load_data
264
 
        def reset():
265
 
            knit._load_data = orig
266
 
        self.addCleanup(reset)
267
 
        from bzrlib._knit_load_data_py import _load_data_py
268
 
        knit._load_data = _load_data_py
269
 
        return _KnitIndex(*args, **kwargs)
270
 
 
271
243
    def test_no_such_file(self):
272
244
        transport = MockTransport()
273
245
 
274
 
        self.assertRaises(NoSuchFile, self.get_knit_index,
275
 
                          transport, "filename", "r")
276
 
        self.assertRaises(NoSuchFile, self.get_knit_index,
277
 
                          transport, "filename", "w", create=False)
 
246
        self.assertRaises(NoSuchFile, _KnitIndex, transport, "filename", "r")
 
247
        self.assertRaises(NoSuchFile, _KnitIndex, transport,
 
248
            "filename", "w", create=False)
278
249
 
279
250
    def test_create_file(self):
280
251
        transport = MockTransport()
281
252
 
282
 
        index = self.get_knit_index(transport, "filename", "w",
 
253
        index = _KnitIndex(transport, "filename", "w",
283
254
            file_mode="wb", create=True)
284
255
        self.assertEqual(
285
256
                ("put_bytes_non_atomic",
289
260
    def test_delay_create_file(self):
290
261
        transport = MockTransport()
291
262
 
292
 
        index = self.get_knit_index(transport, "filename", "w",
 
263
        index = _KnitIndex(transport, "filename", "w",
293
264
            create=True, file_mode="wb", create_parent_dir=True,
294
265
            delay_create=True, dir_mode=0777)
295
266
        self.assertEqual([], transport.calls)
314
285
            _KnitIndex.HEADER,
315
286
            '%s option 0 1 :' % (utf8_revision_id,)
316
287
            ])
317
 
        index = self.get_knit_index(transport, "filename", "r")
 
288
        index = _KnitIndex(transport, "filename", "r")
318
289
        # _KnitIndex is a private class, and deals in utf8 revision_ids, not
319
290
        # Unicode revision_ids.
320
291
        self.assertTrue(index.has_version(utf8_revision_id))
327
298
            _KnitIndex.HEADER,
328
299
            "version option 0 1 .%s :" % (utf8_revision_id,)
329
300
            ])
330
 
        index = self.get_knit_index(transport, "filename", "r")
 
301
        index = _KnitIndex(transport, "filename", "r")
331
302
        self.assertEqual([utf8_revision_id],
332
303
            index.get_parents_with_ghosts("version"))
333
304
 
338
309
            "corrupted options 0 1 .b .c ",
339
310
            "version options 0 1 :"
340
311
            ])
341
 
        index = self.get_knit_index(transport, "filename", "r")
 
312
        index = _KnitIndex(transport, "filename", "r")
342
313
        self.assertEqual(1, index.num_versions())
343
314
        self.assertTrue(index.has_version("version"))
344
315
 
345
316
    def test_read_corrupted_header(self):
346
317
        transport = MockTransport(['not a bzr knit index header\n'])
347
318
        self.assertRaises(KnitHeaderError,
348
 
            self.get_knit_index, transport, "filename", "r")
 
319
            _KnitIndex, transport, "filename", "r")
349
320
 
350
321
    def test_read_duplicate_entries(self):
351
322
        transport = MockTransport([
355
326
            "version options2 1 2 .other :",
356
327
            "version options3 3 4 0 .other :"
357
328
            ])
358
 
        index = self.get_knit_index(transport, "filename", "r")
 
329
        index = _KnitIndex(transport, "filename", "r")
359
330
        self.assertEqual(2, index.num_versions())
360
331
        self.assertEqual(1, index.lookup("version"))
361
332
        self.assertEqual((3, 4), index.get_position("version"))
370
341
            "b option 0 1 0 :",
371
342
            "c option 0 1 1 0 :",
372
343
            ])
373
 
        index = self.get_knit_index(transport, "filename", "r")
 
344
        index = _KnitIndex(transport, "filename", "r")
374
345
        self.assertEqual(["a"], index.get_parents("b"))
375
346
        self.assertEqual(["b", "a"], index.get_parents("c"))
376
347
 
380
351
        transport = MockTransport([
381
352
            _KnitIndex.HEADER
382
353
            ])
383
 
        index = self.get_knit_index(transport, "filename", "r")
 
354
        index = _KnitIndex(transport, "filename", "r")
384
355
        index.add_version(utf8_revision_id, ["option"], 0, 1, [])
385
356
        self.assertEqual(("append_bytes", ("filename",
386
357
            "\n%s option 0 1  :" % (utf8_revision_id,)),
393
364
        transport = MockTransport([
394
365
            _KnitIndex.HEADER
395
366
            ])
396
 
        index = self.get_knit_index(transport, "filename", "r")
 
367
        index = _KnitIndex(transport, "filename", "r")
397
368
        index.add_version("version", ["option"], 0, 1, [utf8_revision_id])
398
369
        self.assertEqual(("append_bytes", ("filename",
399
370
            "\nversion option 0 1 .%s :" % (utf8_revision_id,)),
402
373
 
403
374
    def test_get_graph(self):
404
375
        transport = MockTransport()
405
 
        index = self.get_knit_index(transport, "filename", "w", create=True)
 
376
        index = _KnitIndex(transport, "filename", "w", create=True)
406
377
        self.assertEqual([], index.get_graph())
407
378
 
408
379
        index.add_version("a", ["option"], 0, 1, ["b"])
420
391
            "c option 0 1 1 0 :",
421
392
            "d option 0 1 2 .f :"
422
393
            ])
423
 
        index = self.get_knit_index(transport, "filename", "r")
 
394
        index = _KnitIndex(transport, "filename", "r")
424
395
 
425
396
        self.assertEqual([], index.get_ancestry([]))
426
397
        self.assertEqual(["a"], index.get_ancestry(["a"]))
440
411
            "c option 0 1 0 .f .g :",
441
412
            "d option 0 1 2 .h .j .k :"
442
413
            ])
443
 
        index = self.get_knit_index(transport, "filename", "r")
 
414
        index = _KnitIndex(transport, "filename", "r")
444
415
 
445
416
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
446
417
        self.assertEqual(["a"], index.get_ancestry_with_ghosts(["a"]))
465
436
        transport = MockTransport([
466
437
            _KnitIndex.HEADER
467
438
            ])
468
 
        index = self.get_knit_index(transport, "filename", "r")
 
439
        index = _KnitIndex(transport, "filename", "r")
469
440
 
470
441
        self.assertEqual(0, index.num_versions())
471
442
        self.assertEqual(0, len(index))
486
457
        transport = MockTransport([
487
458
            _KnitIndex.HEADER
488
459
            ])
489
 
        index = self.get_knit_index(transport, "filename", "r")
 
460
        index = _KnitIndex(transport, "filename", "r")
490
461
 
491
462
        self.assertEqual([], index.get_versions())
492
463
 
505
476
            "a option 0 1 :",
506
477
            "b option 0 1 :"
507
478
            ])
508
 
        index = self.get_knit_index(transport, "filename", "r")
 
479
        index = _KnitIndex(transport, "filename", "r")
509
480
 
510
481
        self.assertEqual("a", index.idx_to_name(0))
511
482
        self.assertEqual("b", index.idx_to_name(1))
518
489
            "a option 0 1 :",
519
490
            "b option 0 1 :"
520
491
            ])
521
 
        index = self.get_knit_index(transport, "filename", "r")
 
492
        index = _KnitIndex(transport, "filename", "r")
522
493
 
523
494
        self.assertEqual(0, index.lookup("a"))
524
495
        self.assertEqual(1, index.lookup("b"))
527
498
        transport = MockTransport([
528
499
            _KnitIndex.HEADER
529
500
            ])
530
 
        index = self.get_knit_index(transport, "filename", "r")
 
501
        index = _KnitIndex(transport, "filename", "r")
531
502
 
532
503
        index.add_version("a", ["option"], 0, 1, ["b"])
533
504
        self.assertEqual(("append_bytes",
563
534
        transport = MockTransport([
564
535
            _KnitIndex.HEADER
565
536
            ])
566
 
        index = self.get_knit_index(transport, "filename", "r")
 
537
        index = _KnitIndex(transport, "filename", "r")
567
538
 
568
539
        index.add_versions([
569
540
            ("a", ["option"], 0, 1, ["b"]),
588
559
    def test_delay_create_and_add_versions(self):
589
560
        transport = MockTransport()
590
561
 
591
 
        index = self.get_knit_index(transport, "filename", "w",
 
562
        index = _KnitIndex(transport, "filename", "w",
592
563
            create=True, file_mode="wb", create_parent_dir=True,
593
564
            delay_create=True, dir_mode=0777)
594
565
        self.assertEqual([], transport.calls)
616
587
            _KnitIndex.HEADER,
617
588
            "a option 0 1 :"
618
589
            ])
619
 
        index = self.get_knit_index(transport, "filename", "r")
 
590
        index = _KnitIndex(transport, "filename", "r")
620
591
 
621
592
        self.assertTrue(index.has_version("a"))
622
593
        self.assertFalse(index.has_version("b"))
627
598
            "a option 0 1 :",
628
599
            "b option 1 2 :"
629
600
            ])
630
 
        index = self.get_knit_index(transport, "filename", "r")
 
601
        index = _KnitIndex(transport, "filename", "r")
631
602
 
632
603
        self.assertEqual((0, 1), index.get_position("a"))
633
604
        self.assertEqual((1, 2), index.get_position("b"))
639
610
            "b unknown,line-delta 1 2 :",
640
611
            "c bad 3 4 :"
641
612
            ])
642
 
        index = self.get_knit_index(transport, "filename", "r")
 
613
        index = _KnitIndex(transport, "filename", "r")
643
614
 
644
615
        self.assertEqual("fulltext", index.get_method("a"))
645
616
        self.assertEqual("line-delta", index.get_method("b"))
651
622
            "a opt1 0 1 :",
652
623
            "b opt2,opt3 1 2 :"
653
624
            ])
654
 
        index = self.get_knit_index(transport, "filename", "r")
 
625
        index = _KnitIndex(transport, "filename", "r")
655
626
 
656
627
        self.assertEqual(["opt1"], index.get_options("a"))
657
628
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
663
634
            "b option 1 2 0 .c :",
664
635
            "c option 1 2 1 0 .e :"
665
636
            ])
666
 
        index = self.get_knit_index(transport, "filename", "r")
 
637
        index = _KnitIndex(transport, "filename", "r")
667
638
 
668
639
        self.assertEqual([], index.get_parents("a"))
669
640
        self.assertEqual(["a", "c"], index.get_parents("b"))
676
647
            "b option 1 2 0 .c :",
677
648
            "c option 1 2 1 0 .e :"
678
649
            ])
679
 
        index = self.get_knit_index(transport, "filename", "r")
 
650
        index = _KnitIndex(transport, "filename", "r")
680
651
 
681
652
        self.assertEqual([], index.get_parents_with_ghosts("a"))
682
653
        self.assertEqual(["a", "c"], index.get_parents_with_ghosts("b"))
689
660
            "a option 0 1 :",
690
661
            "b option 0 1 :"
691
662
            ])
692
 
        index = self.get_knit_index(transport, "filename", "r")
 
663
        index = _KnitIndex(transport, "filename", "r")
693
664
 
694
665
        check = index.check_versions_present
695
666
 
700
671
        self.assertRaises(RevisionNotPresent, check, ["c"])
701
672
        self.assertRaises(RevisionNotPresent, check, ["a", "b", "c"])
702
673
 
703
 
    def test_impossible_parent(self):
704
 
        """Test we get KnitCorrupt if the parent couldn't possibly exist."""
705
 
        transport = MockTransport([
706
 
            _KnitIndex.HEADER,
707
 
            "a option 0 1 :",
708
 
            "b option 0 1 4 :"  # We don't have a 4th record
709
 
            ])
710
 
        try:
711
 
            self.assertRaises(errors.KnitCorrupt,
712
 
                              self.get_knit_index, transport, 'filename', 'r')
713
 
        except TypeError, e:
714
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
715
 
                           ' not exceptions.IndexError')
716
 
                and sys.version_info[0:2] >= (2,5)):
717
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
718
 
                                  ' raising new style exceptions with python'
719
 
                                  ' >=2.5')
720
 
            else:
721
 
                raise
722
 
 
723
 
    def test_corrupted_parent(self):
724
 
        transport = MockTransport([
725
 
            _KnitIndex.HEADER,
726
 
            "a option 0 1 :",
727
 
            "b option 0 1 :",
728
 
            "c option 0 1 1v :", # Can't have a parent of '1v'
729
 
            ])
730
 
        try:
731
 
            self.assertRaises(errors.KnitCorrupt,
732
 
                              self.get_knit_index, transport, 'filename', 'r')
733
 
        except TypeError, e:
734
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
735
 
                           ' not exceptions.ValueError')
736
 
                and sys.version_info[0:2] >= (2,5)):
737
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
738
 
                                  ' raising new style exceptions with python'
739
 
                                  ' >=2.5')
740
 
            else:
741
 
                raise
742
 
 
743
 
    def test_corrupted_parent_in_list(self):
744
 
        transport = MockTransport([
745
 
            _KnitIndex.HEADER,
746
 
            "a option 0 1 :",
747
 
            "b option 0 1 :",
748
 
            "c option 0 1 1 v :", # Can't have a parent of 'v'
749
 
            ])
750
 
        try:
751
 
            self.assertRaises(errors.KnitCorrupt,
752
 
                              self.get_knit_index, transport, 'filename', 'r')
753
 
        except TypeError, e:
754
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
755
 
                           ' not exceptions.ValueError')
756
 
                and sys.version_info[0:2] >= (2,5)):
757
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
758
 
                                  ' raising new style exceptions with python'
759
 
                                  ' >=2.5')
760
 
            else:
761
 
                raise
762
 
 
763
 
    def test_invalid_position(self):
764
 
        transport = MockTransport([
765
 
            _KnitIndex.HEADER,
766
 
            "a option 1v 1 :",
767
 
            ])
768
 
        try:
769
 
            self.assertRaises(errors.KnitCorrupt,
770
 
                              self.get_knit_index, transport, 'filename', 'r')
771
 
        except TypeError, e:
772
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
773
 
                           ' not exceptions.ValueError')
774
 
                and sys.version_info[0:2] >= (2,5)):
775
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
776
 
                                  ' raising new style exceptions with python'
777
 
                                  ' >=2.5')
778
 
            else:
779
 
                raise
780
 
 
781
 
    def test_invalid_size(self):
782
 
        transport = MockTransport([
783
 
            _KnitIndex.HEADER,
784
 
            "a option 1 1v :",
785
 
            ])
786
 
        try:
787
 
            self.assertRaises(errors.KnitCorrupt,
788
 
                              self.get_knit_index, transport, 'filename', 'r')
789
 
        except TypeError, e:
790
 
            if (str(e) == ('exceptions must be strings, classes, or instances,'
791
 
                           ' not exceptions.ValueError')
792
 
                and sys.version_info[0:2] >= (2,5)):
793
 
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
794
 
                                  ' raising new style exceptions with python'
795
 
                                  ' >=2.5')
796
 
            else:
797
 
                raise
798
 
 
799
 
    def test_short_line(self):
800
 
        transport = MockTransport([
801
 
            _KnitIndex.HEADER,
802
 
            "a option 0 10  :",
803
 
            "b option 10 10 0", # This line isn't terminated, ignored
804
 
            ])
805
 
        index = self.get_knit_index(transport, "filename", "r")
806
 
        self.assertEqual(['a'], index.get_versions())
807
 
 
808
 
    def test_skip_incomplete_record(self):
809
 
        # A line with bogus data should just be skipped
810
 
        transport = MockTransport([
811
 
            _KnitIndex.HEADER,
812
 
            "a option 0 10  :",
813
 
            "b option 10 10 0", # This line isn't terminated, ignored
814
 
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
815
 
            ])
816
 
        index = self.get_knit_index(transport, "filename", "r")
817
 
        self.assertEqual(['a', 'c'], index.get_versions())
818
 
 
819
 
    def test_trailing_characters(self):
820
 
        # A line with bogus data should just be skipped
821
 
        transport = MockTransport([
822
 
            _KnitIndex.HEADER,
823
 
            "a option 0 10  :",
824
 
            "b option 10 10 0 :a", # This line has extra trailing characters
825
 
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
826
 
            ])
827
 
        index = self.get_knit_index(transport, "filename", "r")
828
 
        self.assertEqual(['a', 'c'], index.get_versions())
829
 
 
830
 
 
831
 
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
832
 
 
833
 
    _test_needs_features = [CompiledKnitFeature]
834
 
 
835
 
    def get_knit_index(self, *args, **kwargs):
836
 
        orig = knit._load_data
837
 
        def reset():
838
 
            knit._load_data = orig
839
 
        self.addCleanup(reset)
840
 
        from bzrlib._knit_load_data_c import _load_data_c
841
 
        knit._load_data = _load_data_c
842
 
        return _KnitIndex(*args, **kwargs)
843
 
 
844
 
 
845
674
 
846
675
class KnitTests(TestCaseWithTransport):
847
676
    """Class containing knit test helper routines."""
917
746
    def test_delta(self):
918
747
        """Expression of knit delta as lines"""
919
748
        k = self.make_test_knit()
920
 
        KnitContent
921
749
        td = list(line_delta(TEXT_1.splitlines(True),
922
750
                             TEXT_1A.splitlines(True)))
923
751
        self.assertEqualDiff(''.join(td), delta_1_1a)
924
752
        out = apply_line_delta(TEXT_1.splitlines(True), td)
925
753
        self.assertEqualDiff(''.join(out), TEXT_1A)
926
754
 
927
 
    def assertDerivedBlocksEqual(self, source, target, noeol=False):
928
 
        """Assert that the derived matching blocks match real output"""
929
 
        source_lines = source.splitlines(True)
930
 
        target_lines = target.splitlines(True)
931
 
        def nl(line):
932
 
            if noeol and not line.endswith('\n'):
933
 
                return line + '\n'
934
 
            else:
935
 
                return line
936
 
        source_content = KnitContent([(None, nl(l)) for l in source_lines])
937
 
        target_content = KnitContent([(None, nl(l)) for l in target_lines])
938
 
        line_delta = source_content.line_delta(target_content)
939
 
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
940
 
            source_lines, target_lines))
941
 
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
942
 
        matcher_blocks = list(list(matcher.get_matching_blocks()))
943
 
        self.assertEqual(matcher_blocks, delta_blocks)
944
 
 
945
 
    def test_get_line_delta_blocks(self):
946
 
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'q\nc\n')
947
 
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1)
948
 
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1A)
949
 
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1B)
950
 
        self.assertDerivedBlocksEqual(TEXT_1B, TEXT_1A)
951
 
        self.assertDerivedBlocksEqual(TEXT_1A, TEXT_1B)
952
 
        self.assertDerivedBlocksEqual(TEXT_1A, '')
953
 
        self.assertDerivedBlocksEqual('', TEXT_1A)
954
 
        self.assertDerivedBlocksEqual('', '')
955
 
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd')
956
 
 
957
 
    def test_get_line_delta_blocks_noeol(self):
958
 
        """Handle historical knit deltas safely
959
 
 
960
 
        Some existing knit deltas don't consider the last line to differ
961
 
        when the only difference whether it has a final newline.
962
 
 
963
 
        New knit deltas appear to always consider the last line to differ
964
 
        in this case.
965
 
        """
966
 
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd\n', noeol=True)
967
 
        self.assertDerivedBlocksEqual('a\nb\nc\nd\n', 'a\nb\nc', noeol=True)
968
 
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'a\nb\nc', noeol=True)
969
 
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\n', noeol=True)
970
 
 
971
755
    def test_add_with_parents(self):
972
756
        """Store in knit with parents"""
973
757
        k = self.make_test_knit()
1187
971
        knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
1188
972
        self.assertEqual(['revid', 'revid2'], knit.versions())
1189
973
        # write a short write to the file and ensure that its ignored
1190
 
        indexfile = file('test.kndx', 'ab')
 
974
        indexfile = file('test.kndx', 'at')
1191
975
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
1192
976
        indexfile.close()
1193
977
        # we should be able to load this file again