Index: tests/pybin/tools.py
===================================================================
--- tests/pybin/tools.py	(revision 5bf1f3ec95f7edb2d985f8c7335018402abe3593)
+++ tests/pybin/tools.py	(revision 1bb2488f0c626690cd402a7004cd0df0db3e31d2)
@@ -8,4 +8,5 @@
 import signal
 import stat
+import subprocess
 import sys
 import time
@@ -20,8 +21,12 @@
 
 # helper functions to run terminal commands
-def sh(cmd, print2stdout = True, input = None):
+def sh(cmd, print2stdout = True, timeout = False, output = None, input = None):
 	# add input redirection if needed
 	if input and os.path.isfile(input):
 		cmd += " < %s" % input
+
+	# add output redirection if needed
+	if output:
+		cmd += " > %s" % output
 
 	# if this is a dry_run, only print the commands that would be ran
@@ -32,7 +37,12 @@
 	# otherwise create a pipe and run the desired command
 	else :
-		proc = Popen(cmd, stdout=None if print2stdout else PIPE, stderr=STDOUT, shell=True)
-		out, err = proc.communicate()
-		return proc.returncode, out
+		proc = subprocess.run(
+			cmd,
+			stdout=None if print2stdout else PIPE,
+			stderr=STDOUT,
+			shell=True,
+			timeout=settings.timeout.single if timeout else None
+		)
+		return proc.returncode, proc.stdout
 
 def is_ascii(fname):
@@ -128,8 +138,4 @@
     return None
 
-def run(exe, output, input):
-	ret, _ = sh("timeout %d %s > %s 2>&1" % (settings.timeout.single, exe, output), input = input)
-	return ret
-
 ################################################################################
 #               file handling
@@ -208,23 +214,4 @@
 	return min( options.jobs, len(tests) ), force
 
-# setup a proper processor pool with correct signal handling
-def setup_pool(jobs):
-	original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
-	pool = multiprocessing.Pool(jobs)
-	signal.signal(signal.SIGINT, original_sigint_handler)
-
-	return pool
-
-# handle signals in scope
-class SignalHandling():
-	def __enter__(self):
-		# enable signal handling
-	    	signal.signal(signal.SIGINT, signal.SIG_DFL)
-
-	def __exit__(self, type, value, traceback):
-		# disable signal handling
-		signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-
 # enable core dumps for all the test children
 resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
@@ -245,7 +232,5 @@
 	column = which('column')
 	if column:
-		cmd = "%s 2> /dev/null" % column
-		proc = Popen(cmd, stdin=PIPE, stderr=None, shell=True)
-		proc.communicate(input=bytes(text + "\n", "UTF-8"))
+		subprocess.run(column, input=bytes(text + "\n", "UTF-8"))
 	else:
 		print(text)
Index: tests/test.py
===================================================================
--- tests/test.py	(revision 5bf1f3ec95f7edb2d985f8c7335018402abe3593)
+++ tests/test.py	(revision 1bb2488f0c626690cd402a7004cd0df0db3e31d2)
@@ -149,5 +149,5 @@
 			if settings.dry_run or is_exe(exe_file):
 				# run test
-				retcode = run(exe_file, out_file, in_file)
+				retcode, _ = sh(exe_file, output=out_file, input=in_file, timeout=True)
 			else :
 				# simply cat the result into the output
@@ -185,5 +185,5 @@
 # run a single test and handle the errors, outputs, printing, exception handling, etc.
 def run_test_worker(t) :
-	with SignalHandling():
+	try :
 		# print formated name
 		name_txt = '{0:{width}}  '.format(t.target(), width=settings.output_width)
@@ -205,5 +205,7 @@
 		sys.stderr.flush()
 
-	return retcode != TestResult.SUCCESS
+		return retcode != TestResult.SUCCESS
+	except KeyboardInterrupt:
+		False
 
 # run the given list of tests with the given parameters
@@ -213,5 +215,5 @@
 
 	# create the executor for our jobs and handle the signal properly
-	pool = setup_pool(jobs)
+	pool = multiprocessing.Pool(jobs)
 
 	# for each test to run
