How to test your own ppx

Hello everyone !

I followed this article from Tarides, but I do have a problem with that kind of test.

To sum up the article, you write your file.ml and the file.expected.ml, you apply the preprocess
on file.ml and check the diff with file.expected.ml. But I have format issue wit that kind of test,
in a way of OCaml AST my file where I applied the ppx and the expected are equals, but the format is
not (so I get an error)

Did someone have ever encountered the same issue ?

When I was writing a ppx_rewriter I would apply the pp on the .expected.ml without changing
the AST with a trick. But with ppx_deriver some changes are applied even If I ask my deriver
to not change anything.
It was a bad looking trick but made my tests pass so I kept to this for a while, but my tests
would benefit a lot a real solution.

When I need to check for equality between two ML files, I always parse them and check equality between the ASTs (using an equality function that ignores locations) and only if unequal, I do “diff” and print out the results.

Sounds interesting. Is this function released in some library ?
If its not it would cool if it was, maybe with some extra functionality such as diffing directly on the ast or something

Emile

  1. I suspect that somewhere in the “pa_ppx_*” collection of packages, the function is to be found, but for the life of me, I don’t remember where.
  2. But that stuff is all built on Camlp5, so I would not suggest you use it unless you are already a Camlp5 user.
  3. It’s simple enough to build, though, that you should be able to use the ppx_deriving PPX rewriter map to do the trick, as follows: use map to generate a map function for the OCaml AST, where you provide your own version of the map for the Location type.

I am not -certain- that this will work, b/c there are a few places where there are lists of Locations. I did it using a “souped-up” map PPX rewriter, called pa_ppx_migrate, that allows more control over the type-directed process of generating the rewriting code.

If you’re still interested (and honestly, if you’re sufficiently invested in the standard PPX ecosystem, you shouldn’t be, except as an academic curiosity, or maybe as an example of something to copy maybe) I can dig around and find the actual code.

But it’s really not difficult to do, if you have sufficiently-powerful map derivers.

2 Likes

I use ppxlib.metaquot to do f [%expr input] = [%expr output]

2 Likes

Thank you, I am now using your solution.

That kind of test fit pretty good in Alcotest:

let check_eq ~expected ~actual name =
  let expr = Alcotest.of_pp Ppxlib.Pprintast.expression in
  Alcotest.(check expr) name expected actual

let test_something () =
  let loc = Location.none in
  let expected = [%expr foo] in
  let actual = f [%expr foo'] in
  check_eq ~expected ~actual "something"

I remember: there’s another thing I do, when I’m trying to check code-transformation against either a golden version, or against some other version of that code-transformation: I take the output of both sides, run them thru ocamlformat, and then diff them.