aboutsummaryrefslogtreecommitdiff
blob: 2f15f256ac492ff14e1c129645bcdb0a65f8e954 (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
import errno
from functools import partial
from unittest import mock

import pytest
from snakeoil.cli.input import NoChoice, userquery
from snakeoil.test.argparse_helpers import FakeStreamFormatter


@pytest.fixture
def mocked_input():
    with mock.patch("builtins.input") as mocked_input:
        yield mocked_input


class TestUserQuery:
    @pytest.fixture(autouse=True)
    def __setup(self):
        self.out = FakeStreamFormatter()
        self.err = FakeStreamFormatter()
        self.query = partial(userquery, out=self.out, err=self.err)

    def test_default_answer(self, mocked_input):
        mocked_input.return_value = ""
        assert self.query("foo") == True

    def test_tuple_prompt(self, mocked_input):
        mocked_input.return_value = ""
        prompt = "perhaps a tuple"
        assert self.query(tuple(prompt.split())) == True
        output = "".join(prompt.split())
        assert (
            self.out.get_text_stream().strip().split("\n")[0][: len(output)] == output
        )

    def test_no_default_answer(self, mocked_input):
        responses = {
            "a": ("z", "Yes"),
            "b": ("y", "No"),
        }
        # no default answer returns None for empty input
        mocked_input.return_value = ""
        assert self.query("foo", responses=responses) == None
        mocked_input.return_value = "a"
        assert self.query("foo", responses=responses) == "z"
        mocked_input.return_value = "b"
        assert self.query("foo", responses=responses) == "y"

    def test_ambiguous_input(self, mocked_input):
        responses = {
            "a": ("z", "Yes"),
            "A": ("y", "No"),
        }
        mocked_input.return_value = "a"
        with pytest.raises(NoChoice):
            self.query("foo", responses=responses)
        error_output = self.err.get_text_stream().strip().split("\n")[1]
        expected = "Response %r is ambiguous (%s)" % (
            mocked_input.return_value,
            ", ".join(sorted(responses.keys())),
        )
        assert error_output == expected

    def test_default_correct_input(self, mocked_input):
        for input, output in (
            ("no", False),
            ("No", False),
            ("yes", True),
            ("Yes", True),
        ):
            mocked_input.return_value = input
            assert self.query("foo") == output

    def test_default_answer_no_matches(self, mocked_input):
        mocked_input.return_value = ""
        with pytest.raises(ValueError):
            self.query("foo", default_answer="foo")
        assert self.out.stream == []

    def test_custom_default_answer(self, mocked_input):
        mocked_input.return_value = ""
        assert self.query("foo", default_answer=False) == False

    def test_eof_nochoice(self, mocked_input):
        # user hits ctrl-d
        mocked_input.side_effect = EOFError
        with pytest.raises(NoChoice):
            self.query("foo")
        output = self.out.get_text_stream().strip().split("\n")[1]
        expected = "Not answerable: EOF on STDIN"
        assert output == expected

    def test_stdin_closed_nochoice(self, mocked_input):
        mocked_input.side_effect = IOError(errno.EBADF, "")
        with pytest.raises(NoChoice):
            self.query("foo")
        output = self.out.get_text_stream().strip().split("\n")[1]
        expected = "Not answerable: STDIN is either closed, or not readable"
        assert output == expected

    def test_unhandled_ioerror(self, mocked_input):
        mocked_input.side_effect = IOError(errno.ENODEV, "")
        with pytest.raises(IOError):
            self.query("foo")

    def test_bad_choice_limit(self, mocked_input):
        # user hits enters a bad choice 3 times in a row
        mocked_input.return_value = "bad"
        with pytest.raises(NoChoice):
            self.query("foo")
        assert mocked_input.call_count == 3
        output = self.err.get_text_stream().strip().split("\n")[1]
        expected = "Sorry, response %r not understood." % (mocked_input.return_value,)
        assert output == expected

    def test_custom_choice_limit(self, mocked_input):
        # user hits enters a bad choice 5 times in a row
        mocked_input.return_value = "haha"
        with pytest.raises(NoChoice):
            self.query("foo", limit=5)
        assert mocked_input.call_count == 5
        output = self.err.get_text_stream().strip().split("\n")[1]
        expected = "Sorry, response %r not understood." % (mocked_input.return_value,)
        assert output == expected