Skip to content

Commit

Permalink
etc: add etc/code-coverage-test-*.py
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-mitchell committed May 29, 2024
1 parent d555e3b commit 4b5c792
Show file tree
Hide file tree
Showing 2 changed files with 258 additions and 0 deletions.
151 changes: 151 additions & 0 deletions etc/code-coverage-test-c.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/env python
"""
"""

# pylint: disable=invalid-name, broad-except

import argparse
import tempfile
import subprocess
import sys
import os
import webbrowser

from os.path import exists, isfile

_ERR_PREFIX = '\033[31merror: '

def exec_string(string):
'execute the string in a subprocess.'
try:
subprocess.check_call(string, shell=True)
except KeyboardInterrupt:
sys.exit('\033[31m\nKilled!\033[0m')
except (subprocess.CalledProcessError, OSError):
sys.exit(_ERR_PREFIX + 'executing:\n' + string + '\n failed!\033[0m')

_PARSER = argparse.ArgumentParser(prog='code-coverage-test-c.py',
usage='%(prog)s [options]')
_PARSER.add_argument('files', nargs='+', type=str,
help='the test files you want to check code coverage for'
+ '(must be at least one)')
_PARSER.add_argument('--gap-root', nargs='?', type=str,
help='the gap root directory (default: ~/gap)',
default='~/gap/')
_PARSER.add_argument('--pkg', nargs='?', type=str,
help='the package to profile (default: None)',
default=None)
_PARSER.add_argument('--build', dest='build', action='store_true',
help='rebuild GAP (default: False)')
_PARSER.set_defaults(build=False)
_PARSER.add_argument('--open', nargs='?', type=str,
help=('open the lcov html page for this file ' +
'(default: None)'),
default=None)
_PARSER.add_argument('--line', nargs='?', type=str,
help=('open the html page for the file specified by --open' +
' at this line (default: None)'),
default=None)
_ARGS = _PARSER.parse_args()

if not _ARGS.gap_root[-1] == '/':
_ARGS.gap_root += '/'

_ARGS.gap_root = os.path.expanduser(_ARGS.gap_root)
if _ARGS.pkg != None:
_ARGS.pkg = _ARGS.gap_root + '/pkg/' + _ARGS.pkg

if not (os.path.exists(_ARGS.gap_root) and os.path.isdir(_ARGS.gap_root)):
sys.exit('\033[31mcode-coverage-test-c.py: error: can\'t find gap root' +
' directory!\033[0m')
if (_ARGS.pkg != None and not (os.path.exists(_ARGS.pkg) and
os.path.isdir(_ARGS.pkg))):
sys.exit('\033[31mcode-coverage-test-c.py: error: can\'t find the pkg' +
' directory %s\033[0m' % _ARGS.pkg)
for f in _ARGS.files:
if not (os.path.exists(f) and os.path.isfile(f)):
sys.exit('\033[31mcode-coverage-test-c.py: error: ' + f +
' does not exist!\033[0m')

_DIR = tempfile.mkdtemp()
print('\033[35musing temporary directory: ' + _DIR + '\033[0m')

_COMMANDS = 'echo "'
for f in _ARGS.files:
_COMMANDS += 'Test(\\"' + f + '\\");;'
_COMMANDS += '"'

# TODO build if files changed since last build or built with the wrong flags,
# by looking in config.log

# for source in :
# if time.ctime(os.path.getmtime(file))

if _ARGS.build:
cwd = os.getcwd()
os.chdir(_ARGS.gap_root)
exec_string('''rm -rf bin/ && \
make clean && \
./configure CFLAGS="-O0 -g --coverage" \
CXXFLAGS="-O0 -g --coverage" \
LDFLAGS="-O0 -g --coverage" && \
make -j8''')
if _ARGS.pkg != None:
os.chdir(_ARGS.pkg)
exec_string('rm -rf bin/ && \
make clean && \
./configure CFLAGS="-O0 -g --coverage" \
CXXFLAGS="-O0 -g --coverage" \
LDFLAGS="-O0 -g --coverage" && \
make -j8''')
os.chdir(cwd)

pro1 = subprocess.Popen(_COMMANDS, stdout=subprocess.PIPE, shell=True)
_RUN_GAP = _ARGS.gap_root + 'bin/gap.sh -A -m 1g -T'

try:
pro2 = subprocess.Popen(_RUN_GAP,
stdin=pro1.stdout,
shell=True)
pro2.wait()
print('')
except KeyboardInterrupt:
pro1.terminate()
pro1.wait()
pro2.terminate()
pro2.wait()
print('\033[31mKilled!\033[0m')
sys.exit(1)
except Exception:
sys.exit('\033[31mcode-coverage-test-c.py: error: something went wrong '
+ 'calling GAP!\033[0m''')
if _ARGS.pkg != None:
exec_string('lcov --capture --directory ' + _ARGS.pkg +
'/src --output-file ' + _DIR + '/lcov.info')
else:
exec_string('lcov --capture --directory ' + _ARGS.gap_root +
'/src --output-file ' + _DIR + '/lcov.info')

exec_string('genhtml ' + _DIR + '/lcov.info --output-directory ' + _DIR +
'/lcov-out')

filename = _DIR + '/lcov-out/'
if _ARGS.open:
filename += _ARGS.open + '.gcov.html'
else:
filename += '/index.html'

if exists(filename) and isfile(filename):
if _ARGS.open and _ARGS.line:
filename += '#' + _ARGS.line
print('file://' + filename)
try:
webbrowser.get('chrome').open('file://' + filename, new=2)
except Exception:
webbrowser.open('file://' + filename, new=2)
else:
sys.exit('\n' + _ERR_PREFIX + 'Failed to open file://' + filename +
'\033[0m')

print('\n\033[32mSUCCESS!\033[0m')
sys.exit(0)
107 changes: 107 additions & 0 deletions etc/code-coverage-test-gap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
"""
This is a simple script to run code coverage for some test files.
"""
# pylint: disable=invalid-name

import argparse
import os
import re
import subprocess
import sys
import tempfile

from os.path import exists, isdir, isfile
from os import getcwd

_ERR_PREFIX = "\033[31mcode-coverage-test-gap.py: error: "
_INFO_PREFIX = "\033[0m\033[1m"

_PARSER = argparse.ArgumentParser(
prog="code-coverage-test-gap.py", usage="%(prog)s [options]"
)
_PARSER.add_argument(
"tstfiles",
nargs="+",
type=str,
help="the test files you want to check code coverage for"
+ "(must be at least one)",
)
_PARSER.add_argument(
"--gap-root",
nargs="?",
type=str,
help="the gap root directory (default: ~/gap)",
default="~/gap/",
)
_PARSER.add_argument(
"--open",
nargs="?",
type=str,
help=("open the html page for this file (default: None)"),
default=None,
)

_ARGS = _PARSER.parse_args()
if not _ARGS.gap_root[-1] == "/":
_ARGS.gap_root += "/"

if exists("gap") and isdir("gap"):
_PROFILE_DIR = "/gap/"
elif exists("lib") and isdir("lib"):
_PROFILE_DIR = "/lib/"
else:
sys.exit(f"{_ERR_PREFIX}no directory gap or lib to profile!\033[0m")

_ARGS.gap_root = os.path.expanduser(_ARGS.gap_root)
if not (exists(_ARGS.gap_root) and isdir(_ARGS.gap_root)):
sys.exit(f"{_ERR_PREFIX}can't find GAP root directory!\033[0m")

for f in _ARGS.tstfiles:
if not (exists(f) and isfile(f)):
sys.exit(f"{_ERR_PREFIX}{f} does not exist!\033[0m")

_DIR = tempfile.mkdtemp()
print(f"{_INFO_PREFIX}Using temporary directory: {_DIR}\033[0m")

_COMMANDS = 'echo "'
_COMMANDS += "".join(rf"Test(\"{f}\");;\n" for f in _ARGS.tstfiles)
_COMMANDS += rf"""UncoverageLineByLine();;
LoadPackage(\"profiling\", false);;
filesdir := \"{getcwd()}{_PROFILE_DIR}\";;\n"""

_COMMANDS += rf"outdir := \"{_DIR}\";;\n"
_COMMANDS += rf"x := ReadLineByLineProfile(\"{_DIR}/profile.gz\");;\n"
_COMMANDS += 'OutputAnnotatedCodeCoverageFiles(x, filesdir, outdir);"'

_RUN_GAP = f"{_ARGS.gap_root}/gap -A -m 1g -T --cover {_DIR}/profile.gz"

with subprocess.Popen(_COMMANDS, stdout=subprocess.PIPE, shell=True) as pro1:
try:
with subprocess.Popen(_RUN_GAP, stdin=pro1.stdout, shell=True) as pro2:
pro2.wait()
except KeyboardInterrupt:
pro1.terminate()
pro1.wait()
sys.exit("\033[31mKilled!\033[0m")
except (subprocess.CalledProcessError, IOError, OSError):
sys.exit(_ERR_PREFIX + "Something went wrong calling GAP!\033[0m")


def rewrite_fname(fname: str) -> str:
return fname.replace("/", "_")


suffix = ""
if _ARGS.open:
filename = f"{_DIR}/{rewrite_fname(getcwd())}/{rewrite_fname(_ARGS.open)}.html"
p = re.compile(r"<tr class='missed'><td><a name=\"line(\d+)\">")
with open(filename, "r", encoding="utf-8") as f:
m = p.search(f.read())
if m:
suffix += "#line" + m.group(1)
else:
filename = _DIR + "/index.html"
print(f"{_INFO_PREFIX}\nSUCCESS!\033[0m")
print(f"{_INFO_PREFIX} See {filename}")
sys.exit(0)

0 comments on commit 4b5c792

Please sign in to comment.