OCaml OUnit bracket example: setup and tear-down - unit-testing

I do not really get how to use bracket setup and tear-down with OUnit (version 2).
Anyone feel like supplying a full example ?
Here is the OUnit2.bracket function documentation:
val bracket : (test_ctxt -> 'a) ->
('a -> test_ctxt -> unit) -> test_ctxt -> 'a
bracket set_up tear_down test_ctxt set up an object
and register it to be tore down in test_ctxt.
You setup a test suite like this:
let test_suite =
"suite" >::: [
"test1" >:: test1_fun
]
And run it like this:
let _ =
run_test_tt_main test_suite
Where do I put the bracket in this workflow ?
Link to OUnit documentation.
The file test_stack.ml in ounit-2.0.0/examples test bracket for OUnit version 1, so that's not useful.

OK, got it after having a look at this file: TestLog.ml
This example will systematically destroy the hashtables after each test as a teardown function.
open ListLabels (* map with label *)
let test_sentence test_ctxt =
assert_equal "Foo" "Foo"
(** In my case, clear a hashtable after each test *)
let tear_down () test_ctxt =
Hashtbl.clear Globals.flags_tbl
(** List of test cases as (name, function) tuples *)
let test_list = [
"sentence", test_sentence;
]
(** Test suite for all tags *)
let tag_test_suite =
"tags" >::: (map test_list ~f:(fun (name, test_fn) ->
name >:: (fun test_ctxt ->
bracket ignore tear_down test_ctxt;
test_fn test_ctxt
)
))

Related

Dune not able to compile my first project

I followed the following steps to create my project
Create the project using the following dune command
dune init proj --kind=lib mymaps
Then added 2 files under the "lib" directory
mymaps.mli
type ('k, 'v) t
val empty : ('k, 'v) t
val insert : 'k -> 'v -> ('k, 'v) t -> ('k, 'v) t
mymaps.ml
type ('k, 'v) t = ('k * 'v) list
let rec insert k v m = match m with
| [] -> [(k, v)]
| (eK, eV) :: tl -> let (nK, nV) = if (eK = k) then (k, v) else (eK, eV) in
(nK, nV) :: insert k v tl
let empty = []
Added the following file under the "tests" directory
mymaps_tests.ml
open Ounit2
open Mymaps
let empty_test =
"empty has no bindings" >:: (fun _ -> assert_equal [] (empty));
let mymap_tests = [empty_test]
let suite = "maps suite" >::: mymap_tests
let _ = run_test_tt_main suite
However when I go to command line and say dune build it says
File "test/dune", line 2, characters 7-13:
2 | (name mymaps))
^^^^^^
Error: Module "Mymaps" doesn't exist.
Here is the link to GitHub for my work https://github.com/abhsrivastava/mymaps
I am following a tutorial on YouTube and I didn't see them implement any modules for the test project, they straight wrote the test. Not sure why is it looking for another Mymaps under test.
I think I found out the issue. the name of the test file should be the same as the name of the project. I renamed my tests file to "mymaps.ml" and then the error went away.

Using qtest and quickcheck, fail to compile the test code

My ocaml setting is the following :
ocaml 4.01.0
opam 1.1.2
qtest 2.0.1 (opam list qtest)
quickcheck 1.0.0
The source code with test inlined :
let rec foo x0 f = function
[] -> 0
| x::xs -> f x (foo x0 f xs);;
(*$T foo
foo 0 (+) [1;2] = 3
*)
qtest -o footest2.ml extract foo.ml
Then unfortunately, footest2.ml fails to compile:
corebuild footest2.native -pkg quickcheck
let ___tests = ref []
let ___add test = ___tests := test::!___tests
open OUnit;;
module Q = Quickcheck;;let ( ==> ) = Q.( ==> );;
Random.self_init()
module Test__environment_0 = struct
open Foo;;
let _test_2 = "foo" >::: [
"foo.ml:6" >:: (
#6 "foo.ml"
let foo = foo in fun () -> OUnit.assert_bool "foo.ml:6: foo 0 (+) [1;2] = 3" (
#6 "foo.ml"
foo 0 (+) [1;2] = 3));
];; let _ = ___add _test_2;;
end
let _ = exit (Runner.run ("" >::: List.rev !___tests))
the error being: "Error: Unbound module Quickcheck"
Indeed, it should be QuickCheck instead of Quickcheck - after fixing this, I got the error : Error: Unbound value Q.==>.
After removal of :
let ( ==> ) = Q.( ==> );;
The compilation fails later :
Error: Unbound module Runner.
But no module called "Runner"...
Any idea to get this working?
Try using the package QTest2Lib instead of qtest or quickcheck:
corebuild footest2.native -pkg QTest2Lib

Inspect operator |> in OCaml's toplevel?

I'd like to see the type of the operator |>, which is of course defined as let |> x f = f x;;.
With other operators, like +, I can simple hit (+);; and the toplevel will tell me it's - : int -> int -> int = <fun>.
But with |>, it says:
Error: Failure: "|> must be applied to two arguments"
My question is, how can I inspect operator |> in OCaml's toplevel.
Append:
OK. Now I know it has something to do with Core. If I define it directly it's OK. But in Core environment it just breaks.
This is my .ocamlinit:
(* Added by OPAM. *)
let () =
try Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH")
with Not_found -> ()
;;
#use "topfind";;
#thread;;
#camlp4o;;
#require "core.top";;
#require "core.syntax";;
open Core.Std;;
It works for me. Here's a complete command line session:
$ ocaml
OCaml version 4.01.0
# let (|>) x f = f x;;
val ( |> ) : 'a -> ('a -> 'b) -> 'b = <fun>
# (|>);;
- : 'a -> ('a -> 'b) -> 'b = <fun>
#
Update
(Pascal Cuoq clearly has the right answer in the comments below. :-)

"Error: The function applied to this argument has type ..." when using named parameters

I'm currently working through "Real Word OCaml", and one of the basic examples with named / labeled parameters doesn't seem to work (using utop 4.01.0):
let languages = ["OCaml"; "Perl"; "C"];;
List.map ~f:String.length languages;;
Produces:
Error: The function applied to this argument has type 'a list -> 'b list
This argument cannot be applied with label ~f
Whereas:
List.map String.length languages;;
Produces the expected output [5; 4; 1].
caml.inria.fr mentions that:
In the core language, as in most languages, arguments are anonymous.
Does this mean that I have to include some kind of external library to make this code work ?
EDIT
Here's my ~/.ocamlinit file (as per the installation instructions for the book):
(* Added by OPAM. *)
let () =
try Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH")
with Not_found -> ()
;;
#use "topfind"
#camlp4o
#thread
#require "core.top"
#require "core.syntax"
As mentioned in #rafix's comment, this can be fixed by putting
open Core.Std ;;
first.
The standard List.map method isn't defined with the label ~f. The type of List.map is ('a -> 'b) -> 'a list -> 'b list, but if you wanted to use the "~f" label, it would have to be "f:('a->'b) -> 'a list -> 'b list". If you wanted to define your own, you would have to define it as such:
let rec myMap ~f l = match l with
| [] -> []
| h::t -> (f h) :: (myMap ~f t);;
val myMap : f:('a -> 'b) -> 'a list -> 'b list = <fun>
and then you could call it like you wanted:
myMap ~f:String.length languages
Cheers!

Loading module with dynlink re-initialises top-level values

I have a problem where I have a global hashtable, and then I load a .cma file with Dynlink, which registers a function in the hashtable.
However, the behaviour I seem to be see is that when the module is dynamically linked, all the global bindings get re-initialised, such that my hashtable is empty.
E.g.:
Table.extensions : (string, string -> string) Hashtbl.t
Extensions.load : unit -> unit (* loads the specified .cma files *)
Extensions.register : string -> (string -> string) -> unit
(* adds entry to Table.extensions, prints name of extension registered *)
Main:
let () =
Extensions.load ();
Hashtbl.iter (fun x _ -> print_endline x) Table.extensions;
Printf.printf "%d extensions loaded\n" (Hashtbl.length Table.extensions)
My program loads one .cma file, so it should print:
Registered extension 'test'
test
1 extensions loaded
Instead I get:
Registered extension 'test'
0 extensions loaded
I've been fighting this for several hours now; no matter how I refactor my code, I get no closer to a working solution.
EDIT: Extensions.load:
Dynlink.allow_unsafe_modules true;;
let load () =
try
let exts = Sys.readdir "exts" in
Array.iter begin fun name ->
try
Dynlink.loadfile (Filename.concat "exts" name);
Printf.printf "Loaded %s\n" name;
with
| Dynlink.Error error -> print_endline (Dynlink.error_message error)
| exn -> print_endline (Printexc.to_string exn)
end exts
with _ -> ()
#ygrek, you were right, there were two instances.
The solution was to build/load just the .cmo, not a .cma.