My use-case is that I am writing tests for a parser. The parser outputs an AST which I convert to an s-expression of sexplib type. I want to compare this s-expression to an expected parse tree, also written as an s-expression. The OUnit 2 assert_equal
function works for this, along with the Sexp.equal
function provided by sexplib:
assert_equal expected actual ~cmp:Sexp.equal
I would also like OUnit to print the s-expression diff when this assertion fails. The assert_equal
function exposes an optional parameter for this with the following type:
?pp_diff:(Format.formatter -> ('a * 'a) -> unit)
So it takes a formatter to which it prints a diff between the two 'a
type instances. Sexplib doesn't quite have something like this built in; it only has the Sexp.pp
family of functions which have signatures like:
val pp_hum : Format.formatter -> t -> unit
So only print out a single s-expression, not the diff between them.
The sexplib authors provide the sexp_diff package for the purpose of diffing s-expressions. I can compute the diff between two s-expressions doing something like:
let diff = Sexp_diff.Algo.diff ~original:expected ~updated:actual
(there is an extra parameter of type unit
in this function which I can't exactly figure out the purpose of - but anyway,)
Unfortunately when it comes to converting this diff
function into a form that would be accepted by pp_diff
I'm at a loss. How can I define a new function which takes a Format.formatter
instance that would do this?
let diff_tree (fmt : Format.formatter) ((expected, actual) : Sexp.t * Sexp.t) : unit =
let diff = Sexp_diff.Algo.diff ~original:expected ~updated:actual in
(** what goes here? *)
Alternatively, should I just give up on the pp_diff
approach and serialize the diff to a string using the sexp_diff
library's display functions, then attach that as a regular string message to assert_equals
?
If you don't need to have the difference well-integrated in term of formatting, you can print the string created by Display
:
let pp_diff ppf (actual,expected) =
let open Sexp_diff in
let diff = Algo.diff ~original:expected ~updated:actual () in
let options = Display.Display_options.(create Layout.Two_column) in
let text = Display.display_with_ansi_colors options diff in
Format.pp_print_string ppf text
Otherwise, you will have to write a printer for the Diff.t
type yourself.
For a basic printers, this could looks like
let rec pp_diff ppf diff =
let sexp = Sexplib.Sexp.pp_hum in
let open Sexp_diff.Diff in
match diff with
| Same x -> Fmt.pf ppf "%a@," sexp x
| Delete x -> Fmt.pf ppf "-%a@," sexp x
| Add x -> Fmt.pf ppf "+%a@," sexp x
| Replace (x,y) -> Fmt.pf ppf "-%a@,+%a@," sexp x sexp y
| Enclose x -> Fmt.pf ppf "@[<v 2>(@,%a@,)@]" Fmt.(list ~sep:cut pp_diff) x