├── .github ├── dependabot.yml └── workflows │ ├── build-test.yml │ ├── lint.yml │ └── opam-dependency-submission.yml ├── .gitignore ├── .ocamlformat ├── .travis.yml ├── CHANGES.md ├── LICENSE ├── Makefile ├── README.md ├── alcotest ├── dune ├── junit_alcotest.ml ├── junit_alcotest.mli └── test │ ├── alcotest_report.expected │ ├── alcotest_report.ml │ └── dune ├── doc ├── api.odocl ├── dev.odocl └── doc.odocl ├── dune-project ├── junit.opam ├── junit ├── dune ├── junit.ml ├── junit.mli ├── junit_xml.ml ├── junit_xml.mli └── test │ ├── dune │ ├── simple.expected │ └── simple.ml ├── junit_alcotest.opam ├── junit_ounit.opam ├── ounit ├── dune ├── junit_ounit.ml └── junit_ounit.mli └── schemes ├── gist.xsd ├── jenkins.xsd ├── junit-schema.xsd └── windyroad.xsd /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build-and-test: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: 13 | - macos-latest 14 | - ubuntu-latest 15 | # - windows-latest 16 | ocaml-version: 17 | - 4.14 18 | - 5.2 19 | 20 | runs-on: ${{ matrix.os }} 21 | 22 | steps: 23 | - name: Checkout code 24 | uses: actions/checkout@v4 25 | 26 | - name: Use OCaml ${{ matrix.ocaml-version }} 27 | uses: ocaml/setup-ocaml@v3 28 | with: 29 | ocaml-compiler: ${{ matrix.ocaml-version }} 30 | dune-cache: true 31 | allow-prerelease-opam: true 32 | 33 | - run: opam install . --deps-only --with-test 34 | 35 | - name: build project 36 | run: opam exec -- dune build 37 | 38 | - name: run test 39 | run: opam exec -- dune runtest 40 | 41 | - name: junit report 42 | uses: dorny/test-reporter@v2 43 | if: success() || failure() # run this step even if previous step failed 44 | with: 45 | name: Test Report 46 | path: "_build/default/alcotest/test/*.xml,_build/default/junit/test/*.xml" 47 | reporter: java-junit 48 | fail-on-error: false # Report results but don't fail the build 49 | fail-on-empty: true # Use an empty test report to detect when something failed with the test runner 50 | 51 | - run: opam install . --deps-only --with-test --criteria='+removed,+count[version-lag,solution]' --solver=builtin-0install 52 | 53 | - name: build project 54 | run: opam exec -- dune build 55 | 56 | - name: run test 57 | run: opam exec -- dune runtest 58 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | lint-fmt: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v4 13 | 14 | - name: Use OCaml 5.2 15 | uses: ocaml/setup-ocaml@v3 16 | with: 17 | ocaml-compiler: 5.2 18 | dune-cache: true 19 | allow-prerelease-opam: true 20 | 21 | - name: Lint fmt 22 | uses: ocaml/setup-ocaml/lint-fmt@v3 23 | 24 | # auto fix formatting, Thanks to robur.coop for the implementation 25 | # https://discuss.ocaml.org/t/ocamlformat-and-github-actions/15464 26 | # lint-auto-fmt: 27 | # runs-on: ubuntu-latest 28 | # steps: 29 | # - name: Checkout code 30 | # uses: actions/checkout@v4 31 | 32 | # - name: Use OCaml 5.2 33 | # uses: ocaml/setup-ocaml@v3 34 | # with: 35 | # ocaml-compiler: 5.2 36 | # dune-cache: true 37 | # allow-prerelease-opam: true 38 | 39 | # - name: Install ocamlformat 40 | # run: grep '^version' .ocamlformat | cut -d '=' -f 2 | xargs -I V opam install ocamlformat=V 41 | 42 | # - name: Format code 43 | # run: | 44 | # git ls-files '*.ml' '*.mli' | xargs opam exec -- ocamlformat --inplace 45 | 46 | # - name: Check for modified files 47 | # id: git-check 48 | # run: echo "modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT 49 | 50 | # - name: Commit and push changes 51 | # if: ${{ steps.git-check.outputs.modified == 'true' }} 52 | # run: | 53 | # git config --global user.name "Automated ocamlformat GitHub action, developed by robur.coop" 54 | # git config --global user.email "autoformat@robur.coop" 55 | # git add -A 56 | # git commit -m "formatted code" 57 | # git push 58 | 59 | lint-opam: 60 | runs-on: ubuntu-latest 61 | steps: 62 | - name: Checkout code 63 | uses: actions/checkout@v4 64 | 65 | - name: Use OCaml 5.2 66 | uses: ocaml/setup-ocaml@v3 67 | with: 68 | ocaml-compiler: 5.2 69 | dune-cache: true 70 | allow-prerelease-opam: true 71 | 72 | - name: Lint opam 73 | uses: ocaml/setup-ocaml/lint-opam@v3 74 | 75 | lint-doc: 76 | runs-on: ubuntu-latest 77 | steps: 78 | - name: Checkout code 79 | uses: actions/checkout@v4 80 | 81 | - name: Use OCaml 5.2 82 | uses: ocaml/setup-ocaml@v3 83 | with: 84 | ocaml-compiler: 5.2 85 | dune-cache: true 86 | allow-prerelease-opam: true 87 | 88 | - name: Lint doc 89 | uses: ocaml/setup-ocaml/lint-doc@v3 90 | -------------------------------------------------------------------------------- /.github/workflows/opam-dependency-submission.yml: -------------------------------------------------------------------------------- 1 | name: Opam Dependency Submission 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | opam-dependency-submission: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout tree 12 | uses: actions/checkout@v4 13 | 14 | - name: Set-up OCaml 5.2 15 | uses: ocaml/setup-ocaml@v3 16 | with: 17 | ocaml-compiler: 5.2 18 | dune-cache: true 19 | allow-prerelease-opam: true 20 | 21 | - name: Opam Dependency Submission 22 | uses: ocaml/setup-ocaml/analysis@v3 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .merlin 2 | *.install 3 | _build 4 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | profile=janestreet 2 | version=0.27.0 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: required 3 | install: wget https://raw.githubusercontent.com/ocaml/ocaml-travisci-skeleton/master/.travis-opam.sh 4 | script: bash -ex .travis-opam.sh 5 | env: 6 | matrix: 7 | - OCAML_VERSION=4.05 PACKAGE="junit" TESTS=true 8 | - OCAML_VERSION=4.05 PACKAGE="junit_alcotest" TESTS=true 9 | - OCAML_VERSION=4.05 PACKAGE="junit_ounit" TESTS=true 10 | - OCAML_VERSION=4.06 PACKAGE="junit" TESTS=true 11 | - OCAML_VERSION=4.06 PACKAGE="junit_alcotest" TESTS=true 12 | - OCAML_VERSION=4.06 PACKAGE="junit_ounit" TESTS=true 13 | - OCAML_VERSION=4.07 PACKAGE="junit" TESTS=true 14 | - OCAML_VERSION=4.07 PACKAGE="junit_alcotest" TESTS=true 15 | - OCAML_VERSION=4.07 PACKAGE="junit_ounit" TESTS=true 16 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 2.3.0 (2025-04-21) 2 | 3 | - upgrade minimal alcotest to 1.9.0 4 | 5 | ## 2.2.0 (2024-12-25) 6 | 7 | - fix bug marking alcotest skipped tests as passed 8 | - reraise exceptions to clean backtraces 9 | - expose arguments of `Alcotest.run` 10 | - increase dependency on alcotest to 1.8.0 11 | 12 | ## 2.1.0 (2024-12-22) 13 | 14 | - add skipped attribute to testsuite (#10) 15 | - fix skipped support for alcotest (#10) 16 | - depend on ounit2 17 | 18 | ## 2.0.1 (2019-02-16) 19 | 20 | - remove pkg 21 | - migrate to opam 2 22 | - migrate to dune 23 | 24 | ## 2.0 (2017-04-30) 25 | 26 | - add alcotest and ounit support 27 | 28 | ## 1.0 (2017-09-5) 29 | 30 | - move to jbuilder 31 | - `Testsuite.make` expects `()` as last argument. 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | As a special exception to the GNU Lesser General Public License, you 2 | may link, statically or dynamically, a "work that uses the Library" 3 | with a publicly distributed version of the Library to produce an 4 | executable file containing portions of the Library, and distribute 5 | that executable file under terms of your choice, without any of the 6 | additional requirements listed in clause 6 of the GNU Library General 7 | Public License. By "a publicly distributed version of the Library", we 8 | mean either the unmodified Library as distributed by the copyright 9 | holder, or a modified version of the Library that is distributed under 10 | the conditions defined in clause 3 of the GNU Library General Public 11 | License. This exception does not however invalidate any other reasons 12 | why the executable file might be covered by the GNU Lesser General 13 | Public License. 14 | 15 | GNU LESSER GENERAL PUBLIC LICENSE 16 | Version 3, 29 June 2007 17 | 18 | Copyright (C) 2007 Free Software Foundation, Inc. 19 | Everyone is permitted to copy and distribute verbatim copies 20 | of this license document, but changing it is not allowed. 21 | 22 | 23 | This version of the GNU Lesser General Public License incorporates 24 | the terms and conditions of version 3 of the GNU General Public 25 | License, supplemented by the additional permissions listed below. 26 | 27 | 0. Additional Definitions. 28 | 29 | As used herein, "this License" refers to version 3 of the GNU Lesser 30 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 31 | General Public License. 32 | 33 | "The Library" refers to a covered work governed by this License, 34 | other than an Application or a Combined Work as defined below. 35 | 36 | An "Application" is any work that makes use of an interface provided 37 | by the Library, but which is not otherwise based on the Library. 38 | Defining a subclass of a class defined by the Library is deemed a mode 39 | of using an interface provided by the Library. 40 | 41 | A "Combined Work" is a work produced by combining or linking an 42 | Application with the Library. The particular version of the Library 43 | with which the Combined Work was made is also called the "Linked 44 | Version". 45 | 46 | The "Minimal Corresponding Source" for a Combined Work means the 47 | Corresponding Source for the Combined Work, excluding any source code 48 | for portions of the Combined Work that, considered in isolation, are 49 | based on the Application, and not on the Linked Version. 50 | 51 | The "Corresponding Application Code" for a Combined Work means the 52 | object code and/or source code for the Application, including any data 53 | and utility programs needed for reproducing the Combined Work from the 54 | Application, but excluding the System Libraries of the Combined Work. 55 | 56 | 1. Exception to Section 3 of the GNU GPL. 57 | 58 | You may convey a covered work under sections 3 and 4 of this License 59 | without being bound by section 3 of the GNU GPL. 60 | 61 | 2. Conveying Modified Versions. 62 | 63 | If you modify a copy of the Library, and, in your modifications, a 64 | facility refers to a function or data to be supplied by an Application 65 | that uses the facility (other than as an argument passed when the 66 | facility is invoked), then you may convey a copy of the modified 67 | version: 68 | 69 | a) under this License, provided that you make a good faith effort to 70 | ensure that, in the event an Application does not supply the 71 | function or data, the facility still operates, and performs 72 | whatever part of its purpose remains meaningful, or 73 | 74 | b) under the GNU GPL, with none of the additional permissions of 75 | this License applicable to that copy. 76 | 77 | 3. Object Code Incorporating Material from Library Header Files. 78 | 79 | The object code form of an Application may incorporate material from 80 | a header file that is part of the Library. You may convey such object 81 | code under terms of your choice, provided that, if the incorporated 82 | material is not limited to numerical parameters, data structure 83 | layouts and accessors, or small macros, inline functions and templates 84 | (ten or fewer lines in length), you do both of the following: 85 | 86 | a) Give prominent notice with each copy of the object code that the 87 | Library is used in it and that the Library and its use are 88 | covered by this License. 89 | 90 | b) Accompany the object code with a copy of the GNU GPL and this license 91 | document. 92 | 93 | 4. Combined Works. 94 | 95 | You may convey a Combined Work under terms of your choice that, 96 | taken together, effectively do not restrict modification of the 97 | portions of the Library contained in the Combined Work and reverse 98 | engineering for debugging such modifications, if you also do each of 99 | the following: 100 | 101 | a) Give prominent notice with each copy of the Combined Work that 102 | the Library is used in it and that the Library and its use are 103 | covered by this License. 104 | 105 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 106 | document. 107 | 108 | c) For a Combined Work that displays copyright notices during 109 | execution, include the copyright notice for the Library among 110 | these notices, as well as a reference directing the user to the 111 | copies of the GNU GPL and this license document. 112 | 113 | d) Do one of the following: 114 | 115 | 0) Convey the Minimal Corresponding Source under the terms of this 116 | License, and the Corresponding Application Code in a form 117 | suitable for, and under terms that permit, the user to 118 | recombine or relink the Application with a modified version of 119 | the Linked Version to produce a modified Combined Work, in the 120 | manner specified by section 6 of the GNU GPL for conveying 121 | Corresponding Source. 122 | 123 | 1) Use a suitable shared library mechanism for linking with the 124 | Library. A suitable mechanism is one that (a) uses at run time 125 | a copy of the Library already present on the user's computer 126 | system, and (b) will operate properly with a modified version 127 | of the Library that is interface-compatible with the Linked 128 | Version. 129 | 130 | e) Provide Installation Information, but only if you would otherwise 131 | be required to provide such information under section 6 of the 132 | GNU GPL, and only to the extent that such information is 133 | necessary to install and execute a modified version of the 134 | Combined Work produced by recombining or relinking the 135 | Application with a modified version of the Linked Version. (If 136 | you use option 4d0, the Installation Information must accompany 137 | the Minimal Corresponding Source and Corresponding Application 138 | Code. If you use option 4d1, you must provide the Installation 139 | Information in the manner specified by section 6 of the GNU GPL 140 | for conveying Corresponding Source.) 141 | 142 | 5. Combined Libraries. 143 | 144 | You may place library facilities that are a work based on the 145 | Library side by side in a single library together with other library 146 | facilities that are not Applications and are not covered by this 147 | License, and convey such a combined library under terms of your 148 | choice, if you do both of the following: 149 | 150 | a) Accompany the combined library with a copy of the same work based 151 | on the Library, uncombined with any other library facilities, 152 | conveyed under the terms of this License. 153 | 154 | b) Give prominent notice with the combined library that part of it 155 | is a work based on the Library, and explaining where to find the 156 | accompanying uncombined form of the same work. 157 | 158 | 6. Revised Versions of the GNU Lesser General Public License. 159 | 160 | The Free Software Foundation may publish revised and/or new versions 161 | of the GNU Lesser General Public License from time to time. Such new 162 | versions will be similar in spirit to the present version, but may 163 | differ in detail to address new problems or concerns. 164 | 165 | Each version is given a distinguishing version number. If the 166 | Library as you received it specifies that a certain numbered version 167 | of the GNU Lesser General Public License "or any later version" 168 | applies to it, you have the option of following the terms and 169 | conditions either of that published version or of any later version 170 | published by the Free Software Foundation. If the Library as you 171 | received it does not specify a version number of the GNU Lesser 172 | General Public License, you may choose any version of the GNU Lesser 173 | General Public License ever published by the Free Software Foundation. 174 | 175 | If the Library as you received it specifies that a proxy can decide 176 | whether future versions of the GNU Lesser General Public License shall 177 | apply, that proxy's public statement of acceptance of any version is 178 | permanent authorization for you to choose that version for the 179 | Library. 180 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | dune build 3 | 4 | doc: 5 | dune build @doc 6 | 7 | test: 8 | dune runtest 9 | 10 | clean: 11 | dune clean 12 | 13 | fmt: 14 | dune build @fmt --auto-promote 15 | 16 | .PHONY: examples doc test build fmt 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OCaml JUnit 2 | 3 | ocaml-junit is a package for the creation of JUnit XML reports. It 4 | provides a typed API to produce valid reports. They are supposed to be 5 | accepted by Jenkins. 6 | 7 | It comes with two packages for support of OUnit and Alcotest. 8 | 9 | ## Installation 10 | 11 | ``` 12 | opam install junit junit_ounit junit_alcotest 13 | ``` 14 | 15 | ## Documentation 16 | 17 | Available [here](https://khady.github.io/ocaml-junit/) 18 | 19 | ## References: 20 | 21 | - [Jenkins](https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd) 22 | - [JUnit-Schema](https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd) 23 | - [Windyroad](http://windyroad.com.au/dl/Open%20Source/JUnit.xsd) 24 | - [a gist](https://gist.github.com/erikd/4192748) 25 | 26 | Those files are archived in directory [`schemes`](schemes) 27 | 28 | License: LGPL either version 3 of the License, or (at your option) any 29 | later version with OCaml linking exception. 30 | -------------------------------------------------------------------------------- /alcotest/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name junit_alcotest) 3 | (public_name junit_alcotest) 4 | (wrapped false) 5 | (synopsis "JUnit XML reports generation for alcotest tests") 6 | (libraries junit alcotest)) 7 | -------------------------------------------------------------------------------- /alcotest/junit_alcotest.ml: -------------------------------------------------------------------------------- 1 | module A = Alcotest 2 | 3 | external reraise : exn -> 'a = "%reraise" 4 | 5 | type exit = unit -> unit 6 | 7 | let push l v = l := v :: !l 8 | 9 | let wrap_test ?classname handle_result (name, s, test) = 10 | let classname = 11 | (* The 'classname' attribute may not be empty, and should contain a period 12 | for best rendering in Jenkins by junit-plugin. 13 | For example, classname="foo.bar.baz" is rendered as a package "foo.bar" 14 | containing a class "baz" containing one or more test cases with their 15 | own name. *) 16 | match classname with 17 | | None | Some "" -> name 18 | | Some path -> path 19 | in 20 | let test () = 21 | try 22 | test (); 23 | Junit.Testcase.pass ~name ~classname ~time:0. |> handle_result 24 | with 25 | | Failure exn_msg as exn -> 26 | Junit.Testcase.failure 27 | ~name 28 | ~classname 29 | ~time:0. 30 | ~typ:"not expected result" 31 | ~message:"test failed" 32 | exn_msg 33 | |> handle_result; 34 | reraise exn 35 | | Alcotest_engine.V1.Core.Skip as exn -> 36 | Junit.Testcase.skipped ~name ~classname ~time:0. |> handle_result; 37 | reraise exn 38 | | exn -> 39 | let exn_msg = Printexc.to_string exn in 40 | Junit.Testcase.error 41 | ~name 42 | ~classname 43 | ~time:0. 44 | ~typ:"exception raised" 45 | ~message:"test crashed" 46 | exn_msg 47 | |> handle_result; 48 | reraise exn 49 | in 50 | name, s, test 51 | ;; 52 | 53 | let run_and_report 54 | ?stdout 55 | ?stderr 56 | ?(and_exit = true) 57 | ?verbose 58 | ?compact 59 | ?tail_errors 60 | ?quick_only 61 | ?show_errors 62 | ?json 63 | ?filter 64 | ?log_dir 65 | ?bail 66 | ?record_backtrace 67 | ?ci 68 | ?package 69 | ?timestamp 70 | ?argv 71 | name 72 | tests 73 | = 74 | let testcases = ref [] in 75 | let testsuite = Junit.Testsuite.make ?package ?timestamp ~name () in 76 | let tests = 77 | List.map 78 | (fun (title, test_set) -> 79 | let classname = Printf.sprintf "%s.%s" name title in 80 | title, List.map (wrap_test ~classname (push testcases)) test_set) 81 | tests 82 | in 83 | let exit = 84 | try 85 | A.run 86 | ?stdout 87 | ?stderr 88 | ?verbose 89 | ?compact 90 | ?tail_errors 91 | ?quick_only 92 | ?show_errors 93 | ?json 94 | ?filter 95 | ?log_dir 96 | ?bail 97 | ?record_backtrace 98 | ?ci 99 | ?argv 100 | ~and_exit:false 101 | name 102 | tests; 103 | fun () -> if and_exit then exit 0 else () 104 | with 105 | | A.Test_error as exn -> fun () -> if and_exit then exit 1 else reraise exn 106 | in 107 | Junit.Testsuite.add_testcases !testcases testsuite, exit 108 | ;; 109 | -------------------------------------------------------------------------------- /alcotest/junit_alcotest.mli: -------------------------------------------------------------------------------- 1 | (** Interface to product JUnit reports for Alcotest 2 | 3 | It tries to provide a layer as thin as possible on top of Alcotest 4 | to allow to port existing test without writing a lot a boilerplate. *) 5 | 6 | (** [wrap_test handle_result test_cases] wraps test cases to create 7 | Junit testcases and pass them to [handle_result]. 8 | 9 | Can be used with {!Alcotest.run} to create customized Junit testsuites if 10 | the output of {!run_and_report} is not as expected. 11 | 12 | @param classname 13 | will populate the 'classname' attribute 14 | for the test case. For best hierarchic rendering in Jenkins, it 15 | should contain a period. For example, "foo.bar.baz" will be rendered 16 | a package "foo.bar" that contains a class "baz", which contains the 17 | current test case and others. Defaults to the name of the test case. *) 18 | val wrap_test 19 | : ?classname:string 20 | -> (Junit.Testcase.t -> unit) 21 | -> unit Alcotest.test_case 22 | -> unit Alcotest.test_case 23 | 24 | (** [exit ()] exits with appropriate code if {!run_and_report}'s 25 | [and_exit] was [true] or raise {!Alcotest.Test_error} in case of 26 | error. *) 27 | type exit = unit -> unit 28 | 29 | (** [run_and_report name tests] is a wrapper around {!Alcotest.run} and {!wrap_test}. 30 | It runs the tests and creates a Junit testsuite from the results. 31 | 32 | As {!Alcotest.run} is always called with [and_exit = false] to be 33 | able to produce a report, the behavior is emulated by the returned 34 | {!exit} function. 35 | 36 | The optional argument [and_exit] controls what happens when the 37 | {!exit} function is called. By default, [and_exit] is set, which 38 | makes the function exit with [0] if everything is fine or [1] if 39 | there is an issue. If [and_exit] is [false], then the function 40 | raises [Test_error] on error. 41 | 42 | [?argv] is forwarded to {!run}. [?package] and [?timestamp] are 43 | forwarded to {!Junit.Testsuite.make}. *) 44 | val run_and_report 45 | : (?package:string 46 | -> ?timestamp:Ptime.t 47 | -> ?argv:string array 48 | -> string 49 | -> (string * unit Alcotest.test_case list) list 50 | -> Junit.Testsuite.t * exit) 51 | Alcotest.with_options 52 | -------------------------------------------------------------------------------- /alcotest/test/alcotest_report.expected: -------------------------------------------------------------------------------- 1 | Invalid_argument("7")Alcotest assertion failure 2 | File "alcotest/junit_alcotest.ml", line 22, character 6: 3 | FAIL string_of_int equals to '7' 4 | 5 | Expected: `"7"' 6 | Received: `"8"' 7 | 8 | Invalid_argument("7")Alcotest assertion failure 9 | File "alcotest/junit_alcotest.ml", line 22, character 6: 10 | FAIL string_of_int equals to '7' 11 | 12 | Expected: `"7"' 13 | Received: `"8"' 14 | 15 | 16 | -------------------------------------------------------------------------------- /alcotest/test/alcotest_report.ml: -------------------------------------------------------------------------------- 1 | module A = Alcotest 2 | module JA = Junit_alcotest 3 | 4 | module To_test = struct 5 | let capit letter = Astring.Char.Ascii.uppercase letter 6 | let plus int_list = List.fold_left (fun a b -> a + b) 0 int_list 7 | end 8 | 9 | let capit () = A.(check char) "Check A" 'A' (To_test.capit 'a') 10 | let plus () = A.(check int) "Sum equals to 7" 7 (To_test.plus [ 1; 1; 2; 3 ]) 11 | let wrong_result () = A.(check string) "string_of_int equals to '7'" "7" (string_of_int 8) 12 | 13 | let raise_unexpected_exn () = 14 | A.(check int) "int_of_string equals to 7" 7 (invalid_arg "7") 15 | ;; 16 | 17 | let test_set = 18 | [ A.test_case "Test with unexpected exception" `Quick raise_unexpected_exn 19 | ; A.test_case "Capitalize" `Quick capit 20 | ; A.test_case "Add entries" `Slow plus 21 | ; A.test_case "Test with wrong result" `Quick wrong_result 22 | ; A.test_case "Test skipped" `Quick (fun () -> A.skip ()) 23 | ] 24 | ;; 25 | 26 | let success_test_set = 27 | [ A.test_case "Capitalize" `Quick capit; A.test_case "Add entries" `Slow plus ] 28 | ;; 29 | 30 | let skipped_test_set = 31 | [ A.test_case "Skipped quick" `Quick (fun () -> A.skip ()) 32 | ; A.test_case "Skipped slow" `Slow (fun () -> A.skip ()) 33 | ] 34 | ;; 35 | 36 | let timestamp = 37 | match Ptime.of_date_time ((2013, 5, 24), ((10, 23, 58), 0)) with 38 | | Some t -> t 39 | | None -> assert false 40 | ;; 41 | 42 | let alcotest path = 43 | let package = "junit_alcotest" in 44 | let testsuite0, _ = 45 | JA.run_and_report 46 | ~package 47 | ~timestamp 48 | "Skip test suite" 49 | [ "Skipped tests", skipped_test_set ] 50 | in 51 | let testsuite1, _ = 52 | JA.run_and_report ~package ~timestamp "My first test" [ "Basic tests", test_set ] 53 | in 54 | let testsuite2, _ = 55 | JA.run_and_report ~package ~timestamp "My second test" [ "Basic tests", test_set ] 56 | in 57 | let testsuite3, exit = 58 | JA.run_and_report 59 | ~and_exit:false 60 | ~package 61 | ~timestamp 62 | "Success test suite" 63 | [ "Good tests", success_test_set ] 64 | in 65 | let report = Junit.make [ testsuite0; testsuite1; testsuite2; testsuite3 ] in 66 | (match path with 67 | | None -> 68 | let xml_report = Junit.to_xml report in 69 | Format.printf "%a\n" (Tyxml.Xml.pp ()) xml_report 70 | | Some path -> Junit.to_file report path); 71 | exit () 72 | ;; 73 | 74 | let () = 75 | let path = 76 | try Some (Sys.getenv "REPORT_PATH") with 77 | | _ -> None 78 | in 79 | alcotest path 80 | ;; 81 | -------------------------------------------------------------------------------- /alcotest/test/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name alcotest_report) 3 | (modules alcotest_report) 4 | (libraries junit junit_alcotest)) 5 | 6 | (rule 7 | (targets alcotest_report.xml) 8 | (action 9 | (setenv 10 | REPORT_PATH 11 | %{targets} 12 | (run %{dep:alcotest_report.exe})))) 13 | 14 | (rule 15 | (alias runtest) 16 | (package junit_alcotest) 17 | (action 18 | (diff %{dep:alcotest_report.expected} %{dep:alcotest_report.xml})) 19 | (deps alcotest_report.exe)) 20 | -------------------------------------------------------------------------------- /doc/api.odocl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khady/ocaml-junit/9bebaba23bb930398fa1e0b513a95352da77cf2d/doc/api.odocl -------------------------------------------------------------------------------- /doc/dev.odocl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khady/ocaml-junit/9bebaba23bb930398fa1e0b513a95352da77cf2d/doc/dev.odocl -------------------------------------------------------------------------------- /doc/doc.odocl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Khady/ocaml-junit/9bebaba23bb930398fa1e0b513a95352da77cf2d/doc/doc.odocl -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 3.0) 2 | (name junit) 3 | -------------------------------------------------------------------------------- /junit.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | maintainer: "Louis Roché " 3 | authors: "Louis Roché " 4 | homepage: "https://github.com/Khady/ocaml-junit" 5 | bug-reports: "https://github.com/Khady/ocaml-junit/issues" 6 | license: "LGPL-3.0-or-later WITH OCaml-LGPL-linking-exception" 7 | dev-repo: "git+https://github.com/Khady/ocaml-junit.git" 8 | doc: "https://khady.github.io/ocaml-junit/" 9 | tags: ["junit" "jenkins"] 10 | depends: [ 11 | "dune" {>= "3.0"} 12 | "ptime" 13 | "tyxml" {>= "4.0.0"} 14 | "odoc" {with-doc & >= "1.1.1"} 15 | "ocamlformat" {= "0.27.0" & with-dev-setup} 16 | ] 17 | build: [ 18 | ["dune" "subst"] {dev} 19 | [ 20 | "dune" 21 | "build" 22 | "-p" 23 | name 24 | "-j" 25 | jobs 26 | "@install" 27 | "@runtest" {with-test} 28 | "@doc" {with-doc} 29 | ] 30 | ] 31 | name: "junit" 32 | synopsis: "JUnit XML reports generation library" 33 | description: "JUnit XML reports generation library" 34 | -------------------------------------------------------------------------------- /junit/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name junit) 3 | (public_name junit) 4 | (wrapped false) 5 | (synopsis "JUnit XML reports generation library") 6 | (libraries tyxml ptime ptime.clock.os)) 7 | -------------------------------------------------------------------------------- /junit/junit.ml: -------------------------------------------------------------------------------- 1 | module Property = struct 2 | type t = 3 | { name : string 4 | ; value : string 5 | } 6 | 7 | let make ~name ~value = { name; value } 8 | end 9 | 10 | module Testcase = struct 11 | type error = Junit_xml.error = 12 | { message : string option 13 | ; typ : string 14 | ; description : string 15 | } 16 | 17 | type failure = Junit_xml.failure = 18 | { message : string option 19 | ; typ : string 20 | ; description : string 21 | } 22 | 23 | type result = Junit_xml.result = 24 | | Error of error 25 | | Failure of failure 26 | | Pass 27 | | Skipped 28 | 29 | type t = 30 | { name : string 31 | ; classname : string 32 | ; time : float 33 | ; result : result 34 | } 35 | 36 | let make ~name ~classname ~time result = { name; classname; time; result } 37 | 38 | let error ?message ~typ ~name ~classname ~time description = 39 | let result = Error { message; typ; description } in 40 | make ~name ~classname ~time result 41 | ;; 42 | 43 | let failure ?message ~typ ~name ~classname ~time description = 44 | let result = Failure { message; typ; description } in 45 | make ~name ~classname ~time result 46 | ;; 47 | 48 | let skipped ~name ~classname ~time = make ~name ~classname ~time Skipped 49 | let pass ~name ~classname ~time = make ~name ~classname ~time Pass 50 | end 51 | 52 | module Testsuite = struct 53 | type t = 54 | { package : string 55 | ; id : int 56 | ; name : string 57 | ; timestamp : Ptime.t 58 | ; hostname : string 59 | ; tests : int 60 | ; failures : int 61 | ; errors : int 62 | ; skipped : int 63 | ; time : float 64 | ; system_out : string option 65 | ; system_err : string option 66 | ; properties : Property.t list 67 | ; testcases : Testcase.t list 68 | } 69 | 70 | let make ?package ?timestamp ?(hostname = "localhost") ?system_out ?system_err ~name () = 71 | let package = 72 | match package with 73 | | None -> name 74 | | Some p -> p 75 | in 76 | let timestamp = 77 | match timestamp with 78 | | None -> Ptime_clock.now () 79 | | Some t -> t 80 | in 81 | { package 82 | ; id = 0 83 | ; name 84 | ; timestamp 85 | ; hostname 86 | ; tests = 0 87 | ; failures = 0 88 | ; errors = 0 89 | ; skipped = 0 90 | ; time = 0. 91 | ; system_out 92 | ; system_err 93 | ; properties = [] 94 | ; testcases = [] 95 | } 96 | ;; 97 | 98 | let add_testcase testcase t = 99 | let t = 100 | { t with 101 | tests = t.tests + 1 102 | ; time = t.time +. testcase.Testcase.time 103 | ; testcases = testcase :: t.testcases 104 | } 105 | in 106 | match testcase.Testcase.result with 107 | | Testcase.Pass -> t 108 | | Testcase.Skipped -> { t with skipped = t.skipped + 1 } 109 | | Testcase.Error _ -> { t with errors = t.errors + 1 } 110 | | Testcase.Failure _ -> { t with failures = t.failures + 1 } 111 | ;; 112 | 113 | let add_testcases testcases t = 114 | List.fold_left (fun t tc -> add_testcase tc t) t testcases 115 | ;; 116 | 117 | let add_property properties t = { t with properties = properties :: t.properties } 118 | 119 | let add_properties properties t = 120 | List.fold_left (fun t tc -> add_property tc t) t properties 121 | ;; 122 | end 123 | 124 | type t = 125 | { next_id : int 126 | ; testsuites : Testsuite.t list 127 | } 128 | 129 | let make testsuites = 130 | let testsuites = List.mapi (fun i t -> Testsuite.{ t with id = i }) testsuites in 131 | let length = List.length testsuites in 132 | { next_id = length; testsuites } 133 | ;; 134 | 135 | let add_testsuite testsuite t = 136 | let ts = Testsuite.{ testsuite with id = t.next_id } in 137 | { next_id = succ t.next_id; testsuites = ts :: t.testsuites } 138 | ;; 139 | 140 | let to_xml (t : t) = 141 | let testsuites = 142 | List.map 143 | (fun t -> 144 | let properties = 145 | let open Property in 146 | List.map 147 | (fun p -> Junit_xml.property ~name:p.name ~value:p.value) 148 | t.Testsuite.properties 149 | in 150 | let testcases = 151 | let open Testcase in 152 | List.map 153 | (fun p -> 154 | Junit_xml.testcase 155 | ~name:p.name 156 | ~classname:p.classname 157 | ~time:p.time 158 | p.result) 159 | t.Testsuite.testcases 160 | in 161 | let open Testsuite in 162 | Junit_xml.testsuite 163 | ?system_out:t.system_out 164 | ?system_err:t.system_err 165 | ~package:t.package 166 | ~id:t.id 167 | ~name:t.name 168 | ~timestamp:(Junit_xml.timestamp t.timestamp) 169 | ~hostname:t.hostname 170 | ~tests:t.tests 171 | ~failures:t.failures 172 | ~errors:t.errors 173 | ~skipped:t.skipped 174 | ~time:t.time 175 | properties 176 | testcases) 177 | t.testsuites 178 | in 179 | Junit_xml.to_xml testsuites 180 | ;; 181 | 182 | let to_file (t : t) filename = 183 | let xml_report = to_xml t in 184 | let oc = open_out filename in 185 | let fmt = Format.formatter_of_out_channel oc in 186 | Format.fprintf fmt "@[%a@]@." (Tyxml.Xml.pp ()) xml_report; 187 | close_out oc; 188 | () 189 | ;; 190 | -------------------------------------------------------------------------------- /junit/junit.mli: -------------------------------------------------------------------------------- 1 | (** High level interface to produce JUnit reports. *) 2 | 3 | (** This module defines functions to create JUnit reports and export 4 | them to XML. This XML is supposed to be accepted by Jenkins. *) 5 | 6 | module Property : sig 7 | (** Properties (e.g., environment settings) set during test execution. *) 8 | 9 | type t 10 | 11 | val make : name:string -> value:string -> t 12 | end 13 | 14 | module Testcase : sig 15 | type t 16 | 17 | (** [error ?message ~typ ~name ~classname ~time description] creates 18 | an error element. 19 | 20 | Indicates that the test errored. An errored test is one that had an 21 | unanticipated problem. e.g., an unchecked throwable; or a 22 | problem with the implementation of the test. Contains as a text 23 | node relevant data for the error, e.g., a stack trace. 24 | 25 | @param message 26 | The error message. e.g., if a java exception is 27 | thrown, the return value of getMessage(). 28 | 29 | @param typ 30 | The type of error that occured. e.g., if a java 31 | execption is thrown the full class name of the exception. 32 | 33 | @param description Description of the error. 34 | 35 | @param name Name of the test method. 36 | 37 | @param classname Full class name for the class the test method is 38 | in. 39 | 40 | @param time Time taken (in seconds) to execute the test. *) 41 | val error 42 | : ?message:string 43 | -> typ:string 44 | -> name:string 45 | -> classname:string 46 | -> time:float 47 | -> string 48 | -> t 49 | 50 | (** [failure ?message ~typ ~name ~classname ~time description] creates 51 | a failure element. 52 | 53 | Indicates that the test failed. A failure is a test which the code has 54 | explicitly failed by using the mechanisms for that 55 | purpose. e.g., via an assertEquals. Contains as a text node 56 | relevant data for the failure, e.g., a stack trace. 57 | 58 | @param message The message specified in the assert. 59 | 60 | @param typ The type of the assert. 61 | 62 | @param description Description of the failure. 63 | 64 | @param name Name of the test method. 65 | 66 | @param classname Full class name for the class the test method is 67 | in. 68 | 69 | @param time Time taken (in seconds) to execute the test. *) 70 | val failure 71 | : ?message:string 72 | -> typ:string 73 | -> name:string 74 | -> classname:string 75 | -> time:float 76 | -> string 77 | -> t 78 | 79 | (** [skipped ~name ~classname ~time] creates a skipped element. 80 | 81 | Indicates that the test has not been launched. 82 | 83 | @param name Name of the test method. 84 | 85 | @param classname Full class name for the class the test method is 86 | in. 87 | 88 | @param time Time taken (in seconds) to execute the test. *) 89 | val skipped : name:string -> classname:string -> time:float -> t 90 | 91 | (** [pass ~name ~classname ~time] creates a pass element. 92 | 93 | Indicates that the test is a success. 94 | 95 | @param name Name of the test method. 96 | 97 | @param classname Full class name for the class the test method is 98 | in. 99 | 100 | @param time Time taken (in seconds) to execute the test. *) 101 | val pass : name:string -> classname:string -> time:float -> t 102 | end 103 | 104 | module Testsuite : sig 105 | (** Contains the results of executing a testsuite. *) 106 | 107 | type t 108 | 109 | (** [make ?package ?timestamp ?hostname ?system_out ?system_err 110 | ~name ()] creates a testsuite. 111 | 112 | Attributes 113 | 114 | @param package Derived from the testsuite name in the 115 | non-aggregated documents. 116 | 117 | @param timestamp 118 | When the test was executed. Timezone may not be 119 | specified. Uses the current time by default. 120 | 121 | @param hostname Host on which the tests were executed. Uses 122 | [localhost] by default. 123 | 124 | @param system_out Data that was written to standard out while 125 | the test was executed. 126 | 127 | @param system_err 128 | Data that was written to standard error while 129 | the test was executed. 130 | 131 | @param name 132 | Full class name of the test for non-aggregated 133 | testsuite documents. Class name without the package for 134 | aggregated testsuites documents. *) 135 | val make 136 | : ?package:string 137 | -> ?timestamp:Ptime.t 138 | -> ?hostname:string 139 | -> ?system_out:string 140 | -> ?system_err:string 141 | -> name:string 142 | -> unit 143 | -> t 144 | 145 | val add_testcases : Testcase.t list -> t -> t 146 | val add_properties : Property.t list -> t -> t 147 | end 148 | 149 | (** Contains an aggregation of testsuite results. *) 150 | type t 151 | 152 | val make : Testsuite.t list -> t 153 | val add_testsuite : Testsuite.t -> t -> t 154 | val to_xml : t -> Tyxml.Xml.elt 155 | val to_file : t -> string -> unit 156 | -------------------------------------------------------------------------------- /junit/junit_xml.ml: -------------------------------------------------------------------------------- 1 | open Tyxml.Xml 2 | 3 | type token = string 4 | type timestamp = string 5 | 6 | let timestamp time = 7 | let (y, m, d), ((hh, ss, mm), _) = Ptime.to_date_time time in 8 | Printf.sprintf "%04d-%02d-%02dT%02d:%02d:%02d" y m d hh ss mm 9 | ;; 10 | 11 | type property = 12 | { name : token 13 | ; value : string 14 | } 15 | 16 | type properties = property list 17 | 18 | let property ~name ~value = { name; value } 19 | 20 | let property_to_xml property = 21 | let name = string_attrib "name" property.name in 22 | let value = string_attrib "value" property.value in 23 | node "property" ~a:[ name; value ] [] 24 | ;; 25 | 26 | let properties_to_xml properties = node "properties" (List.map property_to_xml properties) 27 | 28 | type error = 29 | { message : string option 30 | ; typ : string 31 | ; description : string 32 | } 33 | 34 | let error ?message ~typ description : error = { message; typ; description } 35 | 36 | let error_to_xml (error : error) = 37 | let typ = string_attrib "type" error.typ in 38 | let attributes = [ typ ] in 39 | let attributes = 40 | match error.message with 41 | | None -> attributes 42 | | Some m -> 43 | let message = string_attrib "message" m in 44 | message :: attributes 45 | in 46 | let description = pcdata error.description in 47 | node "error" ~a:attributes [ description ] 48 | ;; 49 | 50 | type failure = 51 | { message : string option 52 | ; typ : string 53 | ; description : string 54 | } 55 | 56 | let failure ?message ~typ description : failure = { message; typ; description } 57 | 58 | let failure_to_xml (failure : failure) = 59 | let typ = string_attrib "type" failure.typ in 60 | let attributes = [ typ ] in 61 | let attributes = 62 | match failure.message with 63 | | None -> attributes 64 | | Some m -> 65 | let message = string_attrib "message" m in 66 | message :: attributes 67 | in 68 | let description = pcdata failure.description in 69 | node "failure" ~a:attributes [ description ] 70 | ;; 71 | 72 | type result = 73 | | Error of error 74 | | Failure of failure 75 | | Pass 76 | | Skipped 77 | 78 | let result_to_xml : result -> Tyxml.Xml.elt = function 79 | | Error e -> error_to_xml e 80 | | Failure f -> failure_to_xml f 81 | | Pass -> Tyxml.Xml.empty () 82 | | Skipped -> node "skipped" [] 83 | ;; 84 | 85 | type testcase = 86 | { name : string 87 | ; classname : token 88 | ; time : float 89 | ; result : result 90 | } 91 | 92 | type testcases = testcase list 93 | 94 | let testcase ~name ~classname ~time result = { name; classname; time; result } 95 | 96 | let testcase_to_xml (testcase : testcase) = 97 | let name = string_attrib "name" testcase.name in 98 | let classname = string_attrib "classname" testcase.classname in 99 | let time = float_attrib "time" testcase.time in 100 | let result = result_to_xml testcase.result in 101 | node "testcase" ~a:[ name; classname; time ] [ result ] 102 | ;; 103 | 104 | type testsuite = 105 | { package : token 106 | ; id : int 107 | ; name : token 108 | ; timestamp : timestamp 109 | ; hostname : token 110 | ; tests : int 111 | ; failures : int 112 | ; errors : int 113 | ; skipped : int 114 | ; time : float 115 | ; properties : properties 116 | ; testcases : testcases 117 | ; system_out : string option 118 | ; system_err : string option 119 | } 120 | 121 | type testsuites = testsuite list 122 | 123 | let testsuite 124 | ?system_out 125 | ?system_err 126 | ~package 127 | ~id 128 | ~name 129 | ~timestamp 130 | ~hostname 131 | ~tests 132 | ~failures 133 | ~errors 134 | ~skipped 135 | ~time 136 | properties 137 | testcases 138 | = 139 | { package 140 | ; id 141 | ; name 142 | ; timestamp 143 | ; hostname 144 | ; tests 145 | ; failures 146 | ; errors 147 | ; skipped 148 | ; time 149 | ; properties 150 | ; testcases 151 | ; system_out 152 | ; system_err 153 | } 154 | ;; 155 | 156 | let testsuite_to_xml testsuite = 157 | let package = string_attrib "package" testsuite.package in 158 | let id = int_attrib "id" testsuite.id in 159 | let name = string_attrib "name" testsuite.name in 160 | let timestamp = string_attrib "timestamp" testsuite.timestamp in 161 | let hostname = string_attrib "hostname" testsuite.hostname in 162 | let tests = int_attrib "tests" testsuite.tests in 163 | let failures = int_attrib "failures" testsuite.failures in 164 | let errors = int_attrib "errors" testsuite.errors in 165 | let skipped = int_attrib "skipped" testsuite.skipped in 166 | let time = float_attrib "time" testsuite.time in 167 | let attributes = 168 | [ package; id; name; timestamp; hostname; tests; failures; errors; skipped; time ] 169 | in 170 | let system_out = 171 | match testsuite.system_out with 172 | | None -> empty () 173 | | Some so -> node "system_out" [ pcdata so ] 174 | in 175 | let system_err = 176 | match testsuite.system_err with 177 | | None -> empty () 178 | | Some se -> node "system_err" [ pcdata se ] 179 | in 180 | let properties = properties_to_xml testsuite.properties in 181 | let testcases = List.map testcase_to_xml testsuite.testcases in 182 | node "testsuite" ~a:attributes (properties :: system_out :: system_err :: testcases) 183 | ;; 184 | 185 | let to_xml testsuites = 186 | let elements = List.map testsuite_to_xml testsuites in 187 | node "testsuites" elements 188 | ;; 189 | -------------------------------------------------------------------------------- /junit/junit_xml.mli: -------------------------------------------------------------------------------- 1 | (** Low level interface to build XML elements. *) 2 | 3 | (** This module defines basic data types for data, attributes and 4 | element occuring in JUnit reports. 5 | 6 | It is based on the XSD provided in 7 | {{:https://github.com/windyroad/JUnit-Schema} JUnit-schema} git 8 | repository. 9 | 10 | Those are low level functions. Values like [id], [failures] or 11 | [tests] will not be checked. 12 | 13 | It allows you to build a report by hand if the facilities that are 14 | offered by {!module:Junit} do not suit your needs. *) 15 | 16 | (** {2 Categories of elements and attributes} *) 17 | 18 | (** This part defines the categories of elements and attributes. *) 19 | 20 | (** {3 Attributes} *) 21 | 22 | (** https://www.w3.org/TR/xmlschema-2/#token 23 | 24 | [Definition:] token represents tokenized strings. The ·value space· of 25 | token is the set of strings that do not contain the carriage return 26 | (#xD), line feed (#xA) nor tab (#x9) characters, that have no 27 | leading or trailing spaces (#x20) and that have no internal 28 | sequences of two or more spaces. The ·lexical space· of token is 29 | the set of strings that do not contain the carriage return (#xD), 30 | line feed (#xA) nor tab (#x9) characters, that have no leading or 31 | trailing spaces (#x20) and that have no internal sequences of two 32 | or more spaces. The ·base type· of token is normalizedString. *) 33 | type token = string 34 | 35 | type timestamp 36 | 37 | val timestamp : Ptime.t -> timestamp 38 | 39 | (** {3 Elements} *) 40 | 41 | (** {4 Properties} *) 42 | 43 | type property 44 | 45 | (** Properties (e.g., environment settings) set during test execution. *) 46 | type properties = property list 47 | 48 | val property : name:token -> value:string -> property 49 | 50 | (** Builds an XML element from a property. *) 51 | val property_to_xml : property -> Tyxml.Xml.elt 52 | 53 | (** {4 Testcases} *) 54 | 55 | (** Indicates that the test errored. An errored test is one that had 56 | an unanticipated problem. e.g., an unchecked throwable; or a problem 57 | with the implementation of the test. Contains as a text node 58 | relevant data for the error, e.g., a stack trace. *) 59 | type error = 60 | { message : string option 61 | ; typ : string 62 | ; description : string 63 | } 64 | 65 | (** [error ?message ~typ description] creates an error element. 66 | 67 | @param message 68 | The error message. e.g., if a java exception is 69 | thrown, the return value of getMessage(). 70 | 71 | @param typ 72 | The type of error that occured. e.g., if a java 73 | execption is thrown the full class name of the exception. 74 | 75 | @param description Description of the error. *) 76 | val error : ?message:string -> typ:string -> string -> error 77 | 78 | (** Builds an XML element from a error. *) 79 | val error_to_xml : error -> Tyxml.Xml.elt 80 | 81 | (** Indicates that the test failed. A failure is a test which the code 82 | has explicitly failed by using the mechanisms for that purpose. e.g., 83 | via an assertEquals. Contains as a text node relevant data for the 84 | failure, e.g., a stack trace. *) 85 | type failure = 86 | { message : string option 87 | ; typ : string 88 | ; description : string 89 | } 90 | 91 | (** [failure ?message ~typ description] creates a failure element. 92 | 93 | @param message The message specified in the assert. 94 | @param typ The type of the assert. 95 | @param description Description of the failure. *) 96 | val failure : ?message:string -> typ:string -> string -> failure 97 | 98 | (** Builds an XML element from a failure. *) 99 | val failure_to_xml : failure -> Tyxml.Xml.elt 100 | 101 | type result = 102 | | Error of error 103 | | Failure of failure 104 | | Pass 105 | | Skipped (** Not part of the spec, but available in jenkins. *) 106 | 107 | (** Builds an XML element from a result. *) 108 | val result_to_xml : result -> Tyxml.Xml.elt 109 | 110 | type testcase 111 | type testcases = testcase list 112 | 113 | (** Creates a testcase. 114 | 115 | @param name Name of the test method. 116 | 117 | @param classname Full class name for the class the test method is 118 | in. 119 | 120 | @param time Time taken (in seconds) to execute the test. 121 | 122 | @param result Result of the test. *) 123 | val testcase : name:token -> classname:token -> time:float -> result -> testcase 124 | 125 | (** Builds an XML element from a testcase. *) 126 | val testcase_to_xml : testcase -> Tyxml.Xml.elt 127 | 128 | (** {4 Testsuites} *) 129 | 130 | (** Contains the results of executing a testsuite. *) 131 | type testsuite 132 | 133 | (** Contains an aggregation of testsuite results. *) 134 | type testsuites = testsuite list 135 | 136 | (** Creates a testsuite. 137 | 138 | Attributes 139 | 140 | @param package Derived from the testsuite name in the non-aggregated 141 | documents. 142 | 143 | @param id 144 | Starts at 0 for the first testsuite and is incremented 145 | by 1 for each following testsuite. 146 | 147 | @param name 148 | Full class name of the test for non-aggregated 149 | testsuite documents. Class name without the package for aggregated 150 | testsuites documents. 151 | 152 | @param timestamp When the test was executed. Timezone may not be 153 | specified. 154 | 155 | @param hostname 156 | Host on which the tests were executed. 'localhost' 157 | should be used if the hostname cannot be determined. 158 | 159 | @param tests 160 | The total number of tests in the suite.The total 161 | number of tests in the suite. 162 | 163 | @param failures 164 | The total number of tests in the suite that 165 | failed. A failure is a test which the code has explicitly failed 166 | by using the mechanisms for that purpose. e.g., via an 167 | assertEquals. 168 | 169 | @param errors 170 | The total number of tests in the suite that 171 | errored. An errored test is one that had an unanticipated 172 | problem. e.g., an unchecked throwable; or a problem with the 173 | implementation of the test. 174 | 175 | @param skipped The total number of tests in the suite that 176 | were skipped. A skipped test is one that was not run because 177 | the conditions for running it were not met. 178 | 179 | @param time Time taken (in seconds) to execute the tests in the 180 | suite. 181 | 182 | Elements 183 | 184 | @param properties Properties (e.g., environment settings) set 185 | during test execution. 186 | 187 | @param testcases List of test executed. 188 | 189 | @param system_out Data that was written to standard out while the 190 | test was executed. 191 | 192 | @param system_err Data that was written to standard error while 193 | the test was executed. *) 194 | val testsuite 195 | : ?system_out:string 196 | -> ?system_err:string 197 | -> package:token 198 | -> id:int 199 | -> name:token 200 | -> timestamp:timestamp 201 | -> hostname:token 202 | -> tests:int 203 | -> failures:int 204 | -> errors:int 205 | -> skipped:int 206 | -> time:float 207 | -> properties 208 | -> testcases 209 | -> testsuite 210 | 211 | (** Builds an XML element from a testsuite. *) 212 | val testsuite_to_xml : testsuite -> Tyxml.Xml.elt 213 | 214 | (** Builds an XML element from a list of testsuites. *) 215 | val to_xml : testsuites -> Tyxml.Xml.elt 216 | -------------------------------------------------------------------------------- /junit/test/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name simple) 3 | (modules simple) 4 | (libraries junit)) 5 | 6 | (rule 7 | (targets simple.xml) 8 | (action 9 | (run %{dep:simple.exe} %{targets}))) 10 | 11 | (rule 12 | (alias runtest) 13 | (deps simple.exe) 14 | (package junit) 15 | (action 16 | (diff %{dep:simple.expected} %{dep:simple.xml}))) 17 | -------------------------------------------------------------------------------- /junit/test/simple.expected: -------------------------------------------------------------------------------- 1 | Assertion failed 2 | -------------------------------------------------------------------------------- /junit/test/simple.ml: -------------------------------------------------------------------------------- 1 | (** Encode this example (from http://help.catchsoftware.com/display/ET/JUnit+Format): 2 | 3 | 4 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | Assertion failed 17 | 18 | 21 | 22 | 23 | 26 | 27 | *) 28 | 29 | let timestamp = 30 | match Ptime.of_date_time ((2013, 5, 24), ((10, 23, 58), 0)) with 31 | | Some t -> t 32 | | None -> assert false 33 | ;; 34 | 35 | let simple path = 36 | let junitXmlReporter = Junit.Testsuite.make ~timestamp ~name:"JUnitXmlReporter" () in 37 | let junitXmlReportConstructor = 38 | let properties = 39 | [ Junit.Property.make ~name:"java.vendor" ~value:"Sun Microsystems Inc." 40 | ; Junit.Property.make ~name:"compiler.debug" ~value:"on" 41 | ; Junit.Property.make ~name:"project.jdk.classpath" ~value:"jdk.classpath.1.6" 42 | ] 43 | in 44 | let testcases = 45 | [ Junit.Testcase.failure 46 | ~name:"should default path to an empty string" 47 | ~classname:"JUnitXmlReporter.constructor" 48 | ~time:0.006 49 | ~message:"test failure" 50 | ~typ:"not equal" 51 | "Assertion failed" 52 | ; Junit.Testcase.skipped 53 | ~name:"should default consolidate to true" 54 | ~classname:"JUnitXmlReporter.constructor" 55 | ~time:0. 56 | ; Junit.Testcase.pass 57 | ~name:"should default useDotNotation to true" 58 | ~classname:"JUnitXmlReporter.constructor" 59 | ~time:0. 60 | ] 61 | in 62 | Junit.Testsuite.make ~timestamp ~name:"JUnitXmlReporter.constructor" () 63 | |> Junit.Testsuite.add_testcases testcases 64 | |> Junit.Testsuite.add_properties properties 65 | in 66 | let report = Junit.make [ junitXmlReporter; junitXmlReportConstructor ] in 67 | match path with 68 | | None -> 69 | let xml_report = Junit.to_xml report in 70 | Format.printf "%a\n" (Tyxml.Xml.pp ()) xml_report 71 | | Some path -> Junit.to_file report path 72 | ;; 73 | 74 | let () = 75 | let path = if Array.length Sys.argv > 1 then Some Sys.argv.(1) else None in 76 | simple path 77 | ;; 78 | -------------------------------------------------------------------------------- /junit_alcotest.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | maintainer: "Louis Roché " 3 | authors: ["Louis Roché "] 4 | homepage: "https://github.com/Khady/ocaml-junit" 5 | bug-reports: "https://github.com/Khady/ocaml-junit/issues" 6 | license: "LGPL-3.0-or-later WITH OCaml-LGPL-linking-exception" 7 | dev-repo: "git+https://github.com/Khady/ocaml-junit.git" 8 | doc: "https://khady.github.io/ocaml-junit/" 9 | tags: ["junit" "jenkins" "alcotest"] 10 | depends: [ 11 | "dune" {>= "3.0"} 12 | "odoc" {with-doc & >= "1.1.1"} 13 | "alcotest" {>= "1.9.0"} 14 | "junit" {= version} 15 | "ocamlformat" {= "0.27.0" & with-dev-setup} 16 | ] 17 | build: [ 18 | ["dune" "subst"] {dev} 19 | [ 20 | "dune" 21 | "build" 22 | "-p" 23 | name 24 | "-j" 25 | jobs 26 | "@install" 27 | "@runtest" {with-test} 28 | "@doc" {with-doc} 29 | ] 30 | ] 31 | name: "junit_alcotest" 32 | synopsis: "JUnit XML reports generation for alcotest tests" 33 | description: "JUnit XML reports generation for alcotest tests" 34 | -------------------------------------------------------------------------------- /junit_ounit.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | maintainer: "Louis Roché " 3 | authors: ["Louis Roché " "Simon Cruanes "] 4 | homepage: "https://github.com/Khady/ocaml-junit" 5 | bug-reports: "https://github.com/Khady/ocaml-junit/issues" 6 | license: "LGPL-3.0-or-later WITH OCaml-LGPL-linking-exception" 7 | dev-repo: "git+https://github.com/Khady/ocaml-junit.git" 8 | doc: "https://khady.github.io/ocaml-junit/" 9 | tags: ["junit" "jenkins" "ounit"] 10 | depends: [ 11 | "dune" {>= "3.0"} 12 | "odoc" {with-doc & >= "1.1.1"} 13 | "ounit2" 14 | "junit" {= version} 15 | "ocamlformat" {= "0.27.0" & with-dev-setup} 16 | ] 17 | build: [ 18 | ["dune" "subst"] {dev} 19 | [ 20 | "dune" 21 | "build" 22 | "-p" 23 | name 24 | "-j" 25 | jobs 26 | "@install" 27 | "@runtest" {with-test} 28 | "@doc" {with-doc} 29 | ] 30 | ] 31 | name: "junit_ounit" 32 | synopsis: "JUnit XML reports generation for OUnit tests" 33 | description: "JUnit XML reports generation for OUnit tests" 34 | -------------------------------------------------------------------------------- /ounit/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name junit_ounit) 3 | (public_name junit_ounit) 4 | (wrapped false) 5 | (synopsis "JUnit XML reports generation for OUnit tests") 6 | (libraries junit ounit2)) 7 | -------------------------------------------------------------------------------- /ounit/junit_ounit.ml: -------------------------------------------------------------------------------- 1 | (** Interface from OUnit result to JUnit reports *) 2 | 3 | module O = OUnit 4 | module J = Junit 5 | 6 | let of_result o = 7 | let time = 0. in 8 | let classname = "" in 9 | let typ = "" in 10 | match o with 11 | | O.RError (path, msg) -> 12 | J.Testcase.error ~typ ~classname ~time ~name:(O.string_of_path path) ~message:msg "" 13 | | O.RSuccess path -> J.Testcase.pass ~classname ~time ~name:(O.string_of_path path) 14 | | O.RFailure (path, msg) -> 15 | J.Testcase.failure ~typ ~classname ~time ~message:msg ~name:(O.string_of_path path) "" 16 | | O.RSkip (path, _msg) -> 17 | J.Testcase.skipped ~classname ~time ~name:(O.string_of_path path) 18 | | O.RTodo (path, _msg) -> 19 | J.Testcase.skipped ~classname ~time ~name:(O.string_of_path path ^ "(todo)") 20 | ;; 21 | 22 | let of_results ~name l = 23 | let l = List.map of_result l in 24 | let suite = J.Testsuite.make ~name () in 25 | J.Testsuite.add_testcases l suite 26 | ;; 27 | 28 | let to_file ~name file l = 29 | let suite = of_results ~name l in 30 | let report = Junit.make [ suite ] in 31 | let xml_report = Junit.to_xml report in 32 | let oc = open_out file in 33 | let fmt = Format.formatter_of_out_channel oc in 34 | Format.fprintf fmt "@[%a@]@." (Tyxml.Xml.pp ()) xml_report; 35 | close_out oc; 36 | () 37 | ;; 38 | -------------------------------------------------------------------------------- /ounit/junit_ounit.mli: -------------------------------------------------------------------------------- 1 | (** Interface from OUnit result to JUnit reports *) 2 | 3 | val of_result : OUnit.test_result -> Junit.Testcase.t 4 | 5 | (** [of_results ~name l] converts the list of results [l] into a 6 | Junit testsuite named [name]. *) 7 | val of_results : name:string -> OUnit.test_results -> Junit.Testsuite.t 8 | 9 | (** Shortcut: converts the test results to a Junit testsuite, and dump 10 | it into the given file as XML. *) 11 | val to_file : name:string -> string -> OUnit.test_results -> unit 12 | -------------------------------------------------------------------------------- /schemes/gist.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | Jenkins xUnit test result schema. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Contains an aggregation of testsuite results 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Derived from testsuite/@name in the non-aggregated documents 29 | 30 | 31 | 32 | 33 | Starts at '0' for the first testsuite and is incremented by 1 for each following testsuite 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Contains the results of exexuting a testsuite 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Indicates that the test errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace 54 | 55 | 56 | 57 | 58 | 59 | 60 | The error message. e.g., if a java exception is thrown, the return value of getMessage() 61 | 62 | 63 | 64 | 65 | The type of error that occured. e.g., if a java execption is thrown the full class name of the exception. 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace 75 | 76 | 77 | 78 | 79 | 80 | 81 | The message specified in the assert 82 | 83 | 84 | 85 | 86 | The type of the assert. 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | Indicates that the test was skipped. A skipped test is a test which was ignored using framework mechanisms. e.g., @Ignore annotation. 96 | 97 | 98 | 99 | 100 | 101 | 102 | Skip type. 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | Name of the test method 113 | 114 | 115 | 116 | 117 | Full class name for the class the test method is in. 118 | 119 | 120 | 121 | 122 | Time taken (in seconds) to execute the test 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | Full class name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | when the test was executed. Timezone may not be specified. 141 | 142 | 143 | 144 | 145 | Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | The total number of tests in the suite 156 | 157 | 158 | 159 | 160 | The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals 161 | 162 | 163 | 164 | 165 | The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. 166 | 167 | 168 | 169 | 170 | The total number of tests in the suite that skipped. A skipped test is a test which was ignored using framework mechanisms. e.g., @Ignore annotation. 171 | 172 | 173 | 174 | 175 | Time taken (in seconds) to execute the tests in the suite 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /schemes/jenkins.xsd: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /schemes/junit-schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | JUnit test result schema for the Apache Ant JUnit and JUnitReport tasks 8 | Copyright © 2011, Windy Road Technology Pty. Limited 9 | The Apache Ant JUnit XML Schema is distributed under the terms of the Apache License Version 2.0 http://www.apache.org/licenses/ 10 | Permission to waive conditions of this license may be requested from Windy Road Support (http://windyroad.org/support). 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Contains an aggregation of testsuite results 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Derived from testsuite/@name in the non-aggregated documents 31 | 32 | 33 | 34 | 35 | Starts at '0' for the first testsuite and is incremented by 1 for each following testsuite 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Contains the results of executing a testsuite 48 | 49 | 50 | 51 | 52 | Properties (e.g., environment settings) set during test execution 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | Indicates that the test errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace 77 | 78 | 79 | 80 | 81 | 82 | 83 | The error message. e.g., if a java exception is thrown, the return value of getMessage() 84 | 85 | 86 | 87 | 88 | The type of error that occured. e.g., if a java execption is thrown the full class name of the exception. 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace 98 | 99 | 100 | 101 | 102 | 103 | 104 | The message specified in the assert 105 | 106 | 107 | 108 | 109 | The type of the assert. 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | Name of the test method 120 | 121 | 122 | 123 | 124 | Full class name for the class the test method is in. 125 | 126 | 127 | 128 | 129 | Time taken (in seconds) to execute the test 130 | 131 | 132 | 133 | 134 | 135 | 136 | Data that was written to standard out while the test was executed 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | Data that was written to standard error while the test was executed 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | Full class name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | when the test was executed. Timezone may not be specified. 168 | 169 | 170 | 171 | 172 | Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | The total number of tests in the suite 183 | 184 | 185 | 186 | 187 | The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals 188 | 189 | 190 | 191 | 192 | The total number of tests in the suite that errorrd. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. 193 | 194 | 195 | 196 | 197 | Time taken (in seconds) to execute the tests in the suite 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /schemes/windyroad.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | JUnit test result schema for the Apache Ant JUnit and JUnitReport tasks 8 | Copyright © 2011, Windy Road Technology Pty. Limited 9 | The Apache Ant JUnit XML Schema is distributed under the terms of the GNU Lesser General Public License (LGPL) http://www.gnu.org/licenses/lgpl.html 10 | Permission to waive conditions of this license may be requested from Windy Road Support (http://windyroad.org/support). 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Contains an aggregation of testsuite results 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Derived from testsuite/@name in the non-aggregated documents 31 | 32 | 33 | 34 | 35 | Starts at '0' for the first testsuite and is incremented by 1 for each following testsuite 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Contains the results of exexuting a testsuite 48 | 49 | 50 | 51 | 52 | Properties (e.g., environment settings) set during test execution 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | Indicates that the test errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace 77 | 78 | 79 | 80 | 81 | 82 | 83 | The error message. e.g., if a java exception is thrown, the return value of getMessage() 84 | 85 | 86 | 87 | 88 | The type of error that occured. e.g., if a java execption is thrown the full class name of the exception. 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace 98 | 99 | 100 | 101 | 102 | 103 | 104 | The message specified in the assert 105 | 106 | 107 | 108 | 109 | The type of the assert. 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | Name of the test method 120 | 121 | 122 | 123 | 124 | Full class name for the class the test method is in. 125 | 126 | 127 | 128 | 129 | Time taken (in seconds) to execute the test 130 | 131 | 132 | 133 | 134 | 135 | 136 | Data that was written to standard out while the test was executed 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | Data that was written to standard error while the test was executed 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | Full class name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | when the test was executed. Timezone may not be specified. 168 | 169 | 170 | 171 | 172 | Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | The total number of tests in the suite 183 | 184 | 185 | 186 | 187 | The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals 188 | 189 | 190 | 191 | 192 | The total number of tests in the suite that errorrd. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. 193 | 194 | 195 | 196 | 197 | Time taken (in seconds) to execute the tests in the suite 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | --------------------------------------------------------------------------------