192 thy |
192 thy |
193 end; |
193 end; |
194 |
194 |
195 (* testing framework *) |
195 (* testing framework *) |
196 |
196 |
197 val target = "Haskell" |
197 val target = "Haskell_Quickcheck" |
198 |
198 |
199 (** invocation of Haskell interpreter **) |
199 (** invocation of Haskell interpreter **) |
200 |
200 |
201 val narrowing_engine = File.read (Path.explode "~~/src/HOL/Tools/Quickcheck/Narrowing_Engine.hs") |
201 val narrowing_engine = File.read (Path.explode "~~/src/HOL/Tools/Quickcheck/Narrowing_Engine.hs") |
202 val pnf_narrowing_engine = File.read (Path.explode "~~/src/HOL/Tools/Quickcheck/PNF_Narrowing_Engine.hs") |
202 val pnf_narrowing_engine = File.read (Path.explode "~~/src/HOL/Tools/Quickcheck/PNF_Narrowing_Engine.hs") |
208 let |
208 let |
209 val path = Path.append (Path.explode "~/.isabelle") (Path.basic (name ^ serial_string ())) |
209 val path = Path.append (Path.explode "~/.isabelle") (Path.basic (name ^ serial_string ())) |
210 val _ = Isabelle_System.mkdirs path; |
210 val _ = Isabelle_System.mkdirs path; |
211 in Exn.release (Exn.capture f path) end; |
211 in Exn.release (Exn.capture f path) end; |
212 |
212 |
213 fun value contains_existentials ctxt (get, put, put_ml) (code, value_name) = |
213 fun value (contains_existentials, (quiet, size)) ctxt (get, put, put_ml) (code, value_name) = |
214 let |
214 let |
215 fun message s = if Config.get ctxt Quickcheck.quiet then () else Output.urgent_message s |
215 fun message s = if quiet then () else Output.urgent_message s |
216 val tmp_prefix = "Quickcheck_Narrowing" |
216 val tmp_prefix = "Quickcheck_Narrowing" |
217 val with_tmp_dir = |
217 val with_tmp_dir = |
218 if Config.get ctxt overlord then with_overlord_dir else Isabelle_System.with_tmp_dir |
218 if Config.get ctxt overlord then with_overlord_dir else Isabelle_System.with_tmp_dir |
219 fun run in_path = |
219 fun run in_path = |
220 let |
220 let |
237 val cmd = "exec \"$ISABELLE_GHC\" -fglasgow-exts " ^ |
237 val cmd = "exec \"$ISABELLE_GHC\" -fglasgow-exts " ^ |
238 (space_implode " " (map File.shell_path [code_file, narrowing_engine_file, main_file])) ^ |
238 (space_implode " " (map File.shell_path [code_file, narrowing_engine_file, main_file])) ^ |
239 " -o " ^ executable ^ ";" |
239 " -o " ^ executable ^ ";" |
240 val _ = if bash cmd <> 0 then error "Compilation with GHC failed" else () |
240 val _ = if bash cmd <> 0 then error "Compilation with GHC failed" else () |
241 fun with_size k = |
241 fun with_size k = |
242 if k > Config.get ctxt Quickcheck.size then |
242 if k > size then |
243 NONE |
243 NONE |
244 else |
244 else |
245 let |
245 let |
246 val _ = message ("Test data size: " ^ string_of_int k) |
246 val _ = message ("Test data size: " ^ string_of_int k) |
247 val (response, _) = bash_output (executable ^ " " ^ string_of_int k) |
247 val (response, _) = bash_output (executable ^ " " ^ string_of_int k) |
262 |> Context.proof_map (exec false ml_code); |
262 |> Context.proof_map (exec false ml_code); |
263 in get ctxt' () end |
263 in get ctxt' () end |
264 end |
264 end |
265 in with_tmp_dir tmp_prefix run end; |
265 in with_tmp_dir tmp_prefix run end; |
266 |
266 |
267 fun dynamic_value_strict contains_existentials cookie thy postproc t = |
267 fun dynamic_value_strict opts cookie thy postproc t = |
268 let |
268 let |
269 val ctxt = Proof_Context.init_global thy |
269 val ctxt = Proof_Context.init_global thy |
270 fun evaluator naming program ((_, vs_ty), t) deps = Exn.interruptible_capture (value contains_existentials ctxt cookie) |
270 fun evaluator naming program ((_, vs_ty), t) deps = Exn.interruptible_capture (value opts ctxt cookie) |
271 (Code_Target.evaluator thy target naming program deps (vs_ty, t)); |
271 (Code_Target.evaluator thy target naming program deps (vs_ty, t)); |
272 in Exn.release (Code_Thingol.dynamic_value thy (Exn.map_result o postproc) evaluator t) end; |
272 in Exn.release (Code_Thingol.dynamic_value thy (Exn.map_result o postproc) evaluator t) end; |
273 |
273 |
274 (** counterexample generator **) |
274 (** counterexample generator **) |
275 |
275 |
371 map (fn (p, (_, (x, T))) => (x, mk_case_term ctxt p qs result)) ps |
371 map (fn (p, (_, (x, T))) => (x, mk_case_term ctxt p qs result)) ps |
372 end |
372 end |
373 |
373 |
374 fun test_term ctxt (limit_time, is_interactive) (t, eval_terms) = |
374 fun test_term ctxt (limit_time, is_interactive) (t, eval_terms) = |
375 let |
375 let |
|
376 val opts = (Config.get ctxt Quickcheck.quiet, Config.get ctxt Quickcheck.size) |
376 val thy = Proof_Context.theory_of ctxt |
377 val thy = Proof_Context.theory_of ctxt |
377 val t' = fold_rev (fn (x, T) => fn t => HOLogic.mk_all (x, T, t)) (Term.add_frees t []) t |
378 val t' = fold_rev (fn (x, T) => fn t => HOLogic.mk_all (x, T, t)) (Term.add_frees t []) t |
378 val pnf_t = make_pnf_term thy t' |
379 val pnf_t = make_pnf_term thy t' |
379 in |
380 in |
380 if Config.get ctxt allow_existentials andalso contains_existentials pnf_t then |
381 if Config.get ctxt allow_existentials andalso contains_existentials pnf_t then |
386 val (qs, prop_t) = finitize (strip_quantifiers pnf_t) |
387 val (qs, prop_t) = finitize (strip_quantifiers pnf_t) |
387 val prop_term = fold_rev (fn (_, (x, T)) => fn t => Abs (x, T, t)) qs prop_t |
388 val prop_term = fold_rev (fn (_, (x, T)) => fn t => Abs (x, T, t)) qs prop_t |
388 val ((prop_def, _), ctxt') = Local_Theory.define ((Binding.conceal (Binding.name "test_property"), NoSyn), |
389 val ((prop_def, _), ctxt') = Local_Theory.define ((Binding.conceal (Binding.name "test_property"), NoSyn), |
389 ((Binding.conceal Binding.empty, [Code.add_default_eqn_attrib]), prop_term)) ctxt |
390 ((Binding.conceal Binding.empty, [Code.add_default_eqn_attrib]), prop_term)) ctxt |
390 val (prop_def', thy') = Local_Theory.exit_result_global Morphism.term (prop_def, ctxt') |
391 val (prop_def', thy') = Local_Theory.exit_result_global Morphism.term (prop_def, ctxt') |
391 val result = dynamic_value_strict true |
392 val result = dynamic_value_strict (true, opts) |
392 (Existential_Counterexample.get, Existential_Counterexample.put, |
393 (Existential_Counterexample.get, Existential_Counterexample.put, |
393 "Narrowing_Generators.put_existential_counterexample") |
394 "Narrowing_Generators.put_existential_counterexample") |
394 thy' (Option.map o map_counterexample) (mk_property qs prop_def') |
395 thy' (Option.map o map_counterexample) (mk_property qs prop_def') |
395 val result' = Option.map (mk_terms ctxt' (fst (strip_quantifiers pnf_t))) result |
396 val result' = Option.map (mk_terms ctxt' (fst (strip_quantifiers pnf_t))) result |
396 in |
397 in |
402 val t' = Term.list_abs_free (Term.add_frees t [], t) |
403 val t' = Term.list_abs_free (Term.add_frees t [], t) |
403 fun wrap f t = list_abs (f (strip_abs t)) |
404 fun wrap f t = list_abs (f (strip_abs t)) |
404 val finitize = if Config.get ctxt finite_functions then wrap finitize_functions else I |
405 val finitize = if Config.get ctxt finite_functions then wrap finitize_functions else I |
405 fun ensure_testable t = |
406 fun ensure_testable t = |
406 Const (@{const_name Quickcheck_Narrowing.ensure_testable}, fastype_of t --> fastype_of t) $ t |
407 Const (@{const_name Quickcheck_Narrowing.ensure_testable}, fastype_of t --> fastype_of t) $ t |
407 val result = dynamic_value_strict false |
408 val result = dynamic_value_strict (false, opts) |
408 (Counterexample.get, Counterexample.put, "Narrowing_Generators.put_counterexample") |
409 (Counterexample.get, Counterexample.put, "Narrowing_Generators.put_counterexample") |
409 thy (Option.map o map) (ensure_testable (finitize t')) |
410 thy (Option.map o map) (ensure_testable (finitize t')) |
410 in |
411 in |
411 Quickcheck.Result {counterexample = Option.map ((curry (op ~~)) (Term.add_free_names t [])) result, |
412 Quickcheck.Result {counterexample = Option.map ((curry (op ~~)) (Term.add_free_names t [])) result, |
412 evaluation_terms = Option.map (K []) result, timings = [], reports = []} |
413 evaluation_terms = Option.map (K []) result, timings = [], reports = []} |