File: cli_test.py

package info (click to toggle)
dumb-init 1.2.5-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid
  • size: 268 kB
  • sloc: python: 677; ansic: 260; makefile: 86; sh: 49
file content (150 lines) | stat: -rw-r--r-- 5,768 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
150
"""Sanity checks for command-line options."""
import re
import signal
from subprocess import PIPE
from subprocess import Popen

import pytest


@pytest.fixture
def current_version():
    return open('VERSION').read().strip()


@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_no_arguments_prints_usage():
    proc = Popen(('dumb-init'), stderr=PIPE)
    _, stderr = proc.communicate()
    assert proc.returncode != 0
    assert stderr == (
        b'Usage: dumb-init [option] program [args]\n'
        b'Try dumb-init --help for full usage.\n'
    )


@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_exits_invalid_with_invalid_args():
    proc = Popen(('dumb-init', '--yolo', '/bin/true'), stderr=PIPE)
    _, stderr = proc.communicate()
    assert proc.returncode == 1
    assert stderr in (
        b"dumb-init: unrecognized option '--yolo'\n",  # glibc
        b'dumb-init: unrecognized option: yolo\n',  # musl
    )


@pytest.mark.parametrize('flag', ['-h', '--help'])
@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_help_message(flag, current_version):
    """dumb-init should say something useful when called with the help flag,
    and exit zero.
    """
    proc = Popen(('dumb-init', flag), stderr=PIPE)
    _, stderr = proc.communicate()
    assert proc.returncode == 0
    assert stderr == (
        b'dumb-init v' + current_version.encode('ascii') + b'\n'
        b'Usage: dumb-init [option] command [[arg] ...]\n'
        b'\n'
        b'dumb-init is a simple process supervisor that forwards signals to children.\n'
        b'It is designed to run as PID1 in minimal container environments.\n'
        b'\n'
        b'Optional arguments:\n'
        b'   -c, --single-child   Run in single-child mode.\n'
        b'                        In this mode, signals are only proxied to the\n'
        b'                        direct child and not any of its descendants.\n'
        b'   -r, --rewrite s:r    Rewrite received signal s to new signal r before proxying.\n'
        b'                        To ignore (not proxy) a signal, rewrite it to 0.\n'
        b'                        This option can be specified multiple times.\n'
        b'   -v, --verbose        Print debugging information to stderr.\n'
        b'   -h, --help           Print this help message and exit.\n'
        b'   -V, --version        Print the current version and exit.\n'
        b'\n'
        b'Full help is available online at https://github.com/Yelp/dumb-init\n'
    )


@pytest.mark.parametrize('flag', ['-V', '--version'])
@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_version_message(flag, current_version):
    """dumb-init should print its version when asked to."""

    proc = Popen(('dumb-init', flag), stderr=PIPE)
    _, stderr = proc.communicate()
    assert proc.returncode == 0
    assert stderr == b'dumb-init v' + current_version.encode('ascii') + b'\n'


@pytest.mark.parametrize('flag', ['-v', '--verbose'])
def test_verbose(flag):
    """dumb-init should print debug output when asked to."""
    proc = Popen(('dumb-init', flag, 'echo', 'oh,', 'hi'), stdout=PIPE, stderr=PIPE)
    stdout, stderr = proc.communicate()
    assert proc.returncode == 0
    assert stdout == b'oh, hi\n'

    # child/parent race to print output after the fork(), can't guarantee exact order
    assert re.search(b'(^|\n)\\[dumb-init\\] setsid complete\\.\n', stderr), stderr  # child
    assert re.search(  # parent
        (
            '(^|\n)\\[dumb-init\\] Child spawned with PID [0-9]+\\.\n'
            '.*'  # child might print here
            '\\[dumb-init\\] Received signal {signal.SIGCHLD}\\.\n'
            '\\[dumb-init\\] A child with PID [0-9]+ exited with exit status 0.\n'
            '\\[dumb-init\\] Forwarded signal 15 to children\\.\n'
            '\\[dumb-init\\] Child exited with status 0\\. Goodbye\\.\n$'
        ).format(signal=signal).encode('utf8'),
        stderr,
        re.DOTALL,
    ), stderr


@pytest.mark.parametrize('flag1', ['-v', '--verbose'])
@pytest.mark.parametrize('flag2', ['-c', '--single-child'])
def test_verbose_and_single_child(flag1, flag2):
    """dumb-init should print debug output when asked to."""
    proc = Popen(('dumb-init', flag1, flag2, 'echo', 'oh,', 'hi'), stdout=PIPE, stderr=PIPE)
    stdout, stderr = proc.communicate()
    assert proc.returncode == 0
    assert stdout == b'oh, hi\n'
    assert re.match(
        (
            '^\\[dumb-init\\] Child spawned with PID [0-9]+\\.\n'
            '\\[dumb-init\\] Received signal {signal.SIGCHLD}\\.\n'
            '\\[dumb-init\\] A child with PID [0-9]+ exited with exit status 0.\n'
            '\\[dumb-init\\] Forwarded signal 15 to children\\.\n'
            '\\[dumb-init\\] Child exited with status 0\\. Goodbye\\.\n$'
        ).format(signal=signal).encode('utf8'),
        stderr,
    )


@pytest.mark.parametrize(
    'extra_args', [
        ('-r',),
        ('-r', ''),
        ('-r', 'herp'),
        ('-r', 'herp:derp'),
        ('-r', '15'),
        ('-r', '15::12'),
        ('-r', '15:derp'),
        ('-r', '15:12', '-r'),
        ('-r', '15:12', '-r', '0'),
        ('-r', '15:12', '-r', '1:32'),
    ],
)
@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_rewrite_errors(extra_args):
    proc = Popen(
        ('dumb-init',) + extra_args + ('echo', 'oh,', 'hi'),
        stdout=PIPE, stderr=PIPE,
    )
    stdout, stderr = proc.communicate()
    assert proc.returncode == 1
    assert stderr == (
        b'Usage: -r option takes <signum>:<signum>, where <signum> '
        b'is between 1 and 31.\n'
        b'This option can be specified multiple times.\n'
        b'Use --help for full usage.\n'
    )