diff --git a/swh/model/from_disk.py b/swh/model/from_disk.py index 45c60ca9c7c4036176d20c70427c0754fc925e14..e1afc6b390620edd141c38833bc06f51dd980ae5 100644 --- a/swh/model/from_disk.py +++ b/swh/model/from_disk.py @@ -12,7 +12,7 @@ from typing import List from .hashutil import MultiHash, HASH_BLOCK_SIZE from .merkle import MerkleLeaf, MerkleNode from .identifiers import ( - directory_identifier, + directory_entry_sort_key, directory_identifier, identifier_to_bytes as id_to_bytes, identifier_to_str as id_to_str, ) @@ -325,11 +325,13 @@ class Directory(MerkleNode): @property def entries(self): + """Child nodes, sorted by name in the same way `directory_identifier` + does.""" if self.__entries is None: - self.__entries = [ + self.__entries = sorted(( self.child_to_directory_entry(name, child) for name, child in self.items() - ] + ), key=directory_entry_sort_key) return self.__entries diff --git a/swh/model/identifiers.py b/swh/model/identifiers.py index 9257de142f168d62f7bbf868c4ed6811d6e57896..85fc76c4585ccba88b8f7e69eca8ae2f9ff34ea1 100644 --- a/swh/model/identifiers.py +++ b/swh/model/identifiers.py @@ -114,7 +114,7 @@ def content_identifier(content): return MultiHash.from_data(content['data']).digest() -def _sort_key(entry): +def directory_entry_sort_key(entry): """The sorting key for tree entries""" if entry['type'] == 'dir': return entry['name'] + b'/' @@ -182,7 +182,7 @@ def directory_identifier(directory): components = [] - for entry in sorted(directory['entries'], key=_sort_key): + for entry in sorted(directory['entries'], key=directory_entry_sort_key): components.extend([ _perms_to_bytes(entry['perms']), b'\x20', diff --git a/swh/model/tests/test_from_disk.py b/swh/model/tests/test_from_disk.py index 1c747370dbd1a1d657cf5ae4efad7af57068ad37..12b00f80b8bbd700bd481b161c78880420cbd03d 100644 --- a/swh/model/tests/test_from_disk.py +++ b/swh/model/tests/test_from_disk.py @@ -449,7 +449,7 @@ class DataMixin: if isinstance(right, Directory): right = right.get_data() - return self.assertCountEqual(left.entries, right['entries']) + assert left.entries == right['entries'] def make_contents(self, directory): for filename, content in self.contents.items(): @@ -579,6 +579,7 @@ class FileToContent(DataMixin, unittest.TestCase): check_path=True) +@pytest.mark.fs class DirectoryToObjects(DataMixin, unittest.TestCase): def setUp(self): super().setUp() @@ -729,6 +730,18 @@ class DirectoryToObjects(DataMixin, unittest.TestCase): len(self.contents) + 1) + def test_directory_entry_order(self): + with tempfile.TemporaryDirectory() as dirname: + dirname = os.fsencode(dirname) + open(os.path.join(dirname, b'foo.'), 'a') + open(os.path.join(dirname, b'foo0'), 'a') + os.mkdir(os.path.join(dirname, b'foo')) + + directory = Directory.from_disk(path=dirname) + + assert [entry['name'] for entry in directory.entries] \ + == [b'foo.', b'foo', b'foo0'] + @pytest.mark.fs class TarballTest(DataMixin, unittest.TestCase):