Haskell Unit Tests with HUnit
I read an OnLamp article today called A Beautiful Regex Matcher… In Haskell. Interesting little piece of code, but there was a (silly little) bug in it. This struck me as a fine opportunity to figure out how HUnit — one of Haskell’s unit testing frameworks — works. Here’s what I came up with:
import Test.HUnit
import Regex
tests = TestList [ TestLabel (makeLabel x) (makeTest x) | x <- test_data ]
where makeTest tup@(reg, str, bool) =
TestCase $ assertEqual (makeLabel tup) bool (match reg str)
makeLabel (reg, str, bool) | True == bool = reg ++ " =~ " ++ str
| False == bool = reg ++ " !~ " ++ str
test_data = [ ("foo", "foo", True),
("foo", "bar", False),
("", "foo", True),
(".", "Foo", True),
(".*", "Foo", True),
("F.", "Foo", True),
("F.*", "Foo", True),
("Fo*", "Foo", True),
("Fo*d", "foo", False),
("Fo*d", "Foooood", True) ]
main = runTestTT tests
As mentioned in the article’s comments, the case where “.*” (“match anything”) is supposed to match “Foo” fails. Here’s the test output for the original code:
% runhaskell TestRegex.hs
### Error in: 4:.* =~ Foo
user error (HUnit:.* =~ Foo
expected: True
but got: False)
### Error in: 6:F.* =~ Foo
user error (HUnit:F.* =~ Foo
expected: True
but got: False)
Cases: 10 Tried: 10 Errors: 2 Failures: 0
Counts {cases = 10, tried = 10, errors = 2, failures = 0}
%
Easy enough. Finding out which cases fail makes tracking the problem down relatively straightforward. Turns out that the “matchstar” method doesn’t deal properly with periods at the end of the match. The original code:
matchstar c [] (y:ys) = c == y
Replace it with the following:
matchstar c [] (y:ys) = if c == '.' then True else c == y
And you get the following out of the unit tests:
% runhaskell TestRegex.hs
Cases: 10 Tried: 10 Errors: 0 Failures: 0
Counts {cases = 10, tried = 10, errors = 0, failures = 0}
%
Horray!