Skip to content

Commit

Permalink
Add the initial functionality (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
DilumAluthge committed Jan 8, 2020
1 parent ccb7c2c commit 34b3496
Show file tree
Hide file tree
Showing 18 changed files with 597 additions and 2 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,45 @@
[![Build Status](https://travis-ci.com/bcbi/MicroCoverage.jl.svg?branch=master)](https://travis-ci.com/bcbi/MicroCoverage.jl/branches)
[![Codecov](https://codecov.io/gh/bcbi/MicroCoverage.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/bcbi/MicroCoverage.jl)
[![Coveralls](https://coveralls.io/repos/github/bcbi/MicroCoverage.jl/badge.svg?branch=master)](https://coveralls.io/github/bcbi/MicroCoverage.jl?branch=master)

## Example

`foo.jl`:
```julia
function foo(x)
if x == 1 || x == 100
return "hello"
else
return "goodbye"
end
end
```

`test_foo.jl`:
```julia
using Test

@test foo(1) == "hello"
```

```julia
julia> using MicroCoverage

julia> MicroCoverage.start("foo.jl")

julia> include("foo.jl")

julia> include("test_foo.jl")

julia> MicroCoverage.stop()
```

## Acknowledgements

- This work was supported in part by National Institutes of Health grants U54GM115677, R01LM011963, and R25MH116440. The content is solely the responsibility of the authors and does not necessarily represent the official views of the National Institutes of Health.

## Related Work

1. [https://github.com/StephenVavasis/microcoverage](https://github.com/StephenVavasis/microcoverage)
2. [https://github.com/JuliaCI/Coverage.jl](https://github.com/JuliaCI/Coverage.jl)
3. [https://github.com/JuliaCI/CoverageTools.jl](https://github.com/JuliaCI/CoverageTools.jl)
17 changes: 16 additions & 1 deletion src/MicroCoverage.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
module MicroCoverage

greet() = print("Hello World!")
include("types.jl")

include("public_interface.jl")

include("assert.jl")
include("clean.jl")
include("coverage_compute.jl")
include("coverage_write.jl")
include("instrument.jl")
include("preview_coverage.jl")
include("read_write_julia_files.jl")
include("restore.jl")
include("start_tracking_file.jl")
include("tracked_files.jl")
include("tracker.jl")
include("utils.jl")

end # end module MicroCoverage
8 changes: 8 additions & 0 deletions src/assert.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function always_assert(condition::Bool,
msg::AbstractString = "")::Nothing
_msg::String = convert(String, msg)::String
if !condition
throw(AlwaysAssertionError(_msg))
end
return nothing
end
17 changes: 17 additions & 0 deletions src/clean.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function _clean_file(filename::String)
coverage_filename = string(filename, ".microcov")
rm(coverage_filename; force = true, recursive = true)
return nothing
end

function _clean_directory(path::String)
microcov_file_suffix = ".microcov"
for (root, dirs, files) in walkdir(".")
for file in files
if endswith(file, microcov_file_suffix)
rm(joinpath(root, file); force = true, recursive = true)
end
end
end
return nothing
end
53 changes: 53 additions & 0 deletions src/coverage_compute.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
function _compute_lnn_to_coverage()
lock(Main.MicroCoverage_lock) do
lnn_to_tracker_indices = Dict{LineNumberNode, Vector{Int}}()
for tracker_idx = 1:length(Main.MicroCoverage_tracker_linenumbernodes)
lnn = Main.MicroCoverage_tracker_linenumbernodes[tracker_idx]
if !haskey(lnn_to_tracker_indices, lnn)
lnn_to_tracker_indices[lnn] = Vector{Int}()
end
push!(lnn_to_tracker_indices[lnn], tracker_idx)
end
lnn_to_coverage = Dict{LineNumberNode, Vector{Bool}}()
for k in keys(lnn_to_tracker_indices)
tracker_indices = lnn_to_tracker_indices[k]
lnn_to_coverage[k] = Main.MicroCoverage_tracker[tracker_indices]
end
return lnn_to_coverage
end
end

function _compute_coverage_file_contents(filename::String;
min_padding::Integer,
max_padding::Integer,
source_code_filename::AbstractString = filename)::Vector{String}
original_source_string::String = read(source_code_filename, String)::String
original_source_lines = split(original_source_string, '\n')
num_lines = length(original_source_lines)
lnn_to_coverage = _compute_lnn_to_coverage()
coverage_lines_prefixes = Vector{String}(undef, num_lines)
for i = 1:num_lines
coverage_lines_prefixes[i] = _coverage_boolvector_to_string(get(lnn_to_coverage,
LineNumberNode(i,
filename), Bool[]))
end
coverage_lines = Vector{String}(undef, num_lines)
for i = 1:num_lines
this_line_coverage_prefix = coverage_lines_prefixes[i]
this_line_padding_length = max(min_padding,
max_padding - length(this_line_coverage_prefix))
this_line_padding = repeat(" ",
max(1, this_line_padding_length))
coverage_lines[i] = string(this_line_coverage_prefix,
this_line_padding,
original_source_lines[i])
end
return coverage_lines
end

function _coverage_boolvector_to_string(line_coverage::Vector{Bool})
if length(line_coverage) < 1
return "-"
end
return string("[", join(string.(convert(Vector{Int}, line_coverage)), ","), "]")
end
30 changes: 30 additions & 0 deletions src/coverage_write.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function _write_coverage(filename::String;
dump_coverage::Bool = false,
dump_coverage_io::IO = stdout,
min_padding::Integer,
max_padding::Integer)::Nothing
coverage_filename = string(filename, ".microcov")
coverage_lines = _compute_coverage_file_contents(filename;
min_padding = min_padding,
max_padding = max_padding)
@debug("Writing coverage to file",
source_file = filename,
coverage_file = coverage_filename)
rm(coverage_filename; force = true, recursive = true)
open(coverage_filename, "w") do io
if dump_coverage
# println(dump_coverage_io, "\n")
println(dump_coverage_io, "# $(coverage_filename):")
end
for x in coverage_lines
if dump_coverage
println(dump_coverage_io, x)
end
println(io, x)
end
if dump_coverage
# println(dump_coverage_io, "\n")
end
end
return nothing
end
196 changes: 196 additions & 0 deletions src/instrument.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# This is the generic fallback
# function instrument(filename::String,
# expr::Expr,
# ::Val,
# lnn::LineNumberNode;
# prepend_tracker::Bool = true)
# return insert_prepended_tracker(filename,
# expr,
# lnn;
# prepend_tracker = prepend_tracker)
# end

function instrument(filename::String,
lnn_1::LineNumberNode,
lnn_2::LineNumberNode;
prepend_tracker::Bool = true)
always_assert(lnn_1 == lnn_2, "lnn_1 == lnn_2")
return lnn_1
end

function instrument(filename::String,
expr::Expr;
prepend_tracker::Bool = true)
return instrument(filename,
expr,
Val(expr.head);
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
expr::Expr,
lnn::LineNumberNode;
prepend_tracker::Bool = true)
return instrument(filename,
expr,
Val(expr.head),
lnn;
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
expr::Expr,
::Val{:block};
prepend_tracker::Bool = true)
original_args = expr.args
always_assert(original_args isa Vector, "original_args isa Vector")
if length(original_args) == 0
return expr
end
always_assert(length(original_args) >= 1, "length(original_args) >= 1")
always_assert( original_args[1] isa LineNumberNode,
"original_args[1] isa LineNumberNode")
return instrument(filename,
expr,
Val(:block),
original_args[1];
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
expr::Expr,
::Val{:block},
lnn::LineNumberNode;
prepend_tracker::Bool = true)
original_args = expr.args
num_args = length(original_args)
arg_to_lnn = Vector{LineNumberNode}(undef, num_args)
current_lnn = lnn
arg_to_lnn[1] = current_lnn
for i = 1:num_args
if original_args[i] isa LineNumberNode
current_lnn = original_args[i]
end
arg_to_lnn[i] = current_lnn
end
new_args = Vector{Any}(undef, num_args)
for i = 1:num_args
new_args[i] = instrument(filename, original_args[i], arg_to_lnn[i])
end
return insert_prepended_tracker(filename,
Expr(:block, new_args...),
lnn;
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
expr::Expr,
::Val{:function},
lnn::LineNumberNode;
prepend_tracker::Bool = true)
original_args = expr.args
num_args = length(original_args)
new_args = Vector{Any}(undef, num_args)
new_args[1] = original_args[1]
for i = 2:num_args
new_args[i] = instrument(filename, original_args[i], lnn)
end
return insert_prepended_tracker(filename,
Expr(:function, new_args...),
lnn;
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
expr::Expr,
::Val{:if},
lnn::LineNumberNode;
prepend_tracker::Bool = true)
original_args = expr.args
num_args = length(original_args)
new_args = Vector{Any}(undef, num_args)
new_args[1] = instrument(filename,
original_args[1],
lnn;
prepend_tracker = false)
for i = 2:num_args
new_args[i] = instrument(filename,
original_args[i],
lnn;
prepend_tracker = prepend_tracker)
end
return insert_prepended_tracker(filename,
Expr(:if, new_args...),
lnn;
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
expr::Expr,
::Val{:||},
lnn::LineNumberNode;
prepend_tracker::Bool = true)
original_args = expr.args
num_args = length(original_args)
new_args = Vector{Any}(undef, num_args)
for i = 1:num_args
new_args[i] = instrument(filename, original_args[i], lnn)
end
return insert_prepended_tracker(filename,
Expr(:||, new_args...),
lnn;
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
expr::Expr,
::Val{:call},
lnn::LineNumberNode;
prepend_tracker::Bool = true)
original_args = expr.args
num_args = length(original_args)
new_args = Vector{Any}(undef, num_args)
new_args[1] = original_args[1]
for i = 2:num_args
new_args[i] = instrument(filename, original_args[i], lnn)
end
return insert_prepended_tracker(filename,
Expr(:call, new_args...),
lnn;
prepend_tracker = prepend_tracker)
end

function instrument(filename::String,
sym::Symbol,
lnn::LineNumberNode)
return sym
end

function instrument(filename::String,
str::AbstractString,
lnn::LineNumberNode)
return str
end

function instrument(filename::String,
n::Number,
lnn::LineNumberNode)
return n
end

function instrument(filename::String,
expr::Expr,
::Val{:return},
lnn::LineNumberNode;
prepend_tracker::Bool = true)
original_args = expr.args
num_args = length(original_args)
new_args = Vector{Any}(undef, num_args)
for i = 1:num_args
new_args[i] = instrument(filename, original_args[i], lnn)
end
return insert_prepended_tracker(filename,
Expr(:return, new_args...),
lnn;
prepend_tracker = prepend_tracker)
end
21 changes: 21 additions & 0 deletions src/preview_coverage.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
function preview_coverage(; dump_coverage_io::IO = stdout,
min_padding::Integer = 1,
max_padding::Integer = 24)
setup()
all_tracked_files = tracked_files()
for x in all_tracked_files
backup_filename = string(x, ".backup")
coverage_filename = string(x, ".microcov")
coverage_lines = _compute_coverage_file_contents(x;
min_padding = min_padding,
max_padding = max_padding,
source_code_filename = backup_filename)
# println(dump_coverage_io, "\n")
println(dump_coverage_io, "# Preview of $(coverage_filename):")
for x in coverage_lines
println(dump_coverage_io, x)
end
# println(dump_coverage_io, "\n")
end
return nothing
end

0 comments on commit 34b3496

Please sign in to comment.