From 276dac96c8d538a97e628e77b162c49642ae519a Mon Sep 17 00:00:00 2001 From: EmileRolley Date: Fri, 20 Aug 2021 19:00:55 +0200 Subject: [PATCH] fix(docs): should fix #399 and unify the markdown syntax --- docs/tutorials/step-1.md | 7 +- docs/tutorials/step-2.md | 17 ++-- docs/tutorials/step-3.md | 174 +++++++++++++++++++++------------------ docs/tutorials/step-4.md | 36 ++++++-- docs/tutorials/step-5.md | 51 ++++++------ docs/tutorials/step-6.md | 19 +++-- docs/tutorials/step-7.md | 26 +++--- docs/tutorials/step-8.md | 147 +++++++++++++++++---------------- 8 files changed, 267 insertions(+), 210 deletions(-) diff --git a/docs/tutorials/step-1.md b/docs/tutorials/step-1.md index 3ce1e09a9..a4270fdfd 100644 --- a/docs/tutorials/step-1.md +++ b/docs/tutorials/step-1.md @@ -5,6 +5,7 @@ real effort from the student: any valid OCaml code will be a valid answer for this exercise. Let us focus on `test.ml`: + ```ocaml open Test_lib open Report @@ -49,9 +50,9 @@ give 1 point to the student. directory. 2. Update index.json 3. Build and run the new instance of your local learn-ocaml platform: -```ocaml -learn-ocaml build; learn-ocaml serve -``` + ```ocaml + learn-ocaml build; learn-ocaml serve + ``` At this point, you should see the exercise in the instance opened on `http://localhost:8080`. Click on grade to get your point! diff --git a/docs/tutorials/step-2.md b/docs/tutorials/step-2.md index 14cf9e5b9..b66a7c6f3 100644 --- a/docs/tutorials/step-2.md +++ b/docs/tutorials/step-2.md @@ -2,12 +2,14 @@ Let us now move to the development a basic grader. We ask the student to implement the identity function and provide the following `template.ml`: + ```ocaml let identity x = "Put your code here" ``` Our goal is to check that this function is modified by the student in such a way that it behaves as our `solution.ml`: + ```ocaml let identity x = x ``` @@ -20,6 +22,7 @@ the student gets one point. Assuming that we choose `int` for this ground type, the behavior described in the previous paragraph is implemented as follows: + ```ocaml open Test_lib open Report @@ -80,18 +83,20 @@ corresponding report. For a function called `my_function`, it will be ## Want to learn more about grading function ? + The next steps will bring you progressively to understand most of the possibilities of grading functions. However, if you want to have a better overview right now, you can go directly to [step 5](step-5.md) where you will: -* find the signature of `test_function_1_against_solution` -* learn a new grading function for functions -* learn how to change the header report -* have a quick resumé of the utility of each optional arguments with a +- find the signature of `test_function_1_against_solution` +- learn a new grading function for functions +- learn how to change the header report +- have a quick resumé of the utility of each optional arguments with a link to the right tutorial. ## Multiple arguments + To grade a function with multiple arguments you simply need to use the corresponding grading function which follows this pattern : `Test_lib.test_function__against_solution` and give @@ -114,12 +119,11 @@ let () = ``` - You can find this example in the `exercises/grade-function-multiple_args` directory (branch: step-2). - ## Polymorphic functions : testing several types + For a polymorphic functions, you may want to test the function with different types. To do so, you can concat the result of numerous grading functions and encapsulate it in a `Section` which has two arguments : @@ -129,7 +133,6 @@ some text and a list of items produced by grading functions. open Test_lib open Report - let exercise_1 = Section ([ Text "Function: "; Code "identity" ; Text " with multiple tested input types." ], [ test_function_1_against_solution diff --git a/docs/tutorials/step-3.md b/docs/tutorials/step-3.md index 4c79a0d86..3e40bc1b8 100644 --- a/docs/tutorials/step-3.md +++ b/docs/tutorials/step-3.md @@ -1,6 +1,7 @@ # Step 3: Grading with generators for Ocaml simple built-in types - You can find the examples below in the - `exercises/sampler-built-in-types` directory (branch: step-3). + +You can find the examples below in the `exercises/sampler-built-in-types` +directory (branch: step-3). As see previously, you can either give manually inputs for the tested functions or you can ask the grader to automatically generate inputs. @@ -17,17 +18,26 @@ tuple) for example. Here is a list of the signatures of predefined samplers : ```ocaml - val sample_int : int sampler - val sample_float : float sampler - val sample_list : ?min_size: int -> ?max_size: int -> ?dups: bool -> ?sorted: bool -> 'a sampler -> 'a list sampler - val sample_array : ?min_size: int -> ?max_size: int -> ?dups: bool -> ?sorted: bool -> 'a sampler -> 'a array sampler - val sample_option : 'a sampler -> 'a option sampler - val sample_string : string sampler - val sample_char : char sampler - val sample_bool : bool sampler +val sample_int : int sampler +val sample_float : float sampler +val sample_list : + ?min_size: int -> + ?max_size: int -> + ?dups: bool -> + ?sorted: bool -> 'a sampler -> 'a list sampler +val sample_array : + ?min_size: int -> + ?max_size: int -> + ?dups: bool -> + ?sorted: bool -> 'a sampler -> 'a array sampler +val sample_option : 'a sampler -> 'a option sampler +val sample_string : string sampler +val sample_char : char sampler +val sample_bool : bool sampler ``` ## How to use a default sampler + When the function to test has inputs of type with predefined sample, you have nothing to do. In the example below, five tests are automatically generated. @@ -46,14 +56,13 @@ int` to be tested for inputs between 12 and 42, you need to give the grade function the sampler you want the same way you will to for type without predefined sampler. There are actually two ways do to that: - ## Method 1 : using the `~sampler` argument + The more general way do provide a sampler is to use the optional argument `~sampler` that has type: `unit -> * * etc.`. - ```ocaml let exercise_2 = test_function_2_against_solution @@ -64,6 +73,7 @@ let exercise_2 = ``` ## Method 2 : redefining the corresponding sampling function. + Another way is to define a sampling function of type `unit -> * * etc.` using the naming convention : `sample_`. In this case, nothing needs to be add to the grade @@ -80,76 +90,80 @@ let exercise_3 = ``` ## More avanced examples - You can find the examples below in the - `exercises/advanced-examples-step-3` directory (branch: step-3). + +You can find the examples below in the `exercises/advanced-examples-step-3` +directory (branch: step-3). There is nothing new to learn in this part, there are only more examples of how to build a sampler for more complexe types. In particular, there are examples with: -* list -```ocaml -let exercise_1 = - test_function_2_against_solution - [%ty: int -> int list -> int list] "push" - ~gen:5 - [] -``` -* tuple -```ocaml -let exercise_2 = - test_function_1_against_solution - [%ty: (int * int) -> int] "first" - ~gen:5 - ~sampler:(fun () -> (Random.int 10, Random.int 10)) - [] -``` -* type option -```ocaml -let exercise_3 = - test_function_1_against_solution - [%ty: int option -> int] "opt" - ~gen:5 - [] - -let sampler_4 () = - let sampler_tuple () = (sample_int (), sample_int ()) in - (sample_option sampler_tuple) () - -let exercise_4 = - test_function_1_against_solution - [%ty: (int * int) option -> int] "opt_add" - ~gen:5 - ~sampler:sampler_4 - [] -``` -* functional type -```ocaml -let sampler_5 () = - let sampler_f () = match Random.int 3 with - | 0 -> succ - | 1 -> pred - | _ -> fun _ -> 0 in - sampler_f (), sample_int () - - -let exercise_5 = - test_function_2_against_solution - [%ty: (int -> int) -> int -> int] "apply" - ~gen:5 - ~sampler:sampler_5 - [] - -``` -* array -```ocaml -let sampler_6 = - sample_array ~min_size:1 ~max_size:10 sample_int - -let exercise_6 = - test_function_1_against_solution - [%ty: int array -> int list] "array_to_list" - ~gen:5 - ~sampler:sampler_6 - [] -``` +- list + ```ocaml + let exercise_1 = + test_function_2_against_solution + [%ty: int -> int list -> int list] "push" + ~gen:5 + [] + ``` + +- tuple + ```ocaml + let exercise_2 = + test_function_1_against_solution + [%ty: (int * int) -> int] "first" + ~gen:5 + ~sampler:(fun () -> (Random.int 10, Random.int 10)) + [] + ``` + +- type option + ```ocaml + let exercise_3 = + test_function_1_against_solution + [%ty: int option -> int] "opt" + ~gen:5 + [] + + let sampler_4 () = + let sampler_tuple () = (sample_int (), sample_int ()) in + (sample_option sampler_tuple) () + + let exercise_4 = + test_function_1_against_solution + [%ty: (int * int) option -> int] "opt_add" + ~gen:5 + ~sampler:sampler_4 + [] + ``` + +- functional type + ```ocaml + let sampler_5 () = + let sampler_f () = match Random.int 3 with + | 0 -> succ + | 1 -> pred + | _ -> fun _ -> 0 in + sampler_f (), sample_int () + + + let exercise_5 = + test_function_2_against_solution + [%ty: (int -> int) -> int -> int] "apply" + ~gen:5 + ~sampler:sampler_5 + [] + ``` + +- array + ```ocaml + let sampler_6 = + sample_array ~min_size:1 ~max_size:10 sample_int + + let exercise_6 = + test_function_1_against_solution + [%ty: int array -> int list] "array_to_list" + ~gen:5 + ~sampler:sampler_6 + [] + ``` diff --git a/docs/tutorials/step-4.md b/docs/tutorials/step-4.md index 28edd6232..b2e62994d 100644 --- a/docs/tutorials/step-4.md +++ b/docs/tutorials/step-4.md @@ -6,13 +6,15 @@ argument or defining a sampler function `sample_my_type`) can be used but required a little more work, especially for parametric types. ## Non parametric type + For non-parametric type, it is exactly the same than in the previous step. -You can find the examples below in the -`exercises/sampler-user-defined-types` directory (branch: step-4). +You can find the examples below in the `exercises/sampler-user-defined-types` +directory (branch: step-4). In the examples, we use the type `color` defined as : + ```ocaml type color = Green | Yellow | Red | Blue ``` @@ -33,6 +35,7 @@ let exercise_1 = ``` ### Method 2: Defining a sampler + You can also define your own sampler and not use the `~sampler` argument with the following rule: a sampler of type `unit -> my_type` has to be named `sample_my_type`. @@ -62,6 +65,7 @@ You can find the examples below in the `exercises/sampler-user-defined-parametric-types` directory (branch: step-4). In the examples below, we use the types: + ```ocaml type col = R | B @@ -69,6 +73,7 @@ type 'a tree = | Leaf | Node of 'a tree * 'a * 'a tree ``` + ### Method 1: using the `~sampler` argument No change here, just don't forget that the optional argument`~sampler` @@ -103,15 +108,16 @@ A sampler of a parametric type `('a * 'b * ... ) my_type` has a type : `(unit -> 'a) -> (unit -> 'b) -> ... -> -> (unit -> ('a * 'b * ...) my_type` and must be named `sample_my_type`. +So for example, if we want to test a function of type `col tree -> int`, so we +need two samplers : -So for example, if we want to test a function of type `col tree -> int`, so we need two samplers : ``` ocaml -(*Not a parametric type*) +(* Not a parametric type *) let sample_col () = match Random.int 2 with | 0 -> B | _ -> R -(*A parametric type*) +(* A parametric type *) let sample_tree (sample: unit -> 'a) : unit -> 'a tree = let rec builder h = match h with | 0 -> Leaf @@ -124,6 +130,7 @@ let sample_tree (sample: unit -> 'a) : unit -> 'a tree = ``` The grading function is then simply : + ```ocaml let exercise_2 = test_function_1_against_solution @@ -134,6 +141,7 @@ let exercise_2 = Note that if instead of [col tree], the input type is [int tree] (or another type with a predefined sampler), you need nothing more. + ```ocaml let exercise_2bis = test_function_1_against_solution @@ -154,8 +162,8 @@ let exercise_3 = [] ``` - ### Advanced examples + More advanced examples (but nothing new) can be found in `exercises/advanced-examples-step-4` directory (branch: step-4). @@ -164,18 +172,21 @@ particular examples for functional types graded with both methods and using the predefined sampler of list. The user-defined type is: + ```ocaml type position = {x: int ; y: int} ``` and its corresponding sampler: + ```ocaml let sample_position () = { x=sample_int () ; y=sample_int () } ``` #### First example: `get_x` -Exactly as shown previously, using metdho 2: +Exactly as shown previously, using method 2: + ```ocaml let exercise_1 = test_function_1_against_solution @@ -199,8 +210,12 @@ let sampler_fun () = match Random.int 3 with | 1 -> pred | _ -> fun x -> if x < 0 then -1 else 1 ``` + ##### Method 1 -For this method, we can just build the proper sampler for all the function arguments. + +For this method, we can just build the proper sampler for all the function +arguments. + ```ocaml let sampler_2 () = (sampler_fun (), sample_list ~min_size:1 ~max_size:10 sample_int ()) @@ -214,6 +229,7 @@ let exercise_2 = ``` ##### Method 2 + For this method, we need to use an alias for type `int -> int`. ```ocaml @@ -229,10 +245,12 @@ let exercise_2bis = ``` #### Third example: 'first_elt' (tuple) + In case you want to grade a function with a tuple as an input type, you can either use method 1 or define an alias and use method 2. ##### Method 1 + ```ocaml let exercise_3 = test_function_1_against_solution @@ -243,6 +261,7 @@ let exercise_3 = ``` ##### Method 2 + ```ocaml type pair_int = int * int let sample_pair_int () = sample_int (), sample_int () @@ -254,7 +273,6 @@ let exercise_3bis = [] ``` - ### Which method should I use ? Both methods work well for a lot of exercises. However for functional diff --git a/docs/tutorials/step-5.md b/docs/tutorials/step-5.md index cd0e5014a..58881b8b2 100644 --- a/docs/tutorials/step-5.md +++ b/docs/tutorials/step-5.md @@ -3,13 +3,14 @@ **Warning** This step is ahead of the current version of [learn-ocaml]. ## Different grading functions for functions + There are 2 main grading functions: -* `test_function__againt_solution`: the usual. Test the +- `test_function__againt_solution`: the usual. Test the student code against a given solution written in the `solution.ml` file. -* `test_function_`: compare the student code to a series of +- `test_function_`: compare the student code to a series of tests where both inputs and expected outputs are given. Note that you still need to write a solution in `solution.ml` to build your exercise session since `learn-ocaml build` tests your grader with @@ -38,9 +39,11 @@ ones: empty report by default. ## `test_function__against_solution` + ### Signature for unary function + ```ocaml - val test_function_1_against_solution : +val test_function_1_against_solution : ?gen: int -> ?test: 'b tester -> ?test_stdout: io_tester -> @@ -52,24 +55,23 @@ ones: ('a -> 'b) Ty.ty -> string -> 'a list -> Learnocaml_report.item ``` -### Mandatory arguments -[`test_function_1_againt_solution_1 ty name tests`]: +### Mandatory arguments [`test_function_1_againt_solution_1 ty name tests`]: -* `ty`: type of the tested function specified for the given tests. It +- `ty`: type of the tested function specified for the given tests. It must not contain type variables (i.e. `'a`, `'b` etc..), match the type of the tests and be compatible with the solution. -* `name`: name of the tested function in the student code and in +- `name`: name of the tested function in the student code and in `solution.ml` -* `tests`: list of tested inputs. +- `tests`: list of tested inputs. ## `test_function_` ### Signature of grading function for unary function ```ocaml - val test_function_1 : +val test_function_1 : ?test: 'b tester -> ?test_stdout: io_tester -> ?test_stderr: io_tester -> @@ -78,17 +80,16 @@ ones: ('a -> 'b) Ty.ty -> string -> ('a * 'b * string * string) list -> Learnocaml_report.item ``` -### Mandatory arguments -[`test_function_1 ty name tests`]: +### Mandatory arguments [`test_function_1 ty name tests`]: -* `ty`: type of the tested function specified for the given tests. It +- `ty`: type of the tested function specified for the given tests. It must not contain type variables (i.e. `'a`, `'b` etc..), match the type of the tests and be compatible with the solution. -* `name`: name of the tested function in the student code and in +- `name`: name of the tested function in the student code and in `solution.ml` -* `(in, out, stderr, stdout) :: _` as `tests`: list of tuples where +- `(in, out, stderr, stdout) :: _` as `tests`: list of tuples where the first element `in` is the argument passed to the tested function (for a three-arguments functions `tests` will instead matched `(in1, in2, in3, out, stderr, stdout)`). The second element `out` is the @@ -97,49 +98,49 @@ ones: ## Optional arguments of grading functions -* `gen`: number of automatically generated tests. See +- `gen`: number of automatically generated tests. See [step-3](step-3.md) and [step-4](step-4.md) for more information. -* `sampler`: used to define sampler for automatically generating +- `sampler`: used to define sampler for automatically generating inputs for tests. See [step-3](step-3.md) and [step-4](step-4.md) for more information. -* `test`: is used to redefine the function which compare the output of +- `test`: is used to redefine the function which compare the output of the student function and the output of the solution. See WIP for more information. -* `test_stdout`: is used to redefine the function which compare the +- `test_stdout`: is used to redefine the function which compare the standard output channel of the student function and the one of the solution. See WIP for more information. -* `test_stderr`: is used to redefine the function which compare the +- `test_stderr`: is used to redefine the function which compare the standard error channel of the student function and the one of the solution. See WIP for more information. -* `after`: is used to redefine a function which is called with the +- `after`: is used to redefine a function which is called with the current tested inputs, the student result and the solution result and returns a report. Enables for example to inspect references introduced with `~before`, `~before_user` or `~before_reference` and build an appropriate report. See WIP for more information. -* `before_reference`: is used to redefine a function called right +- `before_reference`: is used to redefine a function called right before the application of the current tested inputs to the solution. This enables for example to introduce a reference or make a side effect before each test. See WIP for more information. -* `before_user`: is used to redefine a function called right before +- `before_user`: is used to redefine a function called right before the application the current tested inputs to the student function. This enables to introduce a reference or make a side effect between solution evaluation and student function evaluation. See WIP for more information. -* `before`: same as `before_reference` for +- `before`: same as `before_reference` for `test_function_`. Since no solution is evaluated, there is no need to distinguish between before or after solution evaluation. @@ -161,7 +162,9 @@ let exercise_1 = ~sampler:(fun () -> Random.int 42) [0 ; 42] ``` + With `test_function_1`: + ```ocaml let exercise_2 = test_function_1 @@ -175,7 +178,6 @@ Note that since the default comparison function for standard output and error actually ignore them, we could put anything as expected outputs in the tests list. - ### Hello world `Hello world` is also very classical, however grading this function is @@ -183,7 +185,6 @@ a little more tricky since by default standard output and error output are ignored (meaning the student standard/error output can be different from the solution ones without causing a failure). - ```ocaml let exercise_2 = test_function_1_against_solution diff --git a/docs/tutorials/step-6.md b/docs/tutorials/step-6.md index 581ce3355..a95536914 100644 --- a/docs/tutorials/step-6.md +++ b/docs/tutorials/step-6.md @@ -3,22 +3,26 @@ **Warning** This step is ahead of the current version of [learn-ocaml]. ## Test functions for variables + There are 3 test functions for variables. ### Signatures + ```ocaml - val test_variable : - 'a Ty.ty -> string -> 'a -> Learnocaml_report.report +val test_variable : + 'a Ty.ty -> string -> 'a -> Learnocaml_report.report - val test_variable_against_solution : - 'a Ty.ty -> string -> Learnocaml_report.report +val test_variable_against_solution : + 'a Ty.ty -> string -> Learnocaml_report.report - val test_variable_property : - 'a Ty.ty -> string -> ('a -> Learnocaml_report.report) -> Learnocaml_report.report +val test_variable_property : + 'a Ty.ty -> string -> ('a -> Learnocaml_report.report) -> Learnocaml_report.report ``` ### Examples + #### Trivial example with `grade_variable` + `grade_variable` is usually used only for trivial examples since it is directly compared to an expected result and not to a solution. @@ -28,14 +32,17 @@ let exercise_0 = ``` #### More classical example with `grade_variable_against_solution` + `grade_variable_against_solution` is more versatile and works basically like `test_function_against_solution`. + ```ocaml let exercise_1 = grade_variable_against_solution [%ty: float] "norm" ``` #### `grade_variable_property` + `grade_variable_property` is used in specific cases when you want to write your own report depending on the value of the graded variable. diff --git a/docs/tutorials/step-7.md b/docs/tutorials/step-7.md index 93986c0ef..59d83ca94 100644 --- a/docs/tutorials/step-7.md +++ b/docs/tutorials/step-7.md @@ -1,11 +1,13 @@ ## Step 7: Modifying the comparison functions (testers) with the optional arguments [~test], [~test_stdout], [~test_stderr] ### Tester `~test` + Tester are functions used to compare the student output result with the solution output result. The output result can be either `Ok _` or `Error _` (i.e. a raised exception). #### Signatures of predefined testers and tester builders + See [Test_lib documentation](https://github.com/ocaml-sf/learn-ocaml/blob/master/src/grader/test_lib.mli) for more information. Some typical examples are shown below. @@ -84,6 +86,7 @@ let exercise_3 = ``` ### IO testers `~test_stdout` and `~test_stderr` + IO testers are used to compare string such are standard output. By default, the values of `test_stdout` and `test_sdterr` are @@ -91,20 +94,21 @@ By default, the values of `test_stdout` and `test_sdterr` are and error outputs. #### Signatures of predefined IO testers and IO tester builders + See [Test_lib documentation](https://github.com/ocaml-sf/learn-ocaml/blob/master/src/grader/test_lib.mli) for more information. Some typical examples are shown below. ```ocaml - val io_test_ignore : io_tester - val io_test_equals : - ?trim: char list -> ?drop: char list -> io_tester - val io_test_lines : - ?trim: char list -> ?drop: char list -> - ?skip_empty: bool -> ?test_line: io_tester -> io_tester - val io_test_items : - ?split: char list -> ?trim: char list -> ?drop: char list -> - ?skip_empty: bool -> ?test_item: io_tester -> io_tester +val io_test_ignore : io_tester +val io_test_equals : + ?trim: char list -> ?drop: char list -> io_tester +val io_test_lines : + ?trim: char list -> ?drop: char list -> + ?skip_empty: bool -> ?test_line: io_tester -> io_tester +val io_test_items : + ?split: char list -> ?trim: char list -> ?drop: char list -> + ?skip_empty: bool -> ?test_item: io_tester -> io_tester ``` #### Examples @@ -171,6 +175,7 @@ result need to be redefined. The first function graded is a function that generates randomly integer. We want the integer to be between 0 and 10. + ```ocaml let p x = if x >= 0 && x < 10 then true else false @@ -186,6 +191,7 @@ Obviously we only check here that the output integer is in the right range (but this is just a trivial example). The second example is a function using the previous one to generate a list of integers. + ```ocaml let p_list l = (* Check all elements of the input list are in the right range*) @@ -202,4 +208,4 @@ let exercise_2 = ~gen:0 ~test:(test_eq_ok (fun x _ -> p_list x)) [10 ; 20] -``` \ No newline at end of file +``` diff --git a/docs/tutorials/step-8.md b/docs/tutorials/step-8.md index 965a077c3..1b26ab136 100644 --- a/docs/tutorials/step-8.md +++ b/docs/tutorials/step-8.md @@ -76,75 +76,75 @@ tests/odd_even.ml Here is in details the source code of the exercise : - **descr.md**: - -> * implement the function `add : peano -> peano -> peano` ; -> * implement the functions `odd : peano -> bool` and `even : peano -> bool`. + > * implement the function `add : peano -> peano -> peano` ; + > * implement the functions `odd : peano -> bool` and `even : peano -> bool`. - **prelude.ml**: -```ocaml -type peano = Z | S of peano -``` + ```ocaml + type peano = Z | S of peano + ``` - **solution.ml**: -```ocaml -let rec add n = function -| Z -> n -| S m -> S (add n m) - -let rec odd = function -| Z -> false -| S n -> even n -and even = function -| Z -> true -| S n -> odd n -``` + ```ocaml + let rec add n = function + | Z -> n + | S m -> S (add n m) + + let rec odd = function + | Z -> false + | S n -> even n + and even = function + | Z -> true + | S n -> odd n + ``` - **test.ml**: -```ocaml -let () = -Check.safe_set_result [ Add.test ; Odd_even.test ] -``` + ```ocaml + let () = + Check.safe_set_result [ Add.test ; Odd_even.test ] + ``` Note that **test.ml** is very compact because it simply combines functions defined in separated files. - **../lib/check.ml**: -```ocaml -open Test_lib -open Report - -let safe_set_result tests = -set_result @@ -ast_sanity_check code_ast @@ fun () -> -List.mapi (fun i test -> -Section ([ Text ("Question " ^ string_of_int i ^ ":") ], -test ())) tests -``` + ```ocaml + open Test_lib + open Report + + let safe_set_result tests = + set_result @@ + ast_sanity_check code_ast @@ fun () -> + List.mapi (fun i test -> + Section ([ Text ("Question " ^ string_of_int i ^ ":") ], + test ())) tests + ``` - **../lib/check.mli**: -```ocaml -val safe_set_result : (unit -> Report.t) list -> unit -``` + ```ocaml + val safe_set_result : (unit -> Report.t) list -> unit + ``` - **tests/add.ml**: -```ocaml -let test () = -Test_lib.test_function_2_against_solution -[%ty : peano -> peano -> peano ] "add" -[ (Z, Z) ; (S(Z), S(S(Z))) ] -``` + ```ocaml + let test () = + Test_lib.test_function_2_against_solution + [%ty : peano -> peano -> peano ] "add" + [ (Z, Z) ; (S(Z), S(S(Z))) ] + ``` - **tests/odd_even.ml**: -```ocaml -let test () = -Test_lib.test_function_1_against_solution -[%ty : peano -> bool ] "odd" -[ Z ; S(Z) ; S(S(Z)) ] -@ -Test_lib.test_function_1_against_solution -[%ty : peano -> bool ] "even" -[ Z ; S(Z) ; S(S(Z)) ] -``` + ```ocaml + let test () = + Test_lib.test_function_1_against_solution + [%ty : peano -> bool ] "odd" + [ Z ; S(Z) ; S(S(Z)) ] + @ + Test_lib.test_function_1_against_solution + [%ty : peano -> bool ] "even" + [ Z ; S(Z) ; S(S(Z)) ] + ``` + Remember that **Test_lib** internally requires a user-defined sampler `sample_peano : unit -> peano` to generate value of type `peano`. This sampler has to be present in the toplevel environment -- and not in a module -- in order @@ -152,39 +152,46 @@ to be found by the introspection primitives during grading. Therefore, we define this sampler in a file starting with the annotation `[@@@included]`. - **tests/samples.ml**: -```ocaml -[@@@included] + ```ocaml + [@@@included] -let sample_peano () = -let rec aux = function -| 0 -> Z -| n -> S (aux (n-1)) -in aux (Random.int 42) -``` + let sample_peano () = + let rec aux = function + | 0 -> Z + | n -> S (aux (n-1)) + in aux (Random.int 42) + ``` Finally, the content of **test.ml** will be evaluated in the following environment: ```ocaml val print_html : 'a -> 'b + type peano = Z | S of peano -module Code : -sig -val add : peano -> peano -> peano -val odd : peano -> bool -val even : peano -> bool + +module Code : sig + val add : peano -> peano -> peano + val odd : peano -> bool + val even : peano -> bool end -module Solution : -sig -val add : peano -> peano -> peano -val odd : peano -> bool -val even : peano -> bool + +module Solution : sig + val add : peano -> peano -> peano + val odd : peano -> bool + val even : peano -> bool end + module Test_lib : Test_lib.S + module Report = Learnocaml_report + module Check : sig val check_all : (unit -> Report.t) list -> unit end + val sample_peano : unit -> peano + module Add : sig val test : unit -> Report.t end + module Odd_even : sig val test : unit -> Report.t end ```