33# This source code is licensed under the MIT license found in the
44# LICENSE file in the root directory of this source tree.
55
6+ import dataclasses
7+ import json
68import logging
9+ import os
710from pathlib import Path
11+ from typing import Optional , Iterable , List
812
9- from ... import commands , configuration as configuration_module
13+ import libcst as cst
14+
15+ from ... import (
16+ commands ,
17+ configuration as configuration_module ,
18+ coverage_collector as collector ,
19+ log ,
20+ )
1021from . import remote_logging , statistics
1122
1223LOG : logging .Logger = logging .getLogger (__name__ )
@@ -22,13 +33,60 @@ def find_root(
2233 return working_directory
2334
2435
36+ @dataclasses .dataclass (frozen = True )
37+ class FileCoverage :
38+ filepath : str
39+ covered_lines : List [int ]
40+ uncovered_lines : List [int ]
41+
42+
43+ def collect_coverage_for_module (relative_path : str , module : cst .Module ) -> FileCoverage :
44+ module_with_metadata = cst .MetadataWrapper (module )
45+ coverage_collector = collector .CoverageCollector ()
46+ try :
47+ module_with_metadata .visit (coverage_collector )
48+ except RecursionError :
49+ LOG .warning (f"LibCST encountered recursion error in `{ relative_path } `" )
50+ return FileCoverage (
51+ filepath = relative_path ,
52+ covered_lines = sorted (coverage_collector .covered_lines ),
53+ uncovered_lines = sorted (coverage_collector .uncovered_lines ),
54+ )
55+
56+
57+ def collect_coverage_for_path (
58+ path : Path , working_directory : str
59+ ) -> Optional [FileCoverage ]:
60+ module = statistics .parse_path_to_module (path )
61+ relative_path = os .path .relpath (str (path ), working_directory )
62+ return (
63+ collect_coverage_for_module (relative_path , module )
64+ if module is not None
65+ else None
66+ )
67+
68+
69+ def collect_coverage_for_paths (
70+ paths : Iterable [Path ], working_directory : str
71+ ) -> List [FileCoverage ]:
72+ result : List [FileCoverage ] = []
73+ for path in paths :
74+ coverage = collect_coverage_for_path (path , working_directory )
75+ if coverage is not None :
76+ result .append (coverage )
77+ return result
78+
79+
2580def run_coverage (
2681 configuration : configuration_module .Configuration , working_directory : str
2782) -> commands .ExitCode :
28- sources = statistics .find_paths_to_parse (
29- [find_root (configuration , Path (working_directory ))]
83+ data = collect_coverage_for_paths (
84+ statistics .find_paths_to_parse (
85+ [find_root (configuration , Path (working_directory ))]
86+ ),
87+ working_directory ,
3088 )
31- LOG . warning ( f"SOURCES = { list ( sources ) } " )
89+ log . stdout . write ( json . dumps ([ dataclasses . asdict ( entry ) for entry in data ]) )
3290 return commands .ExitCode .SUCCESS
3391
3492
0 commit comments