summaryrefslogtreecommitdiffstats
path: root/tests/test_interpreter.py
blob: 6cbfd44ae0ced60341029beb5399ec1b1c0ec23b (plain)
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
150
151
152
153
154
155
156
157
158
159
160
161
162
import textwrap
from typing import Optional

import pytest

from debputy.highlevel_manifest import HighLevelManifest
from debputy.highlevel_manifest_parser import YAMLManifestParser
from debputy.interpreter import extract_shebang_interpreter
from debputy.plugin.api import virtual_path_def
from debputy.plugin.api.test_api import build_virtual_file_system
from debputy.transformation_rules import NormalizeShebangLineTransformation


@pytest.mark.parametrize(
    "raw_shebang,original_command,command_full_basename,command_stem,correct_command,corrected_shebang_line",
    [
        (
            b"#!       /usr/bin/false\r\n",
            "/usr/bin/false",
            "false",
            "false",
            None,
            None,
        ),
        (
            b"#!/usr/bin/python3 -b",
            "/usr/bin/python3",
            "python3",
            "python",
            "/usr/bin/python3",
            None,
        ),
        (
            b"#!/usr/bin/env python3 -b",
            "/usr/bin/env python3",
            "python3",
            "python",
            "/usr/bin/python3",
            "#! /usr/bin/python3 -b",
        ),
        (
            b"#! /bin/env python3.12-dbg -b",
            "/bin/env python3.12-dbg",
            "python3.12-dbg",
            "python",
            "/usr/bin/python3.12-dbg",
            "#! /usr/bin/python3.12-dbg -b",
        ),
        (
            b"#! /usr/bin/bash",
            "/usr/bin/bash",
            "bash",
            "bash",
            "/bin/bash",
            "#! /bin/bash",
        ),
        (
            b"#! /usr/bin/env sh",
            "/usr/bin/env sh",
            "sh",
            "sh",
            "/bin/sh",
            "#! /bin/sh",
        ),
        (
            b"#! /usr/local/bin/perl",
            "/usr/local/bin/perl",
            "perl",
            "perl",
            "/usr/bin/perl",
            "#! /usr/bin/perl",
        ),
    ],
)
def test_interpreter_detection(
    raw_shebang: bytes,
    original_command: str,
    command_full_basename: str,
    command_stem: str,
    correct_command: Optional[str],
    corrected_shebang_line: Optional[str],
) -> None:
    interpreter = extract_shebang_interpreter(raw_shebang)
    # The `and ...` part is just to get the raw line in the error message
    assert interpreter is not None or raw_shebang == b""

    assert interpreter.original_command == original_command
    assert interpreter.command_full_basename == command_full_basename
    assert interpreter.command_stem == command_stem
    assert interpreter.correct_command == correct_command
    assert interpreter.corrected_shebang_line == corrected_shebang_line
    assert interpreter.fixup_needed == (corrected_shebang_line is not None)


@pytest.mark.parametrize(
    "raw_data",
    [
        b"#!#!#!       /usr/bin/false",
        b"#!perl",  # Used in files as an editor hint
        b"\x7FELF/usr/bin/perl",
        b"\x00\01\x02\x03/usr/bin/perl",
        b"PK\x03\x03/usr/bin/perl",
    ],
)
def test_interpreter_negative(raw_data: bytes) -> None:
    assert extract_shebang_interpreter(raw_data) is None


@pytest.fixture
def empty_manifest(
    amd64_dpkg_architecture_variables,
    dpkg_arch_query,
    source_package,
    package_single_foo_arch_all_cxt_amd64,
    amd64_substitution,
    no_profiles_or_build_options,
    debputy_plugin_feature_set,
) -> HighLevelManifest:
    # We need an empty directory to avoid triggering packager provided files.
    debian_dir = build_virtual_file_system([])
    return YAMLManifestParser(
        "debian/test-debputy.manifest",
        source_package,
        package_single_foo_arch_all_cxt_amd64,
        amd64_substitution,
        amd64_dpkg_architecture_variables,
        dpkg_arch_query,
        no_profiles_or_build_options,
        debputy_plugin_feature_set,
        debian_dir=debian_dir,
    ).build_manifest()


def test_interpreter_rewrite(empty_manifest: HighLevelManifest) -> None:
    condition_context = empty_manifest.condition_context("foo")
    fs_root = build_virtual_file_system(
        [
            virtual_path_def("usr/bin/foo", content="random data"),
            virtual_path_def(
                "usr/bin/foo.sh",
                materialized_content="#!/usr/bin/sh\nset -e\n",
            ),
        ]
    )
    interpreter_normalization = NormalizeShebangLineTransformation()
    interpreter_normalization.transform_file_system(fs_root, condition_context)
    foo = fs_root.lookup("usr/bin/foo")
    foo_sh = fs_root.lookup("usr/bin/foo.sh")

    assert foo.is_file
    with foo.open() as fd:
        assert fd.read() == "random data"

    assert foo_sh.is_file
    with foo_sh.open() as fd:
        expected = textwrap.dedent(
            """\
        #! /bin/sh
        set -e
        """
        )
        assert fd.read() == expected