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.
Related
I am writing my first Ocaml+OUnit2+Dune project. but in my unit test when I say open Mymaps it says "Unbound module Mymaps"
The structure of my project is as follows
mymaps
|
|-> lib
|-> dune
|-> mymaps.mli
|-> mymaps.ml
|-> test
|->mymaps_test.ml
|-> dune
|->dune-project
|->mymaps.opam
content of mymaps_test.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
content of mymaps.mli
type ('k, 'v) t
val empty : ('k, 'v) t
val insert : 'k -> 'v -> ('k, 'v) t -> ('k, 'v) t
content of 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 = []
Contents of test/dune file
(test
(name mymaps_test)
(libraries ounit2))
Contents of lib/dune file
(library
(public_name mymaps)
(name mymaps))
Here is the link to Github https://github.com/abhsrivastava/mymaps.git
Please let me know why my test is not able to open the module. Why is it saying Unbound module Mymaps. (I also tried lower case mymaps, and MyMaps, but it just cannot see it).
You need to list mymaps in the libraries stanza of the test project, as you would with any other project in your workspace. There's nothing else indicating that this is the library you mean to test.
(test
(name mymaps_test)
(libraries mymaps ounit2))
I'm following Real World OCaml to get started with the language, and, at one point, I am to make use of s-expressions in a module signature. Here's my mli file:
open Core.Std
(** Configuration type for query handlers *)
type config with sexp
(** Name of the query handler *)
val name : string
(** Query handler abstract type *)
type t
(** Create a query handler from an existing [config] *)
val create : config -> t
(** Evaluate a query, where both input and output an s-expressions. *)
val eval : t -> Sexp.t -> Sexp.t Or_error.t
But, when compiling an implementation of that interface, I get the following error:
File "Query_Handler.mli", line 4, characters 12-16:
Error: Syntax error
Command exited with code 2.
So I opened utop to try with sexp on a simpler example:
module type Test = sig
type t with sexp
end;;
But I get the following error:
Error: Parse Error: "end" expected after [sig_items] (in [module type])
However, sexplib is installed and neither the book nor my searches on the Internet mention any "prerequisites" for using this syntax.
I feel like I'm missing something. Any idea? :(
This is because the sexp library had been rewritten to use Extension Point, instead of Camlp4.
open Core.Std
module type Query_handler = sig
(** Configuration for a query handler. Note that this can be
Converted to and from an s-expression *)
type config [##deriving sexp]
(** The name of the query-handling service *)
val name : string
(** The state of the query handler *)
type t
(** Create a new query handler from a config *)
val create : config -> t
(** Evaluate a given query, where both input and output are
s-expressions *)
val eval : t -> Sexp.t -> Sexp.t Or_error.t
end
module Unique = struct
type config = int [##deriving sexp]
type t = { mutable next_id: int }
let name = "unique"
let create start_at = { next_id = start_at }
let eval t sexp =
match Or_error.try_with (fun () -> unit_of_sexp sexp) with
| Error _ as err -> err
| Ok () ->
let response = Ok (Int.sexp_of_t t.next_id) in
t.next_id <- t.next_id + 1;
response
end
module List_dir = struct
type config = string [##deriving sexp]
type t = { cwd: string }
(** [is_abs p] Returns true if [p] is an absolute path *)
let is_abs p =
String.length p > 0 && p.[0] = '/'
let name = "ls"
let create cwd = { cwd }
let eval t sexp =
match Or_error.try_with (fun () -> string_of_sexp sexp) with
| Error _ as err -> err
| Ok dir ->
let dir =
if is_abs dir then dir
else Filename.concat t.cwd dir
in
Ok (Array.sexp_of_t String.sexp_of_t (Sys.readdir dir))
end
module type Query_handler_instance = sig
module Query_handler : Query_handler
val this : Query_handler.t
end
let build_instance (type a)
(module Q : Query_handler with type config = a)
config =
(module struct
module Query_handler = Q
let this = Q.create config
end : Query_handler_instance)
let build_dispatch_table handlers =
let table = String.Table.create () in
List.iter handlers
~f:(fun ((module I : Query_handler_instance) as instance) ->
Hashtbl.replace table ~key:I.Query_handler.name ~data:instance);
table
let dispatch dispatch_table name_and_query =
match name_and_query with
| Sexp.List [Sexp.Atom name; query] ->
begin match Hashtbl.find dispatch_table name with
| None ->
Or_error.error "Could not find matching handler"
name String.sexp_of_t
| Some (module I : Query_handler_instance) ->
I.Query_handler.eval I.this query
end
| _ ->
Or_error.error_string "malformed query"
let rec cli dispatch_table =
printf ">>> %!";
let result =
match In_channel.input_line stdin with
| None -> `Stop
| Some line ->
match Or_error.try_with (fun () -> Sexp.of_string line) with
| Error e -> `Continue (Error.to_string_hum e)
| Ok query ->
begin match dispatch dispatch_table query with
| Error e -> `Continue (Error.to_string_hum e)
| Ok s -> `Continue (Sexp.to_string_hum s)
end;
in
match result with
| `Stop -> ()
| `Continue msg ->
printf "%s\n%!" msg;
cli dispatch_table
let unique_instance = build_instance (module Unique) 0
let list_dir_instance = build_instance (module List_dir) "/var"
module Loader = struct
type config = (module Query_handler) list sexp_opaque [##deriving sexp]
type t = { known : (module Query_handler) String.Table.t
; active : (module Query_handler_instance) String.Table.t
}
let name ="loader"
let create known_list =
let active = String.Table.create () in
let known = String.Table.create () in
List.iter known_list
~f:(fun ((module Q : Query_handler) as q) ->
Hashtbl.replace known ~key:Q.name ~data:q);
{ known; active }
let load t handler_name config =
if Hashtbl.mem t.active handler_name then
Or_error.error "Can't re-register an active handler"
handler_name String.sexp_of_t
else
match Hashtbl.find t.known handler_name with
| None ->
Or_error.error "Unknown handler" handler_name String.sexp_of_t
| Some (module Q : Query_handler) ->
let instance =
(module struct
module Query_handler = Q
let this = Q.create (Q.config_of_sexp config)
end : Query_handler_instance)
in
Hashtbl.replace t.active ~key:handler_name ~data:instance;
Ok Sexp.unit
let unload t handler_name =
if not (Hashtbl.mem t.active handler_name) then
Or_error.error "Handler not active" handler_name String.sexp_of_t
else if handler_name = name then
Or_error.error_string "It's unwise to unload yourself"
else (
Hashtbl.remove t.active handler_name;
Ok Sexp.unit
)
type request =
| Load of string * Sexp.t
| Unload of string
| Known_services
| Active_services [##deriving sexp]
let eval t sexp =
match Or_error.try_with (fun () -> request_of_sexp sexp) with
| Error _ as err -> err
| Ok resp ->
match resp with
| Load (name,config) -> load t name config
| Unload name -> unload t name
| Known_services ->
Ok [%sexp ((Hashtbl.keys t.known ) : string list)]
| Active_services ->
Ok [%sexp ((Hashtbl.keys t.active) : string list)]
end
This is my ~/.ocamlinit; just comment out the camlp4. utop should work happy.
#use "topfind";;
#warnings "+9"
#thread;;
(*camlp4;;*)
#require "core.top";;
#require "core_extended";;
#require "core_bench";;
#require "ppx_jane";;
#require "ctypes";;
#require "ctypes.foreign";;
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
)
))
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!
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.