Skip to content

Commit

Permalink
add color tests using tty
Browse files Browse the repository at this point in the history
  • Loading branch information
cconverse711 committed Apr 12, 2024
1 parent 5afb3b8 commit ce0d840
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 43 deletions.
53 changes: 38 additions & 15 deletions test/cli/other_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,18 @@ def test_preprocessor_error(tmpdir):
assert exitcode != 0


ANSI_BOLD = "\x1b[1m"
ANSI_FG_RED = "\x1b[31m"
ANSI_FG_DEFAULT = "\x1b[39m"
ANSI_FG_RESET = "\x1b[0m"


def test_clicolor_force(tmpdir):
test_file = os.path.join(tmpdir, 'test.c')
with open(test_file, 'wt') as f:
f.write('#error test\nx=1;\n')
exitcode, _, stderr = cppcheck([test_file], env={"CLICOLOR_FORCE":"1"})

ANSI_BOLD = "\x1b[1m"
ANSI_FG_RED = "\x1b[31m"
ANSI_FG_DEFAULT = "\x1b[39m"
ANSI_FG_RESET = "\x1b[0m"


assert exitcode == 0
if sys.platform == "win32":
assert stderr
Expand All @@ -94,26 +95,48 @@ def test_clicolor_force(tmpdir):
assert ANSI_FG_RED in stderr
assert ANSI_FG_DEFAULT in stderr
assert ANSI_FG_RESET in stderr




if sys.platform != "win32":
def test_color_tty(tmpdir):
test_file = os.path.join(tmpdir, 'test.c')
with open(test_file, 'wt') as f:
f.write('#error test\nx=1;\n')
exitcode, _, stderr = cppcheck([test_file], tty=True)

assert exitcode == 0
assert ANSI_BOLD in stderr
assert ANSI_FG_RED in stderr
assert ANSI_FG_DEFAULT in stderr
assert ANSI_FG_RESET in stderr

def test_no_color_tty(tmpdir):
test_file = os.path.join(tmpdir, 'test.c')
with open(test_file, 'wt') as f:
f.write('#error test\nx=1;\n')
exitcode, _, stderr = cppcheck([test_file], env={"NO_COLOR": "1"}, tty=True)

assert exitcode == 0
assert stderr
assert ANSI_BOLD not in stderr
assert ANSI_FG_RED not in stderr
assert ANSI_FG_DEFAULT not in stderr
assert ANSI_FG_RESET not in stderr


def test_no_color(tmpdir):
test_file = os.path.join(tmpdir, 'test.c')
with open(test_file, 'wt') as f:
f.write('#error test\nx=1;\n')
exitcode, _, stderr = cppcheck([test_file], env={"NO_COLOR": "1", "CLICOLOR_FORCE":"1"})

ANSI_BOLD = "\x1b[1m"
ANSI_FG_RED = "\x1b[31m"
ANSI_FG_DEFAULT = "\x1b[39m"
ANSI_FG_RESET = "\x1b[0m"


assert exitcode == 0
assert stderr
assert ANSI_BOLD not in stderr
assert ANSI_FG_RED not in stderr
assert ANSI_FG_DEFAULT not in stderr
assert ANSI_FG_RESET not in stderr


def test_invalid_library(tmpdir):
args = ['--library=none', '--library=posix', '--library=none2', 'file.c']
Expand Down
113 changes: 85 additions & 28 deletions test/cli/testutils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import errno
import logging
import os
import select
import signal
import subprocess
import time

# Create Cppcheck project file
import sys
Expand Down Expand Up @@ -72,8 +75,82 @@ def __lookup_cppcheck_exe():
return exe_path


def __run_subprocess_tty(args, env=None, cwd=None, timeout=None):
import pty
mo, so = pty.openpty()
me, se = pty.openpty()
p = subprocess.Popen(args, stdout=so, stderr=se, env=env, cwd=cwd)
for fd in [so, se]:
os.close(fd)

select_timeout = 0.04 # seconds
readable = [mo, me]
result = {mo: b'', me: b''}
try:
start_time = time.monotonic()
while readable:
ready, _, _ = select.select(readable, [], [], select_timeout)
for fd in ready:
try:
data = os.read(fd, 512)
except OSError as e:
if e.errno != errno.EIO:
raise
# EIO means EOF on some systems
readable.remove(fd)
else:
if not data: # EOF
readable.remove(fd)
result[fd] += data
if timeout is not None and (time.monotonic() - start_time):
break
finally:
for fd in [mo, me]:
os.close(fd)
if p.poll() is None:
p.kill()
return_code = p.wait()

stdout = result[mo]
stderr = result[me]
return return_code, stdout, stderr


def __run_subprocess(args, env=None, cwd=None, timeout=None):
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd)

try:
comm = p.communicate(timeout=timeout)
return_code = p.returncode
p = None
except subprocess.TimeoutExpired:
import psutil
# terminate all the child processes
child_procs = psutil.Process(p.pid).children(recursive=True)
if len(child_procs) > 0:
for child in child_procs:
child.terminate()
try:
# call with timeout since it might be stuck
p.communicate(timeout=5)
p = None
except subprocess.TimeoutExpired:
pass
raise
finally:
if p:
# sending the signal to the process groups causes the parent Python process to terminate as well
#os.killpg(os.getpgid(p.pid), signal.SIGTERM) # Send the signal to all the process groups
p.terminate()
comm = p.communicate()

stdout = comm[0]
stderr = comm[1]
return return_code, stdout, stderr


# Run Cppcheck with args
def cppcheck(args, env=None, remove_checkers_report=True, cwd=None, cppcheck_exe=None, timeout=None):
def cppcheck(args, env=None, remove_checkers_report=True, cwd=None, cppcheck_exe=None, timeout=None, tty=False):
exe = cppcheck_exe if cppcheck_exe else __lookup_cppcheck_exe()
assert exe is not None, 'no cppcheck binary found'

Expand Down Expand Up @@ -113,33 +190,13 @@ def cppcheck(args, env=None, remove_checkers_report=True, cwd=None, cppcheck_exe
args.append(arg_executor)

logging.info(exe + ' ' + ' '.join(args))
p = subprocess.Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd)
try:
comm = p.communicate(timeout=timeout)
return_code = p.returncode
p = None
except subprocess.TimeoutExpired:
import psutil
# terminate all the child processes
child_procs = psutil.Process(p.pid).children(recursive=True)
if len(child_procs) > 0:
for child in child_procs:
child.terminate()
try:
# call with timeout since it might be stuck
p.communicate(timeout=5)
p = None
except subprocess.TimeoutExpired:
pass
raise
finally:
if p:
# sending the signal to the process groups causes the parent Python process to terminate as well
#os.killpg(os.getpgid(p.pid), signal.SIGTERM) # Send the signal to all the process groups
p.terminate()
comm = p.communicate()
stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n')
stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n')

run_subprocess = __run_subprocess_tty if tty else __run_subprocess
return_code, stdout, stderr = run_subprocess([exe] + args, env=env, cwd=cwd, timeout=timeout)

stdout = stdout.decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n')
stderr = stderr.decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n')

if remove_checkers_report:
if stderr.find('[checkersReport]\n') > 0:
start_id = stderr.find('[checkersReport]\n')
Expand Down

0 comments on commit ce0d840

Please sign in to comment.