Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In SML, how to assert that a particular exception is thrown?

Without taking the effort to actually clone JUnit or something, I'm throwing together a few utility functions to help test some SML code. I do know about QCheck, but it can't do this one thing either and isn't what I want generally. (But if you know of another automated-testing framework for SML, please speak up.)

I'd like to be able to assert that some function will throw an exception, e.g., given a function

fun broken x = raise Fail

I'd like to be able to write something like

throws ("ERROR: function is not broken enough!", fn () => broken 1, Fail)

and have it throw an error if the given function does not raise the expected exception.

I tried to write a throws function with type (string * exn * (unit -> unit)) -> unit like so:

  fun throws (msg, e, func) = func ()
    handle e' => if e = e'
                     then ()
                     else raise ERROR (SOME msg)

But this generates a bunch of compile-time errors, apparently because ML does not define equality over exceptions:

sexp-tests.sml:54.31-57.49 Error: types of rules don't agree [equality type required]
  earlier rule(s): ''Z -> unit
  this rule: exn -> 'Y
  in rule:
    exn => raise exn
sexp-tests.sml:54.31-57.49 Error: handler domain is not exn [equality type required]
  handler domain: ''Z
  in expression:
    func ()
    handle 
        e' => if e = e' then () else raise (ERROR <exp>)
    | exn => raise exn

As a workaround, I suspect I could just reuse an existing assert function I have:

assert ((broken 1; false) handle Fail => true | _ => false)

But it's a bit more thinking and typing.

So, is there any way to write that throws function in SML?

like image 511
Wang Avatar asked May 13 '11 05:05

Wang


1 Answers

This following function should work:

exception ERROR of string option;

fun throwError msg = raise ERROR (SOME msg);

fun throws (msg, func, e) =
    (func (); throwError msg) handle e' =>
        if exnName e = exnName e'
        then ()
        else raise throwError msg

This uses the function exnName, which gets the name of the exception as a string, and uses that for comparison instead.

More importantly, it also handles the case where no exception is thrown at all, and gives an error on that, too.

Alternatively, if you simply need a boolean value, indicating whether the exception was thrown or not, you can use:

fun bthrows (func, e) = (func (); false) handle e' => exnName e = exnName e'

Note that, for the case of Fail, you'll actually have to create an instance of a Fail-exception, for instance like so:

throws ("ERROR: Oh no!", fn () => test 5, Fail "")

Alternatively, you could take the name of the exception, for a cleaner general case:

fun throws (msg, func, e) =
    (func (); throwError msg) handle e' =>
        if e = exnName e'
        then ()
        else raise throwError msg

fun bthrows (func, e) = (func (); false) handle e' => e = exnName e'

And then use it like this:

throws ("ERROR: Oh no!", fn () => test 5, "Fail")
like image 104
Sebastian Paaske Tørholm Avatar answered Sep 29 '22 12:09

Sebastian Paaske Tørholm