diff --git a/Makefile b/Makefile index d6d044aebe2a88c183a3477078b4574087e5699d..5ff002277f9313e9675975904459bdca593ddb92 100644 --- a/Makefile +++ b/Makefile @@ -910,6 +910,7 @@ PLUGIN_CMO:= partitioning/split_strategy domains/domain_mode value_parameters \ partitioning/partitioning_index partitioning/trace_partitioning \ engine/mem_exec engine/iterator engine/initialization \ engine/compute_functions engine/analysis register \ + utils/unit_tests \ $(APRON_CMO) $(NUMERORS_CMO) PLUGIN_CMI:= values/abstract_value values/abstract_location \ domains/abstract_domain domains/simpler_domains diff --git a/headers/header_spec.txt b/headers/header_spec.txt index 7fdecae8316b409d5dc49098688c2cf5cd52f1a9..d43eeffc48af86d043e959faf807147e54c171e7 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -1367,6 +1367,8 @@ src/plugins/value/utils/state_import.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/utils/state_import.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/utils/structure.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/utils/structure.mli: CEA_LGPL_OR_PROPRIETARY +src/plugins/value/utils/unit_tests.ml: CEA_LGPL_OR_PROPRIETARY +src/plugins/value/utils/unit_tests.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/utils/value_perf.ml: CEA_LGPL_OR_PROPRIETARY src/plugins/value/utils/value_perf.mli: CEA_LGPL_OR_PROPRIETARY src/plugins/value/utils/value_results.ml: CEA_LGPL_OR_PROPRIETARY diff --git a/src/kernel_services/abstract_interp/bottom.mli b/src/kernel_services/abstract_interp/bottom.mli index 799ccedfc3f67f9507b9bcb5b89893eeb0bb109a..0af283e70bf755cd874fc5ea3bc51725fd8007d6 100644 --- a/src/kernel_services/abstract_interp/bottom.mli +++ b/src/kernel_services/abstract_interp/bottom.mli @@ -42,7 +42,7 @@ val non_bottom: 'a or_bottom -> 'a val equal: ('a -> 'a -> bool) -> 'a or_bottom -> 'a or_bottom -> bool val compare: ('a -> 'a -> int) -> 'a or_bottom -> 'a or_bottom -> int -val is_included: ('a -> 'a -> bool) -> 'a or_bottom -> 'a or_bottom -> bool +val is_included: ('a -> 'b -> bool) -> 'a or_bottom -> 'b or_bottom -> bool val join: ('a -> 'a -> 'a) -> 'a or_bottom -> 'a or_bottom -> 'a or_bottom val join_list: ('a -> 'a -> 'a) -> 'a or_bottom list -> 'a or_bottom val narrow: ('a -> 'a -> 'a or_bottom) -> 'a or_bottom -> 'a or_bottom -> 'a or_bottom diff --git a/src/plugins/value/Eva.mli b/src/plugins/value/Eva.mli index 4de6198ed1a43b3c1d4a9c2d0a344b378eee5dc6..a11a0dca9aa2bab2ab56cfc918b2dbb915cd0c8e 100644 --- a/src/plugins/value/Eva.mli +++ b/src/plugins/value/Eva.mli @@ -60,3 +60,9 @@ module Eval_terms: sig @return None on either an evaluation error or on unsupported construct. *) val predicate_deps: eval_env -> Cil_types.predicate -> logic_deps option end + + +module Unit_tests: sig + (** Runs the unit tests of Eva. *) + val run: unit -> unit +end diff --git a/src/plugins/value/utils/unit_tests.ml b/src/plugins/value/utils/unit_tests.ml new file mode 100644 index 0000000000000000000000000000000000000000..043a2a726fa93a52dba15278fa6a4ca2881935e0 --- /dev/null +++ b/src/plugins/value/utils/unit_tests.ml @@ -0,0 +1,135 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It 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 *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +(* If true, prints each operation performed for the tests. Otherwise, only + prints wrong operations. *) +let print = false + +let report bug format = + if print || bug + then Value_parameters.result ("%s" ^^ format) (if bug then "BUG " else "") + else Format.ifprintf Format.std_formatter format + + +(* Tests the semantics of sign values, by comparison with the Ival semantics. *) +module Sign = struct + + module Sign = struct + include Sign_value + let zero = zero + let pos = one + let neg = inject_int Cil.uintType Integer.minus_one + let pos_or_zero = join zero pos + let neg_or_zero = join zero neg + let pos_neg = join pos neg + end + + module Ival = struct + include Ival + let positive = inject_range (Some Integer.one) None + let negative = inject_range None (Some Integer.minus_one) + let pos irange = + let max = Eval_typ.range_upper_bound irange in + inject_range (Some Integer.one) (Some max) + let neg irange = + let min = Eval_typ.range_lower_bound irange in + inject_range (Some min) (Some Integer.minus_one) + end + + module Cval = Main_values.CVal + + (* List of pairs (ival, sign) such that γ(ival) ⊆ γ(sign) and all values in + γ(sign) are in the range of [ikind]. *) + let test_values ikind = + let irange = Eval_typ.ik_range ikind in + let list = + [ Ival.zero, Sign.zero; + Ival.pos irange, Sign.pos; + Ival.zero, Sign.pos_or_zero; + Ival.pos irange, Sign.pos_or_zero; ] + in + if not irange.Eval_typ.i_signed + then list + else + list @ + [ Ival.neg irange, Sign.neg; + Ival.zero, Sign.neg_or_zero; + Ival.neg irange, Sign.neg_or_zero; + Ival.pos irange, Sign.pos_neg; + Ival.neg irange, Sign.pos_neg; ] + + let make_cvalue values = + List.map (fun (ival, sign) -> Cvalue.V.inject_ival ival, sign) values + + (* Checks whether γ(ival) ⊆ γ(sign). *) + let is_included cvalue sign = + try + let ival = Cvalue.V.project_ival cvalue in + (not (Ival.contains_zero ival) || sign.Sign.zero) && + (Ival.(is_bottom (narrow ival positive)) || sign.Sign.pos) && + (Ival.(is_bottom (narrow ival negative)) || sign.Sign.neg) + with Cvalue.V.Not_based_on_null -> Sign.(equal sign top) + + let test_unop unop typ values = + let test (cval, sign) = + let cval_res = Cval.forward_unop typ unop cval in + let sign_res = Sign.forward_unop typ unop sign in + let bug = not (Bottom.is_included is_included cval_res sign_res) in + report bug "%a %a = %a while %a %a = %a" + Printer.pp_unop unop Cval.pretty cval (Bottom.pretty Cval.pretty) cval_res + Printer.pp_unop unop Sign.pretty sign (Bottom.pretty Sign.pretty) sign_res + in + List.iter test values + + let test_binop binop typ values = + let test (cval1, sign1) (cval2, sign2) = + let cval_res = Cval.forward_binop typ binop cval1 cval2 in + let sign_res = Sign.forward_binop typ binop sign1 sign2 in + let bug = not (Bottom.is_included is_included cval_res sign_res) in + report bug "%a %a %a = %a while %a %a %a = %a" + Cval.pretty cval1 Printer.pp_binop binop Cval.pretty cval2 + (Bottom.pretty Cval.pretty) cval_res + Sign.pretty sign1 Printer.pp_binop binop Sign.pretty sign2 + (Bottom.pretty Sign.pretty) sign_res + in + List.iter (fun x -> List.iter (test x) values) values + + let test_typ ikind = + let typ = TInt (ikind, []) + and values = make_cvalue (test_values ikind) in + let apply f op = f op typ values in + List.iter (apply test_unop) [Neg; BNot; LNot]; + List.iter (apply test_binop) + [PlusA; MinusA; Mult; Div; BAnd; BOr; BXor; LAnd; LOr] + + let test () = List.iter test_typ [IInt; IUInt] +end + +(** Runs all tests. *) +let run () = + Value_parameters.result "Runs unit tests: %s." + (if print + then "all operations will be printed" + else "only faulty operations will be printed"); + Sign.test () diff --git a/src/plugins/value/utils/unit_tests.mli b/src/plugins/value/utils/unit_tests.mli new file mode 100644 index 0000000000000000000000000000000000000000..8783260b33accee3d55c8edf0a5002e41d3ed89b --- /dev/null +++ b/src/plugins/value/utils/unit_tests.mli @@ -0,0 +1,27 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It 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 *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(** Currently tested by this file: + - semantics of sign values. *) + +(** Runs some programmatic tests on Eva. *) +val run: unit -> unit diff --git a/src/plugins/value/values/sign_value.mli b/src/plugins/value/values/sign_value.mli index 055794019f7c3ace0d52ffd9652037f60c978d4e..94dd0faa8383c843fc97a7e8093f992c51f74bd5 100644 --- a/src/plugins/value/values/sign_value.mli +++ b/src/plugins/value/values/sign_value.mli @@ -22,6 +22,12 @@ (** Sign domain: abstraction of integer numerical values by their signs. *) -include Abstract_value.Leaf +type signs = { + pos: bool; (** true: maybe positive, false: never positive *) + zero: bool; (** true: maybe zero, false: never zero *) + neg: bool; (** true: maybe negative, false: never negative *) +} + +include Abstract_value.Leaf with type t = signs val pretty_debug: t Pretty_utils.formatter diff --git a/tests/value/oracle/unit_tests.res.oracle b/tests/value/oracle/unit_tests.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..35ce0d1ac58fd2746a03ac9dc7555281595b0f8e --- /dev/null +++ b/tests/value/oracle/unit_tests.res.oracle @@ -0,0 +1,2 @@ +[kernel] Parsing tests/value/unit_tests.i (no preprocessing) +[eva] Runs unit tests: only faulty operations will be printed. diff --git a/tests/value/unit_tests.i b/tests/value/unit_tests.i new file mode 100644 index 0000000000000000000000000000000000000000..cabb28768924493f9964d6329934d0356b2e5143 --- /dev/null +++ b/tests/value/unit_tests.i @@ -0,0 +1,7 @@ +/* run.config + EXECNOW: make -s @PTEST_DIR@/@PTEST_NAME@.cmxs + OPT: -load-module @PTEST_DIR@/@PTEST_NAME@ +*/ +/* run.config* + DONTRUN: +*/ diff --git a/tests/value/unit_tests.ml b/tests/value/unit_tests.ml new file mode 100644 index 0000000000000000000000000000000000000000..48e3b3405bcb13af995b3a50f15aebaa64b09b54 --- /dev/null +++ b/tests/value/unit_tests.ml @@ -0,0 +1,3 @@ +(* Programmatic tests of Eva, run by unit_tests.i. *) + +let () = Db.Main.extend Eva.Unit_tests.run