diff --git a/Project.toml b/Project.toml index d161a8a..9099d77 100644 --- a/Project.toml +++ b/Project.toml @@ -1,13 +1,14 @@ name = "ExtendedLocalCoverage" uuid = "eb248270-a497-541c-8f75-6bb2aa2715dc" +version = "0.1.2" authors = ["Alberto Mengali "] -version = "0.1.1" [deps] CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab" CoverageTools = "c36e975a-824b-4404-a568-ef97ca766997" LocalCoverage = "5f6e1e16-694c-5876-87ef-16b5274f298e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" @@ -17,6 +18,7 @@ CondaPkg = "0.2.24" CoverageTools = "1.3.2" LocalCoverage = "0.8.2" Pkg = "1" +PrettyTables = "2, 3" PythonCall = "0.9.23" Revise = "3.7.1" TOML = "1.0.3" diff --git a/README.md b/README.md index fb27b42..4677d7e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ [![Lint workflow Status](https://github.com/disberd/ExtendedLocalCoverage.jl/actions/workflows/Lint.yml/badge.svg?branch=main)](https://github.com/disberd/ExtendedLocalCoverage.jl/actions/workflows/Lint.yml?query=branch%3Amain) [![Docs workflow Status](https://github.com/disberd/ExtendedLocalCoverage.jl/actions/workflows/Docs.yml/badge.svg?branch=main)](https://github.com/disberd/ExtendedLocalCoverage.jl/actions/workflows/Docs.yml?query=branch%3Amain) [![Coverage](https://codecov.io/gh/disberd/ExtendedLocalCoverage.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/disberd/ExtendedLocalCoverage.jl) -[![DOI](https://zenodo.org/badge/DOI/FIXME)](https://doi.org/FIXME) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) [![All Contributors](https://img.shields.io/github/all-contributors/disberd/ExtendedLocalCoverage.jl?labelColor=5e1ec7&color=c0ffee&style=flat-square)](#contributors) [![BestieTemplate](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/JuliaBesties/BestieTemplate.jl/main/docs/src/assets/badge.json)](https://github.com/JuliaBesties/BestieTemplate.jl) @@ -17,6 +16,9 @@ This package simply extends the functionality of [LocalCoverage.jl](https://gith - Automatically create an xml cobertura coverage and an html report using `pycobertura`. - The `pycobertura` library is automatically installed and used thanks to `CondaPkg.jl` and `PythonCall.jl`, not requiring the user to manually install python or `lcov` which is only available on non-windows systems. +This package was mainly created to provide easier access to code coverage within a private self-hosted gitlab instance, where codecov can not be used as is, but an HTML coverage report can be directly integrated within the gitlab web interface. + +It is also convenient for getting an HTML coverage report locally without always relying on codecov from GH actions. You can see an example of the generated HTML coverage report [here](https://disberd.github.io/ExtendedLocalCoverage.jl/coverage_example/), which was generated by calling `generate_package_coverage(; force_paths_relative = true)` on the [PlutoPlotly.jl](https://github.com/JuliaPluto/PlutoPlotly.jl) package. @@ -35,7 +37,7 @@ Pkg.add(url="https://github.com/disberd/ExtendedLocalCoverage.jl") For the basic usage which automatically extracts source files (including extensions) and generates the coverage report both in xml and html format, you can simply do the following (within the env of the package you are developing and making sure you have ExtendedLocalCoverage.jl in the `LOAD_PATH`): ```julia using ExtendedLocalCoverage -generate_package_coverage() +cov_data = generate_package_coverage(); ``` See the [developer documentation](https://disberd.github.io/ExtendedLocalCoverage.jl/dev) for more details. diff --git a/src/ExtendedLocalCoverage.jl b/src/ExtendedLocalCoverage.jl index b3a2f3c..4b36a50 100644 --- a/src/ExtendedLocalCoverage.jl +++ b/src/ExtendedLocalCoverage.jl @@ -10,6 +10,9 @@ import Pkg export generate_package_coverage, generate_html_report +# This is a temporary fix to fix PrettyTables issues until https://github.com/JuliaCI/LocalCoverage.jl/pull/68 is merged. +include("show_fix.jl") + function extract_package_info(pkg_dir) project_toml = TOML.tryparsefile(joinpath(pkg_dir, "Project.toml")) pkg_name = project_toml["name"] @@ -105,7 +108,7 @@ function generate_package_coverage(pkg = nothing; use_existing_lcov = false, run return true end LocalCoverage.generate_coverage(pkg; run_test, test_args, folder_list=[], file_list) - end + end |> WrappedPackageCoverage if print_to_stdout show(IOContext(stdout, :print_gaps => true), cov) end diff --git a/src/show_fix.jl b/src/show_fix.jl new file mode 100644 index 0000000..4bc2690 --- /dev/null +++ b/src/show_fix.jl @@ -0,0 +1,89 @@ +using LocalCoverage: PackageCoverage, format_line +using PrettyTables: PrettyTables, pretty_table + +""" + WrappedPackageCoverage(summary::PackageCoverage) + +Structure wrapping the `PackageCoverage` struct to add a custom `show` method for fixing PrettyTables issues until https://github.com/JuliaCI/LocalCoverage.jl/pull/68 is merged. +""" +struct WrappedPackageCoverage + summary::PackageCoverage +end + +function Base.show(io::IO, wrapped::WrappedPackageCoverage) + (; summary) = wrapped + (; files, package_dir) = summary + row_data = map(format_line, files) + push!(row_data, format_line(summary)) + row_coverage = map(x -> x.coverage_percentage, row_data) + rows = map(row_data) do row + (; name, total, hit, missed, coverage_percentage, gaps) = row + percentage = isnan(coverage_percentage) ? "-" : "$(round(Int, coverage_percentage))%" + (; name, total, hit, missed, percentage, gaps) + end + header = ["Filename", "Lines", "Hit", "Miss", "%"] + percentage_column = length(header) + alignment = [:l, :r, :r, :r, :r] + columns_width = fill(-1, 5) # We need strictly negative number to autosize in PrettyTables 3.0, but this also works in v2 + if get(io, :print_gaps, false) + push!(header, "Gaps") + push!(alignment, :l) + display_cols = last(get(io, :displaysize, 100)) + push!(columns_width, display_cols - 45) + else + rows = map(row -> Base.structdiff(row, NamedTuple{(:gaps,)}), rows) + end + # PrettyTables 3.0 changed Highlighter to TextHighlighter, which up to currently published version (v3.10) does not provide the kwargs constructor (despite having it documented). We create here a patch to handle both cases + Highlighter(f; kwargs...) = @static if pkgversion(PrettyTables) < v"3.0.0" + PrettyTables.Highlighter(f; kwargs...) + else + PrettyTables.TextHighlighter(f, PrettyTables.Crayon(;kwargs...)) + end + + highlighters = ( + Highlighter( + (data, i, j) -> j == percentage_column && row_coverage[i] <= 50, + bold = true, + foreground = :red, + ), + Highlighter((data, i, j) -> j == percentage_column && row_coverage[i] <= 70, + foreground = :yellow), + Highlighter((data, i, j) -> j == percentage_column && row_coverage[i] >= 90, + foreground = :green), + ) + + # Kwargs of `pretty_table` itself also changed in PrettyTables 3.0, so we have to branch here as well + @static if pkgversion(PrettyTables) < v"3.0.0" + pretty_table( + io, + rows; + title = "Coverage of $(package_dir)", + header, + alignment, + crop = :none, + linebreaks = true, + columns_width, + autowrap = true, + highlighters, + body_hlines = [length(rows) - 1], + ) + else + pretty_table( + io, + rows; + title = "Coverage of $(package_dir)", + column_labels = [header], + alignment, + # The crop kwarg is not present anymore, split into the next two ones + fit_table_in_display_horizontally = false, + fit_table_in_display_vertically = false, + line_breaks = true, + fixed_data_column_widths = columns_width, + auto_wrap = true, + highlighters = collect(highlighters), # v3 expects a vector instead of a Tuple + table_format = PrettyTables.TextTableFormat(; + horizontal_lines_at_data_rows = [length(rows) - 1], + ), + ) + end +end \ No newline at end of file