src/Tools/quickcheck.ML
changeset 42026 da9b58f1db42
parent 42025 cb5b1e85b32e
child 42028 bd6515e113b2
equal deleted inserted replaced
42025:cb5b1e85b32e 42026:da9b58f1db42
    26   datatype test_params = Test_Params of {default_type: typ list, expect : expectation};
    26   datatype test_params = Test_Params of {default_type: typ list, expect : expectation};
    27   val test_params_of : Proof.context -> test_params
    27   val test_params_of : Proof.context -> test_params
    28   val map_test_params : (typ list * expectation -> typ list * expectation)
    28   val map_test_params : (typ list * expectation -> typ list * expectation)
    29     -> Context.generic -> Context.generic
    29     -> Context.generic -> Context.generic
    30   val add_generator:
    30   val add_generator:
    31     string * (Proof.context -> term -> int -> term list option * report option)
    31     string * (Proof.context -> term * term list -> int -> term list option * report option)
    32       -> Context.generic -> Context.generic
    32       -> Context.generic -> Context.generic
    33   val add_batch_generator:
    33   val add_batch_generator:
    34     string * (Proof.context -> term list -> (int -> term list option) list)
    34     string * (Proof.context -> term list -> (int -> term list option) list)
    35       -> Context.generic -> Context.generic
    35       -> Context.generic -> Context.generic
    36   (* testing terms and proof states *)
    36   (* testing terms and proof states *)
    37   val test_term: Proof.context -> bool * bool -> term ->
    37   val test_term: Proof.context -> bool * bool -> term * term list ->
    38     (string * term) list option * ((string * int) list * ((int * report) list) option)
    38     (string * term) list option * ((string * int) list * ((int * report) list) option)
    39   val test_goal_terms:
    39   val test_goal_terms:
    40     Proof.context -> bool * bool -> (string * typ) list -> term list
    40     Proof.context -> bool * bool -> (string * typ) list -> (term * term list) list  
    41       -> (string * term) list option * ((string * int) list * ((int * report) list) option) list
    41       -> (string * term) list option * ((string * int) list * ((int * report) list) option) list
    42   val quickcheck: (string * string list) list -> int -> Proof.state -> (string * term) list option
    42   val quickcheck: (string * string list) list -> int -> Proof.state -> (string * term) list option
    43   (* testing a batch of terms *)
    43   (* testing a batch of terms *)
    44   val test_terms: Proof.context -> term list -> (string * term) list option list option
    44   val test_terms: Proof.context -> term list -> (string * term) list option list option
    45 end;
    45 end;
   107     (merge (op =) (default_type1, default_type2), merge_expectation (expect1, expect2));
   107     (merge (op =) (default_type1, default_type2), merge_expectation (expect1, expect2));
   108 
   108 
   109 structure Data = Generic_Data
   109 structure Data = Generic_Data
   110 (
   110 (
   111   type T =
   111   type T =
   112     ((string * (Proof.context -> term -> int -> term list option * report option)) list
   112     ((string * (Proof.context -> term * term list -> int -> term list option * report option)) list
   113       * (string * (Proof.context -> term list -> (int -> term list option) list)) list)
   113       * (string * (Proof.context -> term list -> (int -> term list option) list)) list)
   114       * test_params;
   114       * test_params;
   115   val empty = (([], []), Test_Params {default_type = [], expect = No_Expectation});
   115   val empty = (([], []), Test_Params {default_type = [], expect = No_Expectation});
   116   val extend = I;
   116   val extend = I;
   117   fun merge (((generators1, batch_generators1), params1), ((generators2, batch_generators2), params2)) : T =
   117   fun merge (((generators1, batch_generators1), params1), ((generators2, batch_generators2), params2)) : T =
   180     handle TimeLimit.TimeOut =>
   180     handle TimeLimit.TimeOut =>
   181       if is_interactive then exc () else raise TimeLimit.TimeOut
   181       if is_interactive then exc () else raise TimeLimit.TimeOut
   182   else
   182   else
   183     f ()
   183     f ()
   184 
   184 
   185 fun test_term ctxt (limit_time, is_interactive) t =
   185 fun test_term ctxt (limit_time, is_interactive) (t, eval_terms) =
   186   let
   186   let
   187     val (names, t') = apfst (map fst) (prep_test_term t);
   187     val (names, t') = apfst (map fst) (prep_test_term t);
   188     val current_size = Unsynchronized.ref 0
   188     val current_size = Unsynchronized.ref 0
   189     fun excipit s =
   189     fun excipit s =
   190       "Quickcheck " ^ s ^ " while testing at size " ^ string_of_int (!current_size)
   190       "Quickcheck " ^ s ^ " while testing at size " ^ string_of_int (!current_size)
   191     val (test_fun, comp_time) = cpu_time "quickcheck compilation" (fn () => mk_tester ctxt t');
   191     val (test_fun, comp_time) = cpu_time "quickcheck compilation"
       
   192       (fn () => mk_tester ctxt (t', eval_terms));
   192     fun with_size test_fun k reports =
   193     fun with_size test_fun k reports =
   193       if k > Config.get ctxt size then
   194       if k > Config.get ctxt size then
   194         (NONE, reports)
   195         (NONE, reports)
   195       else
   196       else
   196         let
   197         let
   236 (* In the end there is probably no generic quickcheck interface left... *)
   237 (* In the end there is probably no generic quickcheck interface left... *)
   237 
   238 
   238 fun test_term_with_increasing_cardinality ctxt (limit_time, is_interactive) ts =
   239 fun test_term_with_increasing_cardinality ctxt (limit_time, is_interactive) ts =
   239   let
   240   let
   240     val thy = ProofContext.theory_of ctxt
   241     val thy = ProofContext.theory_of ctxt
       
   242     val (ts, eval_terms) = split_list ts
   241     val (freess, ts') = split_list (map prep_test_term ts)
   243     val (freess, ts') = split_list (map prep_test_term ts)
   242     val Ts = map snd (hd freess)
   244     val Ts = map snd (hd freess)
   243     val (test_funs, comp_time) = cpu_time "quickcheck compilation"
   245     val (test_funs, comp_time) = cpu_time "quickcheck compilation"
   244       (fn () => map (mk_tester ctxt) ts')
   246       (fn () => map (mk_tester ctxt) (ts' ~~ eval_terms))
   245     fun test_card_size (card, size) =
   247     fun test_card_size (card, size) =
   246       (* FIXME: why decrement size by one? *)
   248       (* FIXME: why decrement size by one? *)
   247       case fst (the (nth test_funs (card - 1)) (size - 1)) of
   249       case fst (the (nth test_funs (card - 1)) (size - 1)) of
   248         SOME ts => SOME (map fst (nth freess (card - 1)) ~~ ts)
   250         SOME ts => SOME (map fst (nth freess (card - 1)) ~~ ts)
   249       | NONE => NONE
   251       | NONE => NONE
   288           Syntax.string_of_sort_global thy S))
   290           Syntax.string_of_sort_global thy S))
   289       end
   291       end
   290       | subst T = T;
   292       | subst T = T;
   291   in (map_types o map_atyps) subst end;
   293   in (map_types o map_atyps) subst end;
   292 
   294 
   293 datatype wellsorted_error = Wellsorted_Error of string | Term of term
   295 datatype wellsorted_error = Wellsorted_Error of string | Term of term * term list
   294 
   296 
   295 fun test_goal_terms lthy (limit_time, is_interactive) insts check_goals =
   297 fun test_goal_terms lthy (limit_time, is_interactive) insts check_goals =
   296   let
   298   let
       
   299     fun map_goal_and_eval_terms f (check_goal, eval_terms) = (f check_goal, map f eval_terms)
   297     val thy = ProofContext.theory_of lthy
   300     val thy = ProofContext.theory_of lthy
   298     val default_insts =
   301     val default_insts =
   299       if Config.get lthy finite_types then (get_finite_types lthy) else (default_type lthy)
   302       if Config.get lthy finite_types then (get_finite_types lthy) else (default_type lthy)
   300     val inst_goals =
   303     val inst_goals =
   301       map (fn check_goal =>
   304       map (fn (check_goal, eval_terms) =>
   302         if not (null (Term.add_tfree_names check_goal [])) then
   305         if not (null (Term.add_tfree_names check_goal [])) then
   303           map (fn T =>
   306           map (fn T =>
   304             (pair (SOME T) o Term o Object_Logic.atomize_term thy o monomorphic_term thy insts T)
   307             (pair (SOME T) o Term o apfst (Object_Logic.atomize_term thy))
   305               check_goal
   308               (map_goal_and_eval_terms (monomorphic_term thy insts T) (check_goal, eval_terms))
   306               handle WELLSORTED s => (SOME T, Wellsorted_Error s)) default_insts
   309               handle WELLSORTED s => (SOME T, Wellsorted_Error s)) default_insts
   307         else
   310         else
   308           [(NONE, Term (Object_Logic.atomize_term thy check_goal))]) check_goals
   311           [(NONE, Term (Object_Logic.atomize_term thy check_goal, eval_terms))]) check_goals
   309     val error_msg =
   312     val error_msg =
   310       cat_lines
   313       cat_lines
   311         (maps (map_filter (fn (_, Term t) => NONE | (_, Wellsorted_Error s) => SOME s)) inst_goals)
   314         (maps (map_filter (fn (_, Term t) => NONE | (_, Wellsorted_Error s) => SOME s)) inst_goals)
   312     fun is_wellsorted_term (T, Term t) = SOME (T, t)
   315     fun is_wellsorted_term (T, Term t) = SOME (T, t)
   313       | is_wellsorted_term (_, Wellsorted_Error s) = NONE
   316       | is_wellsorted_term (_, Wellsorted_Error s) = NONE
   330       collect_results test_term' [] correct_inst_goals
   333       collect_results test_term' [] correct_inst_goals
   331     else
   334     else
   332       collect_results (test_term lthy (limit_time, is_interactive)) [] (maps (map snd) correct_inst_goals)
   335       collect_results (test_term lthy (limit_time, is_interactive)) [] (maps (map snd) correct_inst_goals)
   333   end;
   336   end;
   334 
   337 
   335 fun test_goal (time_limit, is_interactive) insts i state =
   338 fun test_goal (time_limit, is_interactive) (insts, eval_terms) i state =
   336   let
   339   let
   337     val lthy = Proof.context_of state;
   340     val lthy = Proof.context_of state;
   338     val thy = Proof.theory_of state;
   341     val thy = Proof.theory_of state;
   339     fun strip (Const ("all", _) $ Abs (_, _, t)) = strip t
   342     fun strip (Const ("all", _) $ Abs (_, _, t)) = strip t
   340       | strip t = t;
   343       | strip t = t;
   347     val assms = if Config.get lthy no_assms then [] else case some_locale
   350     val assms = if Config.get lthy no_assms then [] else case some_locale
   348      of NONE => Assumption.all_assms_of lthy
   351      of NONE => Assumption.all_assms_of lthy
   349       | SOME locale => Assumption.local_assms_of lthy (Locale.init locale thy);
   352       | SOME locale => Assumption.local_assms_of lthy (Locale.init locale thy);
   350     val proto_goal = Logic.list_implies (map Thm.term_of assms, subst_bounds (frees, strip gi));
   353     val proto_goal = Logic.list_implies (map Thm.term_of assms, subst_bounds (frees, strip gi));
   351     val check_goals = case some_locale
   354     val check_goals = case some_locale
   352      of NONE => [proto_goal]
   355      of NONE => [(proto_goal, eval_terms)]
   353       | SOME locale => map (fn (_, phi) => Morphism.term phi proto_goal)
   356       | SOME locale =>
   354         (Locale.registrations_of (Context.Theory thy) (*FIXME*) locale);
   357         map (fn (_, phi) => (Morphism.term phi proto_goal, map (Morphism.term phi) eval_terms))
       
   358           (Locale.registrations_of (Context.Theory thy) (*FIXME*) locale);
   355   in
   359   in
   356     test_goal_terms lthy (time_limit, is_interactive) insts check_goals
   360     test_goal_terms lthy (time_limit, is_interactive) insts check_goals
   357   end
   361   end
   358 
   362 
   359 (* pretty printing *)
   363 (* pretty printing *)
   396   let
   400   let
   397     val ctxt = Proof.context_of state;
   401     val ctxt = Proof.context_of state;
   398     val res =
   402     val res =
   399       state
   403       state
   400       |> Proof.map_context (Config.put report false #> Config.put quiet true)
   404       |> Proof.map_context (Config.put report false #> Config.put quiet true)
   401       |> try (test_goal (false, false) [] 1);
   405       |> try (test_goal (false, false) ([], []) 1);
   402   in
   406   in
   403     case res of
   407     case res of
   404       NONE => (false, state)
   408       NONE => (false, state)
   405     | SOME (NONE, report) => (false, state)
   409     | SOME (NONE, report) => (false, state)
   406     | SOME (cex, report) => (true, Proof.goal_message (K (Pretty.chunks [Pretty.str "",
   410     | SOME (cex, report) => (true, Proof.goal_message (K (Pretty.chunks [Pretty.str "",
   469 fun quickcheck_params_cmd args = Context.theory_map (fold parse_test_param args);
   473 fun quickcheck_params_cmd args = Context.theory_map (fold parse_test_param args);
   470 
   474 
   471 fun gen_quickcheck args i state =
   475 fun gen_quickcheck args i state =
   472   state
   476   state
   473   |> Proof.map_context_result (fn ctxt => fold parse_test_param_inst args (([], []), ctxt))
   477   |> Proof.map_context_result (fn ctxt => fold parse_test_param_inst args (([], []), ctxt))
   474   |> (fn ((insts, eval_terms), state') => test_goal (true, true) insts i state'
   478   |> (fn ((insts, eval_terms), state') => test_goal (true, true) (insts, eval_terms) i state'
   475   |> tap (fn (SOME x, _) => if expect (Proof.context_of state') = No_Counterexample then
   479   |> tap (fn (SOME x, _) => if expect (Proof.context_of state') = No_Counterexample then
   476                error ("quickcheck expected to find no counterexample but found one") else ()
   480                error ("quickcheck expected to find no counterexample but found one") else ()
   477            | (NONE, _) => if expect (Proof.context_of state') = Counterexample then
   481            | (NONE, _) => if expect (Proof.context_of state') = Counterexample then
   478                error ("quickcheck expected to find a counterexample but did not find one") else ()))
   482                error ("quickcheck expected to find a counterexample but did not find one") else ()))
   479 
   483