37
38:- module(plunit,
39 [ set_test_options/1, 40 begin_tests/1, 41 begin_tests/2, 42 end_tests/1, 43 run_tests/0, 44 run_tests/1, 45 run_tests/2, 46 load_test_files/1, 47 running_tests/0, 48 current_test/5, 49 current_test_unit/2, 50 test_report/1 51 ]). 52
58
59:- autoload(library(statistics), [call_time/2]). 60:- autoload(library(apply),
61 [maplist/3, include/3, maplist/2, foldl/4, partition/4]). 62:- autoload(library(lists), [member/2, append/2, flatten/2, append/3]). 63:- autoload(library(option), [ option/3, option/2, select_option/3 ]). 64:- autoload(library(ordsets), [ord_intersection/3]). 65:- autoload(library(error), [must_be/2, domain_error/2]). 66:- autoload(library(aggregate), [aggregate_all/3]). 67:- autoload(library(streams), [with_output_to/3]). 68:- autoload(library(ansi_term), [ansi_format/3]). 69:- if(exists_source(library(time))). 70:- autoload(library(time), [call_with_time_limit/2]). 71:- endif. 72
73:- public
74 unit_module/2. 75
76:- meta_predicate
77 valid_options(1, +),
78 count(0, -). 79
80 83
84swi :- catch(current_prolog_flag(dialect, swi), _, fail).
85sicstus :- catch(current_prolog_flag(dialect, sicstus), _, fail).
86
87throw_error(Error_term,Impldef) :-
88 throw(error(Error_term,context(Impldef,_))).
89
90:- set_prolog_flag(generate_debug_info, false). 91current_test_flag(optimise, Value) =>
92 current_prolog_flag(optimise, Value).
93current_test_flag(occurs_check, Value) =>
94 ( current_prolog_flag(plunit_occurs_check, Value0)
95 -> Value = Value0
96 ; current_prolog_flag(occurs_check, Value)
97 ).
98current_test_flag(Name, Value), atom(Name) =>
99 atom_concat(plunit_, Name, Flag),
100 current_prolog_flag(Flag, Value).
101current_test_flag(Name, Value), var(Name) =>
102 global_test_option(Opt, _, _Type, _Default),
103 functor(Opt, Name, 1),
104 current_test_flag(Name, Value).
105
106set_test_flag(Name, Value) :-
107 Opt =.. [Name, Value],
108 global_test_option(Opt),
109 !,
110 atom_concat(plunit_, Name, Flag),
111 set_prolog_flag(Flag, Value).
112set_test_flag(Name, _) :-
113 domain_error(test_flag, Name).
114
115current_test_flags(Flags) :-
116 findall(Flag, current_test_flag(Flag), Flags).
117
118current_test_flag(Opt) :-
119 current_test_flag(Name, Value),
120 Opt =.. [Name, Value].
121
123goal_expansion(forall(C,A),
124 \+ (C, \+ A)).
125goal_expansion(current_module(Module,File),
126 module_property(Module, file(File))).
127
128
129 132
133:- initialization init_flags. 134
135init_flags :-
136 ( global_test_option(Option, _Value, Type, Default),
137 Default \== (-),
138 Option =.. [Name,_],
139 atom_concat(plunit_, Name, Flag),
140 flag_type(Type, FlagType),
141 create_prolog_flag(Flag, Default, [type(FlagType), keep(true)]),
142 fail
143 ; true
144 ).
145
146flag_type(boolean, FlagType) => FlagType = boolean.
147flag_type(Type, FlagType), Type = oneof(Atoms), maplist(atom, Atoms) =>
148 FlagType = Type.
149flag_type(oneof(_), FlagType) => FlagType = term.
150flag_type(positive_integer, FlagType) => FlagType = integer.
151flag_type(number, FlagType) => FlagType = float.
152
153
154
202
203set_test_options(Options) :-
204 flatten([Options], List),
205 maplist(set_test_option, List).
206
207set_test_option(sto(true)) =>
208 print_message(warning, plunit(sto(true))).
209set_test_option(jobs(Jobs)) =>
210 must_be(positive_integer, Jobs),
211 set_test_option_flag(jobs(Jobs)).
212set_test_option(Option),
213 compound(Option), global_test_option(Option) =>
214 set_test_option_flag(Option).
215set_test_option(Option) =>
216 domain_error(option, Option).
217
218global_test_option(Opt) :-
219 global_test_option(Opt, Value, Type, _Default),
220 must_be(Type, Value).
221
222global_test_option(load(Load), Load, oneof([never,always,normal]), normal).
223global_test_option(output(Cond), Cond, oneof([always,on_failure]), on_failure).
224global_test_option(format(Feedback), Feedback, oneof([tty,log]), tty).
225global_test_option(silent(Silent), Silent, boolean, false).
226global_test_option(show_blocked(Blocked), Blocked, boolean, false).
227global_test_option(run(When), When, oneof([manual,make,make(all)]), make).
228global_test_option(occurs_check(Mode), Mode, oneof([false,true,error]), -).
229global_test_option(cleanup(Bool), Bool, boolean, true).
230global_test_option(jobs(Count), Count, positive_integer, 1).
231global_test_option(timeout(Number), Number, number, 3600).
232
233set_test_option_flag(Option) :-
234 Option =.. [Name, Value],
235 set_test_flag(Name, Value).
236
240
241loading_tests :-
242 current_test_flag(load, Load),
243 ( Load == always
244 -> true
245 ; Load == normal,
246 \+ current_test_flag(optimise, true)
247 ).
248
249 252
253:- thread_local
254 loading_unit/4. 255:- dynamic
256 current_unit/4, 257 test_file_for/2. 258
264
265begin_tests(Unit) :-
266 begin_tests(Unit, []).
267
268begin_tests(Unit, Options) :-
269 must_be(atom, Unit),
270 map_sto_option(Options, Options1),
271 valid_options(test_set_option, Options1),
272 make_unit_module(Unit, Name),
273 source_location(File, Line),
274 begin_tests(Unit, Name, File:Line, Options1).
275
276map_sto_option(Options0, Options) :-
277 select_option(sto(Mode), Options0, Options1),
278 !,
279 map_sto(Mode, Flag),
280 Options = [occurs_check(Flag)|Options1].
281map_sto_option(Options, Options).
282
283map_sto(rational_trees, Flag) => Flag = false.
284map_sto(finite_trees, Flag) => Flag = true.
285map_sto(Mode, _) => domain_error(sto, Mode).
286
287
288:- if(swi). 289begin_tests(Unit, Name, File:Line, Options) :-
290 loading_tests,
291 !,
292 '$set_source_module'(Context, Context),
293 ( current_unit(Unit, Name, Context, Options)
294 -> true
295 ; retractall(current_unit(Unit, Name, _, _)),
296 assert(current_unit(Unit, Name, Context, Options))
297 ),
298 '$set_source_module'(Old, Name),
299 '$declare_module'(Name, test, Context, File, Line, false),
300 discontiguous(Name:'unit test'/4),
301 '$set_predicate_attribute'(Name:'unit test'/4, trace, false),
302 discontiguous(Name:'unit body'/2),
303 asserta(loading_unit(Unit, Name, File, Old)).
304begin_tests(Unit, Name, File:_Line, _Options) :-
305 retractall(current_unit(Unit, Name, _, _)), 306 '$set_source_module'(Old, Old),
307 asserta(loading_unit(Unit, Name, File, Old)).
308
309:- else. 310
312
313user:term_expansion((:- begin_tests(Set)),
314 [ (:- begin_tests(Set)),
315 (:- discontiguous(test/2)),
316 (:- discontiguous('unit body'/2)),
317 (:- discontiguous('unit test'/4))
318 ]).
319
320begin_tests(Unit, Name, File:_Line, Options) :-
321 loading_tests,
322 !,
323 ( current_unit(Unit, Name, _, Options)
324 -> true
325 ; retractall(current_unit(Unit, Name, _, _)),
326 assert(current_unit(Unit, Name, -, Options))
327 ),
328 asserta(loading_unit(Unit, Name, File, -)).
329begin_tests(Unit, Name, File:_Line, _Options) :-
330 retractall(current_unit(Unit, Name, _, _)),
331 asserta(loading_unit(Unit, Name, File, -)).
332
333:- endif. 334
341
342end_tests(Unit) :-
343 loading_unit(StartUnit, _, _, _),
344 !,
345 ( Unit == StartUnit
346 -> once(retract(loading_unit(StartUnit, _, _, Old))),
347 '$set_source_module'(_, Old)
348 ; throw_error(context_error(plunit_close(Unit, StartUnit)), _)
349 ).
350end_tests(Unit) :-
351 throw_error(context_error(plunit_close(Unit, -)), _).
352
355
356:- if(swi). 357
358unit_module(Unit, Module) :-
359 atom_concat('plunit_', Unit, Module).
360
361make_unit_module(Unit, Module) :-
362 unit_module(Unit, Module),
363 ( current_module(Module),
364 \+ current_unit(_, Module, _, _),
365 predicate_property(Module:H, _P),
366 \+ predicate_property(Module:H, imported_from(_M))
367 -> throw_error(permission_error(create, plunit, Unit),
368 'Existing module')
369 ; true
370 ).
371
372:- else. 373
374:- dynamic
375 unit_module_store/2. 376
377unit_module(Unit, Module) :-
378 unit_module_store(Unit, Module),
379 !.
380
381make_unit_module(Unit, Module) :-
382 prolog_load_context(module, Module),
383 assert(unit_module_store(Unit, Module)).
384
385:- endif. 386
387 390
395
396expand_test(Name, Options0, Body,
397 [ 'unit test'(Name, Line, Options, Module:'unit body'(Id, Vars)),
398 ('unit body'(Id, Vars) :- !, Body)
399 ]) :-
400 source_location(_File, Line),
401 prolog_load_context(module, Module),
402 ( prolog_load_context(variable_names, Bindings)
403 -> true
404 ; Bindings = []
405 ),
406 atomic_list_concat([Name, '@line ', Line], Id),
407 term_variables(Options0, OptionVars0), sort(OptionVars0, OptionVars),
408 term_variables(Body, BodyVars0), sort(BodyVars0, BodyVars),
409 ord_intersection(OptionVars, BodyVars, VarList),
410 Vars =.. [vars|VarList],
411 ( is_list(Options0) 412 -> Options1 = Options0
413 ; Options1 = [Options0]
414 ),
415 maplist(expand_option(Bindings), Options1, Options2),
416 join_true_options(Options2, Options3),
417 map_sto_option(Options3, Options4),
418 valid_options(test_option, Options4),
419 valid_test_mode(Options4, Options).
420
421expand_option(_, Var, _) :-
422 var(Var),
423 !,
424 throw_error(instantiation_error,_).
425expand_option(Bindings, Cmp, true(Cond)) :-
426 cmp(Cmp),
427 !,
428 var_cmp(Bindings, Cmp, Cond).
429expand_option(_, error(X), throws(error(X, _))) :- !.
430expand_option(_, exception(X), throws(X)) :- !. 431expand_option(_, error(F,C), throws(error(F,C))) :- !. 432expand_option(_, true, true(true)) :- !.
433expand_option(_, O, O).
434
435cmp(_ == _).
436cmp(_ = _).
437cmp(_ =@= _).
438cmp(_ =:= _).
439
440var_cmp(Bindings, Expr, cmp(Name, Expr)) :-
441 arg(_, Expr, Var),
442 var(Var),
443 member(Name=V, Bindings),
444 V == Var,
445 !.
446var_cmp(_, Expr, Expr).
447
448join_true_options(Options0, Options) :-
449 partition(true_option, Options0, True, Rest),
450 True \== [],
451 !,
452 maplist(arg(1), True, Conds0),
453 flatten(Conds0, Conds),
454 Options = [true(Conds)|Rest].
455join_true_options(Options, Options).
456
457true_option(true(_)).
458
459valid_test_mode(Options0, Options) :-
460 include(test_mode, Options0, Tests),
461 ( Tests == []
462 -> Options = [true([true])|Options0]
463 ; Tests = [_]
464 -> Options = Options0
465 ; throw_error(plunit(incompatible_options, Tests), _)
466 ).
467
468test_mode(true(_)).
469test_mode(all(_)).
470test_mode(set(_)).
471test_mode(fail).
472test_mode(throws(_)).
473
474
476
477expand(end_of_file, _) :-
478 loading_unit(Unit, _, _, _),
479 !,
480 end_tests(Unit), 481 fail.
482expand((:-end_tests(_)), _) :-
483 !,
484 fail.
485expand(_Term, []) :-
486 \+ loading_tests.
487expand((test(Name) :- Body), Clauses) :-
488 !,
489 expand_test(Name, [], Body, Clauses).
490expand((test(Name, Options) :- Body), Clauses) :-
491 !,
492 expand_test(Name, Options, Body, Clauses).
493expand(test(Name), _) :-
494 !,
495 throw_error(existence_error(body, test(Name)), _).
496expand(test(Name, _Options), _) :-
497 !,
498 throw_error(existence_error(body, test(Name)), _).
499
500:- multifile
501 system:term_expansion/2. 502
503system:term_expansion(Term, Expanded) :-
504 ( loading_unit(_, _, File, _)
505 -> source_location(ThisFile, _),
506 ( File == ThisFile
507 -> true
508 ; source_file_property(ThisFile, included_in(File, _))
509 ),
510 expand(Term, Expanded)
511 ).
512
513
514 517
524
525valid_options(Pred, Options) :-
526 must_be(list, Options),
527 verify_options(Options, Pred).
528
529verify_options([], _).
530verify_options([H|T], Pred) :-
531 ( call(Pred, H)
532 -> verify_options(T, Pred)
533 ; throw_error(domain_error(Pred, H), _)
534 ).
535
536valid_options(Pred, Options0, Options, Rest) :-
537 must_be(list, Options0),
538 partition(Pred, Options0, Options, Rest).
539
543
544test_option(Option) :-
545 test_set_option(Option),
546 !.
547test_option(true(_)).
548test_option(fail).
549test_option(throws(_)).
550test_option(all(_)).
551test_option(set(_)).
552test_option(nondet).
553test_option(fixme(_)).
554test_option(forall(X)) :-
555 must_be(callable, X).
556test_option(timeout(Seconds)) :-
557 must_be(number, Seconds).
558
563
564test_set_option(blocked(X)) :-
565 must_be(ground, X).
566test_set_option(condition(X)) :-
567 must_be(callable, X).
568test_set_option(setup(X)) :-
569 must_be(callable, X).
570test_set_option(cleanup(X)) :-
571 must_be(callable, X).
572test_set_option(occurs_check(V)) :-
573 must_be(oneof([false,true,error]), V).
574test_set_option(concurrent(V)) :-
575 must_be(boolean, V),
576 print_message(informational, plunit(concurrent)).
577test_set_option(timeout(Seconds)) :-
578 must_be(number, Seconds).
579
580 583
584:- meta_predicate
585 reify_tmo(0, -, +),
586 reify(0, -),
587 capture_output(0,-),
588 capture_output(0,-,+),
589 got_messages(0,-). 590
592
593:- if(current_predicate(call_with_time_limit/2)). 594reify_tmo(Goal, Result, Options) :-
595 option(timeout(Time), Options),
596 Time > 0,
597 !,
598 reify(call_with_time_limit(Time, Goal), Result0),
599 ( Result0 = throw(time_limit_exceeded)
600 -> Result = throw(time_limit_exceeded(Time))
601 ; Result = Result0
602 ).
603:- endif. 604reify_tmo(Goal, Result, _Options) :-
605 reify(Goal, Result).
606
611
612reify(Goal, Result) :-
613 ( catch(Goal, E, true)
614 -> ( var(E)
615 -> Result = true
616 ; Result = throw(E)
617 )
618 ; Result = false
619 ).
620
627
628capture_output(Goal, Output) :-
629 current_test_flag(output, OutputMode),
630 capture_output(Goal, Output, [output(OutputMode)]).
631
632capture_output(Goal, Msgs-Output, Options) :-
633 option(output(How), Options, always),
634 ( How == always
635 -> call(Goal),
636 Msgs = false 637 ; with_output_to(string(Output), got_messages(Goal, Msgs),
638 [ capture([user_output, user_error]),
639 color(true)
640 ])
641 ).
642
644
645got_messages(Goal, Result) :-
646 ( current_prolog_flag(on_warning, status)
647 ; current_prolog_flag(on_error, status)
648 ), !,
649 nb_delete(plunit_got_message),
650 setup_call_cleanup(
651 asserta(( user:thread_message_hook(_Term, Kind, _Lines) :-
652 got_message(Kind), fail), Ref),
653 Goal,
654 erase(Ref)),
655 ( nb_current(plunit_got_message, true)
656 -> Result = true
657 ; Result = false
658 ).
659got_messages(Goal, false) :-
660 call(Goal).
661
662:- public got_message/1. 663got_message(warning) :-
664 current_prolog_flag(on_warning, status), !,
665 nb_setval(plunit_got_message, true).
666got_message(error) :-
667 current_prolog_flag(on_error, status), !,
668 nb_setval(plunit_got_message, true).
669
670
671 674
675:- dynamic
676 output_streams/2, 677 test_count/1, 678 passed/5, 679 failed/5, 680 timeout/5, 681 failed_assertion/7, 682 blocked/4, 683 fixme/5, 684 running/5, 685 forall_failures/2. 686
716
717run_tests :-
718 run_tests(all).
719
720run_tests(Set) :-
721 run_tests(Set, []).
722
723run_tests(all, Options) :-
724 !,
725 findall(Unit, current_test_unit(Unit,_), Units),
726 run_tests(Units, Options).
727run_tests(Set, Options) :-
728 valid_options(global_test_option, Options, Global, Rest),
729 current_test_flags(Old),
730 setup_call_cleanup(
731 set_test_options(Global),
732 ( flatten([Set], List),
733 maplist(runnable_tests, List, Units),
734 with_mutex(plunit, run_tests_sync(Units, Rest))
735 ),
736 set_test_options(Old)).
737
738run_tests_sync(Units0, Options) :-
739 cleanup,
740 count_tests(Units0, Units, Count),
741 asserta(test_count(Count)),
742 save_output_state,
743 setup_call_cleanup(
744 setup_trap_assertions(Ref),
745 call_time(setup_jobs_and_run_units(Count, Units, Summary, Options),
746 Time),
747 report_and_cleanup(Ref, Time, Options)),
748 ( option(summary(Summary), Options)
749 -> true
750 ; test_summary_passed(Summary) 751 ).
752
753setup_jobs_and_run_units(Count, Units, Summary, Options) :-
754 setup_call_cleanup(
755 setup_jobs(Count),
756 ( run_units(Units, Options),
757 test_summary(_All, Summary)
758 ),
759 cleanup_jobs).
760
765
766report_and_cleanup(Ref, Time, Options) :-
767 cleanup_trap_assertions(Ref),
768 report(Time, Options),
769 cleanup_after_test.
770
771
775
776run_units(Units, _Options) :-
777 maplist(schedule_unit, Units),
778 job_wait(_).
779
786
787:- det(runnable_tests/2). 788runnable_tests(Spec, Unit:RunnableTests) :-
789 unit_from_spec(Spec, Unit, Tests, Module, UnitOptions),
790 ( option(blocked(Reason), UnitOptions)
791 -> info(plunit(blocked(unit(Unit, Reason)))),
792 RunnableTests = []
793 ; \+ condition(Module, unit(Unit), UnitOptions)
794 -> RunnableTests = []
795 ; var(Tests)
796 -> findall(TestID,
797 runnable_test(Unit, _Test, Module, TestID),
798 RunnableTests)
799 ; flatten([Tests], TestList),
800 findall(TestID,
801 ( member(Test, TestList),
802 runnable_test(Unit,Test,Module, TestID)
803 ),
804 RunnableTests)
805 ).
806
807runnable_test(Unit, Name, Module, @(Test,Line)) :-
808 current_test(Unit, Name, Line, _Body, TestOptions),
809 ( option(blocked(Reason), TestOptions)
810 -> Test = blocked(Name, Reason)
811 ; condition(Module, test(Unit,Name,Line), TestOptions),
812 Test = Name
813 ).
814
815unit_from_spec(Unit0:Tests0, Unit, Tests, Module, Options), atom(Unit0) =>
816 Unit = Unit0,
817 Tests = Tests0,
818 ( current_unit(Unit, Module, _Supers, Options)
819 -> true
820 ; throw_error(existence_error(unit_test, Unit), _)
821 ).
822unit_from_spec(Unit0, Unit, _, Module, Options), atom(Unit0) =>
823 Unit = Unit0,
824 ( current_unit(Unit, Module, _Supers, Options)
825 -> true
826 ; throw_error(existence_error(unit_test, Unit), _)
827 ).
828
834
835count_tests(Units0, Units, Count) :-
836 count_tests(Units0, Units, 0, Count).
837
838count_tests([], T, C0, C) =>
839 T = [],
840 C = C0.
841count_tests([_:[]|T0], T, C0, C) =>
842 count_tests(T0, T, C0, C).
843count_tests([Unit:Tests|T0], T, C0, C) =>
844 partition(is_blocked, Tests, Blocked, Use),
845 maplist(assert_blocked(Unit), Blocked),
846 ( Use == []
847 -> count_tests(T0, T, C0, C)
848 ; length(Use, N),
849 C1 is C0+N,
850 T = [Unit:Use|T1],
851 count_tests(T0, T1, C1, C)
852 ).
853
854is_blocked(@(blocked(_,_),_)) => true.
855is_blocked(_) => fail.
856
857assert_blocked(Unit, @(blocked(Test, Reason), Line)) =>
858 assert(blocked(Unit, Test, Line, Reason)).
859
864
865run_unit(_Unit:[]) =>
866 true.
867run_unit(Unit:Tests) =>
868 unit_module(Unit, Module),
869 unit_options(Unit, UnitOptions),
870 ( setup(Module, unit(Unit), UnitOptions)
871 -> begin_unit(Unit),
872 call_time(run_unit_2(Unit, Tests), Time),
873 test_summary(Unit, Summary),
874 end_unit(Unit, Summary.put(time, Time)),
875 cleanup(Module, UnitOptions)
876 ; job_info(end(unit(Unit, _{error:setup_failed})))
877 ).
878
879begin_unit(Unit) :-
880 job_info(begin(unit(Unit))),
881 job_feedback(informational, begin(Unit)).
882
883end_unit(Unit, Summary) :-
884 job_info(end(unit(Unit, Summary))),
885 job_feedback(informational, end(Unit, Summary)).
886
887run_unit_2(Unit, Tests) :-
888 forall(member(Test, Tests),
889 run_test(Unit, Test)).
890
891
892unit_options(Unit, Options) :-
893 current_unit(Unit, _Module, _Supers, Options).
894
895
896cleanup :-
897 set_flag(plunit_test, 1),
898 retractall(output_streams(_,_)),
899 retractall(test_count(_)),
900 retractall(passed(_, _, _, _, _)),
901 retractall(failed(_, _, _, _, _)),
902 retractall(timeout(_, _, _, _, _)),
903 retractall(failed_assertion(_, _, _, _, _, _, _)),
904 retractall(blocked(_, _, _, _)),
905 retractall(fixme(_, _, _, _, _)),
906 retractall(running(_,_,_,_,_)),
907 retractall(forall_failures(_,_)).
908
909cleanup_after_test :-
910 ( current_test_flag(cleanup, true)
911 -> cleanup
912 ; true
913 ).
914
915
919
920run_tests_in_files(Files) :-
921 findall(Unit, unit_in_files(Files, Unit), Units),
922 ( Units == []
923 -> true
924 ; run_tests(Units)
925 ).
926
927unit_in_files(Files, Unit) :-
928 is_list(Files),
929 !,
930 member(F, Files),
931 absolute_file_name(F, Source,
932 [ file_type(prolog),
933 access(read),
934 file_errors(fail)
935 ]),
936 unit_file(Unit, Source).
937
938
939 942
946
947make_run_tests(Files) :-
948 current_test_flag(run, When),
949 ( When == make
950 -> run_tests_in_files(Files)
951 ; When == make(all)
952 -> run_tests
953 ; true
954 ).
955
956 959
960:- if(swi). 961
962:- dynamic prolog:assertion_failed/2. 963
964setup_trap_assertions(Ref) :-
965 asserta((prolog:assertion_failed(Reason, Goal) :-
966 test_assertion_failed(Reason, Goal)),
967 Ref).
968
969cleanup_trap_assertions(Ref) :-
970 erase(Ref).
971
972test_assertion_failed(Reason, Goal) :-
973 thread_self(Me),
974 running(Unit, Test, Line, Progress, Me),
975 ( catch(get_prolog_backtrace(10, Stack), _, fail),
976 assertion_location(Stack, AssertLoc)
977 -> true
978 ; AssertLoc = unknown
979 ),
980 report_failed_assertion(Unit:Test, Line, AssertLoc,
981 Progress, Reason, Goal),
982 assert_cyclic(failed_assertion(Unit, Test, Line, AssertLoc,
983 Progress, Reason, Goal)).
984
985assertion_location(Stack, File:Line) :-
986 append(_, [AssertFrame,CallerFrame|_], Stack),
987 prolog_stack_frame_property(AssertFrame,
988 predicate(prolog_debug:assertion/1)),
989 !,
990 prolog_stack_frame_property(CallerFrame, location(File:Line)).
991
992report_failed_assertion(UnitTest, Line, AssertLoc,
993 Progress, Reason, Goal) :-
994 print_message(
995 error,
996 plunit(failed_assertion(UnitTest, Line, AssertLoc,
997 Progress, Reason, Goal))).
998
999:- else. 1000
1001setup_trap_assertions(_).
1002cleanup_trap_assertions(_).
1003
1004:- endif. 1005
1006
1007 1010
1014
1015run_test(Unit, @(Test,Line)) :-
1016 unit_module(Unit, Module),
1017 Module:'unit test'(Test, Line, TestOptions, Body),
1018 unit_options(Unit, UnitOptions),
1019 run_test(Unit, Test, Line, UnitOptions, TestOptions, Body).
1020
1024
1025run_test(Unit, Name, Line, UnitOptions, Options, Body) :-
1026 option(forall(Generator), Options),
1027 !,
1028 unit_module(Unit, Module),
1029 start_test(Unit, @(Name,Line), Nth),
1030 State = state(0),
1031 call_time(forall(Module:Generator, 1032 ( incr_forall(State, I),
1033 run_test_once6(Unit, Name,
1034 forall(Generator, Nth-I), Line,
1035 UnitOptions, Options, Body)
1036 )),
1037 Time),
1038 arg(1, State, Generated),
1039 progress(Unit:Name, Nth, forall(end, Nth, Generated), Time).
1040run_test(Unit, Name, Line, UnitOptions, Options, Body) :-
1041 start_test(Unit, @(Name,Line), Nth),
1042 run_test_once6(Unit, Name, Nth, Line, UnitOptions, Options, Body).
1043
1044start_test(_Unit, _TestID, Nth) :-
1045 flag(plunit_test, Nth, Nth+1).
1046
1047incr_forall(State, I) :-
1048 arg(1, State, I0),
1049 I is I0+1,
1050 nb_setarg(1, State, I).
1051
1056
1057run_test_once6(Unit, Name, Progress, Line, UnitOptions, Options, Body) :-
1058 current_test_flag(timeout, DefTimeOut),
1059 current_test_flag(occurs_check, DefOccurs),
1060 inherit_option(timeout, Options, [UnitOptions], DefTimeOut, Options1),
1061 inherit_option(occurs_check, Options1, [UnitOptions], DefOccurs, Options2),
1062 run_test_once(Unit, Name, Progress, Line, Options2, Body).
1063
1064inherit_option(Name, Options0, Chain, Default, Options) :-
1065 Term =.. [Name,_Value],
1066 ( option(Term, Options0)
1067 -> Options = Options0
1068 ; member(Opts, Chain),
1069 option(Term, Opts)
1070 -> Options = [Term|Options0]
1071 ; Default == (-)
1072 -> Options = Options0
1073 ; Opt =.. [Name,Default],
1074 Options = [Opt|Options0]
1075 ).
1076
1081
1082run_test_once(Unit, Name, Progress, Line, Options, Body) :-
1083 option(occurs_check(Occurs), Options),
1084 !,
1085 begin_test(Unit, Name, Line, Progress),
1086 setup_call_cleanup(
1087 push_prolog_flag(occurs_check, Occurs),
1088 capture_output(run_test_6(Unit, Name, Line, Options, Body, Result),
1089 Output),
1090 pop_prolog_flag(occurs_check)),
1091 end_test(Unit, Name, Line, Progress),
1092 report_result(Result, Progress, Output, Options).
1093run_test_once(Unit, Name, Progress, Line, Options, Body) :-
1094 begin_test(Unit, Name, Line, Progress),
1095 capture_output(run_test_6(Unit, Name, Line, Options, Body, Result),
1096 Output),
1097 end_test(Unit, Name, Line, Progress),
1098 report_result(Result, Progress, Output, Options).
1099
1101
1102:- det(report_result/4). 1103report_result(failure(Unit, Name, Line, How, Time),
1104 Progress, Output, Options) =>
1105 failure(Unit, Name, Progress, Line, How, Time, Output, Options).
1106report_result(success(Unit, Name, Line, Determinism, Time),
1107 Progress, Output, Options) =>
1108 success(Unit, Name, Progress, Line, Determinism, Time, Output, Options).
1109report_result(setup_failed(Unit, Name, Line, Time, Output, Result),
1110 Progress, _Output, Options) =>
1111 failure(Unit, Name, Progress, Line,
1112 setup_failed(Result), Time, Output, Options).
1113
1133
1134run_test_6(Unit, Name, Line, Options, Body, Result) :-
1135 option(setup(Setup), Options),
1136 !,
1137 unit_module(Unit, Module),
1138 capture_output(call_time(reify(call_ex(Module, Setup), SetupResult),
1139 Time),
1140 Output),
1141 ( SetupResult == true
1142 -> run_test_7(Unit, Name, Line, Options, Body, Result),
1143 cleanup(Module, Options)
1144 ; Result = setup_failed(Unit, Name, Line, Time, Output, SetupResult)
1145 ).
1146run_test_6(Unit, Name, Line, Options, Body, Result) :-
1147 unit_module(Unit, Module),
1148 run_test_7(Unit, Name, Line, Options, Body, Result),
1149 cleanup(Module, Options).
1150
1157
1158run_test_7(Unit, Name, Line, Options, Body, Result) :-
1159 option(true(Cmp), Options), 1160 !,
1161 unit_module(Unit, Module),
1162 call_time(reify_tmo(call_det(Module:Body, Det), Result0, Options), Time),
1163 ( Result0 == true
1164 -> cmp_true(Cmp, Module, CmpResult),
1165 ( CmpResult == []
1166 -> Result = success(Unit, Name, Line, Det, Time)
1167 ; Result = failure(Unit, Name, Line, CmpResult, Time)
1168 )
1169 ; Result0 == false
1170 -> Result = failure(Unit, Name, Line, failed, Time)
1171 ; Result0 = throw(E2)
1172 -> Result = failure(Unit, Name, Line, throw(E2), Time)
1173 ).
1174run_test_7(Unit, Name, Line, Options, Body, Result) :-
1175 option(fail, Options), 1176 !,
1177 unit_module(Unit, Module),
1178 call_time(reify_tmo(Module:Body, Result0, Options), Time),
1179 ( Result0 == true
1180 -> Result = failure(Unit, Name, Line, succeeded, Time)
1181 ; Result0 == false
1182 -> Result = success(Unit, Name, Line, true, Time)
1183 ; Result0 = throw(E)
1184 -> Result = failure(Unit, Name, Line, throw(E), Time)
1185 ).
1186run_test_7(Unit, Name, Line, Options, Body, Result) :-
1187 option(throws(Expect), Options), 1188 !,
1189 unit_module(Unit, Module),
1190 call_time(reify_tmo(Module:Body, Result0, Options), Time),
1191 ( Result0 == true
1192 -> Result = failure(Unit, Name, Line, no_exception, Time)
1193 ; Result0 == false
1194 -> Result = failure(Unit, Name, Line, failed, Time)
1195 ; Result0 = throw(E)
1196 -> ( match_error(Expect, E)
1197 -> Result = success(Unit, Name, Line, true, Time)
1198 ; Result = failure(Unit, Name, Line, wrong_error(Expect, E), Time)
1199 )
1200 ).
1201run_test_7(Unit, Name, Line, Options, Body, Result) :-
1202 option(all(Answer), Options), 1203 !,
1204 nondet_test(all(Answer), Unit, Name, Line, Options, Body, Result).
1205run_test_7(Unit, Name, Line, Options, Body, Result) :-
1206 option(set(Answer), Options), 1207 !,
1208 nondet_test(set(Answer), Unit, Name, Line, Options, Body, Result).
1209
1213
1214nondet_test(Expected, Unit, Name, Line, Options, Body, Result) :-
1215 unit_module(Unit, Module),
1216 result_vars(Expected, Vars),
1217 ( call_time(reify_tmo(findall(Vars, Module:Body, Bindings),
1218 Result0, Options), Time)
1219 -> ( Result0 == true
1220 -> ( nondet_compare(Expected, Bindings, Unit, Name, Line)
1221 -> Result = success(Unit, Name, Line, true, Time)
1222 ; Result = failure(Unit, Name, Line,
1223 [wrong_answer(Expected, Bindings)], Time)
1224 )
1225 ; Result0 = throw(E)
1226 -> Result = failure(Unit, Name, Line, throw(E), Time)
1227 )
1228 ).
1229
1230cmp_true([], _, L) =>
1231 L = [].
1232cmp_true([Cmp|T], Module, L) =>
1233 E = error(Formal,_),
1234 cmp_goal(Cmp, Goal),
1235 ( catch(Module:Goal, E, true)
1236 -> ( var(Formal)
1237 -> cmp_true(T, Module, L)
1238 ; L = [cmp_error(Cmp,E)|L1],
1239 cmp_true(T, Module, L1)
1240 )
1241 ; L = [wrong_answer(Cmp)|L1],
1242 cmp_true(T, Module, L1)
1243 ).
1244
1245cmp_goal(cmp(_Var, Expr), Goal) => Goal = Expr.
1246cmp_goal(Expr, Goal) => Goal = Expr.
1247
1248
1253
1254result_vars(Expected, Vars) :-
1255 arg(1, Expected, CmpOp),
1256 arg(1, CmpOp, Vars).
1257
1265
1266nondet_compare(all(Cmp), Bindings, _Unit, _Name, _Line) :-
1267 cmp(Cmp, _Vars, Op, Values),
1268 cmp_list(Values, Bindings, Op).
1269nondet_compare(set(Cmp), Bindings0, _Unit, _Name, _Line) :-
1270 cmp(Cmp, _Vars, Op, Values0),
1271 sort(Bindings0, Bindings),
1272 sort(Values0, Values),
1273 cmp_list(Values, Bindings, Op).
1274
1275cmp_list([], [], _Op).
1276cmp_list([E0|ET], [V0|VT], Op) :-
1277 call(Op, E0, V0),
1278 cmp_list(ET, VT, Op).
1279
1281
1282cmp(Var == Value, Var, ==, Value).
1283cmp(Var =:= Value, Var, =:=, Value).
1284cmp(Var = Value, Var, =, Value).
1285:- if(swi). 1286cmp(Var =@= Value, Var, =@=, Value).
1287:- else. 1288:- if(sicstus). 1289cmp(Var =@= Value, Var, variant, Value). 1290:- endif. 1291:- endif. 1292
1293
1298
1299:- if((swi;sicstus)). 1300call_det(Goal, Det) :-
1301 call_cleanup(Goal,Det0=true),
1302 ( var(Det0) -> Det = false ; Det = true ).
1303:- else. 1304call_det(Goal, true) :-
1305 call(Goal).
1306:- endif. 1307
1312
1313match_error(Expect, Rec) :-
1314 subsumes_term(Expect, Rec).
1315
1326
1327setup(Module, Context, Options) :-
1328 option(setup(Setup), Options),
1329 !,
1330 capture_output(reify(call_ex(Module, Setup), Result), Output),
1331 ( Result == true
1332 -> true
1333 ; print_message(error,
1334 plunit(error(setup, Context, Output, Result))),
1335 fail
1336 ).
1337setup(_,_,_).
1338
1342
1343condition(Module, Context, Options) :-
1344 option(condition(Cond), Options),
1345 !,
1346 capture_output(reify(call_ex(Module, Cond), Result), Output),
1347 ( Result == true
1348 -> true
1349 ; Result == false
1350 -> fail
1351 ; print_message(error,
1352 plunit(error(condition, Context, Output, Result))),
1353 fail
1354 ).
1355condition(_, _, _).
1356
1357
1361
1362call_ex(Module, Goal) :-
1363 Module:(expand_goal(Goal, GoalEx),
1364 GoalEx).
1365
1370
1371cleanup(Module, Options) :-
1372 option(cleanup(Cleanup), Options, true),
1373 ( catch(call_ex(Module, Cleanup), E, true)
1374 -> ( var(E)
1375 -> true
1376 ; print_message(warning, E)
1377 )
1378 ; print_message(warning, goal_failed(Cleanup, '(cleanup handler)'))
1379 ).
1380
1381success(Unit, Name, Progress, Line, Det, Time, _Output, Options) :-
1382 option(fixme(Reason), Options),
1383 !,
1384 ( ( Det == true
1385 ; memberchk(nondet, Options)
1386 )
1387 -> progress(Unit:Name, Progress, fixme(passed), Time),
1388 Ok = passed
1389 ; progress(Unit:Name, Progress, fixme(nondet), Time),
1390 Ok = nondet
1391 ),
1392 flush_output(user_error),
1393 assert(fixme(Unit, Name, Line, Reason, Ok)).
1394success(Unit, Name, Progress, Line, _, Time, Output, Options) :-
1395 failed_assertion(Unit, Name, Line, _,Progress,_,_),
1396 !,
1397 failure(Unit, Name, Progress, Line, assertion, Time, Output, Options).
1398success(Unit, Name, Progress, Line, _, Time, Output, Options) :-
1399 Output = true-_,
1400 !,
1401 failure(Unit, Name, Progress, Line, message, Time, Output, Options).
1402success(Unit, Name, Progress, Line, Det, Time, _Output, Options) :-
1403 assert(passed(Unit, Name, Line, Det, Time)),
1404 ( ( Det == true
1405 ; memberchk(nondet, Options)
1406 )
1407 -> progress(Unit:Name, Progress, passed, Time)
1408 ; unit_file(Unit, File),
1409 print_message(warning, plunit(nondet(File:Line, Unit:Name, Progress)))
1410 ).
1411
1416
1417failure(Unit, Name, Progress, Line, _, Time, _Output, Options),
1418 memberchk(fixme(Reason), Options) =>
1419 assert(fixme(Unit, Name, Line, Reason, failed)),
1420 progress(Unit:Name, Progress, fixme(failed), Time).
1421failure(Unit, Name, Progress, Line, time_limit_exceeded(Limit), Time,
1422 Output, Options) =>
1423 assert_cyclic(timeout(Unit, Name, Line, Limit, Time)),
1424 progress(Unit:Name, Progress, timeout(Limit), Time),
1425 report_failure(Unit, Name, Progress, Line, timeout(Limit), Time, Output, Options).
1426failure(Unit, Name, Progress, Line, E, Time, Output, Options) =>
1427 assert_cyclic(failed(Unit, Name, Line, E, Time)),
1428 progress(Unit:Name, Progress, failed, Time),
1429 report_failure(Unit, Name, Progress, Line, E, Time, Output, Options).
1430
1438
1439:- if(swi). 1440assert_cyclic(Term) :-
1441 acyclic_term(Term),
1442 !,
1443 assert(Term).
1444assert_cyclic(Term) :-
1445 Term =.. [Functor|Args],
1446 recorda(cyclic, Args, Id),
1447 functor(Term, _, Arity),
1448 length(NewArgs, Arity),
1449 Head =.. [Functor|NewArgs],
1450 assert((Head :- recorded(_, Var, Id), Var = NewArgs)).
1451:- else. 1452:- if(sicstus). 1453:- endif. 1454assert_cyclic(Term) :-
1455 assert(Term).
1456:- endif. 1457
1458
1459 1462
1463:- if(current_prolog_flag(threads, true)). 1464
1465:- dynamic
1466 job_data/2, 1467 scheduled_unit/1. 1468
1469schedule_unit(_:[]) :-
1470 !.
1471schedule_unit(UnitAndTests) :-
1472 UnitAndTests = Unit:_Tests,
1473 job_data(Queue, _),
1474 !,
1475 assertz(scheduled_unit(Unit)),
1476 thread_send_message(Queue, unit(UnitAndTests)).
1477schedule_unit(Unit) :-
1478 run_unit(Unit).
1479
1483
1484setup_jobs(Count) :-
1485 ( current_test_flag(jobs, Jobs0),
1486 integer(Jobs0)
1487 -> true
1488 ; current_prolog_flag(cpu_count, Jobs0)
1489 ),
1490 Jobs is min(Count, Jobs0),
1491 Jobs > 1,
1492 !,
1493 message_queue_create(Q, [alias(plunit_jobs)]),
1494 length(TIDs, Jobs),
1495 foldl(create_plunit_job(Q), TIDs, 1, _),
1496 asserta(job_data(Q, TIDs)),
1497 job_feedback(informational, jobs(Jobs)).
1498setup_jobs(_) :-
1499 job_feedback(informational, jobs(1)).
1500
1501create_plunit_job(Q, TID, N, N1) :-
1502 N1 is N + 1,
1503 atom_concat(plunit_job_, N, Alias),
1504 thread_create(plunit_job(Q), TID, [alias(Alias)]).
1505
1506plunit_job(Queue) :-
1507 repeat,
1508 ( catch(thread_get_message(Queue, Job,
1509 [ timeout(10)
1510 ]),
1511 error(_,_), fail)
1512 -> job(Job),
1513 fail
1514 ; !
1515 ).
1516
1517job(unit(Unit:Tests)) =>
1518 run_unit(Unit:Tests).
1519job(test(Unit, Test)) =>
1520 run_test(Unit, Test).
1521
1522cleanup_jobs :-
1523 retract(job_data(Queue, TIDSs)),
1524 !,
1525 message_queue_destroy(Queue),
1526 maplist(thread_join, TIDSs).
1527cleanup_jobs.
1528
1532
1533job_wait(Unit) :-
1534 thread_wait(\+ scheduled_unit(Unit),
1535 [ wait_preds([scheduled_unit/1]),
1536 timeout(1)
1537 ]),
1538 !.
1539job_wait(Unit) :-
1540 job_data(_Queue, TIDs),
1541 member(TID, TIDs),
1542 thread_property(TID, status(running)),
1543 !,
1544 job_wait(Unit).
1545job_wait(_).
1546
1547
1548job_info(begin(unit(Unit))) =>
1549 print_message(silent, plunit(begin(Unit))).
1550job_info(end(unit(Unit, Summary))) =>
1551 retractall(scheduled_unit(Unit)),
1552 print_message(silent, plunit(end(Unit, Summary))).
1553
1554:- else. 1555
1556schedule_unit(Unit) :-
1557 run_unit(Unit).
1558
1559setup_jobs(_) :-
1560 print_message(silent, plunit(jobs(1))).
1561cleanup_jobs.
1562job_wait(_).
1563job_info(_).
1564
1565:- endif. 1566
1567
1568
1569 1572
1583
1584begin_test(Unit, Test, Line, Progress) :-
1585 thread_self(Me),
1586 assert(running(Unit, Test, Line, Progress, Me)),
1587 unit_file(Unit, File),
1588 test_count(Total),
1589 job_feedback(information, begin(Unit:Test, File:Line, Progress/Total)).
1590
1591end_test(Unit, Test, Line, Progress) :-
1592 thread_self(Me),
1593 retractall(running(_,_,_,_,Me)),
1594 unit_file(Unit, File),
1595 test_count(Total),
1596 job_feedback(information, end(Unit:Test, File:Line, Progress/Total)).
1597
1601
1602running_tests :-
1603 running_tests(Running),
1604 print_message(informational, plunit(running(Running))).
1605
1606running_tests(Running) :-
1607 test_count(Total),
1608 findall(running(Unit:Test, File:Line, Progress/Total, Thread),
1609 ( running(Unit, Test, Line, Progress, Thread),
1610 unit_file(Unit, File)
1611 ), Running).
1612
1613
1617
1618current_test(Unit, Test, Line, Body, Options) :-
1619 current_unit(Unit, Module, _Supers, _UnitOptions),
1620 Module:'unit test'(Test, Line, Options, Body).
1621
1625
1626current_test_unit(Unit, UnitOptions) :-
1627 current_unit(Unit, _Module, _Supers, UnitOptions).
1628
1629
1630count(Goal, Count) :-
1631 aggregate_all(count, Goal, Count).
1632
1637
1638test_summary(Unit, Summary) :-
1639 count(failed(Unit, _0Test, _0Line, _Reason, _0Time), Failed),
1640 count(timeout(Unit, _0Test, _0Line, _Limit, _0Time), Timeout),
1641 count(passed(Unit, _0Test, _0Line, _Det, _0Time), Passed),
1642 count(blocked(Unit, _0Test, _0Line, _0Reason), Blocked),
1643 count(fixme(Unit, _0Test, _0Line, _0Reason, _0How), Fixme),
1644 test_count(Total),
1645 Summary = plunit{total:Total,
1646 passed:Passed,
1647 failed:Failed,
1648 timeout:Timeout,
1649 blocked:Blocked,
1650 fixme:Fixme}.
1651
1652test_summary_passed(Summary) :-
1653 _{failed: 0} :< Summary.
1654
1658
1659report(Time, _Options) :-
1660 test_summary(_, Summary),
1661 print_message(silent, plunit(Summary)),
1662 _{ passed:Passed,
1663 failed:Failed,
1664 timeout:Timeout,
1665 blocked:Blocked,
1666 fixme:Fixme
1667 } :< Summary,
1668 ( Passed+Failed+Timeout+Blocked+Fixme =:= 0
1669 -> info(plunit(no_tests))
1670 ; Failed+Timeout =:= 0
1671 -> report_blocked(Blocked),
1672 report_fixme,
1673 test_count(Total),
1674 info(plunit(all_passed(Total, Passed, Time)))
1675 ; report_blocked(Blocked),
1676 report_fixme,
1677 report_failed(Failed),
1678 report_timeout(Timeout),
1679 info(plunit(passed(Passed))),
1680 info(plunit(total_time(Time)))
1681 ).
1682
1683report_blocked(0) =>
1684 true.
1685report_blocked(Blocked) =>
1686 findall(blocked(Unit:Name, File:Line, Reason),
1687 ( blocked(Unit, Name, Line, Reason),
1688 unit_file(Unit, File)
1689 ),
1690 BlockedTests),
1691 info(plunit(blocked(Blocked, BlockedTests))).
1692
1693report_failed(Failed) :-
1694 print_message(error, plunit(failed(Failed))).
1695
1696report_timeout(Count) :-
1697 print_message(warning, plunit(timeout(Count))).
1698
1699report_fixme :-
1700 report_fixme(_,_,_).
1701
1702report_fixme(TuplesF, TuplesP, TuplesN) :-
1703 fixme(failed, TuplesF, Failed),
1704 fixme(passed, TuplesP, Passed),
1705 fixme(nondet, TuplesN, Nondet),
1706 print_message(informational, plunit(fixme(Failed, Passed, Nondet))).
1707
1708
1709fixme(How, Tuples, Count) :-
1710 findall(fixme(Unit, Name, Line, Reason, How),
1711 fixme(Unit, Name, Line, Reason, How), Tuples),
1712 length(Tuples, Count).
1713
1714report_failure(Unit, Name, Progress, Line, Error,
1715 Time, Output, _Options) =>
1716 test_count(Total),
1717 job_feedback(error, failed(Unit:Name, Progress/Total, Line,
1718 Error, Time, Output)).
1719
1720
1725
1726test_report(fixme) :-
1727 !,
1728 report_fixme(TuplesF, TuplesP, TuplesN),
1729 append([TuplesF, TuplesP, TuplesN], Tuples),
1730 print_message(informational, plunit(fixme(Tuples))).
1731test_report(What) :-
1732 throw_error(domain_error(report_class, What), _).
1733
1734
1735 1738
1743
1744unit_file(Unit, File), nonvar(Unit) =>
1745 unit_file_(Unit, File),
1746 !.
1747unit_file(Unit, File) =>
1748 unit_file_(Unit, File).
1749
1750unit_file_(Unit, File) :-
1751 current_unit(Unit, Module, _Context, _Options),
1752 module_property(Module, file(File)).
1753unit_file_(Unit, PlFile) :-
1754 test_file_for(TestFile, PlFile),
1755 module_property(Module, file(TestFile)),
1756 current_unit(Unit, Module, _Context, _Options).
1757
1758
1759 1762
1767
1768load_test_files(_Options) :-
1769 State = state(0,0),
1770 ( source_file(File),
1771 file_name_extension(Base, Old, File),
1772 Old \== plt,
1773 file_name_extension(Base, plt, TestFile),
1774 exists_file(TestFile),
1775 inc_arg(1, State),
1776 ( test_file_for(TestFile, File)
1777 -> true
1778 ; load_files(TestFile,
1779 [ if(changed),
1780 imports([])
1781 ]),
1782 inc_arg(2, State),
1783 asserta(test_file_for(TestFile, File))
1784 ),
1785 fail
1786 ; State = state(Total, Loaded),
1787 print_message(informational, plunit(test_files(Total, Loaded)))
1788 ).
1789
1790inc_arg(Arg, State) :-
1791 arg(Arg, State, N0),
1792 N is N0+1,
1793 nb_setarg(Arg, State, N).
1794
1795
1796 1799
1804
1805info(Term) :-
1806 message_level(Level),
1807 print_message(Level, Term).
1808
1823
1824progress(UnitTest, _Progress, forall(end, Nth, FTotal), Time) =>
1825 ( retract(forall_failures(Nth, FFailed))
1826 -> true
1827 ; FFailed = 0
1828 ),
1829 test_count(Total),
1830 job_feedback(information, progress(UnitTest, forall(FTotal,FFailed), Nth/Total, Time)).
1831progress(UnitTest, Progress, Result, Time), Progress = forall(_Gen, Nth-_I) =>
1832 with_mutex(plunit_forall_counter,
1833 update_forall_failures(Nth, Result)),
1834 test_count(Total),
1835 job_feedback(information, progress(UnitTest, Result, Progress/Total, Time)).
1836progress(UnitTest, Progress, Result, Time) =>
1837 test_count(Total),
1838 job_feedback(information, progress(UnitTest, Result, Progress/Total, Time)).
1839
1840update_forall_failures(_Nth, passed) =>
1841 true.
1842update_forall_failures(Nth, _) =>
1843 ( retract(forall_failures(Nth, Failed0))
1844 -> true
1845 ; Failed0 = 0
1846 ),
1847 Failed is Failed0+1,
1848 asserta(forall_failures(Nth, Failed)).
1849
1850message_level(Level) :-
1851 ( current_test_flag(silent, true)
1852 -> Level = silent
1853 ; Level = informational
1854 ).
1855
1856locationprefix(File:Line) -->
1857 !,
1858 [ url(File:Line), ':'-[], nl, ' ' ].
1859locationprefix(test(Unit,_Test,Line)) -->
1860 !,
1861 { unit_file(Unit, File) },
1862 locationprefix(File:Line).
1863locationprefix(unit(Unit)) -->
1864 !,
1865 [ 'PL-Unit: unit ~w: '-[Unit] ].
1866locationprefix(FileLine) -->
1867 { throw_error(type_error(locationprefix,FileLine), _) }.
1868
1869:- discontiguous
1870 message//1. 1871:- '$hide'(message//1). 1872
1873message(error(context_error(plunit_close(Name, -)), _)) -->
1874 [ 'PL-Unit: cannot close unit ~w: no open unit'-[Name] ].
1875message(error(context_error(plunit_close(Name, Start)), _)) -->
1876 [ 'PL-Unit: cannot close unit ~w: current unit is ~w'-[Name, Start] ].
1877message(plunit(nondet(Pos, Test, Progress))) -->
1878 locationprefix(Pos),
1879 test_name(Test, Progress),
1880 [ ': Test succeeded with choicepoint'-[] ].
1881message(error(plunit(incompatible_options, Tests), _)) -->
1882 [ 'PL-Unit: incompatible test-options: ~p'-[Tests] ].
1883message(plunit(sto(true))) -->
1884 [ 'Option sto(true) is ignored. See `occurs_check` option.'-[] ].
1885message(plunit(test_files(Total, Loaded))) -->
1886 [ 'Found ~D .plt test files, loaded ~D'-[Total, Loaded] ].
1887
1888 1889message(plunit(jobs(1))) -->
1890 !.
1891message(plunit(jobs(N))) -->
1892 [ 'Testing with ~D parallel jobs'-[N] ].
1893message(plunit(begin(_Unit))) -->
1894 { tty_feedback },
1895 !.
1896message(plunit(begin(Unit))) -->
1897 [ 'Start unit: ~w~n'-[Unit], flush ].
1898message(plunit(end(_Unit, _Summary))) -->
1899 { tty_feedback },
1900 !.
1901message(plunit(end(Unit, Summary))) -->
1902 ( {test_summary_passed(Summary)}
1903 -> [ 'End unit ~w: passed (~3f sec CPU)'-[Unit, Summary.time.cpu] ]
1904 ; [ ansi(error, 'End unit ~w: **FAILED (~3f sec CPU)', [Unit, Summary.time.cpu]) ]
1905 ).
1906message(plunit(blocked(unit(Unit, Reason)))) -->
1907 [ 'PL-Unit: ~w blocked: ~w'-[Unit, Reason] ].
1908message(plunit(running([]))) -->
1909 !,
1910 [ 'PL-Unit: no tests running' ].
1911message(plunit(running([One]))) -->
1912 !,
1913 [ 'PL-Unit: running ' ],
1914 running(One).
1915message(plunit(running(More))) -->
1916 !,
1917 [ 'PL-Unit: running tests:', nl ],
1918 running(More).
1919message(plunit(fixme([]))) --> !.
1920message(plunit(fixme(Tuples))) -->
1921 !,
1922 fixme_message(Tuples).
1923message(plunit(total_time(Time))) -->
1924 [ 'Test run completed'-[] ],
1925 test_time(Time).
1926
1927 1928message(plunit(blocked(1, Tests))) -->
1929 !,
1930 [ 'one test is blocked'-[] ],
1931 blocked_tests(Tests).
1932message(plunit(blocked(N, Tests))) -->
1933 [ '~D tests are blocked'-[N] ],
1934 blocked_tests(Tests).
1935
1936blocked_tests(Tests) -->
1937 { current_test_flag(show_blocked, true) },
1938 !,
1939 [':'-[]],
1940 list_blocked(Tests).
1941blocked_tests(_) -->
1942 [ ' (use run_tests/2 with ', ansi(code, 'show_blocked(true)', []),
1943 ' for details)'-[]
1944 ].
1945
1946list_blocked([]) --> !.
1947list_blocked([blocked(Unit:Test, Pos, Reason)|T]) -->
1948 [nl],
1949 locationprefix(Pos),
1950 test_name(Unit:Test, -),
1951 [ ': ~w'-[Reason] ],
1952 list_blocked(T).
1953
1954 1955message(plunit(no_tests)) -->
1956 !,
1957 [ 'No tests to run' ].
1958message(plunit(all_passed(1, 1, Time))) -->
1959 !,
1960 [ 'test passed' ],
1961 test_time(Time).
1962message(plunit(all_passed(Total, Total, Time))) -->
1963 !,
1964 [ 'All ~D tests passed'-[Total] ],
1965 test_time(Time).
1966message(plunit(all_passed(Total, Count, Time))) -->
1967 !,
1968 { SubTests is Count-Total },
1969 [ 'All ~D (+~D sub-tests) tests passed'- [Total, SubTests] ],
1970 test_time(Time).
1971
1972test_time(Time) -->
1973 { var(Time) }, !.
1974test_time(Time) -->
1975 [ ' in ~3f seconds (~3f cpu)'-[Time.wall, Time.cpu] ].
1976
1977message(plunit(passed(Count))) -->
1978 !,
1979 [ '~D tests passed'-[Count] ].
1980message(plunit(failed(0))) -->
1981 !,
1982 [].
1983message(plunit(failed(1))) -->
1984 !,
1985 [ '1 test failed'-[] ].
1986message(plunit(failed(N))) -->
1987 [ '~D tests failed'-[N] ].
1988message(plunit(timeout(0))) -->
1989 !,
1990 [].
1991message(plunit(timeout(N))) -->
1992 [ '~D tests timed out'-[N] ].
1993message(plunit(fixme(0,0,0))) -->
1994 [].
1995message(plunit(fixme(Failed,0,0))) -->
1996 !,
1997 [ 'all ~D tests flagged FIXME failed'-[Failed] ].
1998message(plunit(fixme(Failed,Passed,0))) -->
1999 [ 'FIXME: ~D failed; ~D passed'-[Failed, Passed] ].
2000message(plunit(fixme(Failed,Passed,Nondet))) -->
2001 { TotalPassed is Passed+Nondet },
2002 [ 'FIXME: ~D failed; ~D passed; (~D nondet)'-
2003 [Failed, TotalPassed, Nondet] ].
2004
2005message(plunit(begin(Unit:Test, _Location, Progress))) -->
2006 { tty_columns(SummaryWidth, _Margin),
2007 test_name_summary(Unit:Test, SummaryWidth, NameS),
2008 progress_string(Progress, ProgressS)
2009 },
2010 ( { tty_feedback,
2011 tty_clear_to_eol(CE)
2012 }
2013 -> [ at_same_line, '\r[~w] ~w ..~w'-[ProgressS, NameS,
2014 CE], flush ]
2015 ; { jobs(_) }
2016 -> [ '[~w] ~w ..'-[ProgressS, NameS] ]
2017 ; [ '[~w] ~w ..'-[ProgressS, NameS], flush ]
2018 ).
2019message(plunit(end(_UnitTest, _Location, _Progress))) -->
2020 [].
2021message(plunit(progress(_UnitTest, Status, _Progress, _Time))) -->
2022 { Status = forall(_Gen,_NthI)
2023 ; Status == assertion
2024 },
2025 !.
2026message(plunit(progress(Unit:Test, Status, Progress, Time))) -->
2027 { jobs(_),
2028 !,
2029 tty_columns(SummaryWidth, Margin),
2030 test_name_summary(Unit:Test, SummaryWidth, NameS),
2031 progress_string(Progress, ProgressS),
2032 progress_tag(Status, Tag, _Keep, Style)
2033 },
2034 [ ansi(Style, '[~w] ~w ~`.t ~w (~3f sec)~*|',
2035 [ProgressS, NameS, Tag, Time.wall, Margin]) ].
2036message(plunit(progress(_UnitTest, Status, _Progress, Time))) -->
2037 { tty_columns(_SummaryWidth, Margin),
2038 progress_tag(Status, Tag, _Keep, Style)
2039 },
2040 [ at_same_line, ansi(Style, '~`.t ~w (~3f sec)~*|',
2041 [Tag, Time.wall, Margin]) ],
2042 ( { tty_feedback }
2043 -> [flush]
2044 ; []
2045 ).
2046message(plunit(failed(Unit:Test, Progress, Line, Failure, _Time, Output))) -->
2047 { unit_file(Unit, File) },
2048 locationprefix(File:Line),
2049 test_name(Unit:Test, Progress),
2050 [': '-[] ],
2051 failure(Failure),
2052 test_output(Output).
2053message(plunit(timeout(Unit:Test, Progress, Line, Limit, Output))) -->
2054 { unit_file(Unit, File) },
2055 locationprefix(File:Line),
2056 test_name(Unit:Test, Progress),
2057 [': '-[] ],
2058 timeout(Limit),
2059 test_output(Output).
2060:- if(swi). 2061message(plunit(failed_assertion(Unit:Test, Line, AssertLoc,
2062 Progress, Reason, Goal))) -->
2063 { unit_file(Unit, File) },
2064 locationprefix(File:Line),
2065 test_name(Unit:Test, Progress),
2066 [ ': assertion'-[] ],
2067 assertion_location(AssertLoc, File),
2068 assertion_reason(Reason), ['\n\t'],
2069 assertion_goal(Unit, Goal).
2070
2071assertion_location(File:Line, File) -->
2072 [ ' at line ~w'-[Line] ].
2073assertion_location(File:Line, _) -->
2074 [ ' at ', url(File:Line) ].
2075assertion_location(unknown, _) -->
2076 [].
2077
2078assertion_reason(fail) -->
2079 !,
2080 [ ' failed'-[] ].
2081assertion_reason(Error) -->
2082 { message_to_string(Error, String) },
2083 [ ' raised "~w"'-[String] ].
2084
2085assertion_goal(Unit, Goal) -->
2086 { unit_module(Unit, Module),
2087 unqualify(Goal, Module, Plain)
2088 },
2089 [ 'Assertion: ~p'-[Plain] ].
2090
2091unqualify(Var, _, Var) :-
2092 var(Var),
2093 !.
2094unqualify(M:Goal, Unit, Goal) :-
2095 nonvar(M),
2096 unit_module(Unit, M),
2097 !.
2098unqualify(M:Goal, _, Goal) :-
2099 callable(Goal),
2100 predicate_property(M:Goal, imported_from(system)),
2101 !.
2102unqualify(Goal, _, Goal).
2103
2104test_output(Msgs-String) -->
2105 { nonvar(Msgs) },
2106 !,
2107 test_output(String).
2108test_output("") --> [].
2109test_output(Output) -->
2110 [ ansi(code, '~N~s', [Output]) ].
2111
2112:- endif. 2113 2114message(plunit(error(Where, Context, _Output, throw(Exception)))) -->
2115 locationprefix(Context),
2116 { message_to_string(Exception, String) },
2117 [ 'error in ~w: ~w'-[Where, String] ].
2118message(plunit(error(Where, Context, _Output, false))) -->
2119 locationprefix(Context),
2120 [ 'setup failed in ~w'-[Where] ].
2121
2122 2123message(plunit(test_output(_, Output))) -->
2124 [ '~s'-[Output] ].
2125 2126:- if(swi). 2127message(interrupt(begin)) -->
2128 { thread_self(Me),
2129 running(Unit, Test, Line, Progress, Me),
2130 !,
2131 unit_file(Unit, File),
2132 restore_output_state
2133 },
2134 [ 'Interrupted test '-[] ],
2135 running(running(Unit:Test, File:Line, Progress, Me)),
2136 [nl],
2137 '$messages':prolog_message(interrupt(begin)).
2138message(interrupt(begin)) -->
2139 '$messages':prolog_message(interrupt(begin)).
2140:- endif. 2141
2142message(concurrent) -->
2143 [ 'concurrent(true) at the level of units is currently ignored.', nl,
2144 'See set_test_options/1 with jobs(Count) for concurrent testing.'
2145 ].
2146
2147test_name(Name, forall(Generator, _Nth-I)/_Total) -->
2148 !,
2149 test_name(Name, -),
2150 [ ' (~d-th forall generator = '-[I],
2151 ansi(code, '~p', [Generator]), ')'-[]
2152 ].
2153test_name(Name, _) -->
2154 !,
2155 [ 'test ', ansi(code, '~q', [Name]) ].
2156
2157running(running(Unit:Test, File:Line, _Progress, Thread)) -->
2158 thread(Thread),
2159 [ '~q:~q at '-[Unit, Test], url(File:Line) ].
2160running([H|T]) -->
2161 ['\t'], running(H),
2162 ( {T == []}
2163 -> []
2164 ; [nl], running(T)
2165 ).
2166
2167thread(main) --> !.
2168thread(Other) -->
2169 [' [~w] '-[Other] ].
2170
2171:- if(swi). 2172write_term(T, OPS) -->
2173 ['~W'-[T,OPS] ].
2174:- else. 2175write_term(T, _OPS) -->
2176 ['~q'-[T]].
2177:- endif. 2178
2179expected_got_ops_(Ex, E, OPS, Goals) -->
2180 [' Expected: '-[]], write_term(Ex, OPS), [nl],
2181 [' Got: '-[]], write_term(E, OPS), [],
2182 ( { Goals = [] } -> []
2183 ; [nl, ' with: '-[]], write_term(Goals, OPS), []
2184 ).
2185
2186
2187failure(List) -->
2188 { is_list(List) },
2189 !,
2190 [ nl ],
2191 failures(List).
2192failure(Var) -->
2193 { var(Var) },
2194 !,
2195 [ 'Unknown failure?' ].
2196failure(succeeded(Time)) -->
2197 !,
2198 [ 'must fail but succeeded in ~2f seconds~n'-[Time] ].
2199failure(wrong_error(Expected, Error)) -->
2200 !,
2201 { copy_term(Expected-Error, Ex-E, Goals),
2202 numbervars(Ex-E-Goals, 0, _),
2203 write_options(OPS)
2204 },
2205 [ 'wrong error'-[], nl ],
2206 expected_got_ops_(Ex, E, OPS, Goals).
2207failure(wrong_answer(cmp(Var, Cmp))) -->
2208 { Cmp =.. [Op,Answer,Expected],
2209 !,
2210 copy_term(Expected-Answer, Ex-A, Goals),
2211 numbervars(Ex-A-Goals, 0, _),
2212 write_options(OPS)
2213 },
2214 [ 'wrong answer for ', ansi(code, '~w', [Var]),
2215 ' (compared using ~w)'-[Op], nl ],
2216 expected_got_ops_(Ex, A, OPS, Goals).
2217failure(wrong_answer(Cmp)) -->
2218 { Cmp =.. [Op,Answer,Expected],
2219 !,
2220 copy_term(Expected-Answer, Ex-A, Goals),
2221 numbervars(Ex-A-Goals, 0, _),
2222 write_options(OPS)
2223 },
2224 [ 'wrong answer (compared using ~w)'-[Op], nl ],
2225 expected_got_ops_(Ex, A, OPS, Goals).
2226failure(wrong_answer(CmpExpected, Bindings)) -->
2227 { ( CmpExpected = all(Cmp)
2228 -> Cmp =.. [_Op1,_,Expected],
2229 Got = Bindings,
2230 Type = all
2231 ; CmpExpected = set(Cmp),
2232 Cmp =.. [_Op2,_,Expected0],
2233 sort(Expected0, Expected),
2234 sort(Bindings, Got),
2235 Type = set
2236 )
2237 },
2238 [ 'wrong "~w" answer:'-[Type] ],
2239 [ nl, ' Expected: ~q'-[Expected] ],
2240 [ nl, ' Found: ~q'-[Got] ].
2241:- if(swi). 2242failure(cmp_error(_Cmp, Error)) -->
2243 { message_to_string(Error, Message) },
2244 [ 'Comparison error: ~w'-[Message] ].
2245failure(throw(Error)) -->
2246 { Error = error(_,_),
2247 !,
2248 message_to_string(Error, Message)
2249 },
2250 [ 'received error: ~w'-[Message] ].
2251:- endif. 2252failure(message) -->
2253 !,
2254 [ 'Generated unexpected warning or error'-[] ].
2255failure(setup_failed(throw(Error))) -->
2256 { Error = error(_,_),
2257 !,
2258 message_to_string(Error, Message)
2259 },
2260 [ 'test setup goal raised error: ~w'-[Message] ].
2261failure(setup_failed(_)) -->
2262 !,
2263 [ 'test setup goal failed' ].
2264failure(Why) -->
2265 [ '~p'-[Why] ].
2266
2267failures([]) -->
2268 !.
2269failures([H|T]) -->
2270 !,
2271 failure(H), [nl],
2272 failures(T).
2273
2274timeout(Limit) -->
2275 [ 'Timeout exceeeded (~2f sec)'-[Limit] ].
2276
2277fixme_message([]) --> [].
2278fixme_message([fixme(Unit, _Name, Line, Reason, How)|T]) -->
2279 { unit_file(Unit, File) },
2280 fixme_message(File:Line, Reason, How),
2281 ( {T == []}
2282 -> []
2283 ; [nl],
2284 fixme_message(T)
2285 ).
2286
2287fixme_message(Location, Reason, failed) -->
2288 [ 'FIXME: ~w: ~w'-[Location, Reason] ].
2289fixme_message(Location, Reason, passed) -->
2290 [ 'FIXME: ~w: passed ~w'-[Location, Reason] ].
2291fixme_message(Location, Reason, nondet) -->
2292 [ 'FIXME: ~w: passed (nondet) ~w'-[Location, Reason] ].
2293
2294
2295write_options([ numbervars(true),
2296 quoted(true),
2297 portray(true),
2298 max_depth(100),
2299 attributes(portray)
2300 ]).
2301
2306
2307test_name_summary(Term, MaxLen, Summary) :-
2308 summary_string(Term, Text),
2309 atom_length(Text, Len),
2310 ( Len =< MaxLen
2311 -> Summary = Text
2312 ; End is MaxLen//2,
2313 Pre is MaxLen - End - 2,
2314 sub_string(Text, 0, Pre, _, PreText),
2315 sub_string(Text, _, End, 0, PostText),
2316 format(string(Summary), '~w..~w', [PreText,PostText])
2317 ).
2318
2319summary_string(Unit:Test, String) =>
2320 summary_string(Test, String1),
2321 atomics_to_string([Unit, String1], :, String).
2322summary_string(@(Name,Vars), String) =>
2323 format(string(String), '~W (using ~W)',
2324 [ Name, [numbervars(true), quoted(false)],
2325 Vars, [numbervars(true), portray(true), quoted(true)]
2326 ]).
2327summary_string(Name, String) =>
2328 term_string(Name, String, [numbervars(true), quoted(false)]).
2329
2333
2334progress_string(forall(_Vars, N-I)/Total, S) =>
2335 format(string(S), '~w-~w/~w', [N,I,Total]).
2336progress_string(Progress, S) =>
2337 term_string(Progress, S).
2338
2344
2345progress_tag(passed, Tag, Keep, Style) =>
2346 Tag = passed, Keep = false, Style = comment.
2347progress_tag(fixme(passed), Tag, Keep, Style) =>
2348 Tag = passed, Keep = false, Style = comment.
2349progress_tag(fixme(_), Tag, Keep, Style) =>
2350 Tag = fixme, Keep = true, Style = warning.
2351progress_tag(nondet, Tag, Keep, Style) =>
2352 Tag = '**NONDET', Keep = true, Style = warning.
2353progress_tag(timeout(_Limit), Tag, Keep, Style) =>
2354 Tag = '**TIMEOUT', Keep = true, Style = warning.
2355progress_tag(assertion, Tag, Keep, Style) =>
2356 Tag = '**FAILED', Keep = true, Style = error.
2357progress_tag(failed, Tag, Keep, Style) =>
2358 Tag = '**FAILED', Keep = true, Style = error.
2359progress_tag(forall(_,0), Tag, Keep, Style) =>
2360 Tag = passed, Keep = false, Style = comment.
2361progress_tag(forall(_,_), Tag, Keep, Style) =>
2362 Tag = '**FAILED', Keep = true, Style = error.
2363
2364
2365 2368
2369save_output_state :-
2370 stream_property(Output, alias(user_output)),
2371 stream_property(Error, alias(user_error)),
2372 asserta(output_streams(Output, Error)).
2373
2374restore_output_state :-
2375 output_streams(Output, Error),
2376 !,
2377 set_stream(Output, alias(user_output)),
2378 set_stream(Error, alias(user_error)).
2379restore_output_state.
2380
2381
2382
2383 2386
2392
2393:- dynamic
2394 jobs/1, 2395 job_window/1, 2396 job_status_line/3. 2397
2398job_feedback(_, jobs(Jobs)) :-
2399 retractall(jobs(_)),
2400 Jobs > 1,
2401 asserta(jobs(Jobs)),
2402 tty_feedback,
2403 !,
2404 retractall(job_window(_)),
2405 asserta(job_window(Jobs)),
2406 retractall(job_status_line(_,_,_)),
2407 jobs_redraw.
2408job_feedback(_, jobs(Jobs)) :-
2409 !,
2410 retractall(job_window(_)),
2411 info(plunit(jobs(Jobs))).
2412job_feedback(_, Msg) :-
2413 job_window(_),
2414 !,
2415 with_mutex(plunit_feedback, job_feedback(Msg)).
2416job_feedback(Level, Msg) :-
2417 print_message(Level, plunit(Msg)).
2418
2419job_feedback(begin(Unit:Test, _Location, Progress)) =>
2420 tty_columns(SummaryWidth, _Margin),
2421 test_name_summary(Unit:Test, SummaryWidth, NameS),
2422 progress_string(Progress, ProgressS),
2423 tty_clear_to_eol(CE),
2424 job_format(comment, '\r[~w] ~w ..~w',
2425 [ProgressS, NameS, CE]),
2426 flush_output.
2427job_feedback(end(_UnitTest, _Location, _Progress)) =>
2428 true.
2429job_feedback(progress(_UnitTest, Status, _Progress, Time)) =>
2430 ( hide_progress(Status)
2431 -> true
2432 ; tty_columns(_SummaryWidth, Margin),
2433 progress_tag(Status, Tag, _Keep, Style),
2434 job_finish(Style, '~`.t ~w (~3f sec)~*|',
2435 [Tag, Time.wall, Margin])
2436 ).
2437job_feedback(failed(UnitTest, Progress, Line, Error, Time, Output)) =>
2438 tty_columns(_SummaryWidth, Margin),
2439 progress_tag(failed, Tag, _Keep, Style),
2440 job_finish(Style, '~`.t ~w (~3f sec)~*|',
2441 [Tag, Time.wall, Margin]),
2442 print_test_output(Error, Output),
2443 ( ( Error = timeout(_) 2444 ; Error == assertion 2445 )
2446 -> true
2447 ; print_message(Style, plunit(failed(UnitTest, Progress, Line,
2448 Error, Time, "")))
2449 ),
2450 jobs_redraw.
2451job_feedback(begin(_Unit)) => true.
2452job_feedback(end(_Unit, _Summary)) => true.
2453
2454hide_progress(assertion).
2455hide_progress(forall(_,_)).
2456hide_progress(failed).
2457hide_progress(timeout(_)).
2458
2459print_test_output(Error, _Msgs-Output) =>
2460 print_test_output(Error, Output).
2461print_test_output(_, "") => true.
2462print_test_output(assertion, Output) =>
2463 print_message(debug, plunit(test_output(error, Output))).
2464print_test_output(message, Output) =>
2465 print_message(debug, plunit(test_output(error, Output))).
2466print_test_output(_, Output) =>
2467 print_message(debug, plunit(test_output(informational, Output))).
2468
2472
2473jobs_redraw :-
2474 job_window(N),
2475 !,
2476 tty_columns(_, Width),
2477 tty_header_line(Width),
2478 forall(between(1,N,Line), job_redraw_worker(Line)),
2479 tty_header_line(Width).
2480jobs_redraw.
2481
2482job_redraw_worker(Line) :-
2483 ( job_status_line(Line, Fmt, Args)
2484 -> ansi_format(comment, Fmt, Args)
2485 ; true
2486 ),
2487 nl.
2488
2494
2495job_format(Style, Fmt, Args) :-
2496 job_self(Job),
2497 job_format(Job, Style, Fmt, Args, true).
2498
2504
2505job_finish(Style, Fmt, Args) :-
2506 job_self(Job),
2507 job_finish(Job, Style, Fmt, Args).
2508
2509:- det(job_finish/4). 2510job_finish(Job, Style, Fmt, Args) :-
2511 retract(job_status_line(Job, Fmt0, Args0)),
2512 !,
2513 string_concat(Fmt0, Fmt, Fmt1),
2514 append(Args0, Args, Args1),
2515 job_format(Job, Style, Fmt1, Args1, false).
2516
2517:- det(job_format/5). 2518job_format(Job, Style, Fmt, Args, Save) :-
2519 job_window(Jobs),
2520 Up is Jobs+2-Job,
2521 flush_output(user_output),
2522 tty_up_and_clear(Up),
2523 ansi_format(Style, Fmt, Args),
2524 ( Save == true
2525 -> retractall(job_status_line(Job, _, _)),
2526 asserta(job_status_line(Job, Fmt, Args))
2527 ; true
2528 ),
2529 tty_down_and_home(Up),
2530 flush_output(user_output).
2531
2532:- det(job_self/1). 2533job_self(Job) :-
2534 job_window(N),
2535 N > 1,
2536 thread_self(Me),
2537 split_string(Me, '_', '', [_,_,S]),
2538 number_string(Job, S).
2539
2544
2545tty_feedback :-
2546 has_tty,
2547 current_test_flag(format, tty).
2548
2549has_tty :-
2550 stream_property(user_output, tty(true)).
2551
2552tty_columns(SummaryWidth, Margin) :-
2553 tty_width(W),
2554 Margin is W-8,
2555 SummaryWidth is max(20,Margin-34).
2556
2557tty_width(W) :-
2558 current_predicate(tty_size/2),
2559 catch(tty_size(_Rows, Cols), error(_,_), fail),
2560 Cols > 25,
2561 !,
2562 W = Cols.
2563tty_width(80).
2564
(Width) :-
2566 ansi_format(comment, '~N~`\u2015t~*|~n', [Width]).
2567
2568:- if(current_predicate(tty_get_capability/3)). 2569tty_clear_to_eol(S) :-
2570 getenv('TERM', _),
2571 catch(tty_get_capability(ce, string, S),
2572 error(_,_),
2573 fail),
2574 !.
2575:- endif. 2576tty_clear_to_eol('\e[K').
2577
2578tty_up_and_clear(Lines) :-
2579 format(user_output, '\e[~dA\r\e[K', [Lines]).
2580
2581tty_down_and_home(Lines) :-
2582 format(user_output, '\e[~dB\r', [Lines]).
2583
2584:- if(swi). 2585
2586:- multifile
2587 prolog:message/3,
2588 prolog:message_action/2. 2589
2590prolog:message(Term) -->
2591 message(Term).
2592
2594
2595prolog:message_action(make(done(Files)), _) :-
2596 make_run_tests(Files).
2597
2598:- endif. 2599
2600:- if(sicstus). 2601
2602user:generate_message_hook(Message) -->
2603 message(Message),
2604 [nl]. 2605
2612
2613user:message_hook(informational, plunit(begin(Unit)), _Lines) :-
2614 format(user_error, '% PL-Unit: ~w ', [Unit]),
2615 flush_output(user_error).
2616user:message_hook(informational, plunit(end(_Unit)), _Lines) :-
2617 format(user, ' done~n', []).
2618
2619:- endif. 2620
2621 2624
2625'$nowarn_autoload'(set_test_options/1, _).
2626'$nowarn_autoload'(run_tests/0, _).
2627'$nowarn_autoload'(run_tests/1, _).
2628'$nowarn_autoload'(run_tests/2, _).
2629'$nowarn_autoload'(running_tests/0, _).
2630'$nowarn_autoload'(current_test/5, _).
2631'$nowarn_autoload'(current_test_unit/2, _).
2632'$nowarn_autoload'(test_report/1, _)