import os
import signal
from itertools import chain

import pytest

from testing import NORMAL_SIGNALS
from testing import print_signals
from testing import process_state


@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_proxies_signals():
    """Ensure dumb-init proxies regular signals to its child."""
    with print_signals() as (proc, _):
        for signum in NORMAL_SIGNALS:
            proc.send_signal(signum)
            assert proc.stdout.readline() == '{}\n'.format(signum).encode('ascii')


def _rewrite_map_to_args(rewrite_map):
    return chain.from_iterable(
        ('-r', '{}:{}'.format(src, dst)) for src, dst in rewrite_map.items()
    )


@pytest.mark.parametrize(
    'rewrite_map,sequence,expected', [
        (
            {},
            [signal.SIGTERM, signal.SIGQUIT, signal.SIGCONT, signal.SIGINT],
            [signal.SIGTERM, signal.SIGQUIT, signal.SIGCONT, signal.SIGINT],
        ),

        (
            {signal.SIGTERM: signal.SIGINT},
            [signal.SIGTERM, signal.SIGQUIT, signal.SIGCONT, signal.SIGINT],
            [signal.SIGINT, signal.SIGQUIT, signal.SIGCONT, signal.SIGINT],
        ),

        (
            {
                signal.SIGTERM: signal.SIGINT,
                signal.SIGINT: signal.SIGTERM,
                signal.SIGQUIT: signal.SIGQUIT,
            },
            [signal.SIGTERM, signal.SIGQUIT, signal.SIGCONT, signal.SIGINT],
            [signal.SIGINT, signal.SIGQUIT, signal.SIGCONT, signal.SIGTERM],
        ),

        # Lowest possible and highest possible signals.
        (
            {1: 31, 31: 1},
            [1, 31],
            [31, 1],
        ),
    ],
)
@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_proxies_signals_with_rewrite(rewrite_map, sequence, expected):
    """Ensure dumb-init can rewrite signals."""
    with print_signals(_rewrite_map_to_args(rewrite_map)) as (proc, _):
        for send, expect_receive in zip(sequence, expected):
            proc.send_signal(send)
            assert proc.stdout.readline() == '{}\n'.format(expect_receive).encode('ascii')


@pytest.mark.usefixtures('both_debug_modes', 'setsid_enabled')
def test_default_rewrites_can_be_overriden_with_setsid_enabled():
    """In setsid mode, dumb-init should allow overwriting the default
    rewrites (but still suspend itself).
    """
    rewrite_map = {
        signal.SIGTTIN: signal.SIGTERM,
        signal.SIGTTOU: signal.SIGINT,
        signal.SIGTSTP: signal.SIGHUP,
    }
    with print_signals(_rewrite_map_to_args(rewrite_map)) as (proc, _):
        for send, expect_receive in rewrite_map.items():
            assert process_state(proc.pid) in ['running', 'sleeping']
            proc.send_signal(send)

            assert proc.stdout.readline() == '{}\n'.format(expect_receive).encode('ascii')
            os.waitpid(proc.pid, os.WUNTRACED)
            assert process_state(proc.pid) == 'stopped'

            proc.send_signal(signal.SIGCONT)
            assert proc.stdout.readline() == '{}\n'.format(signal.SIGCONT).encode('ascii')
            assert process_state(proc.pid) in ['running', 'sleeping']


@pytest.mark.usefixtures('both_debug_modes', 'both_setsid_modes')
def test_ignored_signals_are_not_proxied():
    """Ensure dumb-init can ignore signals."""
    rewrite_map = {
        signal.SIGTERM: signal.SIGQUIT,
        signal.SIGINT: 0,
        signal.SIGWINCH: 0,
    }
    with print_signals(_rewrite_map_to_args(rewrite_map)) as (proc, _):
        proc.send_signal(signal.SIGTERM)
        proc.send_signal(signal.SIGINT)
        assert proc.stdout.readline() == '{}\n'.format(signal.SIGQUIT).encode('ascii')

        proc.send_signal(signal.SIGWINCH)
        proc.send_signal(signal.SIGHUP)
        assert proc.stdout.readline() == '{}\n'.format(signal.SIGHUP).encode('ascii')