Test Determinator for MobileLab

Reviewed By: martinoluca

Differential Revision: D7709133

fbshipit-source-id: ffb2db2
master
Dino Distefano 7 years ago committed by Facebook Github Bot
parent 69392d4192
commit c80a2b0940

@ -1556,6 +1556,14 @@ and ml_buckets =
~symbols:ml_bucket_symbols ~eq:PolyVariantEqual.( = ) ~symbols:ml_bucket_symbols ~eq:PolyVariantEqual.( = )
and modified_lines =
CLOpt.mk_string ~long:"modified-lines" ~default:""
~in_help:InferCommand.[(Analyze, manual_generic); (Report, manual_generic)]
"Specifies the file containing the modified lines when Infer is run Test Determinator mode. \
--test-determinator --modified-lines modified_lines_file --profiler-sample \
profiler_sample_file"
and modified_targets = and modified_targets =
CLOpt.mk_path_opt ~deprecated:["modified_targets"] ~long:"modified-targets" ~meta:"file" CLOpt.mk_path_opt ~deprecated:["modified_targets"] ~long:"modified-targets" ~meta:"file"
"Read the file of Buck targets modified since the last analysis" "Read the file of Buck targets modified since the last analysis"
@ -2022,11 +2030,28 @@ and symops_per_iteration =
"Set the number of symbolic operations per iteration (see $(b,--iterations))" "Set the number of symbolic operations per iteration (see $(b,--iterations))"
and test_determinator =
CLOpt.mk_bool ~long:"test-determinator" ~default:false
~in_help:InferCommand.[(Analyze, manual_generic); (Report, manual_generic)]
"Run infer in Test Determinator mode. It is used together with the --modified-lines and \
--test-profiler flags \n \
which speficy the relevant arguments. E.g. --test-determinator --modified-lines \
modified_line_file --profiler-sample profiler_sample_file"
and test_filtering = and test_filtering =
CLOpt.mk_bool ~deprecated:["test_filtering"] ~long:"test-filtering" CLOpt.mk_bool ~deprecated:["test_filtering"] ~long:"test-filtering"
"List all the files Infer can report on (should be called from the root of the project)" "List all the files Infer can report on (should be called from the root of the project)"
and profiler_sample =
CLOpt.mk_string ~long:"profiler-sample" ~default:""
~in_help:InferCommand.[(Analyze, manual_generic); (Report, manual_generic)]
"Specifies the file containing the profiler sample when Infer is run Test Determinator mode. \
--test-determinator --modified-lines modified_line_file --profiler-sample \
profiler_sample_file"
and testing_mode = and testing_mode =
CLOpt.mk_bool ~deprecated:["testing_mode"; "-testing_mode"; "tm"] ~deprecated_no:["ntm"] CLOpt.mk_bool ~deprecated:["testing_mode"; "-testing_mode"; "tm"] ~deprecated_no:["ntm"]
~long:"testing-mode" ~long:"testing-mode"
@ -2639,6 +2664,8 @@ and ml_buckets = !ml_buckets
and models_mode = !models_mode and models_mode = !models_mode
and modified_lines = !modified_lines
and modified_targets = !modified_targets and modified_targets = !modified_targets
and monitor_prop_size = !monitor_prop_size and monitor_prop_size = !monitor_prop_size
@ -2803,8 +2830,12 @@ and symops_per_iteration = !symops_per_iteration
and keep_going = !keep_going and keep_going = !keep_going
and test_determinator = !test_determinator
and test_filtering = !test_filtering and test_filtering = !test_filtering
and profiler_sample = !profiler_sample
and testing_mode = !testing_mode and testing_mode = !testing_mode
and threadsafe_aliases = !threadsafe_aliases and threadsafe_aliases = !threadsafe_aliases

@ -501,6 +501,8 @@ val ml_buckets :
val models_mode : bool val models_mode : bool
val modified_lines : string
val modified_targets : string option val modified_targets : string option
val monitor_prop_size : bool val monitor_prop_size : bool
@ -621,8 +623,12 @@ val suggest_nullable : bool
val symops_per_iteration : int option val symops_per_iteration : int option
val test_determinator : bool
val test_filtering : bool val test_filtering : bool
val profiler_sample : string
val testing_mode : bool val testing_mode : bool
val racerd : bool val racerd : bool

@ -9,6 +9,7 @@
open! IStd open! IStd
module L = Logging module L = Logging
module ProfilerSample = Caml.Set.Make (Typ.Procname)
module JNI = struct module JNI = struct
(* https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html *) (* https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html *)
@ -281,9 +282,9 @@ let create_static = create ~kind:Typ.Procname.Java.Static
let create_non_static = create ~kind:Typ.Procname.Java.Non_Static let create_non_static = create ~kind:Typ.Procname.Java.Non_Static
let from_json_string str = let from_json json =
let methods = let methods =
match Yojson.Basic.from_string str with match json with
| `Assoc [_; ("methods", `List j); _] -> | `Assoc [_; ("methods", `List j); _] ->
j j
| _ -> | _ ->
@ -307,3 +308,8 @@ let from_json_string str =
L.(die UserError "Unexpected JSON input for the description of a single method") L.(die UserError "Unexpected JSON input for the description of a single method")
in in
parse_json methods [] parse_json methods []
let from_json_string str = ProfilerSample.of_list (from_json (Yojson.Basic.from_string str))
let from_json_file file = ProfilerSample.of_list (from_json (Yojson.Basic.from_file file))

@ -9,6 +9,8 @@
open! IStd open! IStd
module ProfilerSample : Caml.Set.S with type elt = Typ.Procname.t
module JNI : sig module JNI : sig
module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY : sig module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY : sig
type t = type t =
@ -39,4 +41,6 @@ module JNI : sig
end end
end end
val from_json_string : string -> Typ.Procname.t list val from_json_string : string -> ProfilerSample.t
val from_json_file : string -> ProfilerSample.t

@ -37,6 +37,8 @@ let do_source_file linereader classes program tenv source_basename package_opt s
JFrontend.compute_source_icfg linereader classes program tenv source_basename package_opt JFrontend.compute_source_icfg linereader classes program tenv source_basename package_opt
source_file source_file
in in
if Config.test_determinator then
TestDeterminator.test_to_run source_file cfg Config.modified_lines Config.profiler_sample ;
store_icfg source_file cfg store_icfg source_file cfg

@ -0,0 +1,123 @@
(*
* Copyright (c) 2018 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
module L = Logging
open JavaProfilerSamples
open! IStd
module LineRangeMap = Caml.Map.Make (struct
type t = Typ.Procname.t
let compare = Typ.Procname.compare
end)
let update_line_range_map pdesc map =
let start_node = Procdesc.get_start_node pdesc in
let exit_node = Procdesc.get_exit_node pdesc in
let range = (Procdesc.Node.get_loc start_node, Procdesc.Node.get_loc exit_node) in
let key = Procdesc.get_proc_name pdesc in
LineRangeMap.add key range map
let in_range l range = l >= (fst range).Location.line && l <= (snd range).Location.line
let affected_methods line_range_map changed_lines =
LineRangeMap.fold
(fun key range acc ->
if List.exists ~f:(fun l -> in_range l range) changed_lines then ProfilerSample.add key acc
else acc )
line_range_map ProfilerSample.empty
let compute_affected_methods fname cfg files_changed_lines_map =
L.(debug Capture Verbose) "@\n Looking for file %s in files_changed_line_map @\n" fname ;
match String.Map.find files_changed_lines_map fname with
| Some changed_lines ->
let line_range_map : (Location.t * Location.t) LineRangeMap.t = LineRangeMap.empty in
let line_range_map' =
Typ.Procname.Hash.fold
(fun _ pdesc acc -> update_line_range_map pdesc acc)
cfg line_range_map
in
L.(debug Capture Verbose) "@\n Line Range Map" ;
LineRangeMap.iter
(fun key range ->
L.(debug Capture Verbose)
"@\n %a --> (%a,%a)" Typ.Procname.pp key Location.pp (fst range) Location.pp
(snd range) )
line_range_map' ;
L.(debug Capture Verbose) "@\n End Line Range Map @\n" ;
let affected_methods = affected_methods line_range_map' changed_lines in
L.(debug Capture Verbose) "@\n == Start Printing Affected Methods == " ;
ProfilerSample.iter
(fun m -> L.(debug Capture Verbose) "@\n METHOD> %a " Typ.Procname.pp m)
affected_methods ;
L.(debug Capture Verbose) "@\n == End Printing Affected Methods == @\n" ;
affected_methods
| None ->
L.(debug Capture Verbose)
"@\n File name %s was not found in files_changed_line_map @\n" fname ;
ProfilerSample.empty
let read_changed_lines_file changed_lines_file =
match Utils.read_file changed_lines_file with
| Ok cl_list ->
let changed_lines =
List.fold cl_list ~init:String.Map.empty ~f:(fun acc cl_item ->
let fname, cl = String.rsplit2_exn ~on:':' cl_item in
String.Map.set acc ~key:fname ~data:(FileDiff.parse_unix_diff cl) )
in
changed_lines
| Error _ ->
String.Map.empty
let read_test_samples_file test_samples_file =
match Utils.read_file test_samples_file with
| Ok ts_list ->
let test_samples =
List.fold ts_list ~init:String.Map.empty ~f:(fun acc test_id ->
let test_name, path = String.lsplit2_exn ~on:':' test_id in
let tset = JavaProfilerSamples.from_json_file path in
String.Map.set acc ~key:test_name ~data:tset )
in
test_samples
| Error _ ->
String.Map.empty
let print_test_to_run test_to_run =
L.result "@\n [Result Test Determinator:] Test to run = [" ;
List.iter ~f:(L.result " %s ") test_to_run ;
L.result " ] @\n"
let print_changed_lines changed_lines =
L.(debug Capture Verbose) "@\n Changed lines = {" ;
String.Map.iteri changed_lines ~f:(fun ~key:k ~data:d ->
L.(debug Capture Verbose) "\n %s --> [" k ;
List.iter d ~f:(L.(debug Capture Verbose) " %i ") ;
L.(debug Capture Verbose) " ] " ) ;
L.(debug Capture Verbose) "@\n } @\n"
(* test_to_run = { n | Affected_Method /\ ts_n != 0 } *)
let test_to_run source_file cfg changed_lines_file test_samples_file =
L.(debug Capture Verbose) "@\n ***** Start Test Determinator ***** @\n" ;
let fname = SourceFile.to_rel_path source_file in
let changed_lines = read_changed_lines_file changed_lines_file in
print_changed_lines changed_lines ;
let test_samples = read_test_samples_file test_samples_file in
let affected_methods = compute_affected_methods fname cfg changed_lines in
let test_to_run =
String.Map.fold test_samples ~init:[] ~f:(fun ~key ~data acc ->
let intersection = ProfilerSample.inter affected_methods data in
if ProfilerSample.is_empty intersection then acc else key :: acc )
in
print_test_to_run test_to_run

@ -0,0 +1,10 @@
(*
* Copyright (c) 2018 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
val test_to_run : SourceFile.t -> Procdesc.t Typ.Procname.Hash.t -> string -> string -> unit

@ -159,8 +159,9 @@ let test_jni_to_java_type_with_invalid_input =
let test_from_json_string_with_valid_input = let test_from_json_string_with_valid_input =
let create_test input expected _ = let create_test input expected _ =
let expected = JavaProfilerSamples.ProfilerSample.of_list expected in
let found = JavaProfilerSamples.from_json_string input in let found = JavaProfilerSamples.from_json_string input in
assert_equal ~cmp:(List.equal ~equal:Typ.Procname.equal) expected found assert_equal ~cmp:JavaProfilerSamples.ProfilerSample.equal expected found
in in
let input1 = "{\"whatever\": {}, \"methods\": [], \"foo\": {}}" in let input1 = "{\"whatever\": {}, \"methods\": [], \"foo\": {}}" in
let expected1 = [] in let expected1 = [] in

@ -0,0 +1,14 @@
/*
* Copyright (c) 2018 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
class MyFavouriteClassOne {
void methodOne() {
}
}

@ -0,0 +1,14 @@
/*
* Copyright (c) 2018 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
class MyFavouriteClassTwo {
int methodTwo (int i) {
return i+2;
}
}

@ -0,0 +1 @@
MyFavouriteClassTwo.java:UUUUUUUUUONUONUU

@ -0,0 +1,14 @@
/*
* Copyright (c) 2018 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
class MyFavouriteClassTwo {
int methodTwo (int i) {
return i;
}
}

@ -0,0 +1,20 @@
{
"date": "",
"methods": [
{
"class": "MyFavouriteClassOne",
"id": "",
"method": "methodOne",
"signature": "()V",
"src_file": ""
},
{
"class": "MyFavouriteClassTwo",
"id": "",
"method": "methodTwo",
"signature": "(I)I",
"src_file": ""
}
],
"profiles": []
}

@ -0,0 +1,20 @@
{
"date": "",
"methods": [
{
"class": "MyFavouriteClassXX",
"id": "",
"method": "methodOne",
"signature": "()V",
"src_file": ""
},
{
"class": "MyFavouriteClassYY",
"id": "",
"method": "methodTwo",
"signature": "(I)I",
"src_file": ""
}
],
"profiles": []
}
Loading…
Cancel
Save