Is it possible to assert an error case in HUnit? - haskell

If I have a function which results in an error for a certain input, is it possible to write a test verifying the error occurs for that input?
I do not find this "assert error" functionality available in HUnit. Is it available in HUnit or perhaps in some other test package?

You can catch an error and assert if it doesn't happen using standard exception handling:
errored <- catch (somethingThatErrors >> pure False) handler
if errored then
assertFailure "Did not catch expected error"
else
pure ()
where
handler :: ErrorCall -> IO Bool
handler _ = pure True

Related

Haskell - exit a program with a specified error code

In Haskell, is there a way to exit a program with a specified error code? The resources I've been reading typically point to the error function for exiting a program with an error, but it seems to always terminate the program with an error code of 1.
[martin#localhost Haskell]$ cat error.hs
main = do
error "My English language error message"
[martin#localhost Haskell]$ ghc error.hs
[1 of 1] Compiling Main ( error.hs, error.o )
Linking error ...
[martin#localhost Haskell]$ ./error
error: My English language error message
[martin#localhost Haskell]$ echo $?
1
Use exitWith from System.Exit:
main = exitWith (ExitFailure 2)
I would add some helpers for convenience:
exitWithErrorMessage :: String -> ExitCode -> IO a
exitWithErrorMessage str e = hPutStrLn stderr str >> exitWith e
exitResourceMissing :: IO a
exitResourceMissing = exitWithErrorMessage "Resource missing" (ExitFailure 2)
An alternative that allows an error message only is die
import System.Exit
tests = ... -- some value from the program
testsResult = ... -- Bool value overall status
main :: IO ()
main = do
if testsResult then
print "Tests passed"
else
die (show tests)
The accepted answer allows setting the exit error code though, so it's closer to the exact phrasing of the question.

Testing captured IO from a spawned process

I want to test the return value and the IO output on the following method:
defmodule Speaker do
def speak do
receive do
{ :say, msg } ->
IO.puts(msg)
speak
_other ->
speak # throw away the message
end
end
end
In the ExUnit.CaptureIO docs, there is an example test that does this which looks like the following:
test "checking the return value and the IO output" do
fun = fn ->
assert Enum.each(["some", "example"], &(IO.puts &1)) == :ok
end
assert capture_io(fun) == "some\nexample\n"
end
Given that, I thought I could write the following test that performs a similar action but with a spawned process:
test ".speak with capture io" do
pid = Kernel.spawn(Speaker, :speak, [])
fun = fn ->
assert send(pid, { :say, "Hello" }) == { :say, "Hello" }
end
assert capture_io(fun) == "Hello\n"
end
However, I get the following error message telling me there was no output, even though I can see output on the terminal:
1) test .speak with capture io (SpeakerTest)
test/speaker_test.exs:25
Assertion with == failed
code: capture_io(fun) == "Hello\n"
lhs: ""
rhs: "Hello\n"
stacktrace:
test/speaker_test.exs:30: (test)
So, am I missing something perhaps with regards to testing spawned processes or methods that use the receive macro? How can I change my test to make it pass?
CaptureIO might not be suited for what you're trying to do here. It runs a function and returns the captured output when that function returns. But your function never returns, so seems like this won't work. I came up with the following workaround:
test ".speak with capture io" do
test_process = self()
pid = spawn(fn ->
Process.group_leader(self(), test_process)
Speaker.speak
end)
send(pid, {:say, "Hello"})
assert_receive {:io_request, _, _, {:put_chars, :unicode, "Hello\n"}}
# Just to cleanup pid which dies upon not receiving a correct response
# to the :io_request after a timeout
Process.exit(pid, :kill)
end
It uses Process.group_leader to set the current process as the receiver of IO messages for the tested process and then asserts that these messages arrive.
I had a similar problem, I had a registered process on my Application that would timeout every 10 seconds and write to stdio with IO.binwrite, to simulate multiple timeouts I took upon #Pawel-Obrok answer, but change it as to reply the :io_request with an :io_reply, that way the process would not hang allowing me to send multiple messages.
defp assert_io() do
send(MyProcess, :timeout)
receive do
{:io_request, _, reply_as, {:put_chars, _, msg}} ->
assert msg == "Some IO message"
send(Stats, {:io_reply, reply_as, :ok})
_ ->
flunk
end
end
test "get multiple messages" do
Process.group_leader(Process.whereis(MyProcess), self())
assert_io()
assert_io()
end
If you want to know more about the IO protocol take a look at the erlang docs about it.

Silencing GHC API output (stdout)

I'm using the GHC API to parse a module. If the module contains syntax errors the GHC API writes them to stdout. This interferes with my program, which has another way to report errors. Example session:
$ prog ../stack/src/Stack/Package.hs
../stack/src/Stack/Package.hs:669:0:
error: missing binary operator before token "("
#if MIN_VERSION_Cabal(1, 22, 0)
^
../stack/src/Stack/Package.hs:783:0:
error: missing binary operator before token "("
#if MIN_VERSION_Cabal(1, 22, 0)
^
../stack/src/Stack/Package.hs
error: 1:1 argon: phase `C pre-processor' failed (exitcode = 1)
Only the last one should be outputted. How can I make sure the GHC API does not output anything? I'd like to avoid libraries like silently which solve the problem by redirecting stdout to a temporary file.
I already tried to use GHC.defaultErrorHandler, but while I can catch the exception, GHC API still writes to stdout. Relevant code:
-- | Parse a module with specific instructions for the C pre-processor.
parseModuleWithCpp :: CppOptions
-> FilePath
-> IO (Either (Span, String) LModule)
parseModuleWithCpp cppOptions file =
GHC.defaultErrorHandler GHC.defaultFatalMessager (GHC.FlushOut $ return ()) $
GHC.runGhc (Just libdir) $ do
dflags <- initDynFlags file
let useCpp = GHC.xopt GHC.Opt_Cpp dflags
fileContents <-
if useCpp
then getPreprocessedSrcDirect cppOptions file
else GHC.liftIO $ readFile file
return $
case parseFile dflags file fileContents of
GHC.PFailed ss m -> Left (srcSpanToSpan ss, GHC.showSDoc dflags m)
GHC.POk _ pmod -> Right pmod
Moreover, with this approach I cannot catch the error message (I just get ExitFailure). Removing the line with GHC.defaultErrorHandler gives me the output shown above.
Many thanks to #adamse for pointing me in the right direction! I have found the answer in Hint's code.
It suffices to override logging in the dynamic flags:
initDynFlags :: GHC.GhcMonad m => FilePath -> m GHC.DynFlags
initDynFlags file = do
dflags0 <- GHC.getSessionDynFlags
src_opts <- GHC.liftIO $ GHC.getOptionsFromFile dflags0 file
(dflags1, _, _) <- GHC.parseDynamicFilePragma dflags0 src_opts
let dflags2 = dflags1 { GHC.log_action = customLogAction }
void $ GHC.setSessionDynFlags dflags2
return dflags2
customLogAction :: GHC.LogAction
customLogAction dflags severity _ _ msg =
case severity of
GHC.SevFatal -> fail $ GHC.showSDoc dflags msg
_ -> return () -- do nothing in the other cases (debug, info, etc.)
The default implementation of GHC.log_action can be found here:
http://haddock.stackage.org/lts-3.10/ghc-7.10.2/src/DynFlags.html#defaultLogAction
The code for parsing remains the same in my question, after having removed the line about GHC.defaultErrorHandler, which is no longer needed, assuming one catches exceptions himself.
I have seen this question before and then the answer was to temporarily redirect stdout and stderr.
To redirect stdout to a file as an example:
import GHC.IO.Handle
import System.IO
main = do file <- openFile "stdout" WriteMode
stdout' <- hDuplicate stdout -- you might want to keep track
-- of the original stdout
hDuplicateTo file stdout -- makes the second Handle a
-- duplicate of the first
putStrLn "hi"
hClose file

How can I deal with these "attempt to divide by zero" errors?

When I am initializing a variable to zero, like int i=0, it throws an exception:
Attempt to divide by zero
How is this possible? The exception is thrown in other cases, also, like getting zero indexed value from a collection: collection[0], and if(a%b==0) etc,.
Please suggest how I can deal with this.
DivideByZeroException:
The exception that is thrown when there is an attempt to divide an integral (such as int or long) or decimal value by zero.
This exception will also be thrown in the case of a % b if b evaluates to 0.
Either
Guard the expression such that 0 is never used in the division/modulo, or;
Catch the exception (ick)
You need to catch your Exception and handle it properly.
try
{
// your code that throws exception
}
catch (DivideByZeroException ex)
{
// Perform an appropriate action
// for example display a custom message
Console.WriteLine(ex.Message);
}
For more information see: Exceptions and Exception Handling

no parse exception

I am trying to cach exception caused by read function:
run :: CurrentData -> IO ()
run current = do
{
x <- (getCommandFromUser) `E.catch` handler;
updated <- executeCommand x current;
run updated;
} where handler :: E.IOException -> IO Command
handler e = do putStrLn "wrong command format" >> return (DoNothing);
In this code function getCommandfrom user gets some string from user and then tries to read some data from this string using "read" function
If read fails there is exception thrown:
*** Exception : prelude.read : no parse
and program exits...
I can't catch this exception - what is type of this exception???
I tried also E.SomeException instead of E.IOException...
E is from import Control.Exception As E
"what is type of this exception?" The type is ErrorCall, also available from Control.Exception. An ErrorCall is what is thrown when the error function is called.
Just change the type of handler and it will work. A last resort to get things working is to catch E.SomeException, but that's almost always the wrong thing to do.

Resources