Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add new Alcotest.suite function to make writing tests easier #393

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .ocamlformat-ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
examples/floats.ml
examples/simple.ml
45 changes: 23 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,30 @@ module To_test = struct
end

(* The tests *)
let test_lowercase () =
Alcotest.(check string) "same string" "hello!" (To_test.lowercase "hELLO!")

let test_capitalize () =
Alcotest.(check string) "same string" "World." (To_test.capitalize "world.")

let test_str_concat () =
Alcotest.(check string) "same string" "foobar" (To_test.str_concat ["foo"; "bar"])

let test_list_concat () =
Alcotest.(check (list int)) "same lists" [1; 2; 3] (To_test.list_concat [1] [2; 3])

(* Run it *)
let () =
let open Alcotest in
run "Utils" [
"string-case", [
test_case "Lower case" `Quick test_lowercase;
test_case "Capitalization" `Quick test_capitalize;
];
"string-concat", [ test_case "String mashing" `Quick test_str_concat ];
"list-concat", [ test_case "List mashing" `Slow test_list_concat ];
]
Alcotest.suite "Utils" begin fun group ->
group "string-case" begin fun case ->
case "Lower case" begin fun () ->
Alcotest.(check string) "same string" "hello!" (To_test.lowercase "hELLO!")
end;

case "Capitalization" begin fun () ->
Alcotest.(check string) "same string" "World." (To_test.capitalize "world.")
end;
end;

group "string-concat" begin fun case ->
case "String mashing" begin fun () ->
Alcotest.(check string) "same string" "foobar" (To_test.str_concat ["foo"; "bar"])
end;
end;

group "list-concat" begin fun case ->
case ~speed:`Slow "List mashing" begin fun () ->
Alcotest.(check (list int)) "same lists" [1; 2; 3] (To_test.list_concat [1] [2; 3])
end;
end;
end
```

The result is a self-contained binary which displays the test
Expand Down
51 changes: 27 additions & 24 deletions examples/floats.ml
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,31 @@ For more information, please refer to <http://unlicense.org/>

let e = epsilon_float

let nan () =
Alcotest.(check @@ float e) "NaN is NaN" nan nan;
Alcotest.(check @@ neg @@ float e) "NaN is not number" nan 7.;
Alcotest.(check @@ neg @@ float e) "number is not NaN" 8. nan

let infinity () =
Alcotest.(check @@ float e) "+∞ is +∞" infinity infinity;
Alcotest.(check @@ float e) "-∞ is -∞" neg_infinity neg_infinity;
Alcotest.(check @@ neg @@ float e) "+∞ is not -∞" infinity neg_infinity;
Alcotest.(check @@ neg @@ float e) "-∞ is not +∞" neg_infinity infinity;
Alcotest.(check @@ neg @@ float e) "+∞ is not 3" infinity 3.

let others () =
Alcotest.(check @@ float e) "0 is 0" 0. 0.;
Alcotest.(check @@ float e) "0 is epsilon" 0. e;
Alcotest.(check @@ neg @@ float e) "0 is not 1" 0. 1.;
Alcotest.(check @@ neg @@ float e) "1 is not 0" 1. 0.;
Alcotest.(check @@ float e) ".3 is .3" (0.1 +. 0.2) 0.3

let edge_set = [ ("NaN", `Quick, nan); ("∞", `Quick, infinity) ]
let others_set = [ ("others", `Quick, others) ]

let () =
Alcotest.run "Float tests"
[ ("Edge cases", edge_set); ("Other floats", others_set) ]
Alcotest.suite "Float tests" begin fun group ->
group "Edge cases" begin fun case ->
case "NaN" begin fun () ->
Alcotest.(check @@ float e) "NaN is NaN" nan nan;
Alcotest.(check @@ neg @@ float e) "NaN is not number" nan 7.;
Alcotest.(check @@ neg @@ float e) "number is not NaN" 8. nan
end;

case "∞" begin fun () ->
Alcotest.(check @@ float e) "+∞ is +∞" infinity infinity;
Alcotest.(check @@ float e) "-∞ is -∞" neg_infinity neg_infinity;
Alcotest.(check @@ neg @@ float e) "+∞ is not -∞" infinity neg_infinity;
Alcotest.(check @@ neg @@ float e) "-∞ is not +∞" neg_infinity infinity;
Alcotest.(check @@ neg @@ float e) "+∞ is not 3" infinity 3.
end;
end;

group "Other floats" begin fun case ->
case "others" begin fun () ->
Alcotest.(check @@ float e) "0 is 0" 0. 0.;
Alcotest.(check @@ float e) "0 is epsilon" 0. e;
Alcotest.(check @@ neg @@ float e) "0 is not 1" 0. 1.;
Alcotest.(check @@ neg @@ float e) "1 is not 0" 1. 0.;
Alcotest.(check @@ float e) ".3 is .3" (0.1 +. 0.2) 0.3
end;
end;
end
52 changes: 23 additions & 29 deletions examples/simple.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,27 @@ module To_test = struct
end

(* The tests *)
let test_lowercase () =
Alcotest.(check string) "same string" "hello!" (To_test.lowercase "hELLO!")

let test_capitalize () =
Alcotest.(check string) "same string" "World." (To_test.capitalize "world.")

let test_str_concat () =
Alcotest.(check string)
"same string" "foobar"
(To_test.str_concat [ "foo"; "bar" ])

let test_list_concat () =
Alcotest.(check (list int))
"same lists" [ 1; 2; 3 ]
(To_test.list_concat [ 1 ] [ 2; 3 ])

(* Run it *)
let () =
Alcotest.run "Utils"
[
( "string-case",
[
Alcotest.test_case "Lower case" `Quick test_lowercase;
Alcotest.test_case "Capitalization" `Quick test_capitalize;
] );
( "string-concat",
[ Alcotest.test_case "String mashing" `Quick test_str_concat ] );
( "list-concat",
[ Alcotest.test_case "List mashing" `Slow test_list_concat ] );
]
Alcotest.suite "Utils" begin fun group ->
group "string-case" begin fun case ->
case "Lower case" begin fun () ->
Alcotest.(check string) "same string" "hello!" (To_test.lowercase "hELLO!")
end;

case "Capitalization" begin fun () ->
Alcotest.(check string) "same string" "World." (To_test.capitalize "world.")
end;
end;

group "string-concat" begin fun case ->
case "String mashing" begin fun () ->
Alcotest.(check string) "same string" "foobar" (To_test.str_concat ["foo"; "bar"])
end;
end;

group "list-concat" begin fun case ->
case ~speed:`Slow "List mashing" begin fun () ->
Alcotest.(check (list int)) "same lists" [1; 2; 3] (To_test.list_concat [1] [2; 3])
end;
end;
end
5 changes: 5 additions & 0 deletions src/alcotest-engine/cli.ml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ module Make (P : Platform.MAKER) (M : Monad.S) :
let run =
Config.User.kcreate (fun config ?argv name tl ->
run_with_args' config ~argv name (Term.const ()) tl)

let suite =
Config.User.kcreate (fun config ?argv name register ->
run_with_args' config ~argv name (Term.const ())
(suite_testlist register))
end

module V1 = struct
Expand Down
32 changes: 32 additions & 0 deletions src/alcotest-engine/cli_intf.ml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,38 @@ module V1_types = struct
effect, and [~argv:\[| "ignored"; "--verbose" |\]] will successfully
pass the verbose option. *)

val suite :
(?argv:string array -> string -> (unit group -> unit) -> return)
with_options
(** [suite n register] runs the test suite registered by the registration
function [register]. [n] is the name of the tested library.

Other parameters are the same as they are in [run].

Example usage:

{[
let () =
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ocamlformat settings don't like the formatting of this example. IMHO, the format settings are incorrect :-)

Alcotest.suite "Int" begin fun group ->
group "( + )" begin fun case ->
case "positive + positive" begin fun () ->
Alcotest.(check int) "1 + 1" 2 (1 + 1)
end;

case "positive + negative" begin fun () ->
Alcotest.(check int) "1 + -1" 0 (1 + -1)
end;
end;

group "( - )" begin fun case ->
(* Not actually slow, just for demonstration purposes *)
case ~speed:`Slow "positive - positive" begin fun () ->
Alcotest.(check int) "1 - 1" 0 (1 - 1)
end;
end;
end
]} *)

val run_with_args :
(?argv:string array ->
string ->
Expand Down
18 changes: 18 additions & 0 deletions src/alcotest-engine/core.ml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ module Make (P : Platform.MAKER) (M : Monad.S) = struct
let test_case n s f = (n, s, f)

type 'a test = string * 'a test_case list
type 'a case = ?speed:speed_level -> string -> 'a run -> unit
type 'a group = string -> ('a case -> unit) -> unit

(* global state *)
type 'a t = {
Expand Down Expand Up @@ -417,6 +419,22 @@ module Make (P : Platform.MAKER) (M : Monad.S) = struct
?ci

let run = Config.User.kcreate run'

let suite_testlist register =
let groups = ref [] in
let each_group name register =
let cases = ref [] in
let each_case ?(speed = `Quick) name func =
cases := test_case name speed func :: !cases
in
register each_case;
groups := (name, List.rev !cases) :: !groups
in
register each_group;
List.rev !groups

let suite config name register = run' config name (suite_testlist register)
let suite = Config.User.kcreate suite
end

module V1 = struct
Expand Down
5 changes: 5 additions & 0 deletions src/alcotest-engine/core_intf.ml
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,13 @@ module V1_types = struct
- [ci] (default auto-detected). Whether to enable specific logging for a
CI system. *)

type 'a case = ?speed:speed_level -> string -> ('a -> return) -> unit
type 'a group = string -> ('a case -> unit) -> unit

val run : (string -> unit test list -> return) with_options
val run_with_args : (string -> 'a -> 'a test list -> return) with_options
val suite_testlist : (unit group -> unit) -> unit test list
val suite : (string -> (unit group -> unit) -> return) with_options
end

module type MAKER = functor (_ : Platform.MAKER) (M : Monad.S) -> sig
Expand Down