From 59e8d3f27639a9f480df23e00fa5402025007ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Krone=CC=81?= Date: Mon, 16 Nov 2020 14:21:08 +0100 Subject: [PATCH 1/4] Raise and catch an exception when failing to get terminal attributes --- locust/input_events.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/locust/input_events.py b/locust/input_events.py index bc45bf1417..92e7ca1ebb 100644 --- a/locust/input_events.py +++ b/locust/input_events.py @@ -18,14 +18,18 @@ import tty +class InitError(Exception): + pass + + class UnixKeyPoller: def __init__(self): - try: + if sys.stdout.isatty(): self.stdin = sys.stdin.fileno() self.tattr = termios.tcgetattr(self.stdin) tty.setcbreak(self.stdin, termios.TCSANOW) - except termios.error: - pass + else: + raise InitError("Failed to get terminal attributes during keypoller init. Keyboard input disabled") def cleanup(self): termios.tcsetattr(self.stdin, termios.TCSANOW, self.tattr) @@ -80,9 +84,13 @@ def get_poller(): def input_listener(key_to_func_map): - poller = get_poller() - def input_listener_func(): + try: + poller = get_poller() + except InitError as e: + logging.info(e) + return + try: while True: input = poller.poll() From 54fec34c7977458ab357bc76098f27df61a1ce3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Krone=CC=81?= Date: Mon, 16 Nov 2020 14:24:09 +0100 Subject: [PATCH 2/4] No longer check for empty input strings. --- locust/input_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locust/input_events.py b/locust/input_events.py index 92e7ca1ebb..ad04ee0c8c 100644 --- a/locust/input_events.py +++ b/locust/input_events.py @@ -94,7 +94,7 @@ def input_listener_func(): try: while True: input = poller.poll() - if input is not None: + if input: for key in key_to_func_map: if input == key: key_to_func_map[key]() From 42222697efe726ec8613fed186095d4747561158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Krone=CC=81?= Date: Mon, 16 Nov 2020 14:28:42 +0100 Subject: [PATCH 3/4] Error message is more specifik to check. --- locust/input_events.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locust/input_events.py b/locust/input_events.py index ad04ee0c8c..43f2b337f4 100644 --- a/locust/input_events.py +++ b/locust/input_events.py @@ -24,12 +24,12 @@ class InitError(Exception): class UnixKeyPoller: def __init__(self): - if sys.stdout.isatty(): + if sys.stdin.isatty(): self.stdin = sys.stdin.fileno() self.tattr = termios.tcgetattr(self.stdin) tty.setcbreak(self.stdin, termios.TCSANOW) else: - raise InitError("Failed to get terminal attributes during keypoller init. Keyboard input disabled") + raise InitError("Terminal was not a tty. Keyboard input disabled") def cleanup(self): termios.tcsetattr(self.stdin, termios.TCSANOW, self.tattr) From b8fda99c767a8b1a3e35bf476757766033d78bb7 Mon Sep 17 00:00:00 2001 From: Jonatan Heyman Date: Mon, 16 Nov 2020 15:38:20 +0100 Subject: [PATCH 4/4] Fix broken test for keyboard input in headless mode. Run process using an emulated pty. --- locust/test/test_main.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/locust/test/test_main.py b/locust/test/test_main.py index 1de068bb6a..c563f97a3a 100644 --- a/locust/test/test_main.py +++ b/locust/test/test_main.py @@ -1,5 +1,6 @@ import os import platform +import pty import signal import subprocess import textwrap @@ -297,7 +298,19 @@ def test_web_options(self): proc.terminate() def test_input(self): - with mock_locustfile() as mocked: + LOCUSTFILE_CONTENT = textwrap.dedent(""" + from locust import User, TaskSet, task, between + + class UserSubclass(User): + wait_time = between(0.2, 0.8) + @task + def t(self): + print("Test task is running") + """) + with mock_locustfile(content=LOCUSTFILE_CONTENT) as mocked: + stdin_m, stdin_s = pty.openpty() + stdin = os.fdopen(stdin_m, "wb", 0) + proc = subprocess.Popen( " ".join( [ @@ -312,20 +325,26 @@ def test_input(self): ] ), stderr=STDOUT, - stdin=PIPE, + stdin=stdin_s, stdout=PIPE, shell=True, ) - gevent.sleep(1) - proc.stdin.write(b"w") - proc.stdin.write(b"W") - proc.stdin.write(b"s") - proc.stdin.write(b"S") + + stdin.write(b"w") + gevent.sleep(0.1) + stdin.write(b"W") + gevent.sleep(0.1) + stdin.write(b"s") + gevent.sleep(0.1) + stdin.write(b"S") + gevent.sleep(1) output = proc.communicate()[0].decode("utf-8") + stdin.close() self.assertIn("Spawning 1 users at the rate 100 users/s", output) self.assertIn("Spawning 10 users at the rate 100 users/s", output) self.assertIn("1 Users have been stopped", output) self.assertIn("10 Users have been stopped", output) + self.assertIn("Test task is running", output)