Esempio n. 1
0
 def __init__(self, out="", err="", exit=0, isatty=None, autostart=True):
     self.out_file = BytesIO(b(out))
     self.err_file = BytesIO(b(err))
     self.exit = exit
     self.isatty = isatty
     if autostart:
         self.start()
Esempio n. 2
0
def _runner(out='', err='', **kwargs):
    klass = kwargs.pop('klass', _Dummy)
    runner = klass(Context(config=Config(overrides=kwargs)))
    if 'exits' in kwargs:
        runner.returncode = Mock(return_value=kwargs.pop('exits'))
    out_file = BytesIO(b(out))
    err_file = BytesIO(b(err))
    runner.read_proc_stdout = out_file.read
    runner.read_proc_stderr = err_file.read
    return runner
Esempio n. 3
0
def _runner(out='', err='', **kwargs):
    klass = kwargs.pop('klass', _Dummy)
    runner = klass(Context(config=Config(overrides=kwargs)))
    if 'exits' in kwargs:
        runner.returncode = Mock(return_value=kwargs.pop('exits'))
    out_file = BytesIO(b(out))
    err_file = BytesIO(b(err))
    runner.read_proc_stdout = out_file.read
    runner.read_proc_stderr = err_file.read
    return runner
Esempio n. 4
0
        def wrapper(*args, **kwargs):
            args = list(args)
            pty, os, ioctl = args.pop(), args.pop(), args.pop()
            # Don't actually fork, but pretend we did (with "our" pid differing
            # depending on be_childish) & give 'parent fd' of 3 (typically,
            # first allocated non-stdin/out/err FD)
            pty.fork.return_value = (12345 if be_childish else 0), 3
            # We don't really need to care about waiting since not truly
            # forking/etc, so here we just return a nonzero "pid" + sentinel
            # wait-status value (used in some tests about WIFEXITED etc)
            os.waitpid.return_value = None, Mock(name="exitstatus")
            # Either or both of these may get called, depending...
            os.WEXITSTATUS.return_value = exit
            os.WTERMSIG.return_value = exit
            # If requested, mock isatty to fake out pty detection
            if isatty is not None:
                os.isatty.return_value = isatty
            out_file = BytesIO(b(out))
            err_file = BytesIO(b(err))

            def fakeread(fileno, count):
                fd = {3: out_file, 2: err_file}[fileno]
                ret = fd.read(count)
                # If asked, fake a Linux-platform trailing I/O error.
                if not ret and trailing_error:
                    raise trailing_error
                return ret

            os.read.side_effect = fakeread
            if os_close_error:
                os.close.side_effect = IOError
            if insert_os:
                args.append(os)

            # Do the thing!!!
            f(*args, **kwargs)

            # Short-circuit if we raised an error in fakeread()
            if trailing_error:
                return
            # Sanity checks to make sure the stuff we mocked, actually got ran!
            pty.fork.assert_called_with()
            # Skip rest of asserts if we pretended to be the child
            if be_childish:
                return
            # Expect a get, and then later set, of terminal window size
            assert ioctl.call_args_list[0][0][1] == termios.TIOCGWINSZ
            assert ioctl.call_args_list[1][0][1] == termios.TIOCSWINSZ
            if not skip_asserts:
                for name in ("execve", "waitpid"):
                    assert getattr(os, name).called
                # Ensure at least one of the exit status getters was called
                assert os.WEXITSTATUS.called or os.WTERMSIG.called
                # Ensure something closed the pty FD
                os.close.assert_called_once_with(3)
Esempio n. 5
0
 def multiple_patterns_works_as_expected(self):
     calls = [call(b('betty')), call(b('carnival'))]
     # Technically, I'd expect 'betty' to get called before 'carnival',
     # but under Python 3 it's reliably backwards from Python 2.
     # In real world situations where each prompt sits & waits for its
     # response, this probably wouldn't be an issue, so using
     # any_order=True for now. Thanks again Python 3.
     self._expect_response(
         out="beep boop I am a robot",
         responses={'boop': 'betty', 'robot': 'carnival'},
     ).assert_has_calls(calls, any_order=True)
Esempio n. 6
0
 def multiple_patterns_works_as_expected(self):
     calls = [call(b('betty')), call(b('carnival'))]
     # Technically, I'd expect 'betty' to get called before 'carnival',
     # but under Python 3 it's reliably backwards from Python 2.
     # In real world situations where each prompt sits & waits for its
     # response, this probably wouldn't be an issue, so using
     # any_order=True for now. Thanks again Python 3.
     self._expect_response(
         out="beep boop I am a robot",
         responses={'boop': 'betty', 'robot': 'carnival'},
     ).assert_has_calls(calls, any_order=True)
Esempio n. 7
0
        def wrapper(*args, **kwargs):
            args = list(args)
            pty, os, ioctl = args.pop(), args.pop(), args.pop()
            # Don't actually fork, but pretend we did & that main thread is
            # also the child (pid 0) to trigger execve call; & give 'parent fd'
            # of 1 (stdout).
            pty.fork.return_value = (12345 if be_childish else 0), 1
            # We don't really need to care about waiting since not truly
            # forking/etc, so here we just return a nonzero "pid" + sentinel
            # wait-status value (used in some tests about WIFEXITED etc)
            os.waitpid.return_value = None, Mock(name="exitstatus")
            # Either or both of these may get called, depending...
            os.WEXITSTATUS.return_value = exit
            os.WTERMSIG.return_value = exit
            # If requested, mock isatty to fake out pty detection
            if isatty is not None:
                os.isatty.return_value = isatty
            out_file = BytesIO(b(out))
            err_file = BytesIO(b(err))

            def fakeread(fileno, count):
                fd = {1: out_file, 2: err_file}[fileno]
                ret = fd.read(count)
                # If asked, fake a Linux-platform trailing I/O error.
                if not ret and trailing_error:
                    raise trailing_error
                return ret

            os.read.side_effect = fakeread
            if insert_os:
                args.append(os)
            f(*args, **kwargs)
            # Short-circuit if we raised an error in fakeread()
            if trailing_error:
                return
            # Sanity checks to make sure the stuff we mocked, actually got ran!
            # TODO: inject our mocks back into the tests so they can make their
            # own assertions if desired
            pty.fork.assert_called_with()
            # Skip rest of asserts if we pretended to be the child
            if be_childish:
                return
            # Expect a get, and then later set, of terminal window size
            assert ioctl.call_args_list[0][0][1] == termios.TIOCGWINSZ
            assert ioctl.call_args_list[1][0][1] == termios.TIOCSWINSZ
            if not skip_asserts:
                for name in ("execve", "waitpid"):
                    assert getattr(os, name).called
                # Ensure at least one of the exit status getters was called
                assert os.WEXITSTATUS.called or os.WTERMSIG.called
Esempio n. 8
0
 def string_keys_in_responses_kwarg_yield_values_as_stdin_writes(self):
     self._expect_response(
         out="the house was empty",
         responses={
             'empty': 'handed'
         },
     ).assert_called_once_with(b("handed"))
Esempio n. 9
0
 def input_stream_can_be_overridden(self):
     klass = self._mock_stdin_writer()
     in_stream = StringIO("Hey, listen!")
     self._runner(klass=klass).run(_, in_stream=in_stream)
     # stdin mirroring occurs byte-by-byte
     calls = list(map(lambda x: call(b(x)), "Hey, listen!"))
     klass.write_stdin.assert_has_calls(calls, any_order=False)
Esempio n. 10
0
 def regex_keys_also_work(self):
     self._expect_response(
         out="technically, it's still debt",
         responses={
             r'tech.*debt': 'pay it down'
         },
     ).assert_called_once_with(b('pay it down'))
Esempio n. 11
0
        def wrapper(*args, **kwargs):
            args = list(args)
            pty, os, ioctl = args.pop(), args.pop(), args.pop()
            # Don't actually fork, but pretend we did & that main thread is
            # also the child (pid 0) to trigger execve call; & give 'parent fd'
            # of 1 (stdout).
            pty.fork.return_value = 0, 1
            # We don't really need to care about waiting since not truly
            # forking/etc, so here we just return a nonzero "pid" + sentinel
            # wait-status value (used in some tests about WIFEXITED etc)
            os.waitpid.return_value = None, Mock(name="exitstatus")
            # Either or both of these may get called, depending...
            os.WEXITSTATUS.return_value = exit
            os.WTERMSIG.return_value = exit
            # If requested, mock isatty to fake out pty detection
            if isatty is not None:
                os.isatty.return_value = isatty
            out_file = BytesIO(b(out))
            err_file = BytesIO(b(err))

            def fakeread(fileno, count):
                fd = {1: out_file, 2: err_file}[fileno]
                ret = fd.read(count)
                # If asked, fake a Linux-platform trailing I/O error.
                if not ret and trailing_error:
                    raise trailing_error
                return ret

            os.read.side_effect = fakeread
            if insert_os:
                args.append(os)
            f(*args, **kwargs)
            # Short-circuit if we raised an error in fakeread()
            if trailing_error:
                return
            # Sanity checks to make sure the stuff we mocked, actually got ran!
            # TODO: inject our mocks back into the tests so they can make their
            # own assertions if desired
            pty.fork.assert_called_with()
            # Expect a get, and then later set, of terminal window size
            assert ioctl.call_args_list[0][0][1] == termios.TIOCGWINSZ
            assert ioctl.call_args_list[1][0][1] == termios.TIOCSWINSZ
            if not skip_asserts:
                for name in ("execve", "waitpid"):
                    assert getattr(os, name).called
                # Ensure at least one of the exit status getters was called
                assert os.WEXITSTATUS.called or os.WTERMSIG.called
Esempio n. 12
0
 def input_defaults_to_sys_stdin(self):
     # Execute w/ runner class that has a mocked stdin_writer
     klass = self._mock_stdin_writer()
     self._runner(klass=klass).run(_)
     # Check that mocked writer was called w/ expected data
     # stdin mirroring occurs byte-by-byte
     calls = list(map(lambda x: call(b(x)), "Text!"))
     klass.write_stdin.assert_has_calls(calls, any_order=False)
Esempio n. 13
0
 def both_out_and_err_are_scanned(self):
     bye = call(b("goodbye"))
     # Would only be one 'bye' if only scanning stdout
     self._expect_response(
         out="hello my name is inigo",
         err="hello how are you",
         responses={"hello": "goodbye"},
     ).assert_has_calls([bye, bye])
Esempio n. 14
0
 def multiple_hits_yields_multiple_responses(self):
     holla = call(b('how high?'))
     self._expect_response(
         out="jump, wait, jump, wait",
         responses={
             'jump': 'how high?'
         },
     ).assert_has_calls([holla, holla])
Esempio n. 15
0
 def both_out_and_err_are_scanned(self):
     bye = call(b("goodbye"))
     # Would only be one 'bye' if only scanning stdout
     self._expect_response(
         out="hello my name is inigo",
         err="hello how are you",
         responses={"hello": "goodbye"},
     ).assert_has_calls([bye, bye])
Esempio n. 16
0
 def defaults_to_sys_stdin(self):
     # Execute w/ runner class that has a mocked stdin_writer
     klass = self._mock_stdin_writer()
     self._runner(klass=klass).run(_, out_stream=StringIO())
     # Check that mocked writer was called w/ expected data
     # stdin mirroring occurs byte-by-byte
     calls = list(map(lambda x: call(b(x)), "Text!"))
     klass.write_stdin.assert_has_calls(calls, any_order=False)
Esempio n. 17
0
 def wrapper(*args, **kwargs):
     args = list(args)
     Popen, read, sys_stdin = args.pop(), args.pop(), args.pop()
     process = Popen.return_value
     process.returncode = exit
     process.stdout.fileno.return_value = 1
     process.stderr.fileno.return_value = 2
     # If requested, mock isatty to fake out pty detection
     if isatty is not None:
         sys_stdin.isatty = Mock(return_value=isatty)
     out_file = BytesIO(b(out))
     err_file = BytesIO(b(err))
     def fakeread(fileno, count):
         fd = {1: out_file, 2: err_file}[fileno]
         return fd.read(count)
     read.side_effect = fakeread
     if insert_Popen:
         args.append(Popen)
     f(*args, **kwargs)
Esempio n. 18
0
        def wrapper(*args, **kwargs):
            args = list(args)
            pty, os, ioctl = args.pop(), args.pop(), args.pop()
            # Don't actually fork, but pretend we did & that main thread is
            # also the child (pid 0) to trigger execve call; & give 'parent fd'
            # of 1 (stdout).
            pty.fork.return_value = 0, 1
            # We don't really need to care about waiting since not truly
            # forking/etc, so here we just return a nonzero "pid" + dummy value
            # (normally sent to WEXITSTATUS but we mock that anyway, so.)
            os.waitpid.return_value = None, None
            os.WEXITSTATUS.return_value = exit
            # If requested, mock isatty to fake out pty detection
            if isatty is not None:
                os.isatty.return_value = isatty
            out_file = BytesIO(b(out))
            err_file = BytesIO(b(err))

            def fakeread(fileno, count):
                fd = {1: out_file, 2: err_file}[fileno]
                ret = fd.read(count)
                # If asked, fake a Linux-platform trailing I/O error.
                if not ret and trailing_error:
                    raise trailing_error
                return ret

            os.read.side_effect = fakeread
            if insert_os:
                args.append(os)
            f(*args, **kwargs)
            # Short-circuit if we raised an error in fakeread()
            if trailing_error:
                return
            # Sanity checks to make sure the stuff we mocked, actually got ran!
            # TODO: inject our mocks back into the tests so they can make their
            # own assertions if desired
            pty.fork.assert_called_with()
            # Test the 2nd call to ioctl; the 1st call is doing TIOGSWINSZ
            eq_(ioctl.call_args_list[1][0][1], termios.TIOCSWINSZ)
            if not skip_asserts:
                for name in ('execve', 'waitpid', 'WEXITSTATUS'):
                    assert getattr(os, name).called
Esempio n. 19
0
 def can_be_overridden(self):
     klass = self._mock_stdin_writer()
     in_stream = StringIO("Hey, listen!")
     self._runner(klass=klass).run(
         _,
         in_stream=in_stream,
         out_stream=StringIO(),
     )
     # stdin mirroring occurs byte-by-byte
     calls = list(map(lambda x: call(b(x)), "Hey, listen!"))
     klass.write_stdin.assert_has_calls(calls, any_order=False)
Esempio n. 20
0
 def chunk_sizes_smaller_than_patterns_still_work_ok(self):
     klass = self._mock_stdin_writer()
     klass.read_chunk_size = 1  # < len('jump')
     responses = {'jump': 'how high?'}
     runner = self._runner(klass=klass, out="jump, wait, jump, wait")
     runner.run(_, responses=responses, hide=True)
     holla = call(b('how high?'))
     # Responses happened, period.
     klass.write_stdin.assert_has_calls([holla, holla])
     # And there weren't duplicates!
     eq_(len(klass.write_stdin.call_args_list), 2)
Esempio n. 21
0
 def chunk_sizes_smaller_than_patterns_still_work_ok(self):
     klass = self._mock_stdin_writer()
     klass.read_chunk_size = 1 # < len('jump')
     responses = {'jump': 'how high?'}
     runner = self._runner(klass=klass, out="jump, wait, jump, wait")
     runner.run(_, responses=responses, hide=True)
     holla = call(b('how high?'))
     # Responses happened, period.
     klass.write_stdin.assert_has_calls([holla, holla])
     # And there weren't duplicates!
     eq_(len(klass.write_stdin.call_args_list), 2)
Esempio n. 22
0
        def patterns_span_multiple_lines(self):
            output = """
You only call me
when you have a problem
You never call me
Just to say hi
"""
            self._expect_response(
                out=output,
                responses={r'call.*problem': 'So sorry'},
            ).assert_called_once_with(b('So sorry'))
Esempio n. 23
0
        def patterns_span_multiple_lines(self):
            output = """
You only call me
when you have a problem
You never call me
Just to say hi
"""
            self._expect_response(
                out=output,
                responses={r'call.*problem': 'So sorry'},
            ).assert_called_once_with(b('So sorry'))
Esempio n. 24
0
 def multiple_patterns_across_both_streams(self):
     responses = {
         'boop': 'betty',
         'robot': 'carnival',
         'Destroy': 'your ego',
         'humans': 'are awful',
     }
     calls = map(lambda x: call(b(x)), responses.values())
     # CANNOT assume order due to simultaneous streams.
     # If we didn't say any_order=True we could get race condition fails
     self._expect_response(
         out="beep boop, I am a robot",
         err="Destroy all humans!",
         responses=responses,
     ).assert_has_calls(calls, any_order=True)
Esempio n. 25
0
 def multiple_patterns_across_both_streams(self):
     responses = {
         'boop': 'betty',
         'robot': 'carnival',
         'Destroy': 'your ego',
         'humans': 'are awful',
     }
     calls = map(lambda x: call(b(x)), responses.values())
     # CANNOT assume order due to simultaneous streams.
     # If we didn't say any_order=True we could get race condition fails
     self._expect_response(
         out="beep boop, I am a robot",
         err="Destroy all humans!",
         responses=responses,
     ).assert_has_calls(calls, any_order=True)
Esempio n. 26
0
 def _forward_local(self, kwargs, Client, mocket, select):
     # Tease out bits of kwargs for use in the mocking/expecting.
     # But leave it alone for raw passthru to the API call itself.
     # TODO: unhappy with how much this apes the real code & its sig...
     local_port = kwargs["local_port"]
     remote_port = kwargs.get("remote_port", local_port)
     local_host = kwargs.get("local_host", "localhost")
     remote_host = kwargs.get("remote_host", "localhost")
     # These aren't part of the real sig, but this is easier than trying
     # to reconcile the mock decorators + optional-value kwargs. meh.
     tunnel_exception = kwargs.pop("tunnel_exception", None)
     listener_exception = kwargs.pop("listener_exception", False)
     # Mock setup
     client = Client.return_value
     listener_sock = Mock(name="listener_sock")
     if listener_exception:
         listener_sock.bind.side_effect = listener_exception
     data = b("Some data")
     tunnel_sock = Mock(name="tunnel_sock", recv=lambda n: data)
     local_addr = Mock()
     transport = client.get_transport.return_value
     channel = transport.open_channel.return_value
     # socket.socket is only called once directly
     mocket.return_value = listener_sock
     # The 2nd socket is obtained via an accept() (which should only
     # fire once & raise EAGAIN after)
     listener_sock.accept.side_effect = chain(
         [(tunnel_sock, local_addr)],
         repeat(socket.error(errno.EAGAIN, "nothing yet")),
     )
     obj = tunnel_sock if tunnel_exception is None else tunnel_exception
     select.select.side_effect = _select_result(obj)
     with Connection("host").forward_local(**kwargs):
         # Make sure we give listener thread enough time to boot up :(
         # Otherwise we might assert before it does things. (NOTE:
         # doesn't need to be much, even at 0.01s, 0/100 trials failed
         # (vs 45/100 with no sleep)
         time.sleep(0.015)
         assert client.connect.call_args[1]["hostname"] == "host"
         listener_sock.setsockopt.assert_called_once_with(
             socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         listener_sock.setblocking.assert_called_once_with(0)
         listener_sock.bind.assert_called_once_with(
             (local_host, local_port))
         if not listener_exception:
             listener_sock.listen.assert_called_once_with(1)
             transport.open_channel.assert_called_once_with(
                 "direct-tcpip", (remote_host, remote_port), local_addr)
         # Local write to tunnel_sock is implied by its mocked-out
         # recv() call above...
         # NOTE: don't assert if explodey; we want to mimic "the only
         # error that occurred was within the thread" behavior being
         # tested by thread-exception-handling tests
         if not (tunnel_exception or listener_exception):
             channel.sendall.assert_called_once_with(data)
     # Shutdown, with another sleep because threads.
     time.sleep(0.015)
     if not listener_exception:
         tunnel_sock.close.assert_called_once_with()
         channel.close.assert_called_once_with()
         listener_sock.close.assert_called_once_with()
Esempio n. 27
0
 def _forward_local(self, kwargs, Client, mocket, select):
     # Tease out bits of kwargs for use in the mocking/expecting.
     # But leave it alone for raw passthru to the API call itself.
     # TODO: unhappy with how much this apes the real code & its sig...
     local_port = kwargs['local_port']
     remote_port = kwargs.get('remote_port', local_port)
     local_host = kwargs.get('local_host', 'localhost')
     remote_host = kwargs.get('remote_host', 'localhost')
     # These aren't part of the real sig, but this is easier than trying
     # to reconcile the mock decorators + optional-value kwargs. meh.
     tunnel_exception = kwargs.pop('tunnel_exception', None)
     listener_exception = kwargs.pop('listener_exception', False)
     # Mock setup
     client = Client.return_value
     listener_sock = Mock(name='listener_sock')
     if listener_exception:
         listener_sock.bind.side_effect = listener_exception
     data = b("Some data")
     tunnel_sock = Mock(name='tunnel_sock', recv=lambda n: data)
     local_addr = Mock()
     transport = client.get_transport.return_value
     channel = transport.open_channel.return_value
     # socket.socket is only called once directly
     mocket.return_value = listener_sock
     # The 2nd socket is obtained via an accept() (which should only
     # fire once & raise EAGAIN after)
     listener_sock.accept.side_effect = chain(
         [(tunnel_sock, local_addr)],
         repeat(socket.error(errno.EAGAIN, "nothing yet")),
     )
     obj = tunnel_sock if tunnel_exception is None else tunnel_exception
     select.select.side_effect = _select_result(obj)
     with Connection('host').forward_local(**kwargs):
         # Make sure we give listener thread enough time to boot up :(
         # Otherwise we might assert before it does things. (NOTE:
         # doesn't need to be much, even at 0.01s, 0/100 trials failed
         # (vs 45/100 with no sleep)
         time.sleep(0.015)
         assert client.connect.call_args[1]['hostname'] == 'host'
         listener_sock.setsockopt.assert_called_once_with(
             socket.SOL_SOCKET, socket.SO_REUSEADDR, 1
         )
         listener_sock.setblocking.assert_called_once_with(0)
         listener_sock.bind.assert_called_once_with(
             (local_host, local_port)
         )
         if not listener_exception:
             listener_sock.listen.assert_called_once_with(1)
             transport.open_channel.assert_called_once_with(
                 'direct-tcpip',
                 (remote_host, remote_port),
                 local_addr,
             )
         # Local write to tunnel_sock is implied by its mocked-out
         # recv() call above...
         # NOTE: don't assert if explodey; we want to mimic "the only
         # error that occurred was within the thread" behavior being
         # tested by thread-exception-handling tests
         if not (tunnel_exception or listener_exception):
             channel.sendall.assert_called_once_with(data)
     # Shutdown, with another sleep because threads.
     time.sleep(0.015)
     if not listener_exception:
         tunnel_sock.close.assert_called_once_with()
         channel.close.assert_called_once_with()
         listener_sock.close.assert_called_once_with()
Esempio n. 28
0
 def string_keys_in_responses_kwarg_yield_values_as_stdin_writes(self):
     self._expect_response(
         out="the house was empty",
         responses={'empty': 'handed'},
     ).assert_called_once_with(b("handed"))
Esempio n. 29
0
 def regex_keys_also_work(self):
     self._expect_response(
         out="technically, it's still debt",
         responses={r'tech.*debt': 'pay it down'},
     ).assert_called_once_with(b('pay it down'))
Esempio n. 30
0
 def multiple_hits_yields_multiple_responses(self):
     holla = call(b('how high?'))
     self._expect_response(
         out="jump, wait, jump, wait",
         responses={'jump': 'how high?'},
     ).assert_has_calls([holla, holla])