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.( = )
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 =
CLOpt.mk_path_opt ~deprecated:["modified_targets"] ~long:"modified-targets" ~meta:"file"
"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))"
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 =
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)"
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 =
CLOpt.mk_bool ~deprecated:["testing_mode"; "-testing_mode"; "tm"] ~deprecated_no:["ntm"]
~long:"testing-mode"
@ -2639,6 +2664,8 @@ and ml_buckets = !ml_buckets
and models_mode = !models_mode
and modified_lines = !modified_lines
and modified_targets = !modified_targets
and monitor_prop_size = !monitor_prop_size
@ -2803,8 +2830,12 @@ and symops_per_iteration = !symops_per_iteration
and keep_going = !keep_going
and test_determinator = !test_determinator
and test_filtering = !test_filtering
and profiler_sample = !profiler_sample
and testing_mode = !testing_mode
and threadsafe_aliases = !threadsafe_aliases

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

@ -9,6 +9,7 @@
open! IStd
module L = Logging
module ProfilerSample = Caml.Set.Make (Typ.Procname)
module JNI = struct
(* 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 from_json_string str =
let from_json json =
let methods =
match Yojson.Basic.from_string str with
match json with
| `Assoc [_; ("methods", `List j); _] ->
j
| _ ->
@ -307,3 +308,8 @@ let from_json_string str =
L.(die UserError "Unexpected JSON input for the description of a single method")
in
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
module ProfilerSample : Caml.Set.S with type elt = Typ.Procname.t
module JNI : sig
module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY : sig
type t =
@ -39,4 +41,6 @@ module JNI : sig
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
source_file
in
if Config.test_determinator then
TestDeterminator.test_to_run source_file cfg Config.modified_lines Config.profiler_sample ;
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 create_test input expected _ =
let expected = JavaProfilerSamples.ProfilerSample.of_list expected 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
let input1 = "{\"whatever\": {}, \"methods\": [], \"foo\": {}}" 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