-
Notifications
You must be signed in to change notification settings - Fork 67
/
Copy pathtest_lib.mli
1279 lines (1045 loc) · 51.2 KB
/
test_lib.mli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(* This file is part of Learn-OCaml.
*
* Copyright (C) 2019-2023 OCaml Software Foundation.
* Copyright (C) 2015-2018 OCamlPro.
*
* Learn-OCaml is distributed under the terms of the MIT license. See the
* included LICENSE file for details. *)
(** Documentation for [test_lib] library. [Test_lib] module can be
used to write graders for learn-ocaml. *)
val set_result : Learnocaml_report.t -> unit
type nonrec 'a result = ('a, exn) result
(** {1 AST checkers}
Various functions to explore the student's code AST and
enforce restrictions on the student's code. *)
module Ast_checker : sig
(** Since the user's code is reified, the parsed abstract syntax
tree is available in the testing environment, as a variable
named [code_ast], with type [Parsetree.structure]. As such, it
can be checked using the iterators in the module [Ast_mapper]
from [compiler-libs]. However, [Test_lib] provides some
functions to check the Parsetree. *)
(** {2 Checkers} *)
(** The functional type ['a ast_checker] describes the functions
used for AST introspection. Such a function takes as input the
introspected objects (mainly [Parsetree structure] like
[code_ast] or [Parsetree expression]).
For example, adding [~on_open: (forbid "open")] will prevent the
student from using [open] syntax by returning a [Failure]
report at the first occurrence of the [open] keyword in the
input [Parsetree] structure or expression.
This function has several optional arguments to describe the
behavior of the AST checker on various parts of the AST. Each
argument corresponds to a specific type:
- [~on_expression]: [Parsetree.expression] ;
- [~on_pattern]: [Parsetree.pattern] ;
- [~on_structure_item]: [Parsetree.structure_item] ;
- [~on_external]: [value_description] ;
- [~on_include]: [Parsetree.include_declaration] ;
- [~on_open]: [Parsetree.open_description] ;
Besides, there are more high-level checkers:
- [~on_module_occurence]: this function is called each time a
module is used. The string input is the name of the module.
- [~on_variable_occurence]: function called each time a free
variable (relative to the AST given as argument) is used.
The string input is the name of the variable. The functions
[restrict] and [forbid] can be used to construct this function.
- [~on_function_call]: function called each time a function
call is used. The first argument of this function is the
[Parsetree.expression] of the called function. The second
argument is the arguments represented by a tuple [(name,
expression)].
*)
type 'a ast_checker =
?on_expression: (Parsetree.expression -> Learnocaml_report.t) ->
?on_pattern: (Parsetree.pattern -> Learnocaml_report.t) ->
?on_structure_item: (Parsetree.structure_item -> Learnocaml_report.t) ->
?on_external: (Parsetree.value_description -> Learnocaml_report.t) ->
?on_include: (Parsetree.include_declaration -> Learnocaml_report.t) ->
?on_open: (Parsetree.open_declaration -> Learnocaml_report.t) ->
?on_module_occurence: (string -> Learnocaml_report.t) ->
?on_variable_occurence: (string -> Learnocaml_report.t) ->
?on_function_call: (
(Parsetree.expression
* (string * Parsetree.expression) list) -> Learnocaml_report.t)
-> 'a -> Learnocaml_report.t
(** [ast_check_expr] builds an {{!ast_checker}AST checker} for
[Parsetree] expressions. This function can be used as
functional argument for {!find_binding}. *)
val ast_check_expr : Parsetree.expression ast_checker
(** [ast_check_structure] builds an {{!ast_checker}AST checker}
for [Parsetree] structure. The returned AST checker can
directly be used with [code_ast] which is available in the
grading environment. *)
val ast_check_structure : Parsetree.structure ast_checker
(** {2 Finding top level variable in AST}*)
(** [find_binding code_ast name cb] looks for the top level
variable [name] in [code_ast] and its associated Parsetree
expression [expr] ([let name = expr]). If the variable is
found, it returns an {!Learnocaml_report.Informative} report
concatenated with the report resulting of [cb] applied to
[expr]. Otherwise, it returns a {!Learnocaml_report.Failure}
report. *)
val find_binding :
Parsetree.structure -> string
-> (Parsetree.expression -> Learnocaml_report.t)
-> Learnocaml_report.t
(** {2 Functions for optional arguments of checkers} *)
(** The following functions are classic functions to use as
optional arguments for {!ast_checker} like forbidding,
rectricting or requiring some [Parsetree] structures or
expressions etc..*)
(** {3 Generic functions} *)
(** [forbid k pr ls t] returns a
{{!Learnocaml_report.Failure}Failure} the first time [t] is
tested if [t] is in the list [ls]. The message of the
report is {e The text1 text2 is forbidden} where [text1] is the
result of [pr] applies to [t] and [text2] is value
[k]. Otherwise, an empty report is returned. *)
val forbid :
string -> ('a -> string) -> 'a list -> ('a -> Learnocaml_report.t)
(** [restrict k pr ls t] returns a
{{!Learnocaml_report.Failure}Failure} the first time [t] is
tested if [t] is {e not} in [ls]. The message of the
report is {e The text1 text2 is not allowed} where [text1] is
the result of [pr] applies to [t] and [text2] is value of
[k]. Otherwise, an empty report is returned. *)
val restrict :
string -> ('a -> string) -> 'a list -> ('a -> Learnocaml_report.t)
(** [require k pr _ t] returns a {{!Learnocaml_report.Success
5}Success 5} report the first time this function is called. The
message of the report is {e Found text1 text2} where
[text1] is value of [k] and [text2] is the result of [pr]
applies to [t]. Otherwise, an empty report is returned. *)
val require :
string -> ('a -> string) -> 'a -> ('a -> Learnocaml_report.t)
(** {3 For expressions } *)
(** [forbid_expr name exprs expr] returns a
{{!Learnocaml_report.Failure}Failure} report the first time
[expr] is tested if [expr] is in the list of forbidden
expressions [exprs]. The message of the report is {e
The text1 text2 is forbidden} where [text1] is [expr] and
[text2] is value of [name]. Otherwise, an empty report is
returned. *)
val forbid_expr :
string -> Parsetree.expression list
-> (Parsetree.expression -> Learnocaml_report.t)
(** [restrict_expr name exprs expr] returns a
{{!Learnocaml_report.Failure}Failure} report the first time
[expr] is tested if [expr] is {e not} in the list of allowed
expressions [exprs]. The message of the report is {e
The text1 text2 is not allowed} where [text1] is [expr] and
[text2] is value of [name]. Otherwise, an empty report is
returned. *)
val restrict_expr :
string -> Parsetree.expression list
-> (Parsetree.expression -> Learnocaml_report.t)
(** [require_expr name _ t] returns a {{!Learnocaml_report.Success
5}Success 5} report the first time this function is called. The
message of the success report is {e Found text1 text2} where
[text1] is value of [name] and [text2] is the result of [pr]
applies to [t]. Otherwise, an empty report is returned. *)
val require_expr :
string -> Parsetree.expression
-> (Parsetree.expression -> Learnocaml_report.t)
(** {3 For syntax } *)
(** These functions are very restricted function to either forbid
any use of a particular syntax or require to have at least one
use of it. Their first argument is used to build the message of
the return report and the second argument is simply ignored.
For example, adding [~on_include: (forbid "include") ~on_open:
(forbid "open")] prevents the student from using [open] and
[include] syntaxes. *)
(** [forbid_syntax n _] returns a
{{!Learnocaml_report.Failure}Failure} report the first time it
is called. The message of the report is {e The {b text}
syntax is forbidden} where [text] is the value of
[n]. Otherwise, an empty report is returned. *)
val forbid_syntax : string -> (_ -> Learnocaml_report.t)
(** [require_syntax n _] returns a {{!Learnocaml_report.Success
5}Success 5} report the first time it is called. The message of
the report is {e The {b text} syntax has been found, as
expected} where [text] is the value of [n]. Otherwise, an empty
report is returned. *)
val require_syntax : string -> (_ -> Learnocaml_report.t)
(** {2 AST sanity checks } *)
(** [ast_sanity_check ~modules ast cb] *)
val ast_sanity_check :
?modules: string list -> Parsetree.structure
-> (unit -> Learnocaml_report.t)
-> Learnocaml_report.t
end
(** {1 Testers and IO testers} *)
(** Predefined functions and function builders used for the optional
argument [~test], [~test_stdout] and [~test_stderr] of the various
{{!Test_functions_function.test_functions_fun_sec}grading functions
for functions}. *)
(** Functions of type [tester] are used to compare student result
with solution result. The first {!S.result} is the student
output and the second one is the solution output. *)
type 'a tester =
'a Ty.ty -> 'a result -> 'a result -> Learnocaml_report.t
(** Functions of type [io_tester] are used to compare student
standard out or standard error channels with solution ones. *)
type io_tester =
string -> string -> Learnocaml_report.t
(** Functions of type [io_postcond] are used to verify that student
standard out or standard error channels satisfy a postcondition. *)
type io_postcond =
string -> Learnocaml_report.t
(** The exception [Timeout limit] is raised by [run_timeout]. Thus, the
functions [exec] and [result] can return [Error (Timeout limit)].
The integer [limit] is the maximum time that was allowed, in seconds. *)
exception Timeout of int
module Tester : sig
(** Testers are essentially used for the optional arguments
[~test], [~test_stdout], [~test_stderr] of
{{!Test_functions_function.test_functions_fun_sec}test
functions for functions}. They define the functions which build
three of four parts of the global report returned by test
functions.
{!S.tester} functions compare student output and solution
output and return usually a {!Learnocaml_report.Success 1} if
they match and a {!Learnocaml_report.Failure} if they don't.
{!S.io_tester} functions compare the string outputs on standard
or error channels for student and solution functions and return
usually a {!Learnocaml_report.Success 5} if they match and a
{!Learnocaml_report.Failure} if they don't. *)
(** {2:tester_sec Pre-defined testers and tester builders} *)
(** [test] is a {!S.tester} that compares its two {!S.result} inputs
with OCaml structural equality. This is the default value of [~test]
optional argument of grading functions for functions.*)
val test : 'a tester
(** [test_ignore] is a {!S.tester} that compares only the constructor of its
{!S.result} inputs. The content is ignored. If the constructors
match, an empty report is returned. *)
val test_ignore : 'a tester
(** [test_eq eq] builds a {!S.tester} with function [eq] as comparison
function. *)
val test_eq : ('a result -> 'a result -> bool) -> 'a tester
(** [test_eq_ok eq] builds a {!S.tester} that compares [Ok] results with
[eq] and [Error] results with Ocaml structural equality. *)
val test_eq_ok : ('a -> 'a -> bool) -> 'a tester
(** [test_eq_exn eq] builds a {!S.tester} that compares [Error] results
with [eq] and [Ok] results with Ocaml structural equality. *)
val test_eq_exn : (exn -> exn -> bool) -> 'a tester
(** [test_canon canon] builds a {!S.tester} that compares its two
{!S.result} inputs after application to [canon] function with
Ocaml structural equality. *)
val test_canon : ('a result -> 'a result) -> 'a tester
(** [test_canon_ok canon] builds a {!S.tester} that compares two
[Ok] result inputs after application to [canon] function with
Ocaml structural equality. [Error] results are compared
normally with Ocaml structural equality. *)
val test_canon_ok : ('a -> 'a) -> 'a tester
(** [test_canon_error canon] builds a {!S.tester} that compares two
[Error] result inputs after application to [canon] function with
Ocaml structural equality. [Ok] results are compared
normally with Ocaml structural equality. *)
val test_canon_error : (exn -> exn) -> 'a tester
(** [test_translate conv test ty] builds a {!S.tester} that
translates its inputs [va] and [vb] to ['b results] [va_trans]
and [vb_trans] using the conversion function [conv] and returns
the report of [test ty va_trans vb_trans].*)
val test_translate : ('a -> 'b) -> 'b tester -> 'b Ty.ty -> 'a tester
(** {2:io_tester_sec Pre-defined IO testers and IO tester builders} *)
(** IO testers are essentially used for the optional arguments
[ ~test_stdout] [~test_stderr] of grading functions. *)
(** Important warning : when successful, predefined IO testers
return [Success 5] reports. *)
(** There are two common optional arguments for IO testers :
- [~trim] : list of chars removed at beginning and end of IO
testers input strings.
- [~drop] : list of chars removed from IO testers input
strings *)
(** [io_test_ignore] is the default value of [~test_stdout] and
[~test_stderr]. Returns an empty report whatever its inputs. *)
val io_test_ignore : io_tester
(** [io_test_equals] builds a {!S.io_tester} which compares its
input strings using Ocaml structural equality. *)
val io_test_equals :
?trim: char list -> ?drop: char list -> io_tester
(** [io_test_lines ~skip_empty ~test_line] builds a {!S.io_tester}
which compares each line (separated with ['\n']) of its two
string inputs with [test_line]. The default value of
[test_line] is [io_tester_equals]. If [skip_empty] is set to
[true], the empty lines are skipped (default value is
[false]). *)
val io_test_lines :
?trim: char list -> ?drop: char list ->
?skip_empty: bool -> ?test_line: io_tester -> io_tester
(** [io_test_items ~split ~skip_empty ~test_item] buids a
{!S.io_tester} which splits its two string inputs into several
items using [split] as separators and compares each item with
[test_item] ([io_tester_equals] by default). If [skip_empty] is
set to [true], the empty items are skipped (default value is
[false]). *)
val io_test_items :
?split: char list -> ?trim: char list -> ?drop: char list ->
?skip_empty: bool -> ?test_item: io_tester -> io_tester
end
(** {1 Mutation observer builders} *)
(** Functions to help to build the optional arguments
[~before_reference], [~before_user], [~test] used by grading
functions for {b unary} function with a mutable input. *)
module Mutation : sig
(** Important warning: this part is useful only to grade unary
function using grading functions such than
{!S.Test_functions_function.test_function_1_against_solution}. *)
type 'arg arg_mutation_test_callbacks =
{ before_reference : 'arg -> unit ;
before_user : 'arg -> unit ;
test : 'ret. ?test_result: 'ret tester -> 'ret tester }
(** [arg_mutation_test_callbacks ~test_ref ~dup ~blit ty] returns
a {!Mutation.arg_mutation_test_callbacks} [out] such than the
functions [out.before_reference] and [out.before_user] can
create two copies of the mutable input of type [ty] with
[dup]. One copy [got] is made before executing the user code
and saved before executing the solution. The second copy is
also made at this time and so mutate during solution
execution. They can then be compared with [out.test].
[out.test] is a {!S.tester} which actually builds two reports,
one using its optional argument [~test_result] and one that
compares the values of the references set previously using
[test_ref]. By default [~test_result] is equal to
{!Tester.test_ignore}.
[dup in] returns a copy of [in].
[blit src dst] copies [src] into [dst].*)
val arg_mutation_test_callbacks:
?test: 'a tester -> dup: ('a -> 'a)
-> blit:('a -> 'a -> unit) -> 'a Ty.ty
-> 'a arg_mutation_test_callbacks
(** [array_arg_mutation_test_callbacks ~test_arr ty] builds
[before_user], [before_reference] and [test] such than [test] can
compare mutation of an input array through student code and
solution.
By default, [test_arr] is set to {!Tester.test}.*)
val array_arg_mutation_test_callbacks:
?test: 'a array tester -> 'a array Ty.ty ->
'a array arg_mutation_test_callbacks
(** [ref_arg_mutation_test_callbacks ~test_ref ty] builds
[before_user], [before_reference] and [test] such than [test] can
compare mutation of an input reference through student code and
solution.
By default, [test_ref] is set to {!Tester.test}. *)
val ref_arg_mutation_test_callbacks:
?test: 'a ref tester -> 'a ref Ty.ty ->
'a ref arg_mutation_test_callbacks
end
(** {1 Samplers } *)
(** [Sampler] provides a library of predefined samplers for
{{!Test_functions_function}grading functions}.*)
module Sampler : sig
type 'a sampler = unit -> 'a
(** {2:sampler_sec Samplers} *)
(** [sample_int ()] returns a random integer between -5 and 5. *)
val sample_int : int sampler
(** [sample_float ()] returns a random float between -5. and 5. *)
val sample_float : float sampler
(** [sample_string ()] returns a randomly long random string. *)
val sample_string : string sampler
(** [sample_char ()] returns an alphabet letter randomly. *)
val sample_char : char sampler
(** [sample_bool ()] returns randomly [false] or [true]. *)
val sample_bool : bool sampler
(** {2 Sampler builders} *)
(** [sample_list ~min_size ~max_size sample] returns a list
sampler that generates a list of random length between
[min_size] ([0] by default) and [max_size] ([10] by default)
where each element are generated using [sample].
If [~sorted:true] ([false] by default) the generated list is
sorted (using Stdlib.compare).
If [~dups:false] ([true] by default), all elements of generated
list are unique.*)
val sample_list :
?min_size: int -> ?max_size: int -> ?dups: bool -> ?sorted: bool
-> 'a sampler
-> 'a list sampler
(** [sample_array ~min_size ~max_size sample] returns an array
sampler that generates an array of random length between
[min_size] ([0] by default) and [max_size] ([10] by default)
where each element are generated using [sample].
If [~sorted:true] ([false] by default) the generated array is
sorted.
If [~dups:false] ([true] by default), all elements of generated
arrays are unique, or at least try hard to be in a reasonable time:
if the codomain of [sampler] is too small there might still be
duplicates.*)
val sample_array :
?min_size: int -> ?max_size: int -> ?dups: bool -> ?sorted: bool
-> 'a sampler
-> 'a array sampler
(** [sample_pair s1 s2] returns a sampler that generates a value
of type ['a * 'b] using [s1] and [s2] to generate values of
type ['a] and ['b], respectively, on each call. *)
val sample_pair : 'a sampler -> 'b sampler -> ('a * 'b) sampler
(** [sample_alternatively s] returns a sampler that mimics
randomly the behavior of one of [s] sampler and change at each
call. *)
val sample_alternatively : 'a sampler list -> 'a sampler
(** [sample_case cases] returns a sampler that generates randomly
one of the value of cases. *)
val sample_cases : 'a list -> 'a sampler
(** [sample_option sample] returns a sampler that generates an ['a
option] value using [sample] to generate an ['a] value if
necessary. *)
val sample_option : 'a sampler -> 'a option sampler
(** {2 Utilities} *)
val printable_fun : string -> (_ -> _ as 'f) -> 'f
end
(** For internal use, needed for the default samplers registration *)
module Sampler_reg : sig
type 'a sampler = 'a Sampler.sampler
val sample_int : int sampler
val sample_float : float sampler
val sample_string : string sampler
val sample_char : char sampler
val sample_bool : bool sampler
val sample_list : 'a sampler -> 'a list sampler
val sample_array : 'a sampler -> 'a array sampler
val sample_option : 'a sampler -> 'a option sampler
type ('a, 'b) pair = 'a * 'b
val sample_pair : 'a sampler -> 'b sampler -> ('a, 'b) pair sampler
end
(** {1 Grading functions for references and variables } *)
(** Grading function for variables and references. *)
module Test_functions_ref_var : sig
(** [test_ref ty got exp] returns {!Learnocaml_report.Success 1}
report if reference [got] value is equal to [exp] and
{!Learnocaml_report.Failure} report otherwise.
{e WARNING:} contrary to other grading functions, you cannot
use this function to evaluate a reference defined or modified
in student's code. In this case, you should use
{{!Mutation}mutation functions}. This function should be used
for a reference defined locally (in [test.ml]). *)
val test_ref :
'a Ty.ty -> 'a ref -> 'a -> Learnocaml_report.t
(** [test_variable ty name r] returns {!Learnocaml_report.Success
1} report if variable named [name] exists and is equal to
[r]. Otherwise returns {!Learnocaml_report.Failure} report.*)
val test_variable :
'a Ty.ty -> string -> 'a -> Learnocaml_report.t
(** [test_variable_property ty name cb] returns the report
resulting of application of cb to variable named [name] if it
exists. Otherwise returns {!Learnocaml_report.Failure} report. *)
val test_variable_property :
'a Ty.ty -> string -> ('a -> Learnocaml_report.t) -> Learnocaml_report.t
(** [test_variable ty name r] returns {!Learnocaml_report.Success
1} report if variable named [name] exists and is equal to
variable with the same name defined in solution. Otherwise returns
{!Learnocaml_report.Failure} report.*)
val test_variable_against_solution :
'a Ty.ty -> string -> Learnocaml_report.t
end
(** {1 Grading functions for types} *)
(** Grading function for types. *)
module Test_functions_types : sig
val compatible_type : expected:string -> string -> Learnocaml_report.t
val existing_type : ?score:int -> string -> bool * Learnocaml_report.t
val abstract_type :
?allow_private:bool -> ?score:int -> string -> bool * Learnocaml_report.t
val test_student_code :
'a Ty.ty -> ('a -> Learnocaml_report.t) -> Learnocaml_report.t
val test_module_property :
'a Ty.ty -> string -> ('a -> Learnocaml_report.t) -> Learnocaml_report.t
end
(** {1 Grading functions for functions }*)
(** Grading function for functions. *)
module Test_functions_function : sig
(** {2:test_functions_fun_sec Grading functions for functions}*)
(** Three grading functions for functions are defined for arity one
to four functions:
- [test_function_<args_nb> ty name tests]
- [test_function_<args_nb>_against ty name rf tests]
- [test_function_<args_nb>_against_solution ty name tests]
It tests [args_nb]-arity function named [name] with
non-polymorphic type [ty] (a polymorphic function must be
tested on a completely determined type) through tests
[tests]. If a function named [name] is defined in the student
code and has the right type (or a more generic one), tests
[tests] are checked one by one and the function returns a
report concatening reports of each test. Otherwise a
{!Learnocaml_report.Failure} report is returned.*)
(** {3 Returned report}*)
(** The grading functions for functions return a {!report} which
actually concatenated 4 reports generated by (in this order):
- the tester [~test]
- the IO tester [~test_stdout]
- The IO tester [~test_stderr]
- the post-processing result function [after].
Each of this report can be empty. However by default, [~test]
always returns a non-empty report while the other three returns
empty reports. *)
(** {3 Unary functions}*)
(** [test_function_1 ty name tests] tests the function named
[name] by directly comparing obtained outputs against expected
outputs.
A test [(arg-1, r, out, err)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1] is equal to [r] and if standard output and
standard error messages match [out] and [err] respectively. The
result of a test is a {!Learnocaml_report.Failure} report otherwise.
See {{!optional_arguments_sec} this section} for information about optional
arguments. *)
val test_function_1 :
?test: 'b tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before : ('a -> unit) ->
?after : ('a -> ('b * string * string)
-> ('b * string * string)
-> Learnocaml_report.t) ->
('a -> 'b) Ty.ty -> string
-> ('a * 'b * string * string) list -> Learnocaml_report.t
(** [test_function_1_against ty name rf tests] tests the function
named [name] by comparing outputs obtained with the student
function against outputs of [rf].
A test [arg-1] results in a {!Learnocaml_report.Success 1} report
if the student function applied to [arg-1] gives the same
result than the solution function [rf] applied to [arg-1]. Otherwise
the result of a test is a {!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information about optional
arguments. *)
val test_function_1_against :
?gen: int ->
?test: 'b tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> unit) ->
?before_user : ('a -> unit) ->
?after : ('a
-> ('b * string * string)
-> ('b * string * string)
-> Learnocaml_report.t) ->
?sampler : (unit -> 'a) ->
('a -> 'b) Ty.ty -> string -> ('a -> 'b) -> 'a list -> Learnocaml_report.t
(** [test_function_1_against_solution ty name tests] tests the
function named [name] by comparison to solution function [rf]
which must be defined under name [name] in the corresponding
[solution.ml] file.
A test [arg-1] results in a {!Learnocaml_report.Success 1} report
if the student function applied to [arg-1] gives the same
result than the solution function [rf] applied to
[arg-1]. Otherwise the result of a test is a
{!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_1_against_solution :
?gen: int ->
?test: 'b tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> unit) ->
?before_user : ('a -> unit) ->
?after : ('a
-> ('b * string * string)
-> ('b * string * string)
-> Learnocaml_report.t) ->
?sampler : (unit -> 'a) ->
('a -> 'b) Ty.ty -> string -> 'a list -> Learnocaml_report.t
(** [test_function_1_against_postcond postcond ty name tests] tests that
the function named [name] satisfies the postcondition [postcond].
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_1_against_postcond :
?gen: int ->
?test_stdout: io_postcond ->
?test_stderr: io_postcond ->
?before_reference : ('a -> unit) ->
?before_user : ('a -> unit) ->
?after : ('a -> ('b * string * string) -> Learnocaml_report.t) ->
?sampler : (unit -> 'a) ->
('a -> 'b Ty.ty -> 'b result -> Learnocaml_report.t) ->
('a -> 'b) Ty.ty -> string -> 'a list -> Learnocaml_report.t
(** {3 Binary functions }*)
(** [test_function_2 ty name tests] tests the function named
[name] by directly comparing obtained outputs against expected
outputs.
A test [(arg-1, arg-2, r, out, err)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1] and [arg-2] is equal to [r] and if standard
output and standard error messages match [out] and [err]
respectively. The result of a test is a
{!Learnocaml_report.Failure} report otherwise.
See {{!optional_arguments_sec} this section} for information about optional
arguments. *)
val test_function_2 :
?test: 'c tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before : ('a -> 'b -> unit) ->
?after : ('a -> 'b -> ('c * string * string)
-> ('c * string * string)
-> Learnocaml_report.t) ->
('a -> 'b -> 'c) Ty.ty
-> string
-> ('a * 'b * 'c * string * string) list
-> Learnocaml_report.t
(** [test_function_2_against ty name rf tests] tests the function
named [name] by comparing outputs obtained with the student
function against outputs of [rf].
A test [(arg-1, arg-2)] results in a {!Learnocaml_report.Success
1} report if the student function applied to [arg-1] and
[arg-2] gives the same result than the solution function [rf]
applied to the same arguments. Otherwise the result of a test is a
{!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information about optional
arguments. *)
val test_function_2_against :
?gen: int ->
?test: 'c tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> 'b -> unit) ->
?before_user : ('a -> 'b -> unit) ->
?after : ('a -> 'b
-> ('c * string * string)
-> ('c * string * string)
-> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b) ->
('a -> 'b -> 'c) Ty.ty -> string
-> ('a -> 'b -> 'c)
-> ('a * 'b) list
-> Learnocaml_report.t
(** [test_function_2_against_soltion ty name tests] tests the function
named [name] by comparison to solution function [rf] which must
be defined under name [name] in the corresponding [solution.ml]
file.
A test [(arg-1, arg-2)] results in a {!Learnocaml_report.Success
1} report if the student function applied to [arg-1] and
[arg-2] gives the same result than the solution function [rf]
applied to the same arguments. Otherwise the result of a test
is a {!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_2_against_solution :
?gen: int ->
?test: 'c tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> 'b -> unit) ->
?before_user : ('a -> 'b -> unit) ->
?after : ('a -> 'b
-> ('c * string * string)
-> ('c * string * string)
-> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b) ->
('a -> 'b -> 'c) Ty.ty -> string -> ('a * 'b) list -> Learnocaml_report.t
(** [test_function_2_against_postcond postcond ty name tests] tests that
the function named [name] satisfies the postcondition [postcond].
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_2_against_postcond :
?gen: int ->
?test_stdout: io_postcond ->
?test_stderr: io_postcond ->
?before_reference : ('a -> 'b -> unit) ->
?before_user : ('a -> 'b -> unit) ->
?after : ('a -> 'b -> ('c * string * string) -> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b) ->
('a -> 'b -> 'c Ty.ty -> 'c result -> Learnocaml_report.t) ->
('a -> 'b -> 'c) Ty.ty -> string -> ('a * 'b) list -> Learnocaml_report.t
(** {3 Three-arguments functions }*)
(** [test_function_3 ty name tests] tests the function named
[name] by directly comparing obtained outputs against expected
outputs.
A test [(arg-1, arg-2, arg-3, r, out, err)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1], [arg-2] and [arg-3] is equal to [r] and if
standard output and standard error messages match [out] and
[err] respectively. The result of a test is a
{!Learnocaml_report.Failure} report otherwise.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_3 :
?test: 'd tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before : ('a -> 'b -> 'c -> unit) ->
?after : ('a -> 'b -> 'c
-> ('d * string * string)
-> ('d * string * string) -> Learnocaml_report.t)
-> ('a -> 'b -> 'c -> 'd) Ty.ty -> string
-> ('a * 'b * 'c * 'd * string * string) list
-> Learnocaml_report.t
(** [test_function_3_against ty name rf tests] tests the function
named [name] by comparing outputs obtained with the student
function against outputs of [rf].
A test [(arg-1, arg-2, arg-3)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1], [arg-2] and [arg-3] gives the same result
than the solution function [rf] applied to the same
arguments. Otherwise the result of a test is a
{!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_3_against :
?gen: int ->
?test: 'd tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> 'b -> 'c -> unit) ->
?before_user : ('a -> 'b -> 'c -> unit) ->
?after : ('a -> 'b -> 'c
-> ('d * string * string)
-> ('d * string * string)
-> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b * 'c) ->
('a -> 'b -> 'c -> 'd) Ty.ty
-> string
-> ('a -> 'b -> 'c -> 'd)
-> ('a * 'b * 'c) list
-> Learnocaml_report.t
(** [test_function_3_against_solution ty name tests] tests the function
named [name] by comparison to solution function [rf] which must
be defined under name [name] in the corresponding [solution.ml]
file.
A test [(arg-1, arg-2, arg-3)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1], [arg-2] and [arg-3] gives the same result
than the solution function [rf] applied to the same
arguments. Otherwise the result of a test is a
{!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_3_against_solution :
?gen: int ->
?test: 'd tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> 'b -> 'c -> unit) ->
?before_user : ('a -> 'b -> 'c -> unit) ->
?after : ('a -> 'b -> 'c
-> ('d * string * string)
-> ('d * string * string) -> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b * 'c) ->
('a -> 'b -> 'c -> 'd) Ty.ty
-> string -> ('a * 'b * 'c) list
-> Learnocaml_report.t
(** [test_function_3_against_postcond postcond ty name tests] tests that
the function named [name] satisfies the postcondition [postcond].
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_3_against_postcond :
?gen: int ->
?test_stdout: io_postcond ->
?test_stderr: io_postcond ->
?before_reference : ('a -> 'b -> 'c -> unit) ->
?before_user : ('a -> 'b -> 'c -> unit) ->
?after : ('a -> 'b -> 'c -> ('d * string * string) -> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b * 'c) ->
('a -> 'b -> 'c -> 'd Ty.ty -> 'd result -> Learnocaml_report.t) ->
('a -> 'b -> 'c -> 'd) Ty.ty -> string -> ('a * 'b * 'c) list -> Learnocaml_report.t
(** {3 Four-arguments functions }*)
(** [test_function_4 ty name tests] tests the function named
[name] by directly comparing obtained outputs against expected
outputs.
A test [(arg-1, arg-2, arg-3, arg-4, r, out, err)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1], [arg-2], [arg-3] and [arg-4] is equal to
[r] and if standard output and standard error messages match
[out] and [err] respectively. The result of a test is a
{!Learnocaml_report.Failure} report otherwise.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_4 :
?test: 'e tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before : ('a -> 'b -> 'c -> 'd -> unit) ->
?after : ('a -> 'b -> 'c -> 'd
-> ('e * string * string)
-> ('e * string * string)
-> Learnocaml_report.t)
-> ('a -> 'b -> 'c -> 'd -> 'e) Ty.ty -> string
-> ('a * 'b * 'c * 'd * 'e * string * string) list
-> Learnocaml_report.t
(** [test_function_4_against ty name rf tests] tests the function
named [name] by comparing outputs obtained with the student
function against outputs of [rf].
A test [(arg-1, arg-2, arg-3m arg-4)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1], [arg-2], [arg-3] and [arg-4] gives the same
result than the solution function [rf] applied to the same
arguments. Otherwise the result of a test is a
{!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_4_against :
?gen: int ->
?test: 'e tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> 'b -> 'c -> 'd -> unit) ->
?before_user : ('a -> 'b -> 'c -> 'd -> unit) ->
?after : ('a -> 'b -> 'c -> 'd
-> ('e * string * string)
-> ('e * string * string)
-> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b * 'c * 'd)
-> ('a -> 'b -> 'c -> 'd -> 'e) Ty.ty -> string
-> ('a -> 'b -> 'c -> 'd -> 'e)
-> ('a * 'b * 'c * 'd) list -> Learnocaml_report.t
(** [test_function_4_against_solution ty name tests] tests the
function named [name] by comparison to solution function [rf]
which must be defined under name [name] in the corresponding
[solution.ml] file.
A test [(arg-1, arg-2, arg-3, arg-4)] results in a
{!Learnocaml_report.Success 1} report if the student function
applied to [arg-1], [arg-2], [arg-3] and [arg-4] gives the same
result than the solution function [rf] applied to the same
arguments. Otherwise the result of a test is a
{!Learnocaml_report.Failure} report.
See {{!optional_arguments_sec} this section} for information
about optional arguments. *)
val test_function_4_against_solution :
?gen: int ->
?test: 'e tester ->
?test_stdout: io_tester ->
?test_stderr: io_tester ->
?before_reference : ('a -> 'b -> 'c -> 'd -> unit) ->
?before_user : ('a -> 'b -> 'c -> 'd -> unit) ->
?after : ('a -> 'b -> 'c -> 'd
-> ('e * string * string)
-> ('e * string * string)
-> Learnocaml_report.t) ->
?sampler : (unit -> 'a * 'b * 'c * 'd)
-> ('a -> 'b -> 'c -> 'd -> 'e) Ty.ty -> string
-> ('a * 'b * 'c * 'd) list -> Learnocaml_report.t
(** [test_function_4_against_postcond postcond ty name tests] tests that
the function named [name] satisfies the postcondition [postcond].