Skip to content

Commit

Permalink
Merge branch 'main' of github.com:jaltmayerpizzorno/slipcover into main
Browse files Browse the repository at this point in the history
  • Loading branch information
jaltmayerpizzorno committed Oct 27, 2023
2 parents 870b3f6 + dd88861 commit 7ceba76
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 4 deletions.
8 changes: 5 additions & 3 deletions src/slipcover/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def main():
file_matcher = sc.FileMatcher()

if args.source:
for s in args.source.split(','):
args.source = args.source.split(',')
for s in args.source:
file_matcher.addSource(s)
elif args.script:
file_matcher.addSource(Path(args.script).resolve().parent)
Expand All @@ -72,8 +73,9 @@ def main():


sci = sc.Slipcover(immediate=args.immediate,
d_miss_threshold=args.threshold, branch=args.branch,
skip_covered=args.skip_covered, disassemble=args.dis)
d_miss_threshold=args.threshold, branch=args.branch,
skip_covered=args.skip_covered, disassemble=args.dis,
source=args.source)


if not args.dont_wrap_pytest:
Expand Down
34 changes: 33 additions & 1 deletion src/slipcover/slipcover.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ def simplify(self, path : str) -> str:
class Slipcover:
def __init__(self, immediate: bool = False,
d_miss_threshold: int = 50, branch: bool = False, skip_covered: bool = False,
disassemble: bool = False):
disassemble: bool = False, source: List[str] = None):
self.immediate = immediate
self.d_miss_threshold = d_miss_threshold
self.branch = branch
self.skip_covered = skip_covered
self.disassemble = disassemble
self.source = source

# mutex protecting this state
self.lock = threading.RLock()
Expand Down Expand Up @@ -312,6 +313,34 @@ def deinstrument(self, co, lines: set) -> types.CodeType:
return new_code


def _add_unseen_source_files(self):
import ast

dirs = [Path(d) for d in self.source]

while dirs:
p = dirs.pop()
for file in p.iterdir():
if file.is_dir():
dirs.append(file) # walk this directory, too

elif file.is_file() and file.suffix.lower() == '.py':
file = file.absolute()
filename = str(file)
try:
if filename not in self.code_lines:
t = ast.parse(file.read_text())
if self.branch:
t = br.preinstrument(t)
code = compile(t, filename, "exec")
self.code_lines[filename] = set(Slipcover.lines_from_code(code))
if self.branch:
self.code_branches[filename] = set(Slipcover.branches_from_code(code))

except Exception as e: # for SyntaxError and such... FIXME curate list and catch only those
print(f"Warning: unable to include {filename}: {e}")


def get_coverage(self):
"""Returns coverage information collected."""

Expand All @@ -322,6 +351,9 @@ def get_coverage(self):
for file, lines in newly_seen.items():
self.all_seen[file].update(lines)

if self.source:
self._add_unseen_source_files()

simp = PathSimplifier()

files = dict()
Expand Down
3 changes: 3 additions & 0 deletions tests/imported/bad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# this file has a syntax error that prevents compilation.
dev bar():
pass
5 changes: 5 additions & 0 deletions tests/imported/foo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def also_does_stuff():
x = 1
for i in range(10):
x += i
return x
1 change: 1 addition & 0 deletions tests/imported/subdir/baz.PY
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
baz = True
36 changes: 36 additions & 0 deletions tests/test_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,3 +651,39 @@ def test_fail_under(json_flag):

p = subprocess.run(f"{sys.executable} -m slipcover {json_flag} --branch --fail-under 84 tests/branch.py".split(), check=False)
assert 2 == p.returncode


def test_reports_on_other_sources(tmp_path):
from pathlib import Path
import subprocess
import json

out_file = tmp_path / "out.json"

subprocess.run((f"{sys.executable} -m slipcover --branch --json --out {out_file} " +\
f"--source tests/imported tests/importer.py").split(),
check=True)
with open(out_file, "r") as f:
cov = json.load(f)

init_file = str(Path('tests') / 'imported' / '__init__.py')
foo_file = str(Path('tests') / 'imported' / 'foo.py')
baz_file = str(Path('tests') / 'imported' / 'subdir' / 'baz.PY')

assert init_file in cov['files']
assert [1,2,3,4,5,6,8] == cov['files'][init_file]['executed_lines']
assert [9] == cov['files'][init_file]['missing_lines']
assert [[3,4], [4,5], [4,6]] == cov['files'][init_file]['executed_branches']
assert [[3,6]] == cov['files'][init_file]['missing_branches']

assert foo_file in cov['files']
assert [] == cov['files'][foo_file]['executed_lines']
assert [1, 2, 3, 4, 5] == cov['files'][foo_file]['missing_lines']
assert [] == cov['files'][foo_file]['executed_branches']
assert [[3,4], [3,5]] == cov['files'][foo_file]['missing_branches']

assert baz_file in cov['files']
assert [] == cov['files'][baz_file]['executed_lines']
assert [1] == cov['files'][baz_file]['missing_lines']
assert [] == cov['files'][baz_file]['executed_branches']
assert [] == cov['files'][baz_file]['missing_branches']

0 comments on commit 7ceba76

Please sign in to comment.