File: test_submodule.py

package info (click to toggle)
dulwich 1.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 7,388 kB
  • sloc: python: 99,991; makefile: 163; sh: 67
file content (149 lines) | stat: -rw-r--r-- 5,485 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# test_submodule.py -- tests for submodule.py
# Copyright (C) 2025 Jelmer Vernooij <jelmer@jelmer.uk>
#
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
# General Public License as published by the Free Software Foundation; version 2.0
# or (at your option) any later version. You can redistribute it and/or
# modify it under the terms of either of these two licenses.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# You should have received a copy of the licenses; if not, see
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
# License, Version 2.0.
#

"""Tests for submodule handling."""

import os
import shutil
import tempfile

from dulwich.objects import (
    S_ISGITLINK,
    Tree,
)
from dulwich.repo import Repo
from dulwich.submodule import ensure_submodule_placeholder, iter_cached_submodules

from . import TestCase


class SubmoduleTests(TestCase):
    """Tests for submodule functions."""

    def setUp(self):
        super().setUp()
        self.test_dir = tempfile.mkdtemp()

    def tearDown(self):
        shutil.rmtree(self.test_dir)
        super().tearDown()

    def test_S_ISGITLINK(self) -> None:
        """Test the S_ISGITLINK function for checking gitlink mode."""
        # 0o160000 is the mode used for submodules
        self.assertTrue(S_ISGITLINK(0o160000))
        # Test some other modes to ensure they're not detected as gitlinks
        self.assertFalse(S_ISGITLINK(0o100644))  # regular file
        self.assertFalse(S_ISGITLINK(0o100755))  # executable file
        self.assertFalse(S_ISGITLINK(0o040000))  # directory

    def test_iter_cached_submodules(self) -> None:
        """Test the function to detect and iterate through submodules."""
        # Create a repository and add some content
        repo_dir = os.path.join(self.test_dir, "repo")
        os.makedirs(repo_dir)
        repo = Repo.init(repo_dir)

        # Create a file to add to our tree
        file_path = os.path.join(repo_dir, "file.txt")
        with open(file_path, "wb") as f:
            f.write(b"test file content")

        # Stage and commit the file to create some basic content
        repo.get_worktree().stage(["file.txt"])
        repo.get_worktree().commit(
            message=b"Initial commit",
        )

        # Manually create the raw string for a tree with our file and a submodule
        # Format for tree entries: [mode] [name]\0[sha]

        # Get the blob SHA for our file using the index
        index = repo.open_index()
        file_entry = index[b"file.txt"]
        file_sha = file_entry.sha

        # Convert to binary representation needed for raw tree data
        binary_file_sha = bytes.fromhex(file_sha.decode("ascii"))

        # Generate a valid SHA for the submodule
        submodule_sha = b"1" * repo.object_format.hex_length
        binary_submodule_sha = bytes.fromhex(submodule_sha.decode("ascii"))

        # Create raw tree data
        raw_tree_data = (
            # Regular file entry
            b"100644 file.txt\0"
            + binary_file_sha
            +
            # Submodule entry with gitlink mode
            b"160000 submodule\0"
            + binary_submodule_sha
        )

        # Create a tree from raw data
        from dulwich.objects import ShaFile

        tree = ShaFile.from_raw_string(Tree.type_num, raw_tree_data)

        # Add the tree to the repository
        repo.object_store.add_object(tree)

        # Now we can test the iter_cached_submodules function
        submodules = list(iter_cached_submodules(repo.object_store, tree.id))

        # We should find one submodule
        self.assertEqual(1, len(submodules))

        # Verify the submodule details
        path, sha = submodules[0]
        self.assertEqual(b"submodule", path)
        self.assertEqual(submodule_sha, sha)

    def test_ensure_submodule_placeholder(self) -> None:
        """Test creating submodule placeholder directories."""
        # Create a repository
        repo_path = os.path.join(self.test_dir, "testrepo")
        repo = Repo.init(repo_path, mkdir=True)

        # Test creating a simple submodule placeholder
        ensure_submodule_placeholder(repo, b"libs/mylib")

        # Check that the directory was created
        submodule_path = os.path.join(repo_path, "libs", "mylib")
        self.assertTrue(os.path.isdir(submodule_path))

        # Check that the .git file was created
        git_file_path = os.path.join(submodule_path, ".git")
        self.assertTrue(os.path.isfile(git_file_path))

        # Check the content of the .git file
        with open(git_file_path, "rb") as f:
            content = f.read()
        self.assertEqual(b"gitdir: ../../.git/modules/libs/mylib\n", content)

        # Test with string path
        ensure_submodule_placeholder(repo, "libs/another")
        another_path = os.path.join(repo_path, "libs", "another")
        self.assertTrue(os.path.isdir(another_path))

        # Test idempotency - calling again should not fail
        ensure_submodule_placeholder(repo, b"libs/mylib")