tests/test-manifest.py
changeset 47043 12450fbea288
parent 46780 6266d19556ad
child 48875 6000f5b25c9b
equal deleted inserted replaced
47042:c5e1cc0b4c77 47043:12450fbea288
    79 class basemanifesttests(object):
    79 class basemanifesttests(object):
    80     def parsemanifest(self, text):
    80     def parsemanifest(self, text):
    81         raise NotImplementedError('parsemanifest not implemented by test case')
    81         raise NotImplementedError('parsemanifest not implemented by test case')
    82 
    82 
    83     def testEmptyManifest(self):
    83     def testEmptyManifest(self):
    84         m = self.parsemanifest(EMTPY_MANIFEST)
    84         m = self.parsemanifest(20, EMTPY_MANIFEST)
    85         self.assertEqual(0, len(m))
    85         self.assertEqual(0, len(m))
    86         self.assertEqual([], list(m))
    86         self.assertEqual([], list(m))
    87 
    87 
    88     def testManifest(self):
    88     def testManifest(self):
    89         m = self.parsemanifest(A_SHORT_MANIFEST)
    89         m = self.parsemanifest(20, A_SHORT_MANIFEST)
    90         self.assertEqual([b'bar/baz/qux.py', b'foo'], list(m))
    90         self.assertEqual([b'bar/baz/qux.py', b'foo'], list(m))
    91         self.assertEqual(BIN_HASH_2, m[b'bar/baz/qux.py'])
    91         self.assertEqual(BIN_HASH_2, m[b'bar/baz/qux.py'])
    92         self.assertEqual(b'l', m.flags(b'bar/baz/qux.py'))
    92         self.assertEqual(b'l', m.flags(b'bar/baz/qux.py'))
    93         self.assertEqual(BIN_HASH_1, m[b'foo'])
    93         self.assertEqual(BIN_HASH_1, m[b'foo'])
    94         self.assertEqual(b'', m.flags(b'foo'))
    94         self.assertEqual(b'', m.flags(b'foo'))
    95         with self.assertRaises(KeyError):
    95         with self.assertRaises(KeyError):
    96             m[b'wat']
    96             m[b'wat']
    97 
    97 
    98     def testManifestLongHashes(self):
       
    99         m = self.parsemanifest(b'a\0' + b'f' * 64 + b'\n')
       
   100         self.assertEqual(binascii.unhexlify(b'f' * 64), m[b'a'])
       
   101 
       
   102     def testSetItem(self):
    98     def testSetItem(self):
   103         want = BIN_HASH_1
    99         want = BIN_HASH_1
   104 
   100 
   105         m = self.parsemanifest(EMTPY_MANIFEST)
   101         m = self.parsemanifest(20, EMTPY_MANIFEST)
   106         m[b'a'] = want
   102         m[b'a'] = want
   107         self.assertIn(b'a', m)
   103         self.assertIn(b'a', m)
   108         self.assertEqual(want, m[b'a'])
   104         self.assertEqual(want, m[b'a'])
   109         self.assertEqual(b'a\0' + HASH_1 + b'\n', m.text())
   105         self.assertEqual(b'a\0' + HASH_1 + b'\n', m.text())
   110 
   106 
   111         m = self.parsemanifest(A_SHORT_MANIFEST)
   107         m = self.parsemanifest(20, A_SHORT_MANIFEST)
   112         m[b'a'] = want
   108         m[b'a'] = want
   113         self.assertEqual(want, m[b'a'])
   109         self.assertEqual(want, m[b'a'])
   114         self.assertEqual(b'a\0' + HASH_1 + b'\n' + A_SHORT_MANIFEST, m.text())
   110         self.assertEqual(b'a\0' + HASH_1 + b'\n' + A_SHORT_MANIFEST, m.text())
   115 
   111 
   116     def testSetFlag(self):
   112     def testSetFlag(self):
   117         want = b'x'
   113         want = b'x'
   118 
   114 
   119         m = self.parsemanifest(EMTPY_MANIFEST)
   115         m = self.parsemanifest(20, EMTPY_MANIFEST)
   120         # first add a file; a file-less flag makes no sense
   116         # first add a file; a file-less flag makes no sense
   121         m[b'a'] = BIN_HASH_1
   117         m[b'a'] = BIN_HASH_1
   122         m.setflag(b'a', want)
   118         m.setflag(b'a', want)
   123         self.assertEqual(want, m.flags(b'a'))
   119         self.assertEqual(want, m.flags(b'a'))
   124         self.assertEqual(b'a\0' + HASH_1 + want + b'\n', m.text())
   120         self.assertEqual(b'a\0' + HASH_1 + want + b'\n', m.text())
   125 
   121 
   126         m = self.parsemanifest(A_SHORT_MANIFEST)
   122         m = self.parsemanifest(20, A_SHORT_MANIFEST)
   127         # first add a file; a file-less flag makes no sense
   123         # first add a file; a file-less flag makes no sense
   128         m[b'a'] = BIN_HASH_1
   124         m[b'a'] = BIN_HASH_1
   129         m.setflag(b'a', want)
   125         m.setflag(b'a', want)
   130         self.assertEqual(want, m.flags(b'a'))
   126         self.assertEqual(want, m.flags(b'a'))
   131         self.assertEqual(
   127         self.assertEqual(
   132             b'a\0' + HASH_1 + want + b'\n' + A_SHORT_MANIFEST, m.text()
   128             b'a\0' + HASH_1 + want + b'\n' + A_SHORT_MANIFEST, m.text()
   133         )
   129         )
   134 
   130 
   135     def testCopy(self):
   131     def testCopy(self):
   136         m = self.parsemanifest(A_SHORT_MANIFEST)
   132         m = self.parsemanifest(20, A_SHORT_MANIFEST)
   137         m[b'a'] = BIN_HASH_1
   133         m[b'a'] = BIN_HASH_1
   138         m2 = m.copy()
   134         m2 = m.copy()
   139         del m
   135         del m
   140         del m2  # make sure we don't double free() anything
   136         del m2  # make sure we don't double free() anything
   141 
   137 
   142     def testCompaction(self):
   138     def testCompaction(self):
   143         unhex = binascii.unhexlify
   139         unhex = binascii.unhexlify
   144         h1, h2 = unhex(HASH_1), unhex(HASH_2)
   140         h1, h2 = unhex(HASH_1), unhex(HASH_2)
   145         m = self.parsemanifest(A_SHORT_MANIFEST)
   141         m = self.parsemanifest(20, A_SHORT_MANIFEST)
   146         m[b'alpha'] = h1
   142         m[b'alpha'] = h1
   147         m[b'beta'] = h2
   143         m[b'beta'] = h2
   148         del m[b'foo']
   144         del m[b'foo']
   149         want = b'alpha\0%s\nbar/baz/qux.py\0%sl\nbeta\0%s\n' % (
   145         want = b'alpha\0%s\nbar/baz/qux.py\0%sl\nbeta\0%s\n' % (
   150             HASH_1,
   146             HASH_1,
   162         self.assertEqual(b'', m.flags(b'beta'))
   158         self.assertEqual(b'', m.flags(b'beta'))
   163         with self.assertRaises(KeyError):
   159         with self.assertRaises(KeyError):
   164             m[b'foo']
   160             m[b'foo']
   165 
   161 
   166     def testMatchException(self):
   162     def testMatchException(self):
   167         m = self.parsemanifest(A_SHORT_MANIFEST)
   163         m = self.parsemanifest(20, A_SHORT_MANIFEST)
   168         match = matchmod.match(util.localpath(b'/repo'), b'', [b're:.*'])
   164         match = matchmod.match(util.localpath(b'/repo'), b'', [b're:.*'])
   169 
   165 
   170         def filt(path):
   166         def filt(path):
   171             if path == b'foo':
   167             if path == b'foo':
   172                 assert False
   168                 assert False
   175         match.matchfn = filt
   171         match.matchfn = filt
   176         with self.assertRaises(AssertionError):
   172         with self.assertRaises(AssertionError):
   177             m._matches(match)
   173             m._matches(match)
   178 
   174 
   179     def testRemoveItem(self):
   175     def testRemoveItem(self):
   180         m = self.parsemanifest(A_SHORT_MANIFEST)
   176         m = self.parsemanifest(20, A_SHORT_MANIFEST)
   181         del m[b'foo']
   177         del m[b'foo']
   182         with self.assertRaises(KeyError):
   178         with self.assertRaises(KeyError):
   183             m[b'foo']
   179             m[b'foo']
   184         self.assertEqual(1, len(m))
   180         self.assertEqual(1, len(m))
   185         self.assertEqual(1, len(list(m)))
   181         self.assertEqual(1, len(list(m)))
   191     def testManifestDiff(self):
   187     def testManifestDiff(self):
   192         MISSING = (None, b'')
   188         MISSING = (None, b'')
   193         addl = b'z-only-in-left\0' + HASH_1 + b'\n'
   189         addl = b'z-only-in-left\0' + HASH_1 + b'\n'
   194         addr = b'z-only-in-right\0' + HASH_2 + b'x\n'
   190         addr = b'z-only-in-right\0' + HASH_2 + b'x\n'
   195         left = self.parsemanifest(
   191         left = self.parsemanifest(
   196             A_SHORT_MANIFEST.replace(HASH_1, HASH_3 + b'x') + addl
   192             20, A_SHORT_MANIFEST.replace(HASH_1, HASH_3 + b'x') + addl
   197         )
   193         )
   198         right = self.parsemanifest(A_SHORT_MANIFEST + addr)
   194         right = self.parsemanifest(20, A_SHORT_MANIFEST + addr)
   199         want = {
   195         want = {
   200             b'foo': ((BIN_HASH_3, b'x'), (BIN_HASH_1, b'')),
   196             b'foo': ((BIN_HASH_3, b'x'), (BIN_HASH_1, b'')),
   201             b'z-only-in-left': ((BIN_HASH_1, b''), MISSING),
   197             b'z-only-in-left': ((BIN_HASH_1, b''), MISSING),
   202             b'z-only-in-right': (MISSING, (BIN_HASH_2, b'x')),
   198             b'z-only-in-right': (MISSING, (BIN_HASH_2, b'x')),
   203         }
   199         }
   206         want = {
   202         want = {
   207             b'bar/baz/qux.py': (MISSING, (BIN_HASH_2, b'l')),
   203             b'bar/baz/qux.py': (MISSING, (BIN_HASH_2, b'l')),
   208             b'foo': (MISSING, (BIN_HASH_3, b'x')),
   204             b'foo': (MISSING, (BIN_HASH_3, b'x')),
   209             b'z-only-in-left': (MISSING, (BIN_HASH_1, b'')),
   205             b'z-only-in-left': (MISSING, (BIN_HASH_1, b'')),
   210         }
   206         }
   211         self.assertEqual(want, self.parsemanifest(EMTPY_MANIFEST).diff(left))
   207         self.assertEqual(
       
   208             want, self.parsemanifest(20, EMTPY_MANIFEST).diff(left)
       
   209         )
   212 
   210 
   213         want = {
   211         want = {
   214             b'bar/baz/qux.py': ((BIN_HASH_2, b'l'), MISSING),
   212             b'bar/baz/qux.py': ((BIN_HASH_2, b'l'), MISSING),
   215             b'foo': ((BIN_HASH_3, b'x'), MISSING),
   213             b'foo': ((BIN_HASH_3, b'x'), MISSING),
   216             b'z-only-in-left': ((BIN_HASH_1, b''), MISSING),
   214             b'z-only-in-left': ((BIN_HASH_1, b''), MISSING),
   217         }
   215         }
   218         self.assertEqual(want, left.diff(self.parsemanifest(EMTPY_MANIFEST)))
   216         self.assertEqual(
       
   217             want, left.diff(self.parsemanifest(20, EMTPY_MANIFEST))
       
   218         )
   219         copy = right.copy()
   219         copy = right.copy()
   220         del copy[b'z-only-in-right']
   220         del copy[b'z-only-in-right']
   221         del right[b'foo']
   221         del right[b'foo']
   222         want = {
   222         want = {
   223             b'foo': (MISSING, (BIN_HASH_1, b'')),
   223             b'foo': (MISSING, (BIN_HASH_1, b'')),
   224             b'z-only-in-right': ((BIN_HASH_2, b'x'), MISSING),
   224             b'z-only-in-right': ((BIN_HASH_2, b'x'), MISSING),
   225         }
   225         }
   226         self.assertEqual(want, right.diff(copy))
   226         self.assertEqual(want, right.diff(copy))
   227 
   227 
   228         short = self.parsemanifest(A_SHORT_MANIFEST)
   228         short = self.parsemanifest(20, A_SHORT_MANIFEST)
   229         pruned = short.copy()
   229         pruned = short.copy()
   230         del pruned[b'foo']
   230         del pruned[b'foo']
   231         want = {
   231         want = {
   232             b'foo': ((BIN_HASH_1, b''), MISSING),
   232             b'foo': ((BIN_HASH_1, b''), MISSING),
   233         }
   233         }
   245     def testReversedLines(self):
   245     def testReversedLines(self):
   246         backwards = b''.join(
   246         backwards = b''.join(
   247             l + b'\n' for l in reversed(A_SHORT_MANIFEST.split(b'\n')) if l
   247             l + b'\n' for l in reversed(A_SHORT_MANIFEST.split(b'\n')) if l
   248         )
   248         )
   249         try:
   249         try:
   250             self.parsemanifest(backwards)
   250             self.parsemanifest(20, backwards)
   251             self.fail('Should have raised ValueError')
   251             self.fail('Should have raised ValueError')
   252         except ValueError as v:
   252         except ValueError as v:
   253             self.assertIn('Manifest lines not in sorted order.', str(v))
   253             self.assertIn('Manifest lines not in sorted order.', str(v))
   254 
   254 
   255     def testNoTerminalNewline(self):
   255     def testNoTerminalNewline(self):
   256         try:
   256         try:
   257             self.parsemanifest(A_SHORT_MANIFEST + b'wat')
   257             self.parsemanifest(20, A_SHORT_MANIFEST + b'wat')
   258             self.fail('Should have raised ValueError')
   258             self.fail('Should have raised ValueError')
   259         except ValueError as v:
   259         except ValueError as v:
   260             self.assertIn('Manifest did not end in a newline.', str(v))
   260             self.assertIn('Manifest did not end in a newline.', str(v))
   261 
   261 
   262     def testNoNewLineAtAll(self):
   262     def testNoNewLineAtAll(self):
   263         try:
   263         try:
   264             self.parsemanifest(b'wat')
   264             self.parsemanifest(20, b'wat')
   265             self.fail('Should have raised ValueError')
   265             self.fail('Should have raised ValueError')
   266         except ValueError as v:
   266         except ValueError as v:
   267             self.assertIn('Manifest did not end in a newline.', str(v))
   267             self.assertIn('Manifest did not end in a newline.', str(v))
   268 
   268 
   269     def testHugeManifest(self):
   269     def testHugeManifest(self):
   270         m = self.parsemanifest(A_HUGE_MANIFEST)
   270         m = self.parsemanifest(20, A_HUGE_MANIFEST)
   271         self.assertEqual(HUGE_MANIFEST_ENTRIES, len(m))
   271         self.assertEqual(HUGE_MANIFEST_ENTRIES, len(m))
   272         self.assertEqual(len(m), len(list(m)))
   272         self.assertEqual(len(m), len(list(m)))
   273 
   273 
   274     def testMatchesMetadata(self):
   274     def testMatchesMetadata(self):
   275         """Tests matches() for a few specific files to make sure that both
   275         """Tests matches() for a few specific files to make sure that both
   276         the set of files as well as their flags and nodeids are correct in
   276         the set of files as well as their flags and nodeids are correct in
   277         the resulting manifest."""
   277         the resulting manifest."""
   278         m = self.parsemanifest(A_HUGE_MANIFEST)
   278         m = self.parsemanifest(20, A_HUGE_MANIFEST)
   279 
   279 
   280         match = matchmod.exact([b'file1', b'file200', b'file300'])
   280         match = matchmod.exact([b'file1', b'file200', b'file300'])
   281         m2 = m._matches(match)
   281         m2 = m._matches(match)
   282 
   282 
   283         w = (b'file1\0%sx\n' b'file200\0%sl\n' b'file300\0%s\n') % (
   283         w = (b'file1\0%sx\n' b'file200\0%sl\n' b'file300\0%s\n') % (
   289 
   289 
   290     def testMatchesNonexistentFile(self):
   290     def testMatchesNonexistentFile(self):
   291         """Tests matches() for a small set of specific files, including one
   291         """Tests matches() for a small set of specific files, including one
   292         nonexistent file to make sure in only matches against existing files.
   292         nonexistent file to make sure in only matches against existing files.
   293         """
   293         """
   294         m = self.parsemanifest(A_DEEPER_MANIFEST)
   294         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   295 
   295 
   296         match = matchmod.exact(
   296         match = matchmod.exact(
   297             [b'a/b/c/bar.txt', b'a/b/d/qux.py', b'readme.txt', b'nonexistent']
   297             [b'a/b/c/bar.txt', b'a/b/d/qux.py', b'readme.txt', b'nonexistent']
   298         )
   298         )
   299         m2 = m._matches(match)
   299         m2 = m._matches(match)
   303         )
   303         )
   304 
   304 
   305     def testMatchesNonexistentDirectory(self):
   305     def testMatchesNonexistentDirectory(self):
   306         """Tests matches() for a relpath match on a directory that doesn't
   306         """Tests matches() for a relpath match on a directory that doesn't
   307         actually exist."""
   307         actually exist."""
   308         m = self.parsemanifest(A_DEEPER_MANIFEST)
   308         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   309 
   309 
   310         match = matchmod.match(
   310         match = matchmod.match(
   311             util.localpath(b'/repo'), b'', [b'a/f'], default=b'relpath'
   311             util.localpath(b'/repo'), b'', [b'a/f'], default=b'relpath'
   312         )
   312         )
   313         m2 = m._matches(match)
   313         m2 = m._matches(match)
   314 
   314 
   315         self.assertEqual([], m2.keys())
   315         self.assertEqual([], m2.keys())
   316 
   316 
   317     def testMatchesExactLarge(self):
   317     def testMatchesExactLarge(self):
   318         """Tests matches() for files matching a large list of exact files."""
   318         """Tests matches() for files matching a large list of exact files."""
   319         m = self.parsemanifest(A_HUGE_MANIFEST)
   319         m = self.parsemanifest(20, A_HUGE_MANIFEST)
   320 
   320 
   321         flist = m.keys()[80:300]
   321         flist = m.keys()[80:300]
   322         match = matchmod.exact(flist)
   322         match = matchmod.exact(flist)
   323         m2 = m._matches(match)
   323         m2 = m._matches(match)
   324 
   324 
   325         self.assertEqual(flist, m2.keys())
   325         self.assertEqual(flist, m2.keys())
   326 
   326 
   327     def testMatchesFull(self):
   327     def testMatchesFull(self):
   328         '''Tests matches() for what should be a full match.'''
   328         '''Tests matches() for what should be a full match.'''
   329         m = self.parsemanifest(A_DEEPER_MANIFEST)
   329         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   330 
   330 
   331         match = matchmod.match(util.localpath(b'/repo'), b'', [b''])
   331         match = matchmod.match(util.localpath(b'/repo'), b'', [b''])
   332         m2 = m._matches(match)
   332         m2 = m._matches(match)
   333 
   333 
   334         self.assertEqual(m.keys(), m2.keys())
   334         self.assertEqual(m.keys(), m2.keys())
   335 
   335 
   336     def testMatchesDirectory(self):
   336     def testMatchesDirectory(self):
   337         """Tests matches() on a relpath match on a directory, which should
   337         """Tests matches() on a relpath match on a directory, which should
   338         match against all files within said directory."""
   338         match against all files within said directory."""
   339         m = self.parsemanifest(A_DEEPER_MANIFEST)
   339         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   340 
   340 
   341         match = matchmod.match(
   341         match = matchmod.match(
   342             util.localpath(b'/repo'), b'', [b'a/b'], default=b'relpath'
   342             util.localpath(b'/repo'), b'', [b'a/b'], default=b'relpath'
   343         )
   343         )
   344         m2 = m._matches(match)
   344         m2 = m._matches(match)
   360 
   360 
   361     def testMatchesExactPath(self):
   361     def testMatchesExactPath(self):
   362         """Tests matches() on an exact match on a directory, which should
   362         """Tests matches() on an exact match on a directory, which should
   363         result in an empty manifest because you can't perform an exact match
   363         result in an empty manifest because you can't perform an exact match
   364         against a directory."""
   364         against a directory."""
   365         m = self.parsemanifest(A_DEEPER_MANIFEST)
   365         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   366 
   366 
   367         match = matchmod.exact([b'a/b'])
   367         match = matchmod.exact([b'a/b'])
   368         m2 = m._matches(match)
   368         m2 = m._matches(match)
   369 
   369 
   370         self.assertEqual([], m2.keys())
   370         self.assertEqual([], m2.keys())
   371 
   371 
   372     def testMatchesCwd(self):
   372     def testMatchesCwd(self):
   373         """Tests matches() on a relpath match with the current directory ('.')
   373         """Tests matches() on a relpath match with the current directory ('.')
   374         when not in the root directory."""
   374         when not in the root directory."""
   375         m = self.parsemanifest(A_DEEPER_MANIFEST)
   375         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   376 
   376 
   377         match = matchmod.match(
   377         match = matchmod.match(
   378             util.localpath(b'/repo'), b'a/b', [b'.'], default=b'relpath'
   378             util.localpath(b'/repo'), b'a/b', [b'.'], default=b'relpath'
   379         )
   379         )
   380         m2 = m._matches(match)
   380         m2 = m._matches(match)
   395         )
   395         )
   396 
   396 
   397     def testMatchesWithPattern(self):
   397     def testMatchesWithPattern(self):
   398         """Tests matches() for files matching a pattern that reside
   398         """Tests matches() for files matching a pattern that reside
   399         deeper than the specified directory."""
   399         deeper than the specified directory."""
   400         m = self.parsemanifest(A_DEEPER_MANIFEST)
   400         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   401 
   401 
   402         match = matchmod.match(util.localpath(b'/repo'), b'', [b'a/b/*/*.txt'])
   402         match = matchmod.match(util.localpath(b'/repo'), b'', [b'a/b/*/*.txt'])
   403         m2 = m._matches(match)
   403         m2 = m._matches(match)
   404 
   404 
   405         self.assertEqual(
   405         self.assertEqual(
   406             [b'a/b/c/bar.txt', b'a/b/c/foo.txt', b'a/b/d/ten.txt'], m2.keys()
   406             [b'a/b/c/bar.txt', b'a/b/c/foo.txt', b'a/b/d/ten.txt'], m2.keys()
   407         )
   407         )
   408 
   408 
   409 
   409 
   410 class testmanifestdict(unittest.TestCase, basemanifesttests):
   410 class testmanifestdict(unittest.TestCase, basemanifesttests):
   411     def parsemanifest(self, text):
   411     def parsemanifest(self, nodelen, text):
   412         return manifestmod.manifestdict(text)
   412         return manifestmod.manifestdict(nodelen, text)
       
   413 
       
   414     def testManifestLongHashes(self):
       
   415         m = self.parsemanifest(32, b'a\0' + b'f' * 64 + b'\n')
       
   416         self.assertEqual(binascii.unhexlify(b'f' * 64), m[b'a'])
   413 
   417 
   414     def testObviouslyBogusManifest(self):
   418     def testObviouslyBogusManifest(self):
   415         # This is a 163k manifest that came from oss-fuzz. It was a
   419         # This is a 163k manifest that came from oss-fuzz. It was a
   416         # timeout there, but when run normally it doesn't seem to
   420         # timeout there, but when run normally it doesn't seem to
   417         # present any particular slowness.
   421         # present any particular slowness.
   431             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
   435             b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
   432             b'\x00\x00\xc0\x8aey\x1d}\x01\xd8\xe0\xb9\xf3\xde\x1b\xcf\x17'
   436             b'\x00\x00\xc0\x8aey\x1d}\x01\xd8\xe0\xb9\xf3\xde\x1b\xcf\x17'
   433             b'\xac\xbe'
   437             b'\xac\xbe'
   434         )
   438         )
   435         with self.assertRaises(ValueError):
   439         with self.assertRaises(ValueError):
   436             self.parsemanifest(data)
   440             self.parsemanifest(20, data)
   437 
   441 
   438 
   442 
   439 class testtreemanifest(unittest.TestCase, basemanifesttests):
   443 class testtreemanifest(unittest.TestCase, basemanifesttests):
   440     def parsemanifest(self, text):
   444     def parsemanifest(self, nodelen, text):
   441         return manifestmod.treemanifest(sha1nodeconstants, b'', text)
   445         return manifestmod.treemanifest(sha1nodeconstants, b'', text)
   442 
   446 
   443     def testWalkSubtrees(self):
   447     def testWalkSubtrees(self):
   444         m = self.parsemanifest(A_DEEPER_MANIFEST)
   448         m = self.parsemanifest(20, A_DEEPER_MANIFEST)
   445 
   449 
   446         dirs = [s._dir for s in m.walksubtrees()]
   450         dirs = [s._dir for s in m.walksubtrees()]
   447         self.assertEqual(
   451         self.assertEqual(
   448             sorted(
   452             sorted(
   449                 [b'', b'a/', b'a/c/', b'a/d/', b'a/b/', b'a/b/c/', b'a/b/d/']
   453                 [b'', b'a/', b'a/c/', b'a/d/', b'a/b/', b'a/b/c/', b'a/b/d/']