summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sailcov/main.ml163
1 files changed, 144 insertions, 19 deletions
diff --git a/sailcov/main.ml b/sailcov/main.ml
index d16fe9ba..2ca765e4 100644
--- a/sailcov/main.ml
+++ b/sailcov/main.ml
@@ -7,6 +7,8 @@ let opt_all = ref "all_branches"
let opt_tab_width = ref 4
+let opt_index = ref None
+
type color = {
hue: int;
saturation: int;
@@ -28,12 +30,21 @@ let options =
( "-t",
Arg.String (fun str -> opt_taken := str),
"<file> coverage information for branches taken while executing the model (default: sail_coverage)");
+ ( "--taken",
+ Arg.String (fun str -> opt_taken := str),
+ " long form of -t");
( "-a",
Arg.String (fun str -> opt_all := str),
"<file> information about all possible branches (default: all_branches)");
+ ( "--all",
+ Arg.String (fun str -> opt_all := str),
+ " long form of -a");
( "--tab-width",
Arg.Int (fun n -> opt_tab_width := n),
"<integer> set the tab width for html output (default: 4)");
+ ( "--index",
+ Arg.String (fun str -> opt_index := Some str),
+ " create an index.html page");
( "--covered-hue",
Arg.Int (fun n -> opt_good_color := { !opt_good_color with hue = clamp_degree n }),
"<int> set the hue (between 0 and 360) of the color used for code that is covered (default: 120 (green))");
@@ -65,6 +76,9 @@ type span = {
c2 : int;
}
+let string_of_span span =
+ Printf.sprintf "\"%s\" %d:%d - %d:%d" span.file span.l1 span.c1 span.l2 span.c2
+
module Span = struct
type t = span
let compare s1 s2 = compare s1 s2
@@ -96,18 +110,27 @@ let read_coverage filename =
type source_char = {
mutable badness : int;
mutable goodness : int;
+ mutable bad_zero_width: bool;
char : char;
}
+let zero_width span = span.l1 = span.l2 && span.c1 = span.c2
+
let mark_bad_region source span =
- source.(span.l1 - 1).(span.c1).badness <- source.(span.l1 - 1).(span.c1).badness + 1;
- source.(span.l2 - 1).(span.c2 - 1).badness <- source.(span.l2 - 1).(span.c2 - 1).badness - 1
+ if zero_width span then (
+ source.(span.l2 - 1).(span.c2 - 1).bad_zero_width <- true
+ ) else (
+ source.(span.l1 - 1).(span.c1).badness <- source.(span.l1 - 1).(span.c1).badness + 1;
+ source.(span.l2 - 1).(span.c2 - 1).badness <- source.(span.l2 - 1).(span.c2 - 1).badness - 1
+ )
let mark_good_region source span =
- source.(span.l1 - 1).(span.c1).goodness <- source.(span.l1 - 1).(span.c1).goodness + 1;
- source.(span.l2 - 1).(span.c2 - 1).goodness <- source.(span.l2 - 1).(span.c2 - 1).goodness - 1
-
-let process_line l = Array.init (String.length l) (fun n -> { badness = 0; goodness = 0; char = l.[n] })
+ if not (zero_width span) then (
+ source.(span.l1 - 1).(span.c1).goodness <- source.(span.l1 - 1).(span.c1).goodness + 1;
+ source.(span.l2 - 1).(span.c2 - 1).goodness <- source.(span.l2 - 1).(span.c2 - 1).goodness - 1
+ )
+
+let process_line l = Array.init (String.length l) (fun n -> { badness = 0; goodness = 0; char = l.[n]; bad_zero_width = false })
let read_source filename =
let lines = ref [] in
@@ -135,34 +158,103 @@ let output_html_char chan c =
output_string chan (String.concat "" (List.init !opt_tab_width (fun _ -> "&nbsp;")))
else
output_char chan c
-
+
+let file_info file all taken =
+ let all = SpanSet.filter (fun s -> s.file = Filename.basename file) all in
+ let taken = SpanSet.filter (fun s -> s.file = Filename.basename file) taken in
+ let not_taken = SpanSet.diff all taken in
+
+ let percent =
+ if SpanSet.cardinal all != 0 then
+ let p = 100. *. (Float.of_int (SpanSet.cardinal taken) /. Float.of_int (SpanSet.cardinal all)) in
+ Printf.sprintf "%.0f%%" p
+ else
+ "-"
+ in
+
+ taken,
+ not_taken,
+ Printf.sprintf "%s (%d/%d) %s" (Filename.basename file) (SpanSet.cardinal taken) (SpanSet.cardinal all) percent
+
+let index_css = "
+body, html {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+table {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border-collapse: collapse;
+ overflow: hidden;
+}
+
+.left {
+ width: 20%;
+}
+
+.left .scroll {
+ height: 100vh;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.right {
+ width: 80%;
+}
+
+td {
+ padding: 0;
+ margin: 0;
+}
+
+tr {
+ padding: 0;
+ margin: 0;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+iframe {
+ height: 100%;
+ width: 100%;
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+"
+
let main () =
let all = read_coverage !opt_all in
let taken = read_coverage !opt_taken in
List.iter (fun file ->
- let all = SpanSet.filter (fun s -> s.file = Filename.basename file) all in
- let taken = SpanSet.filter (fun s -> s.file = Filename.basename file) taken in
- let not_taken = SpanSet.diff all taken in
-
+ let taken, not_taken, desc = file_info file all taken in
+ print_endline desc;
+
let source = read_source file in
SpanSet.iter (mark_good_region source) taken;
SpanSet.iter (mark_bad_region source) not_taken;
-
+
let output_file = Filename.remove_extension (Filename.basename file) ^ ".html" in
let chan = open_out output_file in
-
+
let current_goodness = ref 0 in
let current_badness = ref 0 in
-
+
let clamp_lightness l = max 30 (min 80 l) in
-
+
let html_color color darkness =
Printf.sprintf "hsl(%d, %d%%, %d%%)"
color.hue color.saturation (clamp_lightness ((80 + !opt_darken) - darkness * !opt_darken))
in
let good_color () = html_color !opt_good_color !current_goodness in
let bad_color () = html_color !opt_bad_color !current_badness in
-
+
output_string chan "<!DOCTYPE html>\n";
output_string chan "<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n";
output_string chan (Printf.sprintf "<title>%s</title>" (Filename.remove_extension (Filename.basename file)));
@@ -210,6 +302,14 @@ let main () =
) else (
output_html_char chan loc.char
);
+
+ if loc.bad_zero_width then (
+ output_string chan (Printf.sprintf "<span style=\"background-color: %s\">" (bad_color ()));
+ output_string chan "&#171;Invisible branch not taken here&#187";
+ prerr_endline ("zero" ^ file);
+ output_string chan "</span>"
+ );
+
assert (!current_goodness >= 0);
assert (!current_badness >= 0)
@@ -221,9 +321,34 @@ let main () =
output_string chan "</body>\n";
output_string chan "</html>";
- close_out chan;
- Printf.printf "%s (%d/%d)\n" file (SpanSet.cardinal taken) (SpanSet.cardinal all)
- ) !opt_files
+ close_out chan
+ ) !opt_files;
+
+ begin match !opt_index with
+ | Some name ->
+ let chan = open_out (name ^ ".html") in
+
+ output_string chan "<!DOCTYPE html>\n";
+ output_string chan "<html lang=\"en\">\n";
+ Printf.ksprintf (output_string chan)
+ "<head>\n<meta charset=\"utf-8\">\n<title>Coverage Report</title>\n<style>%s</style>\n</head>\n"
+ index_css;
+ output_string chan "<body>\n";
+
+ output_string chan "<table><tr><td class=\"left\"><div class=\"scroll\">";
+ List.iter (fun file ->
+ let _, _, desc = file_info file all taken in
+ Printf.ksprintf (output_string chan) "<a href=\"%s.html\" target=\"source\">%s</a><br>\n"
+ (Filename.remove_extension (Filename.basename file)) desc
+ ) !opt_files;
+ output_string chan "</div></td>";
+
+ output_string chan "<td class=\"right\"><iframe name=\"source\"></iframe></td></tr></table>\n";
+ output_string chan "</body>\n";
+ output_string chan "</html>";
+ close_out chan
+ | None -> ()
+ end
let usage_msg = "usage: sailcov -t <file> -a <file> <.sail files>\n"