learn-ocaml icon indicating copy to clipboard operation
learn-ocaml copied to clipboard

Test_lib.require is broken

Open vanyamil opened this issue 6 years ago • 10 comments

File : src/grader/Test_lib.ml, lines 704-711.

Issue : the function "require" is supposed to compare the incoming expressions "n" to a given expression "expr" and return a success on the first time n is equal to expr. However, there are no comparisons made in that function; instead, it returns the success effectively on the first run of the function, and then the "already" flag stays on.

vanyamil avatar Aug 30 '18 15:08 vanyamil

@OCamlPro-Couderc, can you look at this, please?

yurug avatar Aug 30 '18 20:08 yurug

My mistake, the documentation is wrong (there is a correct version for require_syntax and not for require_expr and require). I just opened a pull request with a correction.

lyrm avatar Sep 01 '18 12:09 lyrm

Hi Lyrm,

Your pull request doesn't seem to correct any part of the require function, would that be someone else correcting it?

Thanks.

vanyamil avatar Sep 01 '18 12:09 vanyamil

I don't think the function is wrong, I think it is just that it does not what you expect it to do. However, the name may not be very appropriate, I will agree with that.

I can't find a good example to use it but an example for require_syntax that does almost the same thing: you can add the optional argument ~on_open: require_syntax "open" to check that the syntax open is used at least one (you obviously can also use require to do that but you will need to write the appropriate converter from Parsetree.open_description to string).

lyrm avatar Sep 01 '18 14:09 lyrm

I see. From my understanding, all require (and for that matter, require_syntax) does is still deliver a success on first call of that function? That's why the ~on_open: use works, because the first use coincides with the first call to the open command?

What would be the way to require a function use in the code then? I am looking to make sure that a certain helper function is used in the graded function.

vanyamil avatar Sep 02 '18 22:09 vanyamil

There might be 2 solutions:

  • If the helper function is declared in prepare.ml, you embed a reference that counts the how many times it has been applied, i.e.
let count_f = ref 0

let f x y =
    incr count_f;
    ...

Then you can simply check its content is different than 0 (using test_ref). Since the function is contained in prepare.ml, its exact implementation is unknown by the student. But it needs a bit of preparation (see #72)

  • Another solution is to use ast_check_structure with
~on_expression:(function 
  { descr = (Pexp_apply ({ descr = (Pexp_ident "f" )}, _) } -> (* Success *)

The second solution seems cleaner, but you won't track the function usage if it has been aliased. If the student aliases the function and never use it, the check will still succeed. But I admit it might be a bit overkill to think about functions aliasing.

picdc avatar Sep 03 '18 09:09 picdc

For posterity, I'd like to post the solution we went with here (since the suggested one is missing some record field names, etc.):

(* ------------- Parse Tree Helpers -------------  *)

let name_of_ident ident = String.concat "." (Longident.flatten ident)

let function_app_checker (f : string) =
  let open Parsetree in
  let found = ref false in
  let checker =
    (function
      | { pexp_desc = Pexp_apply ({pexp_desc = Pexp_ident {Location.txt; _}}, _); _} ->
        if (name_of_ident txt) = f then found := true; []
      | _ -> [])
  in
  (found, checker)

(* ------------------- Tester -------------------  *)
let test_exercise =
  let (found_f, f_checker) = function_app_checker "f" in
  Section (
    find_binding code_ast "test" @@ fun expr ->
      let checked_ast =
        ast_check_expr
        ~on_expression: f_checker
        expr
      in
    (Message ([Text "Checking that"; Code "f"; Text "was used in"; Code "test"], Informative))
    ::
    (if !found_f then
      Message ([Text "Found use of "; Code "f"; Text "in"; Code "test"], Success 1)
      ::
      [] (* rest of the testing code, which should only be executed on success, goes here *)
     else
      [Message ([Text "You must call"; Code "f"; Text "in your"; Code "test"; Text "function"], Failure)])

adhameer avatar Sep 04 '18 16:09 adhameer

Should we improve the documentation?

yurug avatar Sep 07 '18 13:09 yurug

I'm very unclear on how require, require_expr, and require_syntax are actually meant to be used, even after lyrm's PR.

adhameer avatar Sep 07 '18 17:09 adhameer

Okay, I now understand how they're used, but they don't actually seem to be that useful since they don't provide a way of checking if the required expression was found, so you can't produce a failure message if the required expression is not used.

adhameer avatar Oct 11 '18 02:10 adhameer