diff options
-rw-r--r-- | CHANGES.rst | 3 | ||||
-rw-r--r-- | coverage/cmdline.py | 48 | ||||
-rw-r--r-- | coverage/data.py | 41 | ||||
-rw-r--r-- | tests/test_cmdline.py | 40 |
4 files changed, 89 insertions, 43 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 5aaa0c4a..65f0c21a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -28,6 +28,9 @@ Unreleased - Fix: The HTML report now will not overwrite a .gitignore file that already exists in the HTML output directory (follow-on for `issue 1244`_). +- Debug: The `coverage debug data` command will now sniff out combinable data + files, and report on all of them. + .. _changes_612: diff --git a/coverage/cmdline.py b/coverage/cmdline.py index e996ffff..f3a466e3 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -17,7 +17,7 @@ import coverage from coverage import Coverage from coverage import env from coverage.collector import CTracer -from coverage.data import line_counts +from coverage.data import CoverageData, combinable_files, line_counts from coverage.debug import info_formatter, info_header, short_stack from coverage.exceptions import BaseCoverageException, ExceptionDuringRun, NoSource from coverage.execfile import PyRunner @@ -601,8 +601,8 @@ class CoverageScript: elif options.action == "combine": if options.append: self.coverage.load() - data_dirs = args or None - self.coverage.combine(data_dirs, strict=True, keep=bool(options.keep)) + data_paths = args or None + self.coverage.combine(data_paths, strict=True, keep=bool(options.keep)) self.coverage.save() return OK @@ -786,23 +786,12 @@ class CoverageScript: for line in info_formatter(sys_info): print(f" {line}") elif info == 'data': - self.coverage.load() - data = self.coverage.get_data() print(info_header("data")) - print(f"path: {data.data_filename()}") - if data: - print(f"has_arcs: {data.has_arcs()!r}") - summary = line_counts(data, fullpath=True) - filenames = human_sorted(summary.keys()) - print(f"\n{len(filenames)} files:") - for f in filenames: - line = f"{f}: {summary[f]} lines" - plugin = data.file_tracer(f) - if plugin: - line += f" [{plugin}]" - print(line) - else: - print("No data collected") + data_file = self.coverage.config.data_file + self.do_debug_data_file(data_file) + for filename in combinable_files(data_file): + print("-----") + self.do_debug_data_file(filename) elif info == 'config': print(info_header("config")) config_info = self.coverage.config.__dict__.items() @@ -817,6 +806,27 @@ class CoverageScript: return OK + def do_debug_data_file(self, filename): + """Implementation of 'coverage debug data'.""" + data = CoverageData(filename) + filename = data.data_filename() + print(f"path: {filename}") + if not os.path.exists(filename): + print("No data collected: file doesn't exist") + return + data.read() + print(f"has_arcs: {data.has_arcs()!r}") + summary = line_counts(data, fullpath=True) + filenames = human_sorted(summary.keys()) + nfiles = len(filenames) + print(f"{nfiles} file{'' if nfiles == 1 else 's'}:") + for f in filenames: + line = f"{f}: {summary[f]} line{'' if summary[f] == 1 else 's'}" + plugin = data.file_tracer(f) + if plugin: + line += f" [{plugin}]" + print(line) + def unshell_list(s): """Turn a command-line argument into a list.""" diff --git a/coverage/data.py b/coverage/data.py index 68ba7ec3..e7c94b4f 100644 --- a/coverage/data.py +++ b/coverage/data.py @@ -53,11 +53,36 @@ def add_data_to_hash(data, filename, hasher): hasher.update(data.file_tracer(filename)) +def combinable_files(data_file, data_paths=None): + """Make a list of data files to be combined. + + `data_file` is a path to a data file. `data_paths` is a list of files or + directories of files. + + Returns a list of absolute file paths. + """ + data_dir, local = os.path.split(os.path.abspath(data_file)) + + data_paths = data_paths or [data_dir] + files_to_combine = [] + for p in data_paths: + if os.path.isfile(p): + files_to_combine.append(os.path.abspath(p)) + elif os.path.isdir(p): + pattern = os.path.join(os.path.abspath(p), f"{local}.*") + files_to_combine.extend(glob.glob(pattern)) + else: + raise CoverageException(f"Couldn't combine from non-existent path '{p}'") + return files_to_combine + + def combine_parallel_data( data, aliases=None, data_paths=None, strict=False, keep=False, message=None, ): """Combine a number of data files together. + `data` is a CoverageData. + Treat `data.filename` as a file prefix, and combine the data from all of the data files starting with that prefix plus a dot. @@ -79,21 +104,7 @@ def combine_parallel_data( raised. """ - # Because of the os.path.abspath in the constructor, data_dir will - # never be an empty string. - data_dir, local = os.path.split(data.base_filename()) - localdot = local + '.*' - - data_paths = data_paths or [data_dir] - files_to_combine = [] - for p in data_paths: - if os.path.isfile(p): - files_to_combine.append(os.path.abspath(p)) - elif os.path.isdir(p): - pattern = os.path.join(os.path.abspath(p), localdot) - files_to_combine.extend(glob.glob(pattern)) - else: - raise CoverageException(f"Couldn't combine from non-existent path '{p}'") + files_to_combine = combinable_files(data.base_filename(), data_paths) if strict and not files_to_combine: raise CoverageException("No data to combine") diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 1d87c19a..112e5d68 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -817,24 +817,46 @@ class CmdLineWithFilesTest(BaseCmdLineTest): data.write() self.command_line("debug data") - assert self.stdout() == textwrap.dedent("""\ + assert self.stdout() == textwrap.dedent(f"""\ -- data ------------------------------------------------------ - path: FILENAME + path: {data.data_filename()} has_arcs: False - 2 files: file1.py: 17 lines [a_plugin] file2.py: 23 lines - """).replace("FILENAME", data.data_filename()) + """) - def test_debug_data_with_no_data(self): + def test_debug_data_with_no_data_file(self): data = CoverageData() self.command_line("debug data") - assert self.stdout() == textwrap.dedent("""\ + assert self.stdout() == textwrap.dedent(f"""\ -- data ------------------------------------------------------ - path: FILENAME - No data collected - """).replace("FILENAME", data.data_filename()) + path: {data.data_filename()} + No data collected: file doesn't exist + """) + + def test_debug_combinable_data(self): + data1 = CoverageData() + data1.add_lines({"file1.py": range(1, 18), "file2.py": [1]}) + data1.write() + data2 = CoverageData(suffix="123") + data2.add_lines({"file2.py": range(1, 10)}) + data2.write() + + self.command_line("debug data") + assert self.stdout() == textwrap.dedent(f"""\ + -- data ------------------------------------------------------ + path: {data1.data_filename()} + has_arcs: False + 2 files: + file1.py: 17 lines + file2.py: 1 line + ----- + path: {data2.data_filename()} + has_arcs: False + 1 file: + file2.py: 9 lines + """) class CmdLineStdoutTest(BaseCmdLineTest): |