123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158(* 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.
*)openCommonmoduleJ=Json_typemoduleJson_out=Json_iomoduleJson_in=Json_io(*****************************************************************************)(* 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 *)typetests_coverage=(Common.filename,tests_score)Common.assocandtests_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 ...
*)typelines_coverage=(Common.filename,file_lines_coverage)Common.assocandfile_lines_coverage={covered_sites:intlist;all_sites:intlist;}(* 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)=funcov->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.Stringtest_file;J.String(spf"%.3f"score)]))))(* todo: should be autogenerated by ocamltarzan *)let(tests_coverage_of_json:J.json_type->tests_coverage)=funj->matchjwith|J.Object(xs)->xs|>List.map(fun(cover_file,tests_score)->cover_file,matchtests_scorewith|J.Arrayzs->zs|>List.map(funtest_file_score_pair->(matchtest_file_score_pairwith|J.Array[J.Stringtest_file;J.Stringstr_score]->test_file,float_of_stringstr_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)=funcov->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(funl->J.Intl));"all",J.Array(cover.all_sites|>List.map(funl->J.Intl));])))let(lines_coverage_of_json:J.json_type->lines_coverage)=funj->matchjwith|J.Object(xs)->xs|>List.map(fun(file,cover)->file,matchcoverwith|J.Object(["cov",J.Arraycovered_lines;"all",J.Arraycall_sites;])->{covered_sites=covered_lines|>List.map(function|J.Intl->l|_->failwith"Bad json, files_coverage_of_json");all_sites=call_sites|>List.map(function|J.Intl->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)=funcovfile->cov|>json_of_tests_coverage|>Json_out.string_of_json|>Common.write_file~filelet(load_tests_coverage:Common.filename->tests_coverage)=funfile->file|>Json_in.load_json|>tests_coverage_of_jsonlet(save_lines_coverage:lines_coverage->Common.filename->unit)=funcovfile->cov|>json_of_lines_coverage|>Json_out.string_of_json|>Common.write_file~filelet(load_lines_coverage:Common.filename->lines_coverage)=funfile->file|>Json_in.load_json|>lines_coverage_of_json