1:- module(canny_cover,
    2          [ coverages_by_module/2,      % :Goal,-Coverages:dict
    3            coverage_for_modules/4      % :Goal,+Modules,-Module,-Coverage
    4          ]).    5:- autoload(library(apply), [convlist/3]).    6:- autoload(library(strings), [string_lines/2]).    7:- autoload(library(test_cover), [show_coverage/1]).    8:- autoload(library(yall), [(>>)/4]).    9:- autoload(library(dcg/basics), [whites/2, integer/3, number/3, string/3]).
 coverages_by_module(:Goal, -Coverages:dict) is det
Calls Goal within show_coverage/1 while capturing the resulting lines of output; Goal is typically run_tests/0 for running all loaded tests. Parses the lines for coverage statistics by module. Ignores lines that do not represent coverage, and also ignores lines that cover non-module files. Automatically matches prefix-truncated coverage paths as well as full paths.
Arguments:
Coverages- is a module-keyed dictionary of sub-dictionaries carrying three keys: clauses, cov and fail.
   23coverages_by_module(Goal, Coverages) :-
   24    with_output_to(string(String), show_coverage(Goal)),
   25    string_lines(String, Lines),
   26    convlist([Line, Module=coverage{
   27                               clauses:Clauses,
   28                               cov:Cov,
   29                               fail:Fail
   30                           }]>>
   31             (   string_codes(Line, Codes),
   32                 phrase(cover_line(Module, Clauses, Cov, Fail), Codes)
   33             ), Lines, Data),
   34    dict_create(Coverages, coverages, Data).
   35
   36cover_line(Module, Clauses, Cov, Fail) -->
   37    cover_file(Module),
   38    whites,
   39    integer(Clauses),
   40    whites,
   41    number(Cov),
   42    whites,
   43    number(Fail).
   44
   45cover_file(Module) -->
   46    "...",
   47    !,
   48    { module_property(Module, file(File)),
   49      sub_atom(File, _, _, 0, Suffix),
   50      atom_codes(Suffix, Codes)
   51    },
   52    string(Codes).
   53cover_file(Module) -->
   54    { module_property(Module, file(File)),
   55      atom_codes(File, Codes)
   56    },
   57    string(Codes).
 coverage_for_modules(:Goal, +Modules, -Module, -Coverage) is nondet
Non-deterministically finds Coverage dictionaries for all Modules. Bypasses those modules excluded from the required list, typically the list of modules belonging to a particular pack and excluding all system and other supporting modules.
   66coverage_for_modules(Goal, Modules, Module, Coverage) :-
   67    coverages_by_module(Goal, Coverages),
   68    Coverage = Coverages.Module,
   69    memberchk(Module, Modules)