From 3d98f6fdfd1ca3d12541efc6523acfec69782028 Mon Sep 17 00:00:00 2001 From: Martin Jambon Date: Tue, 14 Jan 2020 17:25:50 -0800 Subject: [PATCH 1/4] Jenkins needs testcase classnames to be nonempty, otherwise it produces an unclickable blank cell. --- alcotest/junit_alcotest.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alcotest/junit_alcotest.ml b/alcotest/junit_alcotest.ml index 129eb2f..e5da631 100644 --- a/alcotest/junit_alcotest.ml +++ b/alcotest/junit_alcotest.ml @@ -10,14 +10,14 @@ let wrap_test handle_result (name, s, test) = test (); Junit.Testcase.pass ~name - ~classname:"" + ~classname:name ~time:0. |> handle_result with | Failure exn_msg as exn -> Junit.Testcase.failure ~name - ~classname:"" + ~classname:name ~time:0. ~typ:"not expected result" ~message:"test failed" @@ -28,7 +28,7 @@ let wrap_test handle_result (name, s, test) = let exn_msg = Printexc.to_string exn in Junit.Testcase.error ~name - ~classname:"" + ~classname:name ~time:0. ~typ:"exception raised" ~message:"test crashed" From 788115add090190b163529e1339d2e4d3d5b3460 Mon Sep 17 00:00:00 2001 From: Martin Jambon Date: Tue, 14 Jan 2020 18:01:40 -0800 Subject: [PATCH 2/4] Accept new results with 'dune promote' --- alcotest/test/alcotest_report.expected | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alcotest/test/alcotest_report.expected b/alcotest/test/alcotest_report.expected index 1181394..cf3745d 100644 --- a/alcotest/test/alcotest_report.expected +++ b/alcotest/test/alcotest_report.expected @@ -1,5 +1,5 @@ -Invalid_argument("7")Error string_of_int equals to '7': expecting +Invalid_argument("7")Error string_of_int equals to '7': expecting 7, got -8.Invalid_argument("7")Error string_of_int equals to '7': expecting +8.Invalid_argument("7")Error string_of_int equals to '7': expecting 7, got -8. +8. From b6a74f23a31dd28063d92c211df7ad44a2c9108e Mon Sep 17 00:00:00 2001 From: Martin Jambon Date: Tue, 14 Jan 2020 18:41:16 -0800 Subject: [PATCH 3/4] Populate the classname field with a dot-separated path for hierarchical display by Jenkins. --- alcotest/junit_alcotest.ml | 16 +++++++++++----- alcotest/junit_alcotest.mli | 6 ++++++ alcotest/test/alcotest_report.expected | 6 +++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/alcotest/junit_alcotest.ml b/alcotest/junit_alcotest.ml index e5da631..7ee01c8 100644 --- a/alcotest/junit_alcotest.ml +++ b/alcotest/junit_alcotest.ml @@ -4,20 +4,25 @@ type exit = unit -> unit let push l v = l := v :: !l -let wrap_test handle_result (name, s, test) = +let wrap_test ?path handle_result (name, s, test) = + let classname = + match path with + | None -> name + | Some path -> Printf.sprintf "%s.%s" path name + in let test () = try test (); Junit.Testcase.pass ~name - ~classname:name + ~classname ~time:0. |> handle_result with | Failure exn_msg as exn -> Junit.Testcase.failure ~name - ~classname:name + ~classname ~time:0. ~typ:"not expected result" ~message:"test failed" @@ -28,7 +33,7 @@ let wrap_test handle_result (name, s, test) = let exn_msg = Printexc.to_string exn in Junit.Testcase.error ~name - ~classname:name + ~classname ~time:0. ~typ:"exception raised" ~message:"test crashed" @@ -46,7 +51,8 @@ let run_and_report ?(and_exit=true) ?package ?timestamp ?argv name tests = let testsuite = Junit.Testsuite.make ?package ?timestamp ~name () in let tests = List.map (fun (title, test_set) -> - (title, List.map (wrap_test (push testcases)) test_set) + let path = Printf.sprintf "%s.%s" name title in + (title, List.map (wrap_test ~path (push testcases)) test_set) ) tests in let exit = diff --git a/alcotest/junit_alcotest.mli b/alcotest/junit_alcotest.mli index 0cd243a..7389f2c 100644 --- a/alcotest/junit_alcotest.mli +++ b/alcotest/junit_alcotest.mli @@ -5,6 +5,7 @@ *) val wrap_test : + ?path:string -> (Junit.Testcase.t -> unit) -> unit Alcotest.test_case -> unit Alcotest.test_case @@ -13,6 +14,11 @@ val wrap_test : Can be used with {!run} to create customized Junit testsuites if the output of {!run_and_report} is not as expected. + + @param path if specified is prepended to the test case name to form + a dot-separated path that will populate the 'classname' field of the + XML test case. Jenkins uses dots to detect and display tests as a + hierarchy. *) val run: ?argv:string array -> string -> unit Alcotest.test list -> unit diff --git a/alcotest/test/alcotest_report.expected b/alcotest/test/alcotest_report.expected index cf3745d..e71079a 100644 --- a/alcotest/test/alcotest_report.expected +++ b/alcotest/test/alcotest_report.expected @@ -1,5 +1,5 @@ -Invalid_argument("7")Error string_of_int equals to '7': expecting +Invalid_argument("7")Error string_of_int equals to '7': expecting 7, got -8.Invalid_argument("7")Error string_of_int equals to '7': expecting +8.Invalid_argument("7")Error string_of_int equals to '7': expecting 7, got -8. +8. From b9c1797c06b523dee0b915b31587e305458a014a Mon Sep 17 00:00:00 2001 From: Martin Jambon Date: Thu, 16 Jan 2020 15:34:43 -0800 Subject: [PATCH 4/4] Set the classname attribute to something more reasonable, after having investigated Jenkins/junit-plugin behavior. --- alcotest/junit_alcotest.ml | 17 +++++++++++------ alcotest/junit_alcotest.mli | 11 ++++++----- alcotest/test/alcotest_report.expected | 6 +++--- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/alcotest/junit_alcotest.ml b/alcotest/junit_alcotest.ml index 7ee01c8..d275a8c 100644 --- a/alcotest/junit_alcotest.ml +++ b/alcotest/junit_alcotest.ml @@ -4,11 +4,16 @@ type exit = unit -> unit let push l v = l := v :: !l -let wrap_test ?path handle_result (name, s, test) = +let wrap_test ?classname handle_result (name, s, test) = let classname = - match path with - | None -> name - | Some path -> Printf.sprintf "%s.%s" path name + (* The 'classname' attribute may not be empty, and should contain a period + for best rendering in Jenkins by junit-plugin. + For example, classname="foo.bar.baz" is rendered as a package "foo.bar" + containing a class "baz" containing one or more test cases with their + own name. *) + match classname with + | None | Some "" -> name + | Some path -> path in let test () = try @@ -51,8 +56,8 @@ let run_and_report ?(and_exit=true) ?package ?timestamp ?argv name tests = let testsuite = Junit.Testsuite.make ?package ?timestamp ~name () in let tests = List.map (fun (title, test_set) -> - let path = Printf.sprintf "%s.%s" name title in - (title, List.map (wrap_test ~path (push testcases)) test_set) + let classname = Printf.sprintf "%s.%s" name title in + (title, List.map (wrap_test ~classname (push testcases)) test_set) ) tests in let exit = diff --git a/alcotest/junit_alcotest.mli b/alcotest/junit_alcotest.mli index 7389f2c..2e6a177 100644 --- a/alcotest/junit_alcotest.mli +++ b/alcotest/junit_alcotest.mli @@ -5,7 +5,7 @@ *) val wrap_test : - ?path:string -> + ?classname:string -> (Junit.Testcase.t -> unit) -> unit Alcotest.test_case -> unit Alcotest.test_case @@ -15,10 +15,11 @@ val wrap_test : Can be used with {!run} to create customized Junit testsuites if the output of {!run_and_report} is not as expected. - @param path if specified is prepended to the test case name to form - a dot-separated path that will populate the 'classname' field of the - XML test case. Jenkins uses dots to detect and display tests as a - hierarchy. + @param classname will populate the 'classname' attribute + for the test case. For best hierarchic rendering in Jenkins, it + should contain a period. For example, "foo.bar.baz" will be rendered + a package "foo.bar" that contains a class "baz", which contains the + current test case and others. Defaults to the name of the test case. *) val run: ?argv:string array -> string -> unit Alcotest.test list -> unit diff --git a/alcotest/test/alcotest_report.expected b/alcotest/test/alcotest_report.expected index e71079a..ec836f7 100644 --- a/alcotest/test/alcotest_report.expected +++ b/alcotest/test/alcotest_report.expected @@ -1,5 +1,5 @@ -Invalid_argument("7")Error string_of_int equals to '7': expecting +Invalid_argument("7")Error string_of_int equals to '7': expecting 7, got -8.Invalid_argument("7")Error string_of_int equals to '7': expecting +8.Invalid_argument("7")Error string_of_int equals to '7': expecting 7, got -8. +8.