Haskell QuickCheck Test not running properly when run with Cabal - haskell

Something did not make sense with a Cabal package I was developing, and I have boiled the issue down to the following example:
I have the following simple test module:
module Main where
import Test.QuickCheck (quickCheck)
main = quickCheck False
And the following Cabal file in the same directory:
name: project
version: 0.1.0.0
cabal-version: >= 1.10
build-type: Simple
executable project
main-is: Main.hs
build-depends: base, QuickCheck
default-language: Haskell2010
test-suite project-test
type: exitcode-stdio-1.0
main-is: Main.hs
build-depends: base, QuickCheck
default-language: Haskell2010
The only other files in the directory are dist (created by Cabal upon build) and the cabal sandbox files. Both the executable and the test-suite refer to Main.hs, and therefore I would expect to get the same test results when running "cabal run" as when running "cabal test". Apparently, however, that is not the case.
"cabal run" gives:
Preprocessing executable 'project' for project-0.1.0.0...
Running project...
*** Failed! Falsifiable (after 1 test):
which makes sense because the property test 'quickCheck False' should fail. As expected this is the same result I get when running main in ghci.
"cabal test", however, gives:
Test suite project-test: RUNNING...
Test suite project-test: PASS
Test suite logged to: dist/test/project-0.1.0.0-project-test.log
1 of 1 test suites (1 of 1 test cases) passed.
Why the heck does "cabal test" pass the test case, whereas "cabal run" fails it as expected?

quickCheck doesn't exit the program, therefore, it doesn't set the exit code. After all, you could have several quickCheck tests, which should be independent of each other:
main = do
quickCheck prop1 -- should this exit if the test fails?
quickCheck prop2 -- should this exit if the test fails?
quickCheck prop3 -- should this exit if the test fails?
However, you can easily fix this if you a) exit as soon as one of your tests doesn't pass or b) remember whether a single test hasn't passed and then exit with the correct code.
Using only QuickCheck, but no other library
Exit with failure as soon as a test fails
For this, you simply use quickCheckResult, which you can check with isSuccess from Test.QuickCheck.Test. If you want to use your current quickCheck definition, you can use qualified includes to exchange the default implementation with your special one:
import Control.Monad (when)
import System.Exit (exitFailure)
import Test.QuickCheck hiding (quickCheck, quickCheckWith)
import qualified Test.QuickCheck.Test as Q
quickCheckWith :: Testable prop => Args -> prop -> IO ()
quickCheckWith args p = do
success <- fmap Q.isSuccess $ quickCheckWithResult args p
when (not success) $ exitFailure -- exit if the result is a failure
quickCheck :: Testable prop => prop -> IO ()
quickCheck p = quickCheckWith stdArgs p
You should probably use another name though, especially if other people work on the same project. checkOrExit would be plausible.
Return failure code but run all tests
This is essentially the same, but runs all test. You must use quickCheckResult or quickCheckWithResult again:
import Control.Monad (when)
import System.Exit (exitFailure)
import Test.QuickCheck (quickCheckResult)
import Test.QuickCheck.Test (isSuccess)
main :: IO ()
main = do
let tests = [ quickCheckResult prop1
, quickCheckResult prop2
, quickCheckResult prop3
, quickCheckResult prop4
]
success <- fmap (all isSuccess) . sequence $ tests
when (not success) $ exitFailure
Using additional test libraries
While quickCheck is great for property checking, it doesn't provide a complete testing framework. That's where other full-fledged frameworks such as tasty or hspec come in handy. They can take a Testable a and inspect QuickCheck's result accordingly. An example using hspec:
module Main where
import Test.Hspec
import Test.QuickCheck (property)
main = hspec $ do
describe "<method you would like to test>" $
it "<property/assumption you would like to test>" $
property $ False -- quickCheck
This gives the (more verbose) output
<method you would like to test>
- <property/assumption you would like to test> FAILED [1]
1) <method you would like to test> <property/assumption you would like to test>
Falsifiable (after 1 test):
[remark: the used values would be here, but `False` is a boolean]
Randomized with seed 2077617428
Finished in 0.0019 seconds
1 example, 1 failure
Also, it exits with an error code, so your cabal test will recognize this failed test correctly. Those test frameworks also have additional features, which go beyond of the scope of this answer.
TL;DR
QuickCheck doesn't exit your program, but Cabal only inspects the exit code to determine whether the tests have passed. Since any regularly ending Haskell program returns zero (aka no error), you either need to use exitFailure (or something similar), or a framework that uses exitFailure behind the scenes.
Note that this only holds for tests with type: exitcode-stdio-1.0.

Related

Cannot find module 'Text.HTML.TagSoup'

I have installed tagsoup with
cabal v1-install tagsoup
and verified the install with ghc-pkg list | grep tagsoup
However, in my very simple Haskell 8.6.5 program the statement
import Text.HTML.TagSoup
fails with cannot find module 'Text.HTML.TagSoup'
ghc -v is not useful
cabal new-install tagsoup fails with a ton of errors
import Network.HTTP.Conduit
import Text.HTML.TagSoup
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as CL
main :: IO ()
main = do
lbs <- simpleHttp "https://wiki.haskell.org"
print $ show lbs
-- tagsoup code removed
For this kind of a single file use case, I'd recommend using a Stack script. If you add the following two lines to the top of your file:
#!/usr/bin/env stack
-- stack --resolver lts-13.27 script
You can then run stack filename.hs, which will:
Download GHC if necessary
Download and build all dependencies, based on your import list
Use runghc to run your program
More information:
How to Script
Get Started with Haskell

Haskell - fail to load interface for 'System.Console.ANSI' after installing package

I've been learning haskell for the pask few days, and I find it to be very interesting though I'm not used to pure functional paradigm.
I've been trying to make my first "real" program (after helloworld, fizzbuzz & co), which is to make a Snake game in haskell. To do it, I'd like to clean the console and to print new things in it (I'm on Windows).
I've found this to help me:
How do I clear the terminal screen in Haskell?
So I updated cabal with "cabal update". Then I created my folder "Snake", and cd into it. Then I did "cabal sandbox init", followed by "cabal install ansi-terminal", and created the following file :
import Data.List
import System.IO
import System.Console.ANSI
main = do
putStrLn ("Test")
clearScreen
test <- getInput
putStrLn ("You entered " ++ (show(test)))
When I do "ghc --make Snake.hs", it returns the error "Failed to load interface for 'System.Console.ANSI'"
What exactly am I missing?

How do I install and test a Haskell package that I'm developing locally?

I'm developing a Haskell package and would like to test it as if it were an installed package. That is, I'd like to be able to install it among my other packages and then either import or :m +.
I can successfully build my package and (apparently) can successfully install it using
cabal install
which reports
Resolving dependencies...
Configuring Exos-0.0.1.0...
Building Exos-0.0.1.0...
Installed Exos-0.0.1.0
but all attempts to import the package then fail with something like (e.g. in GHCi)
<no location info>:
Could not find module ‘Exos’
It is not a module in the current program, or in any known package.
even though I can see a populated Exos-0.0.1.0 folder in my Haskell lib directory. (Even "uninstalling" fails with ghc-pkg unregister, which reports ghc-pkg: cannot find package Exos-0.0.1.0.)
How do I install and test a package that I'm developing locally, so that it behaves like part of my collection of installed Haskell packages — specifically so that I can import it (and "uninstall" it) like any other?
My package has the structure
Exos.cabal
Exos/
Cryo/
Exos.hs
Exos/
Display.hs
Core.hs
with (relevant) contents in Exos.cabal
name: Exos
version: 0.0.1.0
build-type: Simple
- ...
library
exposed-modules: Cryo.Exos,
Cryo.Exos.Display
other-modules: Cryo.Exos.Core
-- other-extensions:
build-depends: base >=4.8,
containers >= 0.5.5.1,
split >= 0.2.2,
MissingH >= 1.3.0.1
-- hs-source-dirs:
default-language: Haskell2010
in Exos.hs
module Cryo.Exos
(
f,
g,
otherFunc
) where
import Cryo.Exos.Core
-- ...
in Core.hs
module Cryo.Exos.Core where
-- ...
and in Display.hs
module Cryo.Exos.Display
(
showSomething,
showSomethingElse
) where
import Data.Char
-- ...
import Cryo.Exos.Core
import Cryo.Exos
FWIW, inside my IDE, I can write an and successfully run "Application" with the following section in the above .cabal file
executable enigma-hs
main-is: Main.hs
build-depends: base >=4.8,
Exos
hs-source-dirs: tests
default-language: Haskell2010
and the following (relevant) code in Exos/tests/Main.hs
module Main where
import System.IO
import Cryo.Exos
import Cryo.Exos.Display
main :: IO ()
main = do
putStr $ showSomething
putStr $ showSomethingElse
-- ...
The error is a simple one: If you look at the Main.hs example where you have successfully imported the module you have
import Cryos.Expos
and not
import Expos -- wrong
that's because, the module you are importing is Cryos.Expos from the package Expos.

How to build and use a bootstrap executable in a Cabal package

I would like to Cabalize a Haskell project in order to upload it to Hackage. It consists of one standard .hs file which builds a bootstrap executable, and eight nonstandard .lhs files which are processed by the bootstrap executable to yield the .hs files which build the production executable.
In other words, my project is a custom literate preprocessor (with HTML markup, code grooming, here documents) which is written in itself. It depends on a bootstrap version which can translate the literate code for the production version, but which is not suitable for general use. So I do not want the bootstrap version to be a separate package; it is meant to be used only once, here.
I have been using a Makefile, which is possible in Cabal but not in the spirit of Cabal. What is a clean way to do this, that takes maximal advantage of the generality of Cabal? I have not seen any suggestions for this in the online documentation, short of either rewriting Distribution.Simple or using a Makefile.
I think that releasing your bootstrap executable as a separate package is the easiest solution, and there are instructions available on how to integrate your custom preprocessor with Cabal.
If you don't want to do that, try making your preprocessor part of Setup.hs (don't forget to set the build type to Custom!). You can run your preprocessor during the configure step. Look at how wxcore does this.
Here's a working example (patterned after wxcore):
Setup.hs:
import Distribution.Simple
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Setup
import Distribution.PackageDescription
import Control.Monad (forM_)
import System.Directory (copyFile)
import System.FilePath (replaceExtension)
main :: IO ()
main = defaultMainWithHooks simpleUserHooks { confHook = myConfHook }
-- Replace 'copyFile' with more complicated logic.
runMyPreprocessor :: FilePath -> IO ()
runMyPreprocessor abcFile = copyFile abcFile (replaceExtension abcFile ".hs")
myConfHook :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags
-> IO LocalBuildInfo
myConfHook (pkgDesc, hbi) flags = do
let extraSrc = extraSrcFiles . packageDescription $ pkgDesc
forM_ extraSrc runMyPreprocessor
confHook simpleUserHooks (pkgDesc, hbi) flags
my-preprocessor.cabal:
name: my-preprocessor
version: 0.1.0.0
build-type: Custom
cabal-version: >=1.8
extra-source-files: Main.abc
executable my-preprocessor
main-is: Main.hs
build-depends: base < 5
Main.abc:
module Main
where
main :: IO ()
main = putStrLn "Hello"

How can I import a Haskell module in GHCi?

I am trying to teach myself Haskell from the book Learn You A Haskell for Great Good. I got up to the last section of chapter 7 (Modules), where it tells how to create your own module. I did a copy and paste of the Geometry module given in the book at the beginning of the section. The name of the file is Geometry.hs, as the book suggested, and the file is in the bin directory for ghci, which is where I previously was able to successfully do a load using :l for another .hs file.
When I type the following command in GHCi
import Geometry
I get the following error:
Could not find module 'Geometry' It is not a module in the current
program or in any known package
I must be doing something that is obviously wrong, but I can't figure out what it is.
When you use import ModuleName in GHCi, it works (mostly) in the same way import Data.List works: GHC checks your local package database for the module, loads it, and brings its (exported) contents into scope.
However, Geometry isn't a module of a package installed with ghc-pkg. Therefore, GHC doesn't know that a module Geometry exists at all. Neither does it interactive variant GHCi.
But if you :load a program, things change. GHC will take its used modules into account:
-- Foo.hs
module Foo where
foo :: IO ()
foo = putStrLn "Hello from foo!"
-- Main.hs
module Main where
import Foo (foo)
main :: IO ()
main = foo
$ cd /path/to/your/files
$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> import Foo
<no location info>:
Could not find module ‘Foo’
It is not a module in the current program, or in any known package.
Prelude> :l Main.hs
[1 of 2] Compiling Foo ( Foo.hs, interpreted )
[2 of 2] Compiling Main ( Main.hs, interpreted )
Ok, modules loaded: Main, Foo.
*Main> :l Main.hs
*Main> foo
Hello from foo!
*Main> import Foo
*Main Foo> -- module now loaded
As you can see, importing Foo first failed. However, after we've actually loaded the program that uses Foo, we were able to use import Foo in GHCi.
So if you want to use import in GHCi, make sure that GHC can find your module, either by including it in a wrapper or installing it. If you just want to load the module itself, use :load.
TLDR: the Learn you a Haskell book fails to mention that you have to :load the Geometry.hs file first. Then :m to go back to Prelude and then import Geometry works.
It is now also possible to add the lib flag when installing packages, i.e. to run cabal install --lib packagename and then to import the corresponding package directly in GHCi. In the present case, for example cabal install --lib hgeometry would facilitate importing modules from this geometry package.

Resources