flitter/h_program-lang/coverage_code.ml
Joey Yakimowich-Payne fa600b98f7 Add poc files
2018-05-26 10:55:38 +09:00

156 lines
5.7 KiB
OCaml

(* Yoann Padioleau
*
* Copyright (C) 2010 Facebook
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
* license.txt for more details.
*)
open Common
module J = Json_type
(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(*
* The goal of this module is to provide data structures that can be
* used to mimic the Microsoft Echelon[1] project which given a patch
* try to run the most relevant tests that could be affected by the
* patch. It is probably easier in interpreted languages such as PHP which
* contain simple tracers/profilers.
*
* We can even run the tests and says whether the new code has
* been covered (like in MySql test infrastructure).
*
* For now we just provide types for a mapping from
* a source code file to a list of relevant test files.
*
* References:
* [1] http://research.microsoft.com/apps/pubs/default.aspx?id=69911
*)
(*****************************************************************************)
(* Types *)
(*****************************************************************************)
(* relevant test files exercising source, with term-frequency of
* file in the test *)
type tests_coverage = (Common.filename, tests_score) Common.assoc
and tests_score = (Common.filename * float) list
(* with tarzan *)
(* Note that xdebug by default does not trace assignements but only
* function and method calls, which mean the list of lines returned
* is an under-approximation. We compensate such an approximation by
* also computing the static set of function/method calls so that
* a coverage percentage can be computed.
*
* update: with hphpi tracer, we actually also cover assignement and
* this type is actually independent of such design decision.
* It's line-based though, so don't expect complex path coverage
* or MCDC stuff. Just simple line coverage ...
*)
type lines_coverage = (Common.filename, file_lines_coverage) Common.assoc
and file_lines_coverage = {
covered_sites: int list;
all_sites: int list;
}
(* with tarzan *)
(*****************************************************************************)
(* String of, json, etc *)
(*****************************************************************************)
(* This helps generates a coverage file that 'arc unit' can read *)
let (json_of_tests_coverage: tests_coverage -> J.json_type) = fun cov ->
J.Object (cov +> List.map (fun (cover_file, tests_score) ->
cover_file,
J.Array (tests_score +> List.map (fun (test_file, score) ->
J.Array [J.String test_file; J.String (spf "%.3f" score)]
))
))
(* todo: should be autogenerated by ocamltarzan *)
let (tests_coverage_of_json: J.json_type -> tests_coverage) = fun j ->
match j with
| J.Object (xs) ->
xs +> List.map (fun (cover_file, tests_score) ->
cover_file,
match tests_score with
| J.Array zs ->
zs +> List.map (fun test_file_score_pair ->
(match test_file_score_pair with
| J.Array [J.String test_file; J.String str_score] ->
test_file, float_of_string str_score
| _ -> failwith "Bad json, tests_coverage_of_json"
)
)
| _ -> failwith "Bad json, tests_coverage_of_json"
)
| _ -> failwith "Bad json, tests_coverage_of_json"
(* todo: should be autogenerated by ocamltarzan *)
let (json_of_lines_coverage: lines_coverage -> J.json_type) = fun cov ->
J.Object (cov +> List.map (fun (file, cover) ->
file,
J.Object ([
(* I use short fieldnames to avoid generating a huge JSON file.
*)
"cov", J.Array (cover.covered_sites +> List.map (fun l -> J.Int l));
"all", J.Array (cover.all_sites +> List.map (fun l -> J.Int l));
])
))
let (lines_coverage_of_json: J.json_type -> lines_coverage) = fun j ->
match j with
| J.Object (xs) ->
xs +> List.map (fun (file, cover) ->
file,
match cover with
| J.Object ([
"cov", J.Array covered_lines;
"all", J.Array call_sites;
]) ->
{
covered_sites =
covered_lines +> List.map (function
| J.Int l -> l
| _ -> failwith "Bad json, files_coverage_of_json"
);
all_sites =
call_sites +> List.map (function
| J.Int l -> l
| _ -> failwith "Bad json, files_coverage_of_json"
);
}
| _ -> failwith "Bad json, files_coverage_of_json"
)
| _ -> failwith "Bad json, files_coverage_of_json"
let (save_tests_coverage: tests_coverage -> Common.filename -> unit) =
fun cov file ->
cov +> json_of_tests_coverage +> Json_out.string_of_json
+> Common.write_file ~file
let (load_tests_coverage: Common.filename -> tests_coverage) =
fun file ->
file +> Json_in.load_json +> tests_coverage_of_json
let (save_lines_coverage: lines_coverage -> Common.filename -> unit) =
fun cov file ->
cov +> json_of_lines_coverage +> Json_out.string_of_json
+> Common.write_file ~file
let (load_lines_coverage: Common.filename -> lines_coverage) =
fun file ->
file +> Json_in.load_json +> lines_coverage_of_json