Let's say I have a src file like so:
{-# LANGUAGE CPP #-}
module Alphabet (
#ifdef TEST
alphabet
#endif
) where
alphabet :: [Char]
alphabet = "abcdefghijklmnopqrstuvwxyz"
a .cabal file like so:
name: Alphabet
version: 0.1.0.0
library
build-depends: base >=4.8 && <4.9, containers >=0.5 && <0.6, split >=0.2 && <0.3
hs-source-dirs: src
Exposed-modules: Alphabet
default-language: Haskell2010
test-suite alphabet-test
ghc-options: -Wall -Werror
cpp-options: -DTEST
default-extensions: OverloadedStrings
type: exitcode-stdio-1.0
main-is: Spec.hs
hs-source-dirs: tests
build-depends: Alphabet, base >= 4.8 && < 4.9, containers >= 0.5 && <0.6, split >= 0.2 && < 0.3, hspec, QuickCheck
default-language: Haskell2010
A master test file like so:
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
and a test file like:
module AphabetSpec (spec) where
import Test.Hspec
import Alphabet (alphabet)
spec :: Spec
spec = do
describe "Alphabet.alphabet" $ do
it "returns the alphabet" $ do
alphabet `shouldBe` "abcdefghijklmnopqrstuvwxyz"
now running `cabal test`:
cabal test
Preprocessing library Alphabet-0.1.0.0...
In-place registering Alphabet-0.1.0.0...
Preprocessing test suite 'alphabet-test' for Alphabet-0.1.0.0...
[1 of 1] Compiling Main ( tests/AlphabetSpec.hs, dist/build/alphabet-test/alphabet-test-tmp/AlphabetSpec.o )
tests/AlphabetSpec.hs:4:27:
Module ‘Alphabet’ does not export ‘alphabet’
Why is my CPP not working as expected? How can I fix it?
Why is my CPP not working as expected?
Two-step building. Since your tests depend on the library, it gets build first. The library doesn't have any CPP options set, therefore alphabet doesn't get exported.
When the tests get built, your library is already compiled, and alphabet doesn't get exported. It's a separation of concerns.
How can I fix it?
There are several tricks to work with "hidden" (e.g. non-exported) functions. For one, you can put them all into a .Internal module. That way, users that want to use the hidden bits can do so rather easily, but the casual user doesn't have too many tools at hand.
Another way to handle this is to drop the dependency in the test and instead add the src directory to the tests:
hs-source-dirs: tests, src
However, this also means that you have to rebuild the whole library for tests, but it will enable using CPP.
A third option is to not test alphabet, but instead the observable behaviour of exported functions that depend on it. So instead of testing alphabet, you would test filterAlpha:
filterAlpha :: String -> String
filterAlpha = filter (`elem` alphabet)
You have to test filterAlpha anyway. If there are many functions that use alphabet, it is likely that you will have some test that will notice a regression if you accidentally change it.
The problem was a simply syntax error. #ifdef is wrong, #ifndef is right
Related
How do I specify test suites in HSpec? I'm going to have multiple *.hs test files for each of my modules and I want to just run stack test and for all of the tests to run. How do I set that up?
I tried to list the test modules like this in my cabal file but it doesn't work:
test-suite foo-test
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Spec.hs
, Mod1Spec.hs
build-depends: base
, containers >= 0.5.6.2
, hqfl
, hspec >= 2.2.3
, hspec >= 2.2.3
, mtl >= 2.2.1
, pipes >= 4.1.8
, random >= 1.1
ghc-options: -threaded -rtsopts -with-rtsopts=-N
default-language: Haskell2010
Here is some docs on hspec testing:
http://hspec.github.io/hspec-discover.html
If your main spec module contains just the lines:
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
hspec-discover will scan the directory tree for spec tests.
Also - here is a small hspec example:
https://github.com/hspec/hspec-example
Background
I'm building a logfile parser in Haskell. I'm using stack to build it. Running the stack build command works happily and my project compiles. Running stack test, however, produces the following error:
parser-test: executable not found
I see the the following warning above the error message but I don't know how to avoid the redirect to which it refers.
Warning: output was redirected with -o, but no output will be generated because there is no Main module.
Relevant files
I haven't written any tests yet so the test file is as it was created by stack new. My cabal file looks like this:
...
category: Executable
build-type: Simple
-- extra-source-files:
cabal-version: >=1.10
library
hs-source-dirs: src
exposed-modules: LogParser
build-depends: base >= 4.7 && < 5
, attoparsec
, bytestring
, old-locale
, time
default-language: Haskell2010
executable parser-exe
hs-source-dirs: app
main-is: Main.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base
, attoparsec
, bytestring
, old-locale
, time
, parser
default-language: Haskell2010
test-suite parser-test
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Spec.hs
build-depends: base
, attoparsec
, bytestring
, hspec
, hspec-attoparsec
, old-locale
, time
, parser
ghc-options: -threaded -rtsopts -with-rtsopts=-N
default-language: Haskell2010
source-repository head
type: git
...
I presume I'm missing something but I can't find where what I'm missing is documented.
Desired behaviour
I should see the Test suite not yet implemented message outlined in the stack documentation.
The content of the test/Spec.hs file from the template is:
main :: IO ()
main = putStrLn "Test suite not yet implemented"
You can see this content at commercialhaskell/stack-templates in new-template.hsfiles.
I'm not sure where the module ParserSpec where line comes from (as brought up in the Github issue), but it's not part of the template. On my system, stack new bar && cd bar && stack test succeeds.
As to the reason behind the Main module requirement: it comes from the Cabal library, and as I understand it was added due to limitations in other compilers Cabal supports. In other words, GHC could technically allow this code to compile, but Cabal does not pass in those arguments to remain compatible with other compilers.
I'm looking for help regarding how to write tests for Haskell code that is defined in the Main module.
The project I want to test is a cabal package in which I've defined multiple executables.
Each executable code is declared only made of a single file (one for each project euler problem), and I usually run them individually with the cabal run command.
I have tried to write a test, also in the Main module, but when compiling, the function I'm trying to test is not found ("Not in scope" error).
What is the right way to write tests in this case?
For information, below is the directory layout of my project:
pe/ # root
pe.cabal
src/
Util.hs
Problem001.hs # "module Main where" and declares a main function
Problem002.hs # "module Main where" and declares a main function
(...)
test/
TestProblem001.hs # "module Main where" and declares a main function
Below is an extract from pe.cabal:
test-suite test-all
hs-source-dirs: test
type: exitcode-stdio-1.0
main-is: TestProblem001.hs
build-depends: base, HUnit, Cabal >= 1.9.2
executable problem-001
hs-source-dirs: src
main-is: Problem001.hs
build-depends: base
ghc-options: -Wall -Werror -O2
[edit]
As I couldn't find any resource for this exact requirement, I opted for a project architecture which is easier to test: problems are defined as library and not individual executables anymore.
I do so
test1 = ...
test2 = ...
main = do
args <- getArgs
case args of
... -> check test1
... -> check test2
I'm messing around with the plugins package however I bumped into a problem.
Here's the code:
Util/Header.hs
module Util.Header(PT(..)) where
data PT a = PT a deriving Show
Plug.hs
module Plug(helloPlugin) where
import Util.Header
helloPlugin :: PT Int
helloPlugin = PT 1
Main.hs
module Main where
import Util.Header
import System.Plugins
main :: IO ()
main = do
mv <- load "Plug.o" ["."] [] "helloPlugin"
case mv of
LoadFailure msg -> print msg
LoadSuccess _ v -> print $ show (v :: PT Int)
This all works fine then compiling with ghc. Building with Cabal works fine as well, but when I run the executable I get this error:
plugintest: /home/kevin/.cabal/lib/plugins-1.5.4.0/ghc-7.6.3/HSplugins-1.5.4.0.o: unknown symbol `ghczm7zi6zi3_ErrUtils_zdsinsertzuzdsgo5_info'
plugintest: user error (resolvedObjs failed.)
My very minimalistic cabal file:
name: plugintest
version: 0.1.0.0
license-file: LICENSE
build-type: Simple
cabal-version: >=1.8
library
hs-source-dirs: src
exposed-modules: Util.Header
build-depends: base ==4.6.*, plugins ==1.5.*
executable plugintest
main-is: Main.hs
build-depends: base ==4.6.*, plugins ==1.5.*, plugintest == 0.1.0.0
hs-source-dirs: src
Now I assume the problem is that it can't find the "ErrUtils" module which is part of the ghc package installed in /usr/lib/ghc-7.x.x.
Since it's using cabal it'll use the $HOME/.cabal/lib/ instead.
Now I obviously wouldn't want to use /usr/lib if I wanted to make it distributable. Sadly I'm not very familiar with how packages are managed nor am I familiar with the plugins package.
I have a feeling this is extremly nooby but I wasn't able to find a solution myself.
So a few questions:
How can I get my dependencies to work in a way to make this distributable?
It seems I'll need to know beforehand what my Plugin.o files will depend on before actually being able to use them (If I understand correctly).
Is there a way to package a .o files that I wouldn't have to worry about this problem? (Sorry if this question is too vague, feel free to ignore)
Thanks in advance!
Ok, so I had the exact same problem.
Here is a workaround I found
Change the load call to
load "Plug.o" [".","dist/build/plugintest/plugintest-tmp"] [] "testplugin"
Make sure you compile the thing with -c or by using the "make" library from plugins.
Quite annoyed by this... The error suggests it is having issues linking against the standard libs, so why does showing it these .o files fix it?
Anyways, this worked for me, and didn't require a ton of mucking around with .cabal files.
You must declare your exported- and other- modules in order for Cabal to package them all together. For instance (from https://github.com/tel/happstack-heroku-test)
name: hktest -- note the name here names
-- the *library* which is a package name
-- in scope when building the executable
...
library
exposed-modules:
HKTest
other-modules:
-- there aren't any, but there could be some
build-depends: base >= 4.6 && <4.7
...
, mtl >= 2.1.2
hs-source-dirs: src
executable server
main-is: Server.hs
other-modules:
-- there might be some use to having these here,
-- but they'll be harder to get into GHCi, so I wouldn't
-- recommend it---just put them in the library part
build-depends: base >=4.6 && <4.7
, hktest -- note that I grab all the hktest
-- modules here
hs-source-dirs: exe
If I leave out one of those modules I'll likely get a build error as Cabal compiles files which expect to be able to find symbols that haven't been packaged.
In your case, since you're building an executable, the common pattern exemplified above is to put all of your code into a library and then have the executable side depend upon that library. For instance, in this example the complete text of exe/Server.hs is
module Main where
import qualified HKTest as HK
main :: IO ()
main = HK.main
I'm new to Haskell and I'm trying to structure a program under test. I have decided to use HUnit and Cabal.
From what I have seen a well strucutred project looks the following:
src/
AppName/
Appname.hs
testsuite/
tests/
AppName/
TestRunner.hs
AppName.cabal
Setup.hs
The parts that are a mystery to me are the TestRunner.hs and the AppName.cabal.
What would a testrunner look like that runs all of the test under the testsuite/tests directory and sub directories? And how can it be integrated with Cabal?
Also, how do you put the hackage dependencies in the AppName.cabal and build them from the command line?
I am having a hard time finding a full example building an application from scratch with tests and dependencies.
Thanks
Here's a fragment of the .cabal file I used for one of my recent libraries.
...
Library
Build-depends: base >= 4 && < 5, bytestring, directory, filepath, hslogger,
SHA, zlib
Ghc-options: -Wall
Exposed-modules: Ltc.Store
Test-suite reference
Hs-Source-Dirs: Test, .
Main-Is: ReferenceProps.hs
Type: exitcode-stdio-1.0
Build-Depends: base >= 4 && < 5, bytestring, directory, filepath, hslogger,
SHA, zlib
Ghc-Options: -Wall
Build-Depends: test-framework, test-framework-hunit, test-framework-quickcheck2,
HUnit, QuickCheck
As we can see the cabal file defines a library and a testsuite. The library defines the modules it exports, the packages it depends on, and sets some custom GHC options.
We can easily build and package the library for distribution with:
% cabal configure
% cabal build
% cabal sdist
The testsuite looks a lot like the the library: first off, it has the same dependencies as the library (see the first Build-Depends line), and it then adds some extra test dependencies (see the second Build-Depends line). The testsuite here is a combination of HUnit and QuickCheck tests, and it uses Test-Framework as the runner. The test proper is Test/ReferenceProps.hs. It's a exitcode-stdio type test. This means that cabal will say that the tests pass if ReferenceProps exits with code 0. Otherwise, it will say the tests failed.
The testsuite looks like this (but, here, we're going to use some simple tests for list reversals):
import Data.Monoid
import Test.Framework
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2
import Test.HUnit
import Test.QuickCheck
main :: IO ()
main = defaultMainWithOpts
[ testCase "rev" testRev
, testProperty "listRevRevId" propListRevRevId
] mempty
testRev :: Assertion
testRev = reverse [1, 2, 3] #?= [3, 2, 1]
propListRevRevId :: [Int] -> Property
propListRevRevId xs = not (null xs) ==> reverse (reverse xs) == xs
The main is just a harness. You can also set various options for test-framework by replacing the mempty. The function testRev is a HUnit test, and propListRevRevId is a QuickCheck test; see the relevant docs on how to write these.
Finally, we can run the tests:
% cabal configure --enable-tests
% cabal test