package bisect_ppx

  1. Overview
  2. Docs
Code coverage for OCaml

Install

Dune Dependency

Authors

Maintainers

Sources

2.8.1.tar.gz
sha256=d51292dab8d49a1487bddb8b9c0153a959382097589b0d017683a1d45ec48d93
md5=e8831e6e99292f221ded6fb02652e2a5

Description

Bisect_ppx helps you test thoroughly. It is a small preprocessor that inserts instrumentation at places in your code, such as if-then-else and match expressions. After you run tests, Bisect_ppx gives a nice HTML report showing which places were visited and which were missed.

Usage is simple - add package bisect_ppx when building tests, run your tests, then run the Bisect_ppx report tool on the generated visitation files.

Published: 14 Mar 2022

README

Bisect_ppx  

Bisect_ppx is a code coverage tool for OCaml and Reason. It helps you test thoroughly by showing what's not tested.

You can browse the report seen above online here.


Table of contents

Usage

Dune

Refer to aantron/bisect-starter-dune, which produces this report.

  1. Depend on Bisect_ppx in your opam file:

    depends: [
      "bisect_ppx" {dev & >= "2.5.0"}
      "dune" {>= "2.7.0"}
    ]
    
  2. Mark the code under test for instrumentation by bisect_ppx in your dune file:

    (library
     (public_name my_lib)
     (instrumentation (backend bisect_ppx)))
    
  3. Build and run your test binary. In addition to testing your code, when exiting, it will write one or more files with names like bisect0123456789.coverage:

    find . -name '*.coverage' | xargs rm -f
    dune runtest --instrument-with bisect_ppx --force
    

    The --force flag forces all your tests to run, which is needed for an accurate coverage report.

    To run tests without coverage, do

    dune runtest
    
  4. Generate the coverage report in _coverage/index.html:

    bisect-ppx-report html
    

    You can also generate a short summary in the terminal:

    bisect-ppx-report summary
    

esy

Refer to aantron/bisect-starter-esy, which produces this report.

The instructions are the same as for regular Dune usage, but...

  1. Depend on Bisect_ppx in package.json, instead of in an opam file:

    "devDependencies": {
      "@opam/bisect_ppx": "^2.5.0"
    },
    "dependencies": {
      "@opam/dune": "^2.7.0"
    }
    
  2. Use the esy command for the build and for running binaries:

    esy install
    esy dune runtest --instrument-with bisect_ppx --force
    esy bisect-ppx-report html
    

ReScript

Refer to aantron/bisect-starter-rescript, which produces this report.

  1. Depend on Bisect_ppx in package.json, and install it:

    "devDependencies": {
      "bisect_ppx": "^2.0.0"
    },
    "dependencies": {
      "rescript": "*"
    }
    
    npm install
    

    If pre-built binaries aren't available for your system, the build will automatically fall back to building Bisect_ppx from source using esy, which will take a few minutes the first time. If this happens, you may need to install esy, if it is not already installed:

    npm install -g esy
    npm install
    
  2. Add Bisect_ppx to your bsconfig.json:

    "bs-dependencies": [
      "bisect_ppx"
    ],
    "ppx-flags": [
      "bisect_ppx/ppx"
    ]
    
  3. If you are using Jest, add this to your package.json:

    "jest": {
      "setupFilesAfterEnv": [
        "bisect_ppx/lib/js/src/runtime/js/jest.js"
      ]
    }
    

    Or, if you have enabled the package-specs.in-source flag in bsconfig.json, replace the path by

    "bisect_ppx/src/runtime/js/jest.js"
    

    You can exclude your test cases from the coverage report by adding this to bsconfig.json:

    "ppx-flags": [
      ["bisect_ppx/ppx", "--exclude-files", ".*_test\\.res$$"]
    ]
    

    Usage with Jest requires Bisect_ppx version 2.4.0 or higher. See the aantron/bisect-starter-jest for a complete minimal example project. That repo produces this report.

    If the tests will be running in the browser, at the end of testing, call

    Bisect.Runtime.get_coverage_data();
    

    This returns binary coverage data in a string option, which you should upload or otherwise get out of the browser, and write into a .coverage file.

  4. Build in development with BISECT_ENABLE=yes, run tests, and generate the coverage report in _coverage/index.html:

    BISECT_ENABLE=yes npm run build
    npm run test
    npx bisect-ppx-report html
    

    To exclude your test files from the report, change your PPX flags like so:

    "ppx-flags": [
      ["bisect_ppx/ppx", "--exclude-files", ".*test\\.re"]
    ]
    

    The last argument is a regular expression in the syntax of OCaml's Str module. Note that backslashes need to be escaped both inside the regular expression, and again because they are inside a JSON string.

    Multiple --exclude-files option can be specified if you want to provide multiple patterns.

  5. If your project uses both ReScript and native Dune, native Dune will start picking up OCaml files that are part of the ReScript bisect_ppx package. To prevent this, add a dune file with the following contents to the root of your project:

    (data_only_dirs node_modules)
    

Js_of_ocaml

Refer to aantron/bisect-starter-jsoo, which produces this report.

  1. Follow the Dune instructions above, except that the final test script must be linked with bisect_ppx.runtime (but not instrumented):

    (executable
     (name my_tester)
     (modes js)
     (libraries bisect_ppx.runtime))
    
  2. If the tests will run on Node, call this function at the end of testing to write bisect0123456789.coverage:

    Bisect.Runtime.write_coverage_data ()
    

    If the tests will run in the browser, call

    Bisect.Runtime.get_coverage_data ()
    

    to get binary coverage data in a string option. Upload this string or otherwise extract it from the browser to create a .coverage file.

  3. Build the usual Js_of_ocaml target, including the instrumented code under test, then run the reporter to generate the coverage report in _coverage/index.html:

    dune build my_tester.bc.js --instrument-with bisect_ppx
    node _build/default/my_tester.bc.js   # or in the browser
    bisect-ppx-report html
    

Ocamlfind, Ocamlbuild, and OASIS

  • Ocamlbuild and OASIS instructions can be found at aantron/bisect_ppx-ocamlbuild.

  • With Ocamlfind, you must have your build script issue the right commands, to instrument the code under test, but not the tester:

    ocamlfind opt -package bisect_ppx -c src/source.ml
    ocamlfind opt -c test/test.ml
    ocamlfind opt -linkpkg -package bisect_ppx src/source.cmx test/test.cmx
    

    Running the tester will then produce bisect0123456789.coverage files, which you can process with bisect-ppx-report.


Sending to Coveralls and Codecov

bisect-ppx-report can send reports to Coveralls and Codecov directly from Travis, CircleCI, and GitHub Actions. To do this, run

bisect-ppx-report send-to Coveralls

or

bisect-ppx-report send-to Codecov

When sending specifically from GitHub Actions to Coveralls, use

- run: bisect-ppx-report send-to Coveralls
  env:
    COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    PULL_REQUEST_NUMBER: ${{ github.event.number }}

Put these commands in your CI script in the same place you would run bisect-ppx-report html locally. See bisect-ci-integration-megatest for example CI scripts and current status of these integrations.

If you'd like Bisect_ppx to support other CI and/or coverage services, please send a pull request!

As a workaround for missing CI/coverage integrations, and for development, bisect-ppx-report can also generate a JSON report in Coveralls format, which can be uploaded to a service of your choice using a separate command. For example, to send manually from Travis to Coveralls:

bisect-ppx-report \
  coveralls coverage.json \
  --service-name travis-ci \
  --service-job-id $TRAVIS_JOB_ID
curl -L -F json_file=@./coverage.json https://coveralls.io/api/v1/jobs

For other CI services, replace --service-name and --service-job-id as follows:

CI service --service-name --service-job-id
Travis travis-ci $TRAVIS_JOB_ID
CircleCI circleci $CIRCLE_BUILD_NUM
Semaphore semaphore $REVISION
Jenkins jenkins $BUILD_ID
Codeship codeship $CI_BUILD_NUMBER
GitHub Actions github $GITHUB_RUN_NUMBER

Note that Coveralls-style reports are less precise than the HTML reports generated by Bisect_ppx, because Coveralls considers entire lines as visited or not visited. There can be many expressions on a single line, and the HTML report separately considers each expression as visited or not visited.


Controlling coverage with [@coverage off]

You can tag expressions with [@coverage off], and neither they, nor their subexpressions, will be instrumented by Bisect_ppx.

Likewise, you can tag module-level let-declarations with [@@coverage off], and they won't be instrumented.

You can also turn off instrumentation for blocks of declarations inside a module with [@@@coverage off] and [@@@coverage on].

Finally, you can exclude an entire file by putting [@@@coverage exclude_file] into its top-level module. However, whenever possible, it is recommended to exclude files by not instrumenting with Bisect_ppx to begin with.


Other topics

See advanced usage for:

  • Exhaustiveness checking.

  • Excluding generated files from coverage.

  • SIGTERM handling.

  • Environment variables.

Cornell CS3110 offers a Bisect_ppx tutorial, featuring a video.


Bisect_ppx users

A small sample of projects using Bisect_ppx:


Contributing

Bug reports and pull requests are warmly welcome. Bisect_ppx is developed on GitHub, so please open an issue.

After cloning the repo, try these Makefile targets:

  • make test for unit tests.

  • make usage for build system integration tests, except ReScript.

  • make -C test/js full-test for ReScript. This requires npm and esy.

If you'd like to build an npm package, run npm pack. You can install the resulting .tgz file in another project with npm install. This requires esy, as the Bisect binaries will not be pre-built. The npm package will use esy to build them automatically.

Dependencies (5)

  1. ppxlib >= "0.21.0" & < "0.26.0"
  2. ocaml >= "4.03.0"
  3. dune >= "2.7.0"
  4. cmdliner >= "1.0.0"
  5. base-unix

Dev Dependencies (1)

  1. ocamlformat with-test & = "0.16.0"

  1. agrid
  2. ambient-context
  3. ambient-context-eio
  4. ambient-context-lwt
  5. amf
  6. arp >= "1.0.0" & < "3.1.1"
  7. azure-cosmos-db >= "0.2.3"
  8. base58 >= "0.1.2"
  9. bastet >= "1.2.0"
  10. beluga >= "1.1"
  11. bio_io < "0.5.1"
  12. bisect_ppx-ocamlbuild >= "1.0.1"
  13. bitgenerators
  14. bls12-381 = "0.4.1" | >= "3.0.0" & < "6.0.1"
  15. bls12-381-gen
  16. bls12-381-hash
  17. bls12-381-signature
  18. bls12-381-unix < "0.4.2" | >= "1.0.0"
  19. checked_oint
  20. class_group_vdf
  21. cll
  22. comby
  23. comby-kernel
  24. comby-semantic
  25. daypack-lib
  26. dream
  27. dream-htmx
  28. dream-pure
  29. easy_xlsx
  30. encoding >= "0.0.4"
  31. FPauth
  32. FPauth-core
  33. FPauth-responses
  34. FPauth-strategies
  35. ff >= "0.6.0"
  36. ff-pbt >= "0.6.0"
  37. ff-sig >= "0.6.0"
  38. GT >= "0.5.2"
  39. hyper
  40. irmin >= "3.0.0"
  41. jose
  42. lambdasoup >= "0.6.4"
  43. libsail >= "0.16"
  44. lilac
  45. little_logger
  46. lwt >= "4.2.0" & < "5.4.0"
  47. markup >= "0.8.1"
  48. mazeppa
  49. mec
  50. melange-radix-icons < "0.1.0"
  51. minicaml = "0.3.1"
  52. mirage-block-ccm >= "1.1.0"
  53. mirage-btrees
  54. mnd
  55. mssql
  56. nbd >= "6.0.0"
  57. nbd-unix
  58. obeam
  59. ocaml-protoc-plugin >= "5.0.0"
  60. ocamlformat = "0.11.0" | = "0.18.0" | = "0.19.0"
  61. ocamlformat-rpc < "0.20.0"
  62. octez-bls12-381-hash
  63. octez-bls12-381-polynomial
  64. octez-polynomial
  65. odoc >= "1.4.0" & < "2.0.0" | >= "2.1.0"
  66. ometrics
  67. open_packaging
  68. osnap >= "0.3.0"
  69. owi
  70. partition_map
  71. pf-qubes
  72. pgx
  73. piece_rope >= "0.9.1"
  74. polynomial
  75. ppx_irmin >= "3.0.0"
  76. ppx_make >= "0.3.4"
  77. ppx_subliner >= "0.2.0"
  78. pyml_bindgen < "0.3.0"
  79. randii
  80. rfc6287 >= "1.0.4"
  81. routes >= "0.7.2" & < "1.0.0"
  82. sentry
  83. shared-block-ring >= "2.3.0"
  84. sifun >= "3.0.0"
  85. smtml
  86. spreadsheetml
  87. ssl >= "0.6.0"
  88. swhid
  89. swhid_compute
  90. swhid_types
  91. tcpip >= "6.0.0"
  92. tezos-bls12-381-polynomial
  93. tezos-p2p = "13.0"
  94. tezos-plompiler = "0.1.3"
  95. tezos-plonk = "0.1.3"
  96. tezos-tooling >= "13.0"
  97. tidy_email
  98. tidy_email_mailgun
  99. tidy_email_sendgrid
  100. tidy_email_smtp
  101. timedesc
  102. timere
  103. toml >= "7.0.0"
  104. toml_cconv >= "7.0.0"
  105. validate >= "1.0.0"
  106. weevil
  107. zarr
  108. zarr-eio
  109. zarr-lwt
  110. zarr-sync

Conflicts

None

OCaml

Innovation. Community. Security.