+4  A: 

Well, what I see right away is a type mismatch in your assertion.

assertEqual is String -> a -> a -> Assertion. You pass a String as the second argument, meaning a String should also be the third argument. However, your do expression is not returning String, but rather IO String.

Edit: To expand, once you mess with IO, you can't ever ditch it. You correctly extract the value out of IO with your <-, but then immediately wrap it back into IO with return. If you want to use the string, you must do it inside IO, something like:

do
  contents <- hGetContents handle
  assertEqual "They're equal" "Expected string" contents

Note that then your do would be returning IO Assertion. If you want to use the assertion value, you'd need to unwrap it similarly, and so on. Haskell doesn't let you get away with (hidden) side effects!

J Cooper
Kenny is right about Assertion just being an IO (). I actually haven't used HUnit before :) I'll edit the return out of my example.
J Cooper
+3  A: 

The type of assertEqual is

assertEqual :: String -> a -> a -> Assertion

Since the 2nd argument “"The dog barks at midnight."” is a String, we know a must be String. Therefore the do expression

do 
  content <- hGetContents fileHandle
  return content

must return a String. But String = [Char], therefore Haskell will regard this as a List monad. I don't know why it infers a [String] instead of a [Char] = String, but this at least explains where the List comes.


BTW, an Assertion is already an IO (). If you return an assertion you'll get an IO (IO ()). Probably you mean

withTempFileContainingText :: (Handle -> IO c) -> IO c
withTempFileContainingText lambda = 
    bracketOnError 
        (return . snd =<< openTempFile "." "input.txt")
        (deleteFile)
        (\fileHandle -> do
            hPutStr fileHandle "The dog barks at midnight."
            hClose fileHandle
            lambda fileHandle)   -- # <--- No return here

then the testCase can be written as

testCase :: Assertion
testCase = withTempFileContainingText $ \fileHandle -> do
    conts <- hGetContents fileHandle
    assertEqual "contents as expected" "The dog barks at midnight." conts
    -- # ^-- No return here.
KennyTM
It's a shame that ghc reports the wrong problem in the wrong place.
keithb
A: 

Thanks all for some valuable pointers.

This runs and passes:

withTempFileContainingText lambda = 
        bracketOnError 
            (openTempFile "." "input.txt")
            (\(filePath, fileHandle) -> do
                removeFile filePath)
            (\(filePath, fileHandle) -> do
                startOfFile <- hGetPosn fileHandle 
                hPutStr fileHandle "The dog barks at midnight."
                hFlush fileHandle
                hSetPosn startOfFile
                lambda fileHandle
                removeFile filePath)


testReadFile = runTestTT $ TestLabel "InteractionDomain.readFileContentsToList" (TestList [
    (TestLabel "testing infrastructure: read test file" (TestList [
        TestCase (withTempFileContainingText (\fileHandle -> do
            content <- hGetContents fileHandle 
            assertEqual "contents as expected" "The dog barks at midnight." content))]))])
keithb