File: runners.py

package info (click to toggle)
python-invoke 1.4.1%2Bds-0.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,704 kB
  • sloc: python: 11,377; makefile: 18; sh: 12
file content (141 lines) | stat: -rw-r--r-- 5,005 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
import os
import platform
import time

from mock import Mock
from pytest import skip, raises

from invoke import (
    run,
    Local,
    Context,
    ThreadException,
    Responder,
    FailingResponder,
    WatcherError,
    Failure,
    CommandTimedOut,
)

from _util import assert_cpu_usage


PYPY = platform.python_implementation() == "PyPy"


class Runner_:
    def setup(self):
        os.chdir(os.path.join(os.path.dirname(__file__), "_support"))

    class responding:
        def base_case(self):
            # Basic "doesn't explode" test: respond.py will exit nonzero unless
            # this works, causing a Failure.
            watcher = Responder(r"What's the password\?", "Rosebud\n")
            # Gotta give -u or Python will line-buffer its stdout, so we'll
            # never actually see the prompt.
            run(
                "python -u respond_base.py",
                watchers=[watcher],
                hide=True,
                timeout=5,
            )

        def both_streams(self):
            watchers = [
                Responder("standard out", "with it\n"),
                Responder("standard error", "between chair and keyboard\n"),
            ]
            run(
                "python -u respond_both.py",
                watchers=watchers,
                hide=True,
                timeout=5,
            )

        def watcher_errors_become_Failures(self):
            watcher = FailingResponder(
                pattern=r"What's the password\?",
                response="Rosebud\n",
                sentinel="You're not Citizen Kane!",
            )
            try:
                run(
                    "python -u respond_fail.py",
                    watchers=[watcher],
                    hide=True,
                    timeout=5,
                )
            except Failure as e:
                assert isinstance(e.reason, WatcherError)
                assert e.result.exited is None
            else:
                assert False, "Did not raise Failure!"

    class stdin_mirroring:
        def piped_stdin_is_not_conflated_with_mocked_stdin(self):
            # Re: GH issue #308
            # Will die on broken-pipe OSError if bug is present.
            run("echo 'lollerskates' | inv -c nested_or_piped foo", hide=True)

        def nested_invoke_sessions_not_conflated_with_mocked_stdin(self):
            # Also re: GH issue #308. This one will just hang forever. Woo!
            run("inv -c nested_or_piped calls-foo", hide=True)

        def isnt_cpu_heavy(self):
            "stdin mirroring isn't CPU-heavy"
            # CPU measurement under PyPy is...rather different. NBD.
            if PYPY:
                skip()
            # Python 3.5 has been seen using up to ~6.0s CPU time under Travis
            with assert_cpu_usage(lt=7.0):
                run("python -u busywork.py 10", pty=True, hide=True)

        def doesnt_break_when_stdin_exists_but_null(self):
            # Re: #425 - IOError occurs when bug present
            run("inv -c nested_or_piped foo < /dev/null", hide=True)

    class IO_hangs:
        "IO hangs"

        def _hang_on_full_pipe(self, pty):
            class Whoops(Exception):
                pass

            runner = Local(Context())
            # Force runner IO thread-body method to raise an exception to mimic
            # real world encoding explosions/etc. When bug is present, this
            # will make the test hang until forcibly terminated.
            runner.handle_stdout = Mock(side_effect=Whoops, __name__="sigh")
            # NOTE: both Darwin (10.10) and Linux (Travis' docker image) have
            # this file. It's plenty large enough to fill most pipe buffers,
            # which is the triggering behavior.
            try:
                runner.run("cat /usr/share/dict/words", pty=pty)
            except ThreadException as e:
                assert len(e.exceptions) == 1
                assert e.exceptions[0].type is Whoops
            else:
                assert False, "Did not receive expected ThreadException!"

        def pty_subproc_should_not_hang_if_IO_thread_has_an_exception(self):
            self._hang_on_full_pipe(pty=True)

        def nonpty_subproc_should_not_hang_if_IO_thread_has_an_exception(self):
            self._hang_on_full_pipe(pty=False)

    class timeouts:
        def does_not_fire_when_command_quick(self):
            assert run("sleep 1", timeout=5)

        def triggers_exception_when_command_slow(self):
            before = time.time()
            with raises(CommandTimedOut) as info:
                run("sleep 5", timeout=0.5)
            after = time.time()
            # Fudge real time check a bit, <=0.5 typically fails due to
            # overhead etc. May need raising further to avoid races? Meh.
            assert (after - before) <= 0.75
            # Sanity checks of the exception obj
            assert info.value.timeout == 0.5
            assert info.value.result.command == "sleep 5"