Summary: Given two analysis results, it's now possible to compare them with the following command: infer --diff --report-current reportA.json --report-previous reportB.json --file-renamings file_renamings.json this command will then generate 3 files in `infer-out/differential/{introduced, fixed, preexisting}.json`, whose meaning is the following: - `introduced.json` has all issues in `current` that are not in `previous` - `fixed.json` has all issues in `previous` that are not in `current` - `preexisting.json` has all issues that are in both `current` and `previous` The json files generated can then be used to categorise results coming from incremental analyses of a codebase. Reviewed By: jvillard Differential Revision: D4482517 fbshipit-source-id: 1f7df3emaster
parent
bc852ec0d1
commit
f8a65e698c
@ -0,0 +1,44 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
type t = {
|
||||||
|
introduced: Jsonbug_t.report;
|
||||||
|
fixed: Jsonbug_t.report;
|
||||||
|
preexisting: Jsonbug_t.report;
|
||||||
|
}
|
||||||
|
|
||||||
|
(** Set operations should keep duplicated issues with identical hashes *)
|
||||||
|
let of_reports ~(current_report: Jsonbug_t.report) ~(previous_report: Jsonbug_t.report) : t =
|
||||||
|
let to_map report =
|
||||||
|
List.fold_left
|
||||||
|
~f:(fun map issue -> Map.add_multi map ~key:issue.Jsonbug_t.hash ~data:issue)
|
||||||
|
~init:Int.Map.empty
|
||||||
|
report in
|
||||||
|
let fold_aux ~key:_ ~data (left, both, right) =
|
||||||
|
match data with
|
||||||
|
| `Left left' -> (List.rev_append left' left, both, right)
|
||||||
|
| `Both (both', _) -> (left, List.rev_append both' both, right)
|
||||||
|
| `Right right' -> (left, both, List.rev_append right' right) in
|
||||||
|
let introduced, preexisting, fixed =
|
||||||
|
Map.fold2 (to_map current_report) (to_map previous_report) ~f:fold_aux ~init:([],[],[]) in
|
||||||
|
{introduced; fixed; preexisting}
|
||||||
|
|
||||||
|
|
||||||
|
let to_files {introduced; fixed; preexisting} destdir =
|
||||||
|
Out_channel.write_all
|
||||||
|
(destdir ^/ "introduced.json")
|
||||||
|
~data:(Jsonbug_j.string_of_report introduced);
|
||||||
|
Out_channel.write_all
|
||||||
|
(destdir ^/ "fixed.json")
|
||||||
|
~data:(Jsonbug_j.string_of_report fixed);
|
||||||
|
Out_channel.write_all
|
||||||
|
(destdir ^/ "preexisting.json")
|
||||||
|
~data:(Jsonbug_j.string_of_report preexisting)
|
@ -0,0 +1,18 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
type t = {
|
||||||
|
introduced : Jsonbug_t.report;
|
||||||
|
fixed : Jsonbug_t.report;
|
||||||
|
preexisting : Jsonbug_t.report;
|
||||||
|
}
|
||||||
|
|
||||||
|
val of_reports : current_report:Jsonbug_t.report -> previous_report:Jsonbug_t.report -> t
|
||||||
|
|
||||||
|
val to_files : t -> string -> unit
|
@ -0,0 +1,219 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
module FileRenamings = struct
|
||||||
|
type renaming = {
|
||||||
|
current: string;
|
||||||
|
previous: string;
|
||||||
|
} [@@deriving compare]
|
||||||
|
|
||||||
|
type t = renaming list [@@deriving compare]
|
||||||
|
|
||||||
|
let equal = [%compare.equal : t]
|
||||||
|
|
||||||
|
let empty = []
|
||||||
|
|
||||||
|
let from_renamings rl : t = rl
|
||||||
|
|
||||||
|
(* A json renaming assoc list looks like:
|
||||||
|
[{"current": "aaa.java", "previous": "BBB.java"}, ...] *)
|
||||||
|
let from_json input : t =
|
||||||
|
let j = Yojson.Basic.from_string input in
|
||||||
|
let renaming_of_assoc assoc : renaming =
|
||||||
|
match assoc with
|
||||||
|
| `Assoc [("current", `String current); ("previous", `String previous)] -> {current; previous}
|
||||||
|
| _ -> failwithf "Expected JSON object of the following form: '%s', but instead got: '%s'"
|
||||||
|
"{\"current\": \"aaa.java\", \"previous\": \"BBB.java\"}"
|
||||||
|
(Yojson.Basic.to_string assoc) in
|
||||||
|
match j with
|
||||||
|
| `List json_renamings -> List.map ~f:renaming_of_assoc json_renamings
|
||||||
|
| _ -> failwithf "Expected JSON list but got '%s'" input
|
||||||
|
|
||||||
|
let from_json_file file : t = from_json (In_channel.read_all file)
|
||||||
|
|
||||||
|
let find_previous (t: t) current =
|
||||||
|
let r = List.find ~f:(fun r -> String.equal current r.current) t in
|
||||||
|
Option.map ~f:(fun r -> r.previous) r
|
||||||
|
|
||||||
|
let pp fmt t =
|
||||||
|
let pp_tuple fmt {current; previous} =
|
||||||
|
Format.fprintf fmt "{\"current\": \"%s\", \"previous\": \"%s\"}" current previous in
|
||||||
|
Format.fprintf fmt "[%a]" (Pp.comma_seq pp_tuple) t
|
||||||
|
|
||||||
|
module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY = struct
|
||||||
|
let from_renamings = from_renamings
|
||||||
|
let equal = equal
|
||||||
|
let pp = pp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
(* Remove duplicates between two lists whenever pred is true for such element *)
|
||||||
|
let relative_complements ~cmp ?(pred=(fun _ -> true)) l1 l2 =
|
||||||
|
let rec aux last_dup ((out_l1, out_l2) as out) in_l1 in_l2 =
|
||||||
|
let is_last_seen_dup v = match last_dup with
|
||||||
|
| Some ld -> Int.equal (cmp ld v) 0
|
||||||
|
| None -> false in
|
||||||
|
match in_l1, in_l2 with
|
||||||
|
| i::is, f::fs when Int.equal (cmp i f) 0 -> (* i = f *)
|
||||||
|
if pred i then aux (Some i) (out_l1, out_l2) is fs
|
||||||
|
else aux None (i::out_l1, f::out_l2) is fs
|
||||||
|
| i::is, f::_ when cmp i f < 0 -> (* i < f *)
|
||||||
|
let out_l1' = if is_last_seen_dup i then out_l1 else i::out_l1 in
|
||||||
|
aux last_dup (out_l1', out_l2) is in_l2
|
||||||
|
| _::_, f::fs -> (* i > f *)
|
||||||
|
let out_l2' = if is_last_seen_dup f then out_l2 else f::out_l2 in
|
||||||
|
aux last_dup (out_l1, out_l2') in_l1 fs
|
||||||
|
| i::is, [] when is_last_seen_dup i -> aux last_dup out is in_l2
|
||||||
|
| [], f::fs when is_last_seen_dup f -> aux last_dup out in_l1 fs
|
||||||
|
| _, _ -> List.rev_append in_l1 out_l1, List.rev_append in_l2 out_l2 in
|
||||||
|
let l1_sorted = List.sort ~cmp l1 in
|
||||||
|
let l2_sorted = List.sort ~cmp l2 in
|
||||||
|
aux None ([], []) l1_sorted l2_sorted
|
||||||
|
|
||||||
|
type issue_file_with_renaming = Jsonbug_t.jsonbug * (string option)
|
||||||
|
|
||||||
|
let skip_duplicated_types_on_filenames
|
||||||
|
renamings
|
||||||
|
(diff: Differential.t) : Differential.t =
|
||||||
|
let compare_issue_file_with_renaming (issue1, previous_file1) (issue2, previous_file2) =
|
||||||
|
let f1, f2 =
|
||||||
|
Option.value previous_file1 ~default:issue1.Jsonbug_t.file,
|
||||||
|
Option.value previous_file2 ~default:issue2.Jsonbug_t.file in
|
||||||
|
String.compare f1 f2 in
|
||||||
|
let cmp ((issue1, _) as issue_with_previous_file1) ((issue2, _) as issue_with_previous_file2) =
|
||||||
|
[%compare : string * issue_file_with_renaming]
|
||||||
|
(issue1.Jsonbug_t.bug_type, issue_with_previous_file1)
|
||||||
|
(issue2.Jsonbug_t.bug_type, issue_with_previous_file2) in
|
||||||
|
let introduced, fixed =
|
||||||
|
(* All comparisons will be made against filenames *before* renamings.
|
||||||
|
This way, all introduced and fixed issues can be sorted independently
|
||||||
|
over the same domain. *)
|
||||||
|
let introduced_normalized =
|
||||||
|
List.map diff.introduced
|
||||||
|
~f:(fun i -> i, FileRenamings.find_previous renamings i.Jsonbug_t.file) in
|
||||||
|
let fixed_normalized = List.map diff.fixed ~f:(fun f -> f, None) in
|
||||||
|
let introduced_normalized', fixed_normalized' =
|
||||||
|
relative_complements ~cmp introduced_normalized fixed_normalized in
|
||||||
|
List.map ~f:fst introduced_normalized', List.map ~f:fst fixed_normalized' in
|
||||||
|
{introduced; fixed; preexisting = diff.preexisting}
|
||||||
|
|
||||||
|
let java_anon_class_pattern = Str.regexp "\\$[0-9]+"
|
||||||
|
|
||||||
|
type procedure_id = string
|
||||||
|
|
||||||
|
let compare_procedure_id pid1 pid2 =
|
||||||
|
(* THIS COMPARISON FUNCTION IS INTENDED FOR JAVA ONLY *)
|
||||||
|
let normalize_procedure_id pid =
|
||||||
|
let anon_token = "$ANON" in
|
||||||
|
Str.global_replace java_anon_class_pattern anon_token pid in
|
||||||
|
let pid1_norm = normalize_procedure_id pid1 in
|
||||||
|
let pid2_norm = normalize_procedure_id pid2 in
|
||||||
|
(* NOTE: The CRC may swallow some extra chars if the anon class has more
|
||||||
|
* digits (e.g. ...$9.abcde():int.A1B2 and ...$10.abcde():in.C1FF), and this
|
||||||
|
* makes the 2 strings different.
|
||||||
|
* Cut the length to the min_length to match the 2 strings *)
|
||||||
|
let pid1_norm_trimmed, pid2_norm_trimmed =
|
||||||
|
let min_length = min (String.length pid1_norm) (String.length pid2_norm) in
|
||||||
|
String.sub pid1_norm ~pos:0 ~len:min_length,
|
||||||
|
String.sub pid2_norm ~pos:0 ~len:min_length in
|
||||||
|
String.compare pid1_norm_trimmed pid2_norm_trimmed
|
||||||
|
|
||||||
|
let value_of_qualifier_tag qts tag =
|
||||||
|
match List.find ~f:(fun elem -> String.equal elem.Jsonbug_t.tag tag) qts with
|
||||||
|
| Some qt -> Some qt.Jsonbug_t.value
|
||||||
|
| None -> None
|
||||||
|
|
||||||
|
type file_extension = string [@@deriving compare]
|
||||||
|
|
||||||
|
type weak_hash = string * string * string * int * (string option) [@@deriving compare]
|
||||||
|
|
||||||
|
let skip_anonymous_class_renamings (diff: Differential.t) : Differential.t =
|
||||||
|
(*
|
||||||
|
* THIS HEURISTIC IS INTENDED FOR JAVA ONLY.
|
||||||
|
* Two issues are similar (for the purpose of anonymous class renamings detection)
|
||||||
|
* when all of the following apply:
|
||||||
|
* 1) they are Java files
|
||||||
|
* 2) their weak hashes match
|
||||||
|
* 3) their anonymous procedure ids match
|
||||||
|
*)
|
||||||
|
let string_of_procedure_id issue = SourceFile.strip_crc issue.Jsonbug_t.procedure_id in
|
||||||
|
let extension fname = snd (Filename.split_extension fname) in
|
||||||
|
let cmp (i1:Jsonbug_t.jsonbug) (i2:Jsonbug_t.jsonbug) =
|
||||||
|
[%compare :
|
||||||
|
(file_extension option) * weak_hash * procedure_id]
|
||||||
|
(extension i1.file,
|
||||||
|
(i1.kind, i1.bug_type, i1.file, i1.key,
|
||||||
|
value_of_qualifier_tag i1.qualifier_tags "call_procedure"),
|
||||||
|
string_of_procedure_id i1)
|
||||||
|
(extension i2.file,
|
||||||
|
(i2.kind, i2.bug_type, i2.file, i2.key,
|
||||||
|
value_of_qualifier_tag i2.qualifier_tags "call_procedure"),
|
||||||
|
string_of_procedure_id i2) in
|
||||||
|
let pred (issue: Jsonbug_t.jsonbug) =
|
||||||
|
let is_java_file () =
|
||||||
|
match extension issue.file with
|
||||||
|
| Some ext -> String.equal (String.lowercase ext) "java"
|
||||||
|
| None -> false in
|
||||||
|
let has_anonymous_class_token () =
|
||||||
|
try
|
||||||
|
ignore (Str.search_forward java_anon_class_pattern issue.procedure_id 0);
|
||||||
|
true
|
||||||
|
with Not_found -> false in
|
||||||
|
is_java_file () && has_anonymous_class_token () in
|
||||||
|
let introduced, fixed = relative_complements ~cmp ~pred diff.introduced diff.fixed in
|
||||||
|
{introduced; fixed; preexisting = diff.preexisting}
|
||||||
|
|
||||||
|
(* Filter out null dereferences reported by infer if file has eradicate
|
||||||
|
enabled, to avoid double reporting. *)
|
||||||
|
let resolve_infer_eradicate_conflict
|
||||||
|
(analyzer: Config.analyzer)
|
||||||
|
(filters_of_analyzer: Config.analyzer -> Inferconfig.filters)
|
||||||
|
(diff: Differential.t) : Differential.t =
|
||||||
|
let should_discard_issue (issue: Jsonbug_t.jsonbug) =
|
||||||
|
let file_is_whitelisted () =
|
||||||
|
let source_file = SourceFile.UNSAFE.from_string issue.file in
|
||||||
|
let filters = filters_of_analyzer Config.Eradicate in
|
||||||
|
filters.path_filter source_file in
|
||||||
|
Config.equal_analyzer analyzer Config.Infer &&
|
||||||
|
String.equal issue.bug_type (Localise.to_string Localise.null_dereference) &&
|
||||||
|
file_is_whitelisted () in
|
||||||
|
let filter issues = List.filter ~f:(Fn.non should_discard_issue) issues in
|
||||||
|
{
|
||||||
|
introduced = filter diff.introduced;
|
||||||
|
fixed = filter diff.fixed;
|
||||||
|
preexisting = filter diff.preexisting;
|
||||||
|
}
|
||||||
|
|
||||||
|
let do_filter
|
||||||
|
(diff: Differential.t)
|
||||||
|
(renamings: FileRenamings.t)
|
||||||
|
~(skip_duplicated_types: bool): Differential.t =
|
||||||
|
if Config.filtering then (
|
||||||
|
diff
|
||||||
|
|> (if Config.equal_analyzer Config.analyzer Config.Infer then
|
||||||
|
skip_anonymous_class_renamings
|
||||||
|
else Fn.id)
|
||||||
|
|> (if skip_duplicated_types then
|
||||||
|
skip_duplicated_types_on_filenames renamings
|
||||||
|
else Fn.id)
|
||||||
|
|> (if Config.resolve_infer_eradicate_conflict then
|
||||||
|
resolve_infer_eradicate_conflict Config.analyzer Inferconfig.create_filters
|
||||||
|
else Fn.id))
|
||||||
|
else diff
|
||||||
|
|
||||||
|
module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY = struct
|
||||||
|
let relative_complements = relative_complements
|
||||||
|
let skip_duplicated_types_on_filenames = skip_duplicated_types_on_filenames
|
||||||
|
let java_anon_class_pattern = java_anon_class_pattern
|
||||||
|
let value_of_qualifier_tag = value_of_qualifier_tag
|
||||||
|
let skip_anonymous_class_renamings = skip_anonymous_class_renamings
|
||||||
|
let resolve_infer_eradicate_conflict = resolve_infer_eradicate_conflict
|
||||||
|
end
|
@ -0,0 +1,42 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - 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 FileRenamings :
|
||||||
|
sig
|
||||||
|
type renaming = {
|
||||||
|
current: string;
|
||||||
|
previous: string;
|
||||||
|
}
|
||||||
|
type t
|
||||||
|
val empty : t
|
||||||
|
val from_json : string -> t
|
||||||
|
val from_json_file : string -> t
|
||||||
|
val find_previous : t -> string -> string option
|
||||||
|
|
||||||
|
module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY : sig
|
||||||
|
val from_renamings : renaming list -> t
|
||||||
|
val equal : t -> t -> bool
|
||||||
|
val pp : Format.formatter -> t -> unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
val do_filter : Differential.t -> FileRenamings.t -> skip_duplicated_types:bool -> Differential.t
|
||||||
|
|
||||||
|
module VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY : sig
|
||||||
|
val relative_complements :
|
||||||
|
cmp:('a -> 'a -> int) -> ?pred:('a -> bool) -> 'a list -> 'a list -> 'a list * 'a list
|
||||||
|
val skip_duplicated_types_on_filenames : FileRenamings.t -> Differential.t -> Differential.t
|
||||||
|
val java_anon_class_pattern : Str.regexp
|
||||||
|
val value_of_qualifier_tag : Jsonbug_t.tag_value_record list -> string -> string option
|
||||||
|
val skip_anonymous_class_renamings : Differential.t -> Differential.t
|
||||||
|
val resolve_infer_eradicate_conflict :
|
||||||
|
Config.analyzer ->
|
||||||
|
(Config.analyzer -> Inferconfig.filters) ->
|
||||||
|
Differential.t -> Differential.t
|
||||||
|
end
|
@ -0,0 +1,523 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
open OUnit2
|
||||||
|
|
||||||
|
open DifferentialTestsUtils
|
||||||
|
|
||||||
|
type 'a outcome = Return of 'a | Raise of exn
|
||||||
|
|
||||||
|
let test_file_renamings_from_json =
|
||||||
|
let create_test test_input expected_output _ =
|
||||||
|
let test_output input = DifferentialFilters.FileRenamings.from_json input in
|
||||||
|
let pp_diff fmt (expected, actual) =
|
||||||
|
let pp = DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.pp in
|
||||||
|
Format.fprintf fmt "Expected %a but got %a" pp expected pp actual in
|
||||||
|
match expected_output with
|
||||||
|
| Return exp ->
|
||||||
|
assert_equal
|
||||||
|
~pp_diff
|
||||||
|
~cmp:DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.equal
|
||||||
|
exp
|
||||||
|
(test_output test_input)
|
||||||
|
| Raise exc ->
|
||||||
|
assert_raises exc (fun () -> test_output test_input) in
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"test_file_renamings_from_json_with_good_input",
|
||||||
|
"[" ^
|
||||||
|
"{\"current\": \"aaa.java\", \"previous\": \"BBB.java\"}," ^
|
||||||
|
"{\"current\": \"ccc.java\", \"previous\": \"DDD.java\"}," ^
|
||||||
|
"{\"current\": \"eee.java\", \"previous\": \"FFF.java\"}" ^
|
||||||
|
"]",
|
||||||
|
Return (
|
||||||
|
DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.from_renamings [
|
||||||
|
{DifferentialFilters.FileRenamings.current = "aaa.java"; previous = "BBB.java"};
|
||||||
|
{DifferentialFilters.FileRenamings.current = "ccc.java"; previous = "DDD.java"};
|
||||||
|
{DifferentialFilters.FileRenamings.current = "eee.java"; previous = "FFF.java"};
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_file_renamings_from_json_with_good_empty_input",
|
||||||
|
"[]",
|
||||||
|
Return (
|
||||||
|
DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.from_renamings []
|
||||||
|
)
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_file_renamings_from_json_with_well_formed_but_unexpected_input",
|
||||||
|
"{}",
|
||||||
|
Raise (
|
||||||
|
Failure "Expected JSON list but got '{}'"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_file_renamings_from_json_with_well_formed_but_unexpected_value",
|
||||||
|
"[{\"current\": 1, \"previous\": \"BBB.java\"}]",
|
||||||
|
Raise (
|
||||||
|
Failure ("Expected JSON object of the following form: " ^
|
||||||
|
"'{\"current\": \"aaa.java\", \"previous\": \"BBB.java\"}', " ^
|
||||||
|
"but instead got: '{\"current\":1,\"previous\":\"BBB.java\"}'")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_file_renamings_from_json_with_malformed_input",
|
||||||
|
"A",
|
||||||
|
Raise (
|
||||||
|
Yojson.Json_error "Line 1, bytes 0-1:\nInvalid token 'A'"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
]
|
||||||
|
|> List.map
|
||||||
|
~f:(fun (name, test_input, expected_output) ->
|
||||||
|
name >:: create_test test_input expected_output)
|
||||||
|
|
||||||
|
let test_file_renamings_find_previous =
|
||||||
|
let renamings =
|
||||||
|
DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.from_renamings [
|
||||||
|
{DifferentialFilters.FileRenamings.current = "aaa.java"; previous = "BBB.java"};
|
||||||
|
{DifferentialFilters.FileRenamings.current = "ccc.java"; previous = "DDD.java"};
|
||||||
|
{DifferentialFilters.FileRenamings.current = "eee.java"; previous = "FFF.java"};
|
||||||
|
] in
|
||||||
|
let cmp s1 s2 = [%compare.equal : string option] s1 s2 in
|
||||||
|
let find_previous = DifferentialFilters.FileRenamings.find_previous in
|
||||||
|
let pp_diff fmt (expected, actual) =
|
||||||
|
let pp_str_opt fmt str_opt =
|
||||||
|
let out = match str_opt with
|
||||||
|
| Some str -> "Some " ^ str
|
||||||
|
| None -> "None" in
|
||||||
|
Format.fprintf fmt "%s" out in
|
||||||
|
Format.fprintf fmt "Expected '%a' but got '%a'" pp_str_opt expected pp_str_opt actual in
|
||||||
|
let create_test input expected_previous _ =
|
||||||
|
assert_equal ~cmp ~pp_diff expected_previous (find_previous renamings input) in
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"test_file_renamings_find_previous_with_existing_value",
|
||||||
|
"ccc.java",
|
||||||
|
Some "DDD.java"
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_file_renamings_find_previous_with_existing_value",
|
||||||
|
"abc.java",
|
||||||
|
None
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|> List.map
|
||||||
|
~f:(fun (name, test_input, expected_output) ->
|
||||||
|
name >:: create_test test_input expected_output)
|
||||||
|
|
||||||
|
let test_relative_complements =
|
||||||
|
let create_test pred (l1, l2) (expected_l1, expected_l2) _ =
|
||||||
|
let cmp = Int.compare in
|
||||||
|
let output_l1, output_l2 =
|
||||||
|
DifferentialFilters.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.relative_complements
|
||||||
|
~cmp ~pred l1 l2 in
|
||||||
|
let list_equal l1 l2 = List.equal ~equal:(fun v1 v2 -> Int.equal (cmp v1 v2) 0) l1 l2 in
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "First list") ~cmp:list_equal expected_l1 output_l1;
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Second list") ~cmp:list_equal expected_l2 output_l2 in
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_always_true_pred",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0;1;2;3;4;5], [5;3;7;1;1;2]),
|
||||||
|
([4;0], [7])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_even_numbers_pred",
|
||||||
|
(fun i -> Int.equal (i mod 2) 0), (* skip when even, keep odd *)
|
||||||
|
([0;1;2;3;4;5], [5;3;7;1;1;2]),
|
||||||
|
([5;4;3;1;0], [7;5;3;1;1])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_even_numbers_pred_2",
|
||||||
|
(fun i -> Int.equal (i mod 2) 0), (* skip when even, keep odd *)
|
||||||
|
([0;1;2;3;5;5], [1;1;2;3;4;7]),
|
||||||
|
([5;5;3;1;0], [7;4;3;1;1])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_always_true_pred_and_disjoint_lists_of_different_length",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0;3;2;3;5], [9;7;6;8;4;6;9]),
|
||||||
|
([5;3;3;2;0], [9;9;8;7;6;6;4])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_always_true_pred_and_lists_of_different_length",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0;3;2;3], [9;7;3;8;0;6;9;4]),
|
||||||
|
([2], [9;9;8;7;6;4])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_odd_numbers_on_lists_of_different_length",
|
||||||
|
(fun i -> Int.equal (i mod 2) 1), (* skip when odd, keep even *)
|
||||||
|
([0;3;2;3], [9;7;3;8;0;6;9;4]),
|
||||||
|
([2;0], [9;9;8;7;6;4;0])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_singleton_lists1",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0], [0;1;0;0]),
|
||||||
|
([], [1])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_singleton_lists2",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0;1;0;0], [0]),
|
||||||
|
([1], [])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_singleton_lists3",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0], [0]),
|
||||||
|
([], [])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_singleton_lists4",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0], [1]),
|
||||||
|
([0], [1])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_empty_lists1",
|
||||||
|
(fun _ -> true),
|
||||||
|
([], [0;1;0;0]),
|
||||||
|
([], [1;0;0;0])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_empty_lists2",
|
||||||
|
(fun _ -> true),
|
||||||
|
([0;1;0;0], []),
|
||||||
|
([1;0;0;0], [])
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"test_relative_complements_with_empty_lists3",
|
||||||
|
(fun _ -> true),
|
||||||
|
([], []),
|
||||||
|
([], [])
|
||||||
|
);
|
||||||
|
]
|
||||||
|
|> List.map
|
||||||
|
~f:(fun (name, pred, test_input, expected_output) ->
|
||||||
|
name >:: create_test pred test_input expected_output)
|
||||||
|
|
||||||
|
let test_skip_duplicated_types_on_filenames =
|
||||||
|
let current_report = [
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:22 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1'.java" ~hash:11 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1'.java" ~hash:111 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_4.java" ~hash:4 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:222 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_5.java" ~hash:55 ();
|
||||||
|
] in
|
||||||
|
let previous_report = [
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2'.java" ~hash:222 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_5.java" ~hash:5 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1.java" ~hash:1 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_2" ~file:"file_3.java" ~hash:3 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_2.java" ~hash:2 ();
|
||||||
|
] in
|
||||||
|
let renamings =
|
||||||
|
DifferentialFilters.FileRenamings.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.from_renamings [
|
||||||
|
{DifferentialFilters.FileRenamings.current = "file_2'.java"; previous = "file_2.java"};
|
||||||
|
{DifferentialFilters.FileRenamings.current = "file_1'.java"; previous = "file_1.java"};
|
||||||
|
] in
|
||||||
|
let diff = Differential.of_reports ~current_report ~previous_report in
|
||||||
|
let diff' =
|
||||||
|
DifferentialFilters.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.skip_duplicated_types_on_filenames
|
||||||
|
renamings diff in
|
||||||
|
let do_assert _ =
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of introduced")
|
||||||
|
[4] (sorted_hashes_of_issues diff'.introduced);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of fixed")
|
||||||
|
[3] (sorted_hashes_of_issues diff'.fixed);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of preexisting")
|
||||||
|
[222] (sorted_hashes_of_issues diff'.preexisting) in
|
||||||
|
"test_skip_duplicated_types_on_filenames" >:: do_assert
|
||||||
|
|
||||||
|
let test_value_of_qualifier_tag =
|
||||||
|
let qts =
|
||||||
|
[{Jsonbug_t.tag = "tag1"; value = "value1"}; {Jsonbug_t.tag = "tag2"; value = "value2"}] in
|
||||||
|
let pp_diff fmt (expected, actual) =
|
||||||
|
let to_str v = Option.value v ~default:"NONE" in
|
||||||
|
Format.fprintf fmt "Expected: %s Found: %s" (to_str expected) (to_str actual) in
|
||||||
|
let do_assert _ =
|
||||||
|
assert_equal
|
||||||
|
~cmp:(Option.equal String.equal)
|
||||||
|
~pp_diff
|
||||||
|
(Some "value2")
|
||||||
|
(DifferentialFilters.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.value_of_qualifier_tag
|
||||||
|
qts "tag2");
|
||||||
|
assert_equal
|
||||||
|
~cmp:(Option.equal String.equal)
|
||||||
|
~pp_diff
|
||||||
|
None
|
||||||
|
(DifferentialFilters.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.value_of_qualifier_tag
|
||||||
|
qts "tag3");
|
||||||
|
assert_equal
|
||||||
|
~cmp:(Option.equal String.equal)
|
||||||
|
~pp_diff
|
||||||
|
(Some "value1")
|
||||||
|
(DifferentialFilters.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.value_of_qualifier_tag
|
||||||
|
qts "tag1") in
|
||||||
|
"test_value_of_qualifier_tag" >:: do_assert
|
||||||
|
|
||||||
|
let test_skip_anonymous_class_renamings =
|
||||||
|
let qt1 = [{Jsonbug_t.tag = "call_procedure"; value = "aValue1"}] in
|
||||||
|
let qt2 = [{Jsonbug_t.tag = "call_procedure"; value = "aValue2"}] in
|
||||||
|
|
||||||
|
let create_test input_diff (exp_introduced, exp_fixed, exp_preexisting) _ =
|
||||||
|
let diff' =
|
||||||
|
DifferentialFilters.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.skip_anonymous_class_renamings
|
||||||
|
input_diff in
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of introduced")
|
||||||
|
exp_introduced (sorted_hashes_of_issues diff'.introduced);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of fixed")
|
||||||
|
exp_fixed (sorted_hashes_of_issues diff'.fixed);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of preexisting")
|
||||||
|
exp_preexisting (sorted_hashes_of_issues diff'.preexisting) in
|
||||||
|
(* [(test_name, diff, expected hashes); ...] *)
|
||||||
|
[
|
||||||
|
("test_skip_anonymous_class_renamings_with_long_procedure_ids",
|
||||||
|
Differential.of_reports
|
||||||
|
~current_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
("com.whatever.package00.abcd." ^
|
||||||
|
"ABasicExampleFragment$83.onMenuItemActionExpand(android.view.MenuItem):b." ^
|
||||||
|
"5ab5e18cae498c35d887ce88f3d5fa82")
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~qualifier_tags:qt1
|
||||||
|
~hash:3 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
("com.whatever.package00.abcd." ^
|
||||||
|
"ABasicExampleFragment$83$7.onMenuItemActionExpand(android.view.MenuItem)." ^
|
||||||
|
"522cc747174466169781c9d2fc980dbc")
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~qualifier_tags:qt1
|
||||||
|
~hash:4 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_2"
|
||||||
|
~procedure_id:"procid5.c854fd4a98113d9ab5b82deb3545de89"
|
||||||
|
~file:"b.java"
|
||||||
|
~key:5
|
||||||
|
~hash:5 ();
|
||||||
|
]
|
||||||
|
~previous_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
("com.whatever.package00.abcd." ^
|
||||||
|
"ABasicExampleFragment$9.onMenuItemActionExpand(android.view.MenuItem):bo." ^
|
||||||
|
"ba1776155fba2899542401da5bc779a5")
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~qualifier_tags:qt1
|
||||||
|
~hash:1 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_2"
|
||||||
|
~procedure_id:"procid2.92095aee3f1884c37e96feae031f4931"
|
||||||
|
~file:"b.java"
|
||||||
|
~key:2
|
||||||
|
~hash:2 ();
|
||||||
|
],
|
||||||
|
([4;5], [2], []));
|
||||||
|
("test_skip_anonymous_class_renamings_with_empty_qualifier_tags",
|
||||||
|
Differential.of_reports
|
||||||
|
~current_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:"com.whatever.package.Class$1.foo():bool.bf13089cf4c47ff8ff089a1a4767324f"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:1 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_2"
|
||||||
|
~procedure_id:"com.whatever.package.Class$1.foo():bool.bf13089cf4c47ff8ff089a1a4767324f"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:3 ();
|
||||||
|
]
|
||||||
|
~previous_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
"com.whatever.package.Class$21$1.foo():bool.db89561ad9dab28587c8c04833f09b03"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:2 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_2"
|
||||||
|
~procedure_id:"com.whatever.package.Class$8.foo():bool.cffd4e941668063eb802183dbd3e856d"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:4 ();
|
||||||
|
],
|
||||||
|
([1], [2], []));
|
||||||
|
("test_skip_anonymous_class_renamings_with_matching_non_anonymous_procedure_ids",
|
||||||
|
Differential.of_reports
|
||||||
|
~current_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:"com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:1 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:"com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:3 ();
|
||||||
|
]
|
||||||
|
~previous_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:"com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:2 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:"com.whatever.package.Class.foo():bool.919f37fd0993058a01f438210ba8a247"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:4 ();
|
||||||
|
],
|
||||||
|
([1;3], [2;4], []));
|
||||||
|
("test_skip_anonymous_class_renamings_with_non_java_files",
|
||||||
|
Differential.of_reports
|
||||||
|
~current_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
"com.whatever.package.Class$3$1.foo():bool.9ff39eb5c53c81da9f6a7ade324345b6"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:1 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_2"
|
||||||
|
~procedure_id:"com.whatever.package.Class$1.foo():bool.bf13089cf4c47ff8ff089a1a4767324f"
|
||||||
|
~file:"a.mm"
|
||||||
|
~key:1
|
||||||
|
~hash:3 ();
|
||||||
|
]
|
||||||
|
~previous_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
"com.whatever.package.Class$21$1.foo():bool.db89561ad9dab28587c8c04833f09b03"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~hash:2 ();
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_2"
|
||||||
|
~procedure_id:"com.whatever.package.Class$8.foo():bool.cffd4e941668063eb802183dbd3e856d"
|
||||||
|
~file:"a.mm"
|
||||||
|
~key:1
|
||||||
|
~hash:4 ();
|
||||||
|
],
|
||||||
|
([3], [4], []));
|
||||||
|
("test_skip_anonymous_class_renamings_with_different_call_procedure_qualifier_tags",
|
||||||
|
Differential.of_reports
|
||||||
|
~current_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
"com.whatever.package.Class$3$1.foo():bool.9ff39eb5c53c81da9f6a7ade324345b6"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~qualifier_tags:qt1
|
||||||
|
~hash:1 ();
|
||||||
|
]
|
||||||
|
~previous_report:[
|
||||||
|
create_fake_jsonbug
|
||||||
|
~bug_type:"bug_type_1"
|
||||||
|
~procedure_id:
|
||||||
|
"com.whatever.package.Class$21$1.foo():bool.db89561ad9dab28587c8c04833f09b03"
|
||||||
|
~file:"a.java"
|
||||||
|
~key:1
|
||||||
|
~qualifier_tags:qt2
|
||||||
|
~hash:2 ();
|
||||||
|
],
|
||||||
|
([1], [2], []));
|
||||||
|
] |> List.map
|
||||||
|
~f:(fun (name, diff, expected_output) ->
|
||||||
|
name >:: create_test diff expected_output)
|
||||||
|
|
||||||
|
let test_resolve_infer_eradicate_conflict =
|
||||||
|
let fake_filters_factory analyzer =
|
||||||
|
match analyzer with
|
||||||
|
| Config.Eradicate ->
|
||||||
|
{
|
||||||
|
Inferconfig.path_filter = (function _ -> true); (* all paths are whitelisted *)
|
||||||
|
error_filter = (function _ -> failwith "error_filter is not needed");
|
||||||
|
proc_filter = (function _ -> failwith "proc_filter is not needed");
|
||||||
|
}
|
||||||
|
| _ -> failwith "This mock only supports Eradicate" in
|
||||||
|
let create_test analyzer (exp_introduced, exp_fixed, exp_preexisting) _ =
|
||||||
|
let null_dereference = Localise.to_string Localise.null_dereference in
|
||||||
|
let current_report = [
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1.java" ~hash:1 ();
|
||||||
|
create_fake_jsonbug ~bug_type:null_dereference ~file:"file_2.java" ~hash:2 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_4.java" ~hash:4 ();
|
||||||
|
] in
|
||||||
|
let previous_report = [
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_1.java" ~hash:11 ();
|
||||||
|
create_fake_jsonbug ~bug_type:null_dereference ~file:"file_3.java" ~hash:3 ();
|
||||||
|
create_fake_jsonbug ~bug_type:"bug_type_1" ~file:"file_4.java" ~hash:4 ();
|
||||||
|
] in
|
||||||
|
let diff = Differential.of_reports ~current_report ~previous_report in
|
||||||
|
let diff' =
|
||||||
|
DifferentialFilters.VISIBLE_FOR_TESTING_DO_NOT_USE_DIRECTLY.resolve_infer_eradicate_conflict
|
||||||
|
analyzer fake_filters_factory diff in
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of introduced")
|
||||||
|
exp_introduced (sorted_hashes_of_issues diff'.introduced);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of fixed")
|
||||||
|
exp_fixed (sorted_hashes_of_issues diff'.fixed);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of preexisting")
|
||||||
|
exp_preexisting (sorted_hashes_of_issues diff'.preexisting) in
|
||||||
|
(* [(test_name, analyzer, expected_hashes); ...] *)
|
||||||
|
[
|
||||||
|
("test_resolve_infer_eradicate_conflict_runs_with_infer_analyzer",
|
||||||
|
Config.Infer,
|
||||||
|
([1], [11], [4]));
|
||||||
|
("test_resolve_infer_eradicate_conflict_skips_with_checkers_analyzer",
|
||||||
|
Config.Checkers,
|
||||||
|
([1;2], [3;11], [4]));
|
||||||
|
("test_resolve_infer_eradicate_conflict_skips_with_linters_analyzer",
|
||||||
|
Config.Linters,
|
||||||
|
([1;2], [3;11], [4]));
|
||||||
|
] |> List.map
|
||||||
|
~f:(fun (name, analyzer, expected_output) ->
|
||||||
|
name >:: create_test analyzer expected_output)
|
||||||
|
|
||||||
|
let tests = "differential_filters_suite" >:::
|
||||||
|
test_file_renamings_from_json @
|
||||||
|
test_file_renamings_find_previous @
|
||||||
|
test_relative_complements @
|
||||||
|
test_skip_anonymous_class_renamings @
|
||||||
|
test_resolve_infer_eradicate_conflict @
|
||||||
|
[test_skip_duplicated_types_on_filenames; test_value_of_qualifier_tag]
|
@ -0,0 +1,61 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
open OUnit2
|
||||||
|
|
||||||
|
open DifferentialTestsUtils
|
||||||
|
|
||||||
|
let current_report = [
|
||||||
|
create_fake_jsonbug ~hash:3 ();
|
||||||
|
create_fake_jsonbug ~hash:1 ();
|
||||||
|
create_fake_jsonbug ~hash:2 ();
|
||||||
|
create_fake_jsonbug ~hash:2 ();
|
||||||
|
create_fake_jsonbug ~hash:2 ();
|
||||||
|
]
|
||||||
|
|
||||||
|
let previous_report = [
|
||||||
|
create_fake_jsonbug ~hash:1 ();
|
||||||
|
create_fake_jsonbug ~hash:4 ();
|
||||||
|
create_fake_jsonbug ~hash:1 ();
|
||||||
|
]
|
||||||
|
|
||||||
|
let diff = Differential.of_reports ~current_report ~previous_report
|
||||||
|
|
||||||
|
(* Sets operations should keep duplicated issues with identical hashes *)
|
||||||
|
let test_diff_keeps_duplicated_hashes =
|
||||||
|
let hashes_expected = 3 in
|
||||||
|
let hashes_found = List.fold
|
||||||
|
~init:0
|
||||||
|
~f:(fun acc i -> if Int.equal i.Jsonbug_t.hash 2 then acc + 1 else acc)
|
||||||
|
diff.introduced in
|
||||||
|
let pp_diff fmt (expected, actual) =
|
||||||
|
Format.fprintf fmt
|
||||||
|
"Expected %d issues with hash=2 among the introduced, but got %d instead"
|
||||||
|
expected
|
||||||
|
actual in
|
||||||
|
let do_assert _ = assert_equal ~pp_diff hashes_expected hashes_found in
|
||||||
|
"test_diff_keeps_duplicated_hashes" >:: do_assert
|
||||||
|
|
||||||
|
(* Sets operations to compute introduced, fixed and preexisting issues are correct *)
|
||||||
|
let test_set_operations =
|
||||||
|
let do_assert _ =
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of introduced")
|
||||||
|
[2;2;2;3] (sorted_hashes_of_issues diff.introduced);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of fixed")
|
||||||
|
[4] (sorted_hashes_of_issues diff.fixed);
|
||||||
|
assert_equal
|
||||||
|
~pp_diff:(pp_diff_of_int_list "Hashes of preexisting")
|
||||||
|
[1] (sorted_hashes_of_issues diff.preexisting) in
|
||||||
|
"test_set_operations" >:: do_assert
|
||||||
|
|
||||||
|
let tests = "differential_suite" >::: [test_diff_keeps_duplicated_hashes; test_set_operations]
|
@ -0,0 +1,62 @@
|
|||||||
|
(*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
open! IStd
|
||||||
|
|
||||||
|
let create_fake_jsonbug
|
||||||
|
?(bug_class="bug_class")
|
||||||
|
?(kind="kind")
|
||||||
|
?(bug_type="bug_type")
|
||||||
|
?(qualifier="qualifier")
|
||||||
|
?(severity="severity")
|
||||||
|
?(visibility="visibility")
|
||||||
|
?(line=1)
|
||||||
|
?(column=1)
|
||||||
|
?(procedure="procedure")
|
||||||
|
?(procedure_id="procedure_id")
|
||||||
|
?(procedure_start_line=1)
|
||||||
|
?(file="file/at/a/certain/path.java")
|
||||||
|
?(bug_trace=[])
|
||||||
|
?(key=1234)
|
||||||
|
?(qualifier_tags=[])
|
||||||
|
?(hash=1)
|
||||||
|
?(dotty=None)
|
||||||
|
?(infer_source_loc=None) () : Jsonbug_t.jsonbug =
|
||||||
|
{
|
||||||
|
bug_class;
|
||||||
|
kind;
|
||||||
|
bug_type;
|
||||||
|
qualifier;
|
||||||
|
severity;
|
||||||
|
visibility;
|
||||||
|
line;
|
||||||
|
column;
|
||||||
|
procedure;
|
||||||
|
procedure_id;
|
||||||
|
procedure_start_line;
|
||||||
|
file;
|
||||||
|
bug_trace;
|
||||||
|
key;
|
||||||
|
qualifier_tags;
|
||||||
|
hash;
|
||||||
|
dotty;
|
||||||
|
infer_source_loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pp_diff_of_int_list group_name fmt (expected, actual) =
|
||||||
|
Format.fprintf fmt
|
||||||
|
"[%s]: Expected: [%a] Found: [%a]"
|
||||||
|
group_name
|
||||||
|
(Pp.comma_seq Format.pp_print_int) expected
|
||||||
|
(Pp.comma_seq Format.pp_print_int) actual
|
||||||
|
|
||||||
|
(* Sort hashes to make things easier to compare *)
|
||||||
|
let sorted_hashes_of_issues issues =
|
||||||
|
let hash i = i.Jsonbug_t.hash in
|
||||||
|
List.sort ~cmp:Int.compare (List.map ~f:hash issues)
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"eradicate-whitelist-path-regex": [
|
||||||
|
"src/com"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright (c) 2017 - 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.
|
||||||
|
|
||||||
|
# E2E test involving the resolve_infer_eradicate_conflict filter
|
||||||
|
|
||||||
|
TESTS_DIR = ../..
|
||||||
|
DIFFERENTIAL_ARGS = --resolve-infer-eradicate-conflict
|
||||||
|
CLEAN_EXTRA = src/com/example/Diff*.java src/Diff*.java *.class com/
|
||||||
|
|
||||||
|
include ../../differential.make
|
||||||
|
|
||||||
|
$(CURRENT_REPORT):
|
||||||
|
cp src/com/example/DiffExample.java.current src/com/example/DiffExample.java
|
||||||
|
cp src/DiffExampleTwo.java.current src/DiffExampleTwo.java
|
||||||
|
$(call silent_on_success,\
|
||||||
|
$(INFER_BIN) \
|
||||||
|
-o $(CURRENT_DIR) -- javac src/com/example/DiffExample.java src/DiffExampleTwo.java)
|
||||||
|
|
||||||
|
$(PREVIOUS_REPORT):
|
||||||
|
cp src/com/example/DiffExample.java.previous src/com/example/DiffExample.java
|
||||||
|
$(call silent_on_success,\
|
||||||
|
$(INFER_BIN) -o $(PREVIOUS_DIR) -- javac src/com/example/DiffExample.java)
|
@ -0,0 +1,2 @@
|
|||||||
|
NULL_DEREFERENCE, src/DiffExampleTwo.java, void DiffExampleTwo.doSomethingTwo(), 1, DiffExampleTwo.doSomethingTwo():void.d8149869686ac2ef26a75ac4829094a7, DiffExampleTwo.doSomethingTwo():void
|
||||||
|
RESOURCE_LEAK, src/com/example/DiffExample.java, void DiffExample.openResource(), 5, com.example.DiffExample.openResource():void.75390f44594cb95db15fe8db9d07c4be, com.example.DiffExample.openResource():void
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This example tests the resolve_infer_eradicate_conflict filter
|
||||||
|
class DiffExampleTwo {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSomethingTwo() {
|
||||||
|
int i = this.genString().length();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
// This example tests the resolve_infer_eradicate_conflict filter
|
||||||
|
class DiffExample {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int triggerNpe() {
|
||||||
|
return this.genString().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void openResource() {
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream("AAA");
|
||||||
|
fis.read();
|
||||||
|
fis.close();
|
||||||
|
} catch (Exception exc) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
// This example tests the resolve_infer_eradicate_conflict filter
|
||||||
|
class DiffExample {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int triggerNpe() {
|
||||||
|
return this.genString().length();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
# Copyright (c) 2017 - 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.
|
||||||
|
|
||||||
|
# E2E test involving the skip_anonymous_class_renamings filter
|
||||||
|
|
||||||
|
TESTS_DIR = ../..
|
||||||
|
CLEAN_EXTRA = src/Diff*.java *.class
|
||||||
|
|
||||||
|
include ../../differential.make
|
||||||
|
|
||||||
|
$(CURRENT_REPORT):
|
||||||
|
cp src/DiffExample.java.current src/DiffExample.java
|
||||||
|
$(call silent_on_success, $(INFER_BIN) -o $(CURRENT_DIR) -- javac src/*.java)
|
||||||
|
|
||||||
|
$(PREVIOUS_REPORT):
|
||||||
|
cp src/DiffExample.java.previous src/DiffExample.java
|
||||||
|
$(call silent_on_success, $(INFER_BIN) -o $(PREVIOUS_DIR) -- javac src/*.java)
|
@ -0,0 +1 @@
|
|||||||
|
NULL_DEREFERENCE, src/DiffExample.java, void DiffExample$3$1.doSomething(), 1, DiffExample$3$1.doSomething():void.64afb6aca478af18163141bbb8999018, DiffExample$3$1.doSomething():void
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This example tests the skip_anonymous_class_renamings filter
|
||||||
|
class DiffExample {
|
||||||
|
private int checkAnonymousClasses() {
|
||||||
|
SimpleInterfaceExample sie1 = new SimpleInterfaceExample() {
|
||||||
|
public String getString() { return "111"; }
|
||||||
|
public int aaa() { return this.getString().length(); }
|
||||||
|
};
|
||||||
|
SimpleInterfaceExample sie2 = new SimpleInterfaceExample() {
|
||||||
|
public String getString() { return "111"; }
|
||||||
|
public int aaa() { return this.getString().length(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
new SimpleInterfaceExample() {
|
||||||
|
public String getString() { return null; }
|
||||||
|
public int aaa() {
|
||||||
|
new SimpleNestedInterface() {
|
||||||
|
public void doSomething() {
|
||||||
|
int a = getString().length();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new SimpleInterfaceExample() {
|
||||||
|
public String getString() { return null; }
|
||||||
|
public int aaa() { return this.getString().length(); }
|
||||||
|
}.aaa();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This example tests the skip_anonymous_class_renamings filter
|
||||||
|
class DiffExample {
|
||||||
|
private int checkAnonymousClasses() {
|
||||||
|
SimpleInterfaceExample sie1 = new SimpleInterfaceExample() {
|
||||||
|
public String getString() { return "111"; }
|
||||||
|
public int aaa() { return this.getString().length(); }
|
||||||
|
};
|
||||||
|
SimpleInterfaceExample sie2 = new SimpleInterfaceExample() {
|
||||||
|
public String getString() { return "111"; }
|
||||||
|
public int aaa() { return this.getString().length(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
return new SimpleInterfaceExample() {
|
||||||
|
public String getString() { return null; }
|
||||||
|
public int aaa() { return this.getString().length(); }
|
||||||
|
}.aaa();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This example tests the skip_anonymous_class_renamings filter
|
||||||
|
public interface SimpleInterfaceExample {
|
||||||
|
public String getString();
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This example tests the skip_anonymous_class_renamings filter
|
||||||
|
public interface SimpleNestedInterface {
|
||||||
|
public void doSomething();
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright (c) 2017 - 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.
|
||||||
|
|
||||||
|
# E2E test involving the skip_duplicated_types_on_filenames filter
|
||||||
|
|
||||||
|
TESTS_DIR = ../..
|
||||||
|
CLEAN_EXTRA = src/Diff*.java src/Diff*.java *.class
|
||||||
|
|
||||||
|
include ../../differential.make
|
||||||
|
|
||||||
|
$(CURRENT_REPORT):
|
||||||
|
cp src/DiffExample.java.current src/DiffExample.java
|
||||||
|
cp src/DiffExampleTwo.java.current src/DiffExampleTwo.java
|
||||||
|
$(call silent_on_success,\
|
||||||
|
$(INFER_BIN) -o $(CURRENT_DIR) -- javac src/DiffExample.java src/DiffExampleTwo.java)
|
||||||
|
|
||||||
|
$(PREVIOUS_REPORT):
|
||||||
|
cp src/DiffExample.java.previous src/DiffExample.java
|
||||||
|
$(call silent_on_success,\
|
||||||
|
$(INFER_BIN) -o $(PREVIOUS_DIR) -- javac src/DiffExample.java)
|
@ -0,0 +1,2 @@
|
|||||||
|
RESOURCE_LEAK, src/DiffExample.java, void DiffExample.openResource(), 5, DiffExample.openResource():void.c57b56c0042a220d7416e229c4e61b99, DiffExample.openResource():void
|
||||||
|
NULL_DEREFERENCE, src/DiffExampleTwo.java, void DiffExampleTwo.doSomethingTwo(), 1, DiffExampleTwo.doSomethingTwo():void.d8149869686ac2ef26a75ac4829094a7, DiffExampleTwo.doSomethingTwo():void
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
// This example tests the skip_duplicated_types_on_filenames filter
|
||||||
|
class DiffExample {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int triggerNpeRenamed() {
|
||||||
|
return this.genString().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void openResource() {
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream("AAA");
|
||||||
|
fis.read();
|
||||||
|
fis.close();
|
||||||
|
} catch (Exception exc) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
// This example tests the skip_duplicated_types_on_filenames filter
|
||||||
|
class DiffExample {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rename this method to change its bug_hash
|
||||||
|
private int triggerNPE() {
|
||||||
|
return this.genString().length();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This example tests the skip_duplicated_types_on_filenames filter
|
||||||
|
class DiffExampleTwo {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSomethingTwo() {
|
||||||
|
int i = this.genString().length();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright (c) 2017 - 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.
|
||||||
|
|
||||||
|
# E2E test involving the skip_duplicated_types_on_filenames filter
|
||||||
|
|
||||||
|
TESTS_DIR = ../..
|
||||||
|
DIFFERENTIAL_ARGS = --file-renamings file_renamings.json
|
||||||
|
CLEAN_EXTRA = src/Diff*.java *.class
|
||||||
|
|
||||||
|
include ../../differential.make
|
||||||
|
|
||||||
|
$(CURRENT_REPORT):
|
||||||
|
cp src/DiffExampleRenamed.java.current src/DiffExampleRenamed.java
|
||||||
|
$(call silent_on_success,\
|
||||||
|
$(INFER_BIN) -o $(CURRENT_DIR) -- javac src/DiffExampleRenamed.java)
|
||||||
|
|
||||||
|
$(PREVIOUS_REPORT):
|
||||||
|
cp src/DiffExample.java.previous src/DiffExample.java
|
||||||
|
$(call silent_on_success,\
|
||||||
|
$(INFER_BIN) -o $(PREVIOUS_DIR) -- javac src/DiffExample.java)
|
@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"current": "src/DiffExampleRenamed.java",
|
||||||
|
"previous": "src/DiffExample.java"
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
// This example tests the skip_duplicated_types_on_filenames filter
|
||||||
|
class DiffExample {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int triggerNpe() {
|
||||||
|
return this.genString().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void openResource() {
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream("AAA");
|
||||||
|
fis.read();
|
||||||
|
fis.close();
|
||||||
|
} catch (Exception exc) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 - 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
|
// This example tests the skip_duplicated_types_on_filenames filter
|
||||||
|
class DiffExampleRenamed {
|
||||||
|
private String genString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int triggerNpe() {
|
||||||
|
return this.genString().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void openResource() {
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream("AAA");
|
||||||
|
fis.read();
|
||||||
|
fis.close();
|
||||||
|
} catch (Exception exc) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
# Copyright (c) 2017 - 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.
|
||||||
|
|
||||||
|
# Targets that must be defined: CURRENT_REPORT and PREVIOUS_REPORT
|
||||||
|
# Optional variables: DIFFERENTIAL_ARGS, CLEAN_EXTRA
|
||||||
|
|
||||||
|
ROOT_DIR = $(TESTS_DIR)/../..
|
||||||
|
include $(ROOT_DIR)/Makefile.config
|
||||||
|
|
||||||
|
INFER_OUT = infer-out
|
||||||
|
DIFFERENTIAL_REPORT = $(INFER_OUT)/differential/introduced.json
|
||||||
|
EXPECTED_TEST_OUTPUT = introduced.exp.test
|
||||||
|
INFERPRINT_ISSUES_FIELDS = \
|
||||||
|
"bug_type,file,procedure,line_offset,procedure_id,procedure_id_without_crc"
|
||||||
|
|
||||||
|
CURRENT_DIR = infer-current
|
||||||
|
PREVIOUS_DIR = infer-previous
|
||||||
|
CURRENT_REPORT = $(CURRENT_DIR)/report.json
|
||||||
|
PREVIOUS_REPORT = $(PREVIOUS_DIR)/report.json
|
||||||
|
|
||||||
|
default: analyze
|
||||||
|
|
||||||
|
# the following dependency is to guarantee that the computation of
|
||||||
|
# PREVIOUS_REPORT and CURRENT_REPORT will be serialized
|
||||||
|
$(PREVIOUS_REPORT): $(CURRENT_REPORT)
|
||||||
|
|
||||||
|
.PHONY: analyze
|
||||||
|
analyze: $(CURRENT_REPORT) $(PREVIOUS_REPORT)
|
||||||
|
|
||||||
|
$(DIFFERENTIAL_REPORT): $(CURRENT_REPORT) $(PREVIOUS_REPORT)
|
||||||
|
$(INFER_BIN) -o $(INFER_OUT) --project-root $(CURDIR) --diff \
|
||||||
|
--report-current $(CURRENT_REPORT) --report-previous $(PREVIOUS_REPORT) \
|
||||||
|
$(DIFFERENTIAL_ARGS)
|
||||||
|
|
||||||
|
$(EXPECTED_TEST_OUTPUT): $(DIFFERENTIAL_REPORT) $(INFERPRINT_BIN)
|
||||||
|
$(INFERPRINT_BIN) \
|
||||||
|
--issues-fields $(INFERPRINT_ISSUES_FIELDS) \
|
||||||
|
--from-json-report $(INFER_OUT)/differential/introduced.json \
|
||||||
|
--issues-tests introduced.exp.test
|
||||||
|
$(INFERPRINT_BIN) \
|
||||||
|
--issues-fields $(INFERPRINT_ISSUES_FIELDS) \
|
||||||
|
--from-json-report $(INFER_OUT)/differential/fixed.json \
|
||||||
|
--issues-tests fixed.exp.test
|
||||||
|
$(INFERPRINT_BIN) \
|
||||||
|
--issues-fields $(INFERPRINT_ISSUES_FIELDS) \
|
||||||
|
--from-json-report $(INFER_OUT)/differential/preexisting.json \
|
||||||
|
--issues-tests preexisting.exp.test
|
||||||
|
|
||||||
|
.PHONY: print
|
||||||
|
print: $(EXPECTED_TEST_OUTPUT)
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: print
|
||||||
|
diff -u introduced.exp introduced.exp.test
|
||||||
|
diff -u fixed.exp fixed.exp.test
|
||||||
|
diff -u preexisting.exp preexisting.exp.test
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
$(REMOVE_DIR) *.exp.test $(INFER_OUT) $(CURRENT_DIR) $(PREVIOUS_DIR) \
|
||||||
|
$(CLEAN_EXTRA)
|
Loading…
Reference in new issue