Skip to content

Commit

Permalink
Merge pull request #191 from JuliaCI/jn/read-lcov
Browse files Browse the repository at this point in the history
Add support for reading LCOV files
  • Loading branch information
fingolfin authored Jan 2, 2019
2 parents 5e8e864 + 94a460e commit 6daa007
Show file tree
Hide file tree
Showing 13 changed files with 304 additions and 196 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ notifications:
email: false

script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia --check-bounds=yes etc/travis-test.jl
# submit coverage data, and collect new coverage data on that
- julia --code-coverage=user etc/travis-coverage.jl
Expand Down
98 changes: 98 additions & 0 deletions Manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# This file is machine-generated - editing it directly is not advised

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[BinaryProvider]]
deps = ["Libdl", "Pkg", "SHA", "Test"]
git-tree-sha1 = "055eb2690182ebc31087859c3dd8598371d3ef9e"
uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
version = "0.5.3"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"

[[HTTP]]
deps = ["Base64", "Dates", "Distributed", "IniFile", "MbedTLS", "Sockets", "Test"]
git-tree-sha1 = "b881f69331e85642be315c63d05ed65d6fc8a05b"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.7.1"

[[IniFile]]
deps = ["Test"]
git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.0"

[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[JSON]]
deps = ["Dates", "Distributed", "Mmap", "Sockets", "Test", "Unicode"]
git-tree-sha1 = "1f7a25b53ec67f5e9422f1f551ee216503f4a0fa"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.20.0"

[[LibGit2]]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[MbedTLS]]
deps = ["BinaryProvider", "Dates", "Libdl", "Random", "Sockets", "Test"]
git-tree-sha1 = "c93a87da4081a3de781f34e0540175795a2ce83d"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "0.6.6"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[Pkg]]
deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[Test]]
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
17 changes: 17 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name = "Coverage"
uuid = "a2441757-f6aa-5fb2-8edb-039e3f45d037"
authors = ["Iain Dunning <[email protected]>"]
version = "0.8.0"

[deps]
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433"
MbedTLS = "739be429-bea8-5141-9913-cc70e7f3736d"

[extras]
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Suppressor"]
31 changes: 14 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
Coverage.jl
===========

*Release version*:

[![Coverage](http://pkg.julialang.org/badges/Coverage_0.6.svg)](http://pkg.julialang.org/?pkg=Coverage)
[![Coverage](http://pkg.julialang.org/badges/Coverage_0.7.svg)](http://pkg.julialang.org/?pkg=Coverage)

*Development version*:

[![Build Status](https://travis-ci.org/JuliaCI/Coverage.jl.svg?branch=master)](https://travis-ci.org/JuliaCI/Coverage.jl)
[![Build status](https://ci.appveyor.com/api/projects/status/ooubhlayk9uek2kr/branch/master?svg=true)](https://ci.appveyor.com/project/ararslan/coverage-jl/branch/master)
[![Coverage Status](https://coveralls.io/repos/github/JuliaCI/Coverage.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaCI/Coverage.jl?branch=master)
Expand All @@ -23,24 +16,28 @@ Coverage.jl

### Code coverage

*Step 1:* Navigate to your test directory, and start julia like this:
*Step 1:* Navigate to your test directory, and run julia with the `--code-coverage` option:
```sh
julia --code-coverage=user
julia --code-coverage=tracefile-%p.info --code-coverage=user # available in Julia v1.1+
```
or, if you're running Julia 0.4 or higher,
```sh
julia --code-coverage=user --inline=no
```
(Turning off inlining gives substantially more accurate results, but may slow down your tests.)

*Step 2:* Run your tests (e.g., `include("runtests.jl")`) and quit Julia.

*Step 3:* Navigate to the top-level directory of your package, restart Julia (with no special flags) and analyze your code coverage:

```julia
using Coverage
# defaults to src/; alternatively, supply the folder name as argument
coverage = process_folder()
# process '*.cov' files
coverage = process_folder() # defaults to src/; alternatively, supply the folder name as argument
coverage = append!(coverage, process_folder("deps"))
# process '*.info' files
coverage = merge_coverage_counts(coverage, filter!(
let prefixes = (joinpath(pwd(), "src", ""),
joinpath(pwd(), "deps", ""))
c -> any(p -> startswith(c.filename, p), prefixes)
end,
LCOV.readfolder("test")))
# Get total coverage for all Julia files
covered_lines, total_lines = get_summary(coverage)
# Or process a single file
Expand All @@ -61,7 +58,7 @@ julia --track-allocation=user
```
Then:
- Run whatever commands you wish to test. This first run is to ensure that everything is compiled (because compilation allocates memory).
- Call `clear_malloc_data()` (or, if running julia 0.4 or higher, `Profile.clear_malloc_data()`)
- Call `Profile.clear_malloc_data()`)
- Run your commands again
- Quit julia

Expand All @@ -79,7 +76,7 @@ There are many tools to work with LCOV info-format files as generated by the `ge

```julia
coverage = process_folder()
LCOV.writefile("coverage/lcov.info", coverage)
LCOV.writefile("coverage-lcov.info", coverage)
```

### Cleaning up .cov files
Expand Down
4 changes: 0 additions & 4 deletions etc/travis-coverage.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
if VERSION < v"0.7.0"
cd(Pkg.dir("Coverage"))
end

using Coverage
cov_res = process_folder()
Coveralls.submit(cov_res)
Expand Down
9 changes: 2 additions & 7 deletions etc/travis-test.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
if VERSION < v"0.7.0"
Pkg.clone(pwd())
else
import Pkg
Pkg.develop(Pkg.PackageSpec(url=pwd()))
end

using Pkg
Pkg.build()
Pkg.test("Coverage"; coverage=true)
65 changes: 44 additions & 21 deletions src/Coverage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,20 @@
# https://github.com/JuliaCI/Coverage.jl
#######################################################################
module Coverage

# The code coverage results produced by Julia itself report some
# lines as "null" (cannot be run), when they could have been run
# but were not (should be 0). We use JuliaParser to augment the
# coverage results by identifying this code.
# this file coverage will be checked by test/data/expected.info

using LibGit2

export process_folder, process_file
export clean_folder, clean_file
export process_cov, amend_coverage_from_src!
export get_summary
export analyze_malloc, merge_coverage_counts
export FileCoverage

# The unit for line counts. Counts can be >= 0 or nothing, where
# the nothing means it doesn't make sense to have a count for this
# line (e.g. a comment), but 0 means it could have run but didn't.
const CovCount = Union{Nothing,Int}

export FileCoverage
"""
FileCoverage
Expand Down Expand Up @@ -58,6 +51,7 @@ module Coverage
end
return cov_lines, tot_lines
end

function get_summary(fcs::Vector{FileCoverage})
cov_lines, tot_lines = 0, 0
for fc in fcs
Expand All @@ -69,30 +63,57 @@ module Coverage
end

"""
merge_coverage_counts(a1::Vector{CovCount}, a2::Vector{CovCount})
merge_coverage_counts(a1::Vector{CovCount}, a2::Vector{CovCount}) -> Vector{CovCount}
Given two vectors of line coverage counts, take the pairwise
maximum of both vectors, preseving null counts if both are null.
Given two vectors of line coverage counts, sum together the results,
preseving null counts if both are null.
"""
function merge_coverage_counts(a1::Vector{CovCount},
a2::Vector{CovCount})
n = max(length(a1),length(a2))
n = max(length(a1), length(a2))
a = Vector{CovCount}(undef, n)
for i in 1:n
a1v = isassigned(a1, i) ? a1[i] : nothing
a2v = isassigned(a2, i) ? a2[i] : nothing
a[i] = a1v == nothing ? a2v :
a2v == nothing ? a1v : max(a1v, a2v)
a[i] = a1v === nothing ? a2v :
a2v === nothing ? a1v :
a1v + a2v
end
return a
end

"""
process_cov(filename, folder)
merge_coverage_counts(as::Vector{CovCount}...) -> Vector{CovCount}
Given vectors of line coverage counts, sum together the results,
preseving null counts if both are null.
"""
function merge_coverage_counts(as::Vector{FileCoverage}...)
source_files = FileCoverage[]
seen = Dict{AbstractString, FileCoverage}()
for a in as
for a in a
if a.filename in keys(seen)
coverage = seen[a.filename]
if isempty(coverage.source)
coverage.source = a.source
end
coverage.coverage = merge_coverage_counts(coverage.coverage, a.coverage)
else
coverage = FileCoverage(a.filename, a.source, a.coverage)
seen[a.filename] = coverage
push!(source_files, coverage)
end
end
end
return source_files
end

"""
process_cov(filename, folder) -> Vector{CovCount}
Given a filename for a Julia source file, produce an array of
line coverage counts by reading in all matching .{pid}.cov files.
On Julia 0.3 there was just a .cov file, but this code works fine.
"""
function process_cov(filename, folder)
# Find all coverage files in the folder that match the file we
Expand All @@ -106,10 +127,7 @@ module Coverage
# just never run. We'll report the coverage as all null.
println( """Coverage.process_cov: Coverage file(s) for $filename do not exist.
Assuming file has no coverage.""")
nlines = 0
for line in eachline(filename)
nlines += 1
end
nlines = countlines(filename)
return fill!(Vector{CovCount}(undef, nlines), nothing)
end
# Keep track of the combined coverage
Expand All @@ -136,6 +154,11 @@ module Coverage
a Julia code file, and updates the coverage vector in place.
"""
function amend_coverage_from_src!(coverage::Vector{CovCount}, srcname)
# The code coverage results produced by Julia itself report some
# lines as "null" (cannot be run), when they could have been run
# but were never compiled (thus should be 0).
# We use the Julia parser to augment the coverage results by identifying this code.
#
# To make sure things stay in sync, parse the file position
# corresonding to each new line
linepos = Int[]
Expand All @@ -159,7 +182,7 @@ module Coverage
if l > length(coverage)
error("source file is longer than .cov file; source might have changed")
end
if coverage[l] == nothing
if coverage[l] === nothing
coverage[l] = 0
end
end
Expand Down
Loading

0 comments on commit 6daa007

Please sign in to comment.