You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
infer_clone/infer/src/unit/abstractInterpreterTests.ml

223 lines
4.9 KiB

(*
* Copyright (c) 2015 - 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! Utils
module F = Format
(** Test the generic abstract interpreter by using a simple path counting domain. Path counting is
actually a decent stress test--if you join too much/too little, you'll over/under-count, and
you'll diverge at loops if you don't widen *)
module PathCountDomain = struct
type astate =
| PathCount of int
| Top
let make_path_count c =
(* guarding against overflow *)
if c < 0
then Top
else PathCount c
let initial = make_path_count 1
let is_bottom _ = false
let (<=) ~lhs ~rhs = match lhs, rhs with
| PathCount c1, PathCount c2 -> c1 <= c2
| _, Top -> true
| Top, PathCount _ -> false
let join a1 a2 = match a1, a2 with
| PathCount c1, PathCount c2 -> make_path_count (c1 + c2)
| Top, _ | PathCount _, Top -> Top
let widen ~prev:_ ~next:_ ~num_iters:_ = Top
let pp fmt = function
| PathCount c -> F.fprintf fmt "%d" c
| Top -> F.fprintf fmt "T"
end
module PathCountTransferFunctions = struct
type astate = PathCountDomain.astate
(* just propagate the current path count *)
let exec_instr astate _ _ = astate
end
module NormalTestInterpreter = AnalyzerTester.Make
(ProcCfg.Normal)
(Scheduler.ReversePostorder)
(PathCountDomain)
(PathCountTransferFunctions)
module ExceptionalTestInterpreter = AnalyzerTester.Make
(ProcCfg.Exceptional)
(Scheduler.ReversePostorder)
(PathCountDomain)
(PathCountTransferFunctions)
let tests =
let open OUnit2 in
let open AnalyzerTester.StructuredSil in
let normal_test_list = [
"straightline",
[
invariant "1";
invariant "1"
];
"if",
[
invariant "1";
If (unknown_exp, [], []);
invariant "2";
];
"if_then",
[
If (unknown_exp,
[invariant "1"],
[]
);
invariant "2"
];
"if_else",
[
If (unknown_exp,
[],
[invariant "1"]
);
invariant "2"
];
"if_then_else",
[
If (unknown_exp,
[invariant "1"],
[invariant "1"];
);
invariant "2"
];
"nested_if_then",
[
If (unknown_exp,
[If (unknown_exp, [], []);
invariant "2"],
[]
);
invariant "3"
];
"nested_if_else",
[
If (unknown_exp,
[],
[If (unknown_exp, [], []);
invariant "2"]
);
invariant "3"
];
"nested_if_then_else",
[
If (unknown_exp,
[If (unknown_exp, [], []);
invariant "2"],
[If (unknown_exp, [], []);
invariant "2"]
);
invariant "4"
];
"if_diamond",
[
invariant "1";
If (unknown_exp, [], []);
invariant "2";
If (unknown_exp, [], []);
invariant "4"
];
"loop",
[
invariant "1";
While (unknown_exp, [invariant "T"]);
invariant "T"
];
"if_in_loop",
[
While (unknown_exp,
[If (unknown_exp, [], []);
invariant "T"]
);
invariant "T";
];
"nested_loop_visit",
[
invariant "1";
While (unknown_exp,
[invariant "T";
While (unknown_exp,
[invariant "T"]);
invariant "T"]);
invariant "T";
];
"try",
[
Try (
[
invariant "1"; (* we expect the try block to be visited *)
],
[
invariant "_|_"; (* but not the catch block *)
],
[
invariant "1"; (* we expect the finally block to be visited *)
]
);
invariant "1"
];
] |> NormalTestInterpreter.create_tests in
let exceptional_test_list = [
"try1",
[
Try (
[
invariant "1";
],
[
invariant "1"; (* catch block should be visited *)
],
[
invariant "2"; (* could come from try or catch block *)
]
);
invariant "2"
];
"try1",
[
Try (
[
(* note: each instruction in try block is treated as potentially-excepting... *)
(* point 1 *)
invariant "1"; (* point 2 *)
invariant "1"; (* point 3 *)
],
[
(* ... so |paths through catch block| shoud be |number of instructions in try block| *)
invariant "2"; (* point 4 *)
],
[
invariant "3"; (* could arrive here via (1, 2, 3), (1, 4), or (2, 4) *)
]
);
invariant "3"
];
] |> ExceptionalTestInterpreter.create_tests in
"analyzer_tests_suite">:::(normal_test_list @ exceptional_test_list)