diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -25,3 +25,5 @@
 V 1.3.2: Added testDat to Cabal file to make tests work off the tarball.
 
 V 1.3.2.1: Fixed a hard-coded absolute path in the test-suite.
+
+V. 1.3.3: Added Pileup as new format. Changed all tests to Hspec.
diff --git a/sequence-formats.cabal b/sequence-formats.cabal
--- a/sequence-formats.cabal
+++ b/sequence-formats.cabal
@@ -1,6 +1,6 @@
 cabal-version: >=1.10
 name: sequence-formats
-version: 1.3.2.1
+version: 1.3.3
 license: GPL-3
 license-file: LICENSE
 maintainer: stephan.schiffels@mac.com
@@ -30,6 +30,7 @@
         SequenceFormats.VCF
         SequenceFormats.Eigenstrat
         SequenceFormats.Utils
+        SequenceFormats.Pileup
     hs-source-dirs: src
     default-language: Haskell2010
     build-depends:
@@ -50,15 +51,16 @@
 
 test-suite sequenceFormatTests
     type: exitcode-stdio-1.0
-    main-is: tests.hs
-    hs-source-dirs: src-tests
+    main-is: Spec.hs
+    hs-source-dirs: test
     other-modules:
-        SequenceFormats.Eigenstrat.Test
-        SequenceFormats.Fasta.Test
-        SequenceFormats.FreqSum.Test
-        SequenceFormats.RareAlleleHistogram.Test
-        SequenceFormats.Utils.Test
-        SequenceFormats.VCF.Test
+        SequenceFormats.EigenstratSpec
+        SequenceFormats.FastaSpec
+        SequenceFormats.FreqSumSpec
+        SequenceFormats.RareAlleleHistogramSpec
+        SequenceFormats.UtilsSpec
+        SequenceFormats.VCFSpec
+        SequenceFormats.PileupSpec
     default-language: Haskell2010
     build-depends:
         base >=4.12.0.0,
@@ -71,4 +73,5 @@
         transformers >=0.5.6.2,
         tasty-hunit >=0.10.0.2,
         bytestring >=0.10.8.2,
-        containers >=0.6.0.1
+        containers >=0.6.0.1,
+        hspec >=2.7.1
diff --git a/src-tests/SequenceFormats/Eigenstrat/Test.hs b/src-tests/SequenceFormats/Eigenstrat/Test.hs
deleted file mode 100644
--- a/src-tests/SequenceFormats/Eigenstrat/Test.hs
+++ /dev/null
@@ -1,79 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module SequenceFormats.Eigenstrat.Test (bimReadTest, eigenstratReadTest, eigenstratWriteTest) where
-
-import Control.Foldl (purely, list)
-import Control.Monad.IO.Class (liftIO)
-import Data.Vector (fromList)
-import Pipes (each, runEffect, (>->))
-import qualified Pipes.Prelude as P
-import Pipes.Safe (runSafeT)
-import SequenceFormats.Eigenstrat (readEigenstrat, writeEigenstrat, readBimFile,
-    EigenstratSnpEntry(..), EigenstratIndEntry(..), GenoLine, Sex(..), GenoEntry(..))
-import SequenceFormats.Utils (Chrom(..))
-import Test.Tasty.HUnit (Assertion, assertEqual)
-
-bimReadTest :: Assertion
-bimReadTest = do
-    let esSnpProd = readBimFile "testDat/example.bim"
-    esSnpDat <- runSafeT $ purely P.fold list esSnpProd
-    assertEqual "" testDatEigenstratSnp esSnpDat 
-
-eigenstratReadTest :: Assertion
-eigenstratReadTest = do
-    let esSnpFile = "testDat/example.snp"
-        esIndFile = "testDat/example.ind"
-        esGenoFile = "testDat/example.eigenstratgeno"
-    (indEntries, esProd) <- runSafeT $ readEigenstrat esGenoFile esSnpFile esIndFile
-    assertEqual "eigenstratReadTest_assertIndEntries" testDatEigenstratInd indEntries
-    snpGenoEntries <- runSafeT $ purely P.fold list esProd
-    assertEqual "eigenstratReadTest_assertSnpEntries" testDatEigenstratSnp (map fst snpGenoEntries)
-    assertEqual "eigenstratReadTest_assertGenoEntries" testDatEigenstratGeno
-        (map snd snpGenoEntries)
-
-eigenstratWriteTest :: Assertion
-eigenstratWriteTest = do
-    let tmpGeno = "/tmp/eigenstratWriteTest.geno"
-        tmpSnp = "/tmp/eigenstratWriteTest.snp"
-        tmpInd = "/tmp/eigenstratWriteTest.ind"
-        testDatSnpProd = each testDatEigenstratSnp
-        testDatGenoProd = each testDatEigenstratGeno
-        testDatJointProd = P.zip testDatSnpProd testDatGenoProd
-    liftIO . runSafeT . runEffect $
-        testDatJointProd >-> writeEigenstrat tmpGeno tmpSnp tmpInd testDatEigenstratInd
-    (indEntries, esProd) <- liftIO . runSafeT $ readEigenstrat tmpGeno tmpSnp tmpInd
-    liftIO $ assertEqual "eigenstratWriteTest_assertIndEntries" testDatEigenstratInd indEntries
-    snpGenoEntries <- liftIO . runSafeT $ purely P.fold list esProd
-    liftIO $ assertEqual "eigenstratWriteTest_assertIndEntries" testDatEigenstratSnp
-        (map fst snpGenoEntries)
-    liftIO $ assertEqual "eigenstratWriteTest_assertIndEntries" testDatEigenstratGeno
-        (map snd snpGenoEntries)
-
-testDatEigenstratSnp :: [EigenstratSnpEntry]
-testDatEigenstratSnp = [
-    EigenstratSnpEntry (Chrom "11") 0      0.000000 "rs0000" 'A' 'C',
-    EigenstratSnpEntry (Chrom "11") 100000 0.001000 "rs1111" 'A' 'G',
-    EigenstratSnpEntry (Chrom "11") 200000 0.002000 "rs2222" 'A' 'T',
-    EigenstratSnpEntry (Chrom "11") 300000 0.003000 "rs3333" 'C' 'A',
-    EigenstratSnpEntry (Chrom "11") 400000 0.004000 "rs4444" 'G' 'A',
-    EigenstratSnpEntry (Chrom "11") 500000 0.005000 "rs5555" 'T' 'A',
-    EigenstratSnpEntry (Chrom "11") 600000 0.006000 "rs6666" 'G' 'T']
-
-
-testDatEigenstratInd :: [EigenstratIndEntry]
-testDatEigenstratInd = [
-    EigenstratIndEntry "SAMPLE0" Female "Case",
-    EigenstratIndEntry "SAMPLE1" Male "Case",
-    EigenstratIndEntry "SAMPLE2" Female "Control",
-    EigenstratIndEntry "SAMPLE3" Male "Control",
-    EigenstratIndEntry "SAMPLE4" Female "Control"]
-
-testDatEigenstratGeno :: [GenoLine]
-testDatEigenstratGeno = [
-    fromList [Het, Het, Het, HomAlt, HomAlt],
-    fromList [HomAlt, Het, HomRef, Het, HomRef],
-    fromList [HomRef, Het, Het, HomAlt, Het],
-    fromList [HomAlt, Missing, Het, HomRef, HomRef],
-    fromList [HomRef, Het, Het, HomAlt, HomAlt],
-    fromList [HomAlt, HomAlt, Het, Missing, Het],
-    fromList [HomRef, HomRef, Het, Missing, Missing]]
-
diff --git a/src-tests/SequenceFormats/Fasta/Test.hs b/src-tests/SequenceFormats/Fasta/Test.hs
deleted file mode 100644
--- a/src-tests/SequenceFormats/Fasta/Test.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module SequenceFormats.Fasta.Test (testFastaRead) where
-
-import qualified Data.ByteString.Char8 as BS
-import qualified Pipes.Prelude as P
-import SequenceFormats.Fasta (loadFastaChrom)
-import SequenceFormats.Utils (Chrom(..))
-import System.IO (withFile, IOMode(..))
-import Test.Tasty.HUnit (Assertion, assertEqual)
-
-testFastaRead :: Assertion
-testFastaRead = withFile "testDat/example.fasta" ReadMode $ \h -> do
-    fastaProd <- loadFastaChrom h (Chrom "chr3")
-    fastaString <- BS.unpack . BS.concat <$> P.toListM fastaProd
-    assertEqual "fastaReadTest_assertFastaEntries" testString fastaString
-  where
-    testString = "ACGACGACGACGGGGTTTAAAAAGGGTTTCCTCTCTCTCTGGG"
diff --git a/src-tests/SequenceFormats/FreqSum/Test.hs b/src-tests/SequenceFormats/FreqSum/Test.hs
deleted file mode 100644
--- a/src-tests/SequenceFormats/FreqSum/Test.hs
+++ /dev/null
@@ -1,49 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module SequenceFormats.FreqSum.Test (fsReadTest, fsWriteTest) where
-
-import SequenceFormats.FreqSum (readFreqSumFile, printFreqSumFile, FreqSumEntry(..), 
-    FreqSumHeader(..))
-import SequenceFormats.Utils (Chrom(..))
-
-import Control.Foldl (purely, list)
-import Control.Monad.IO.Class (liftIO)
-import Pipes (each, runEffect, (>->))
-import qualified Pipes.Prelude as P
-import Pipes.Safe (runSafeT)
-import Test.Tasty.HUnit (Assertion, assertEqual)
-
-fsReadTest :: Assertion
-fsReadTest = runSafeT $ do
-    let fsFile = "testDat/example.freqsum"
-    (fsHeader, fsProd) <- readFreqSumFile fsFile
-    liftIO $ assertEqual "fsReadTest_assertIndEntries" testDatFsHeader fsHeader
-    fsEntries <- purely P.fold list fsProd
-    liftIO $ assertEqual "fsReadTest_assertFsEntries" testDatFsEntries fsEntries
-
-fsWriteTest :: Assertion
-fsWriteTest = do
-    let fn = "/tmp/freqSumWriteTest.txt"
-        testDatFsProd = each testDatFsEntries
-    runSafeT . runEffect $ testDatFsProd >-> printFreqSumFile fn testDatFsHeader
-    runSafeT $ do
-        (fsHeader, fsProd) <- readFreqSumFile fn
-        liftIO $ assertEqual "fsWriteTest_assertIndEntries" testDatFsHeader fsHeader
-        fsEntries <- purely P.fold list fsProd
-        liftIO $ assertEqual "fsWriteTest_assertFsEntries" testDatFsEntries fsEntries
-
-testDatFsHeader :: FreqSumHeader 
-testDatFsHeader = FreqSumHeader names numbers
-  where
-    names = ["SAMPLE0", "SAMPLE1", "SAMPLE2", "SAMPLE3", "SAMPLE4"]
-    numbers = [2, 2, 2, 1, 1]
-
-testDatFsEntries :: [FreqSumEntry]
-testDatFsEntries = [
-    FreqSumEntry (Chrom "11") 0      'A' 'C' [Just 1, Just 1,  Just 1, Just 1,  Just 1],
-    FreqSumEntry (Chrom "11") 100000 'A' 'G' [Just 2, Just 1,  Just 0, Just 0,  Just 0],
-    FreqSumEntry (Chrom "11") 200000 'A' 'T' [Just 0, Just 1,  Just 1, Just 1,  Just 1],
-    FreqSumEntry (Chrom "11") 300000 'C' 'A' [Just 2, Nothing, Just 1, Just 0,  Just 0],
-    FreqSumEntry (Chrom "11") 400000 'G' 'A' [Just 0, Just 1,  Just 1, Just 1,  Just 1],
-    FreqSumEntry (Chrom "11") 500000 'T' 'A' [Just 2, Just 2,  Just 1, Nothing, Just 1],
-    FreqSumEntry (Chrom "11") 600000 'G' 'T' [Just 0, Just 0,  Just 1, Nothing, Nothing]]
-
diff --git a/src-tests/SequenceFormats/RareAlleleHistogram/Test.hs b/src-tests/SequenceFormats/RareAlleleHistogram/Test.hs
deleted file mode 100644
--- a/src-tests/SequenceFormats/RareAlleleHistogram/Test.hs
+++ /dev/null
@@ -1,40 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module SequenceFormats.RareAlleleHistogram.Test (testReadHistogram, testWriteHistogram) where
-
-import SequenceFormats.RareAlleleHistogram (RareAlleleHistogram(..), readHistogram, writeHistogramFile)
-
-import Control.Monad.IO.Class (liftIO)
-import qualified Data.Map as Map
-import Test.Tasty.HUnit (Assertion, assertEqual)
-
-testReadHistogram :: Assertion
-testReadHistogram = do
-    hist <- readHistogram "testDat/example.histogram.txt"
-    assertEqual "readHistogramTest" hist testHistogramDat
-
-testWriteHistogram :: Assertion
-testWriteHistogram = do
-    let fn = "/tmp/histogramWriteTest.txt"
-    writeHistogramFile fn testHistogramDat
-    hist <- liftIO  $ readHistogram fn
-    liftIO $ assertEqual "writeHistogramTest" hist testHistogramDat
-
-testHistogramDat :: RareAlleleHistogram
-testHistogramDat = RareAlleleHistogram names nVec 1 10 [] [] 1146826657 counts jnEstimates
-  where
-    names = ["EUR", "SEA", "SIB", "CHK", "SAM"]
-    nVec = [66, 42, 44, 8, 28]
-    counts = Map.fromList [
-        ([1,0,0,0,0], 773148),
-        ([0,1,0,0,0], 527207),
-        ([0,0,1,0,0], 368640),
-        ([0,0,0,0,1], 213918),
-        ([2,0,0,0,0], 158795),
-        ([0,2,0,0,0], 73241)]
-    jnEstimates = Just $ Map.fromList [
-        ([1,0,0,0,0], (6.7e-4, 3.6e-6)),
-        ([0,1,0,0,0], (4.5e-4, 2.5e-6)),
-        ([0,0,1,0,0], (3.2e-4, 2.4e-6)),
-        ([0,0,0,0,1], (1.8e-4, 3.6e-6)),
-        ([2,0,0,0,0], (1.3e-4, 1.4e-6)),
-        ([0,2,0,0,0], (6.3e-5, 7.3e-7))]
diff --git a/src-tests/SequenceFormats/Utils/Test.hs b/src-tests/SequenceFormats/Utils/Test.hs
deleted file mode 100644
--- a/src-tests/SequenceFormats/Utils/Test.hs
+++ /dev/null
@@ -1,12 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module SequenceFormats.Utils.Test (testChromOrder) where
-
-import SequenceFormats.Utils (Chrom(..))
-import Test.Tasty.HUnit (Assertion, assertBool)
-
-testChromOrder :: Assertion
-testChromOrder = do
-    assertBool "testChromSmaller" (Chrom "2" < Chrom "10")
-    assertBool "testChromSmaller2" (Chrom "chr2" < Chrom "10")
-    assertBool "testChromSmaller3" (Chrom "2" < Chrom "chr10")
-    assertBool "testChromSmaller3" (Chrom "chr2" < Chrom "chr10")
diff --git a/src-tests/SequenceFormats/VCF/Test.hs b/src-tests/SequenceFormats/VCF/Test.hs
deleted file mode 100644
--- a/src-tests/SequenceFormats/VCF/Test.hs
+++ /dev/null
@@ -1,61 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module SequenceFormats.VCF.Test (testReadVCF, testGetGenotypes, testGetDosages, testIsTransversionSnp, testIsBiallelicSnp, testVcfToFreqsumEntry) where
-
-import Control.Foldl (list, purely)
-import Control.Monad.IO.Class (liftIO)
-import Pipes.Prelude (fold)
-import Pipes.Safe (runSafeT)
-import SequenceFormats.FreqSum (FreqSumEntry(..))
-import SequenceFormats.Utils (Chrom(..))
-import SequenceFormats.VCF (readVCFfromFile, getGenotypes, getDosages, isTransversionSnp, vcfToFreqSumEntry, isBiallelicSnp, VCFheader(..), VCFentry(..))
-import Test.Tasty.HUnit (Assertion, assertEqual)
-
-testReadVCF :: Assertion
-testReadVCF = runSafeT $ do
-    (vcfH, vcfProd) <- readVCFfromFile "testDat/example.vcf"
-    let vcfHc = vcfHeaderComments vcfH
-    liftIO $ assertEqual "headerLine1" "##fileformat=VCFv4.2" (vcfHc !! 0)
-    liftIO $ assertEqual "headerLine19" "##bcftools_callCommand=call -c -v" (vcfHc !! 18)
-    let n = vcfSampleNames vcfH
-    liftIO $ assertEqual "sample names" ["12880A", "12881A", "12883A", "12884A", "12885A"] n
-    rows <- purely fold list vcfProd
-    liftIO $ assertEqual "vcf1" vcf1 (rows !! 0)
-    liftIO $ assertEqual "vcf7" vcf7 (rows !! 6)
-
-vcf1 :: VCFentry
-vcf1 = VCFentry (Chrom "1") 10492 Nothing "C" ["T"] 15.0302 Nothing ["DP=28", "PV4=1,1,0.30985,1"]
-  ["GT", "PL"] [["0/0", "0,3,37"], ["0/0", "0,6,67"], ["0/1", "51,0,28"], ["0/0", "0,54,255"],
-  ["0/0", "0,9,83"]]
-
-vcf7 :: VCFentry
-vcf7 = VCFentry (Chrom "2") 30923 (Just "rs12345") "G" [] 110.112 Nothing ["DP=5", "FQ=-28.9619"]
-  ["GT", "PL"] [["1/1", "0,0,0"], ["1/1", "0,0,0"], ["1/1", "40,6,0"], ["1/1", "105,9,0"], ["1/1", "0,0,0"]]
-
-testGetGenotypes :: Assertion
-testGetGenotypes = do
-    assertEqual "getGenotypesRight" (Right ["0/0", "0/0", "0/1", "0/0", "0/0"]) (getGenotypes vcf1)
-    assertEqual "getGenotypesLeft" (Left "GT format field not found") (getGenotypes (vcf1 {vcfFormatString=["PL"]}))
-
-testGetDosages :: Assertion
-testGetDosages = do
-    assertEqual "getDosages1" (Right [Just 0, Just 0, Just 1, Just 0, Just 0]) (getDosages vcf1)
-    assertEqual "getDosages2" (Right [Just 0, Just 0, Nothing, Just 0, Just 0])
-        (getDosages (vcf1 {vcfGenotypeInfo=[["0/0", "0,3,37"], ["0/0", "0,6,67"], [".", "51,0,28"], ["0/0", "0,54,255"],
-        ["0/0", "0,9,83"]]}))
-
-testIsTransversionSnp :: Assertion
-testIsTransversionSnp = do
-    assertEqual "testTriAllelic" False (isTransversionSnp "A" ["C", "T"])
-    assertEqual "testTransition1" False (isTransversionSnp "A" ["G"])
-    assertEqual "testTransition2" False (isTransversionSnp "T" ["C"])
-    assertEqual "testTransversion1" True (isTransversionSnp "T" ["A"])
-    assertEqual "testTransversion2" True (isTransversionSnp "C" ["G"])
-
-testVcfToFreqsumEntry :: Assertion
-testVcfToFreqsumEntry =
-    assertEqual "vcfToFreqsumValid" (Right (FreqSumEntry (Chrom "1") 10492 'C' 'T' [Just 0, Just 0, Just 1, Just 0, Just 0])) (vcfToFreqSumEntry vcf1)
-
-testIsBiallelicSnp :: Assertion
-testIsBiallelicSnp = do
-    assertEqual "testTriAllelic" False (isBiallelicSnp "A" ["C", "T"])
-    assertEqual "testBiallelic" True (isBiallelicSnp "A" ["T"])
diff --git a/src-tests/tests.hs b/src-tests/tests.hs
deleted file mode 100644
--- a/src-tests/tests.hs
+++ /dev/null
@@ -1,48 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-import SequenceFormats.RareAlleleHistogram.Test (testWriteHistogram, testReadHistogram)
-import SequenceFormats.Eigenstrat.Test (eigenstratReadTest, bimReadTest, eigenstratWriteTest)
-import SequenceFormats.FreqSum.Test (fsReadTest, fsWriteTest)
-import SequenceFormats.Fasta.Test (testFastaRead)
-import SequenceFormats.Utils.Test (testChromOrder)
-import SequenceFormats.VCF.Test (testReadVCF, testGetGenotypes, testGetDosages, testIsTransversionSnp, testIsBiallelicSnp, testVcfToFreqsumEntry)
-import Test.Tasty (TestTree, testGroup, defaultMain)
-import Test.Tasty.HUnit (testCase)
-
-main :: IO ()
-main = defaultMain allTests
-  where
-    allTests = testGroup "Tests" [eigenstratTests, freqSumTests, fastaTests, rahTests, chromTests,
-        vcfTests]
-
-eigenstratTests :: TestTree
-eigenstratTests = testGroup "Eigenstrat Tests" [
-    testCase "Eigenstrat Read Test" eigenstratReadTest,
-    testCase "Bim Read Test" bimReadTest,
-    testCase "Eigenstrat Write Test" eigenstratWriteTest]
-
-freqSumTests :: TestTree
-freqSumTests = testGroup "FreqSum Tests" [
-    testCase "FreqSum Read Test" fsReadTest,
-    testCase "FreqSum Write Test" fsWriteTest]
-
-fastaTests :: TestTree
-fastaTests = testGroup "Fasta Tests" [testCase "Fasta Read Test" testFastaRead]
-
-rahTests :: TestTree
-rahTests = testGroup "Histogram Tests" [
-  testCase "Histogram Read Test" testReadHistogram,
-  testCase "Histogram Write Test" testWriteHistogram]
-
-chromTests :: TestTree
-chromTests = testGroup "Chrom tests" [
-  testCase "Chromosome ordering tests" testChromOrder]
-
-vcfTests :: TestTree
-vcfTests = testGroup "VCF tests" [
-    testCase "VCF read test" testReadVCF,
-    testCase "VCF getGenotypes" testGetGenotypes,
-    testCase "VCF getDosages" testGetDosages,
-    testCase "VCF isTransitionSnp" testIsTransversionSnp,
-    testCase "VCF isBiallelicSnp" testIsBiallelicSnp,
-    testCase "VCF testVcfToFreqsumEntry" testVcfToFreqsumEntry]
diff --git a/src/SequenceFormats/Eigenstrat.hs b/src/SequenceFormats/Eigenstrat.hs
--- a/src/SequenceFormats/Eigenstrat.hs
+++ b/src/SequenceFormats/Eigenstrat.hs
@@ -10,7 +10,8 @@
     readEigenstrat, writeEigenstrat, writeEigenstratIndFile, writeEigenstratSnp, writeBim, 
     writeEigenstratGeno) where
 
-import SequenceFormats.Utils (consumeProducer, SeqFormatException(..), Chrom(..), readFileProd)
+import SequenceFormats.Utils (consumeProducer, SeqFormatException(..),
+    Chrom(..), readFileProd, word)
 
 import Control.Applicative ((<|>))
 import Control.Exception (throw)
@@ -18,7 +19,6 @@
 import Control.Monad.Catch (MonadThrow)
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import qualified Data.Attoparsec.ByteString.Char8 as A
-import Data.Char (isSpace)
 import Data.Vector (Vector, fromList, toList)
 import qualified Data.ByteString.Char8 as B
 import Pipes (Producer, Pipe, (>->), for, cat, yield, Consumer)
@@ -74,9 +74,6 @@
     void A.endOfLine
     return $ EigenstratSnpEntry (Chrom (B.unpack chrom)) pos geneticPos snpId_ ref alt
     
-word :: A.Parser B.ByteString
-word = A.takeTill isSpace
-
 eigenstratIndParser :: A.Parser EigenstratIndEntry
 eigenstratIndParser = do
     A.skipMany A.space
diff --git a/src/SequenceFormats/Pileup.hs b/src/SequenceFormats/Pileup.hs
new file mode 100644
--- /dev/null
+++ b/src/SequenceFormats/Pileup.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE OverloadedStrings #-}
+module SequenceFormats.Pileup (readPileupFromStdIn, readPileupFromFile, PileupRow(..)) where
+
+import Control.Monad.Catch (MonadThrow)
+import Control.Monad.IO.Class (MonadIO)
+import qualified Data.Attoparsec.ByteString.Char8 as A
+import qualified Data.ByteString.Char8 as B
+import Data.Char (toUpper)
+import Pipes (Producer)
+import qualified Pipes.ByteString as PB
+import Pipes.Safe (MonadSafe)
+
+import SequenceFormats.Utils (Chrom(..), word, readFileProd, consumeProducer)
+
+-- |A datatype to represent a single pileup row for multiple individuals.
+-- The constructor arguments are: Chromosome, Position, Refererence Allelele,
+-- Pileup String per individual
+data PileupRow = PileupRow Chrom Int Char [String] deriving (Eq, Show)
+
+-- |Read a pileup-formatted file from StdIn, for reading from an
+-- external command `samtools mpileup`.
+readPileupFromStdIn :: (MonadIO m, MonadThrow m) => Producer PileupRow m ()
+readPileupFromStdIn = consumeProducer pileupParser PB.stdin
+
+-- |Read pileup from a file.
+readPileupFromFile :: (MonadSafe m) => FilePath -> Producer PileupRow m ()
+readPileupFromFile = consumeProducer pileupParser . readFileProd
+
+pileupParser :: A.Parser PileupRow
+pileupParser = do
+    chrom <- word
+    _ <- A.space
+    pos <- A.decimal
+    _ <- A.space
+    refA <- A.satisfy (A.inClass "ACTGNactgnM")
+     -- for some reason, there is an M in the human reference at
+     -- position 3:60830534 (both in hs37d5 and in hg19)
+    _ <- A.space
+    entries <- parsePileupPerSample refA `A.sepBy1`
+        A.satisfy (\c -> c == ' ' || c == '\t')
+    A.endOfLine
+    let ret = PileupRow (Chrom $ B.unpack chrom) pos refA entries
+    --trace (show ret) $ return ret
+    return ret
+  where
+    parsePileupPerSample refA =
+        processPileupEntry refA <$> A.decimal <* A.space <*> (B.unpack <$> word) <*
+            A.space <* word
+
+processPileupEntry :: Char -> Int -> String -> String
+processPileupEntry refA cov readBaseString =
+    if cov == 0 then "" else go readBaseString
+  where
+    go (x:xs)
+        | (x == '.' || x == ',') = refA : go xs
+        | x `elem` ("ACTGNactgn" :: String) = toUpper x : go xs
+        | x `elem` ("$*" :: String) = go xs
+        | x == '^' = go (drop 1 xs)
+        | (x == '+' || x == '-') =
+            let [(num, rest)] = reads xs in go (drop num rest)
+        | otherwise = error $ "cannot parse read base string: " ++ (x:xs)
+    go [] = []
diff --git a/src/SequenceFormats/Utils.hs b/src/SequenceFormats/Utils.hs
--- a/src/SequenceFormats/Utils.hs
+++ b/src/SequenceFormats/Utils.hs
@@ -5,14 +5,15 @@
 module SequenceFormats.Utils (liftParsingErrors,
                               consumeProducer, readFileProd,
                               SeqFormatException(..),
-                              Chrom(..)) where
+                              Chrom(..), word) where
 
 import Control.Error (readErr)
 import Control.Exception (Exception)
 import Control.Monad.Catch (MonadThrow, throwM)
 import Control.Monad.Trans.Class (lift)
-import qualified Data.ByteString.Char8 as B
 import qualified Data.Attoparsec.ByteString.Char8 as A
+import qualified Data.ByteString.Char8 as B
+import Data.Char (isSpace)
 import Pipes (Producer, next)
 import Pipes.Attoparsec (ParsingError(..), parsed)
 import qualified Pipes.ByteString as PB
@@ -66,3 +67,5 @@
 readFileProd :: (PS.MonadSafe m) => FilePath -> Producer B.ByteString m ()
 readFileProd f = PS.withFile f ReadMode PB.fromHandle
 
+word :: A.Parser B.ByteString
+word = A.takeTill isSpace
diff --git a/src/SequenceFormats/VCF.hs b/src/SequenceFormats/VCF.hs
--- a/src/SequenceFormats/VCF.hs
+++ b/src/SequenceFormats/VCF.hs
@@ -15,7 +15,8 @@
                      vcfToFreqSumEntry,
                      isBiallelicSnp) where
 
-import SequenceFormats.Utils (consumeProducer, Chrom(..), readFileProd, SeqFormatException(..))
+import SequenceFormats.Utils (consumeProducer, Chrom(..),
+    readFileProd, SeqFormatException(..), word)
 import SequenceFormats.FreqSum (FreqSumEntry(..))
 
 import Control.Applicative ((<|>))
@@ -97,7 +98,6 @@
     vcfEntryParserTruncated = VCFentry <$> (Chrom . B.unpack <$> word) <* sp <*> A.decimal <* sp <*> parseId <*
         sp <*> word <* sp <*> parseAlternativeAlleles <* sp <*> A.double <* sp <*> parseFilter <*
         sp <*> parseInfoFields <*> pure [] <*> pure [] <* A.endOfLine
-    word = A.takeTill isSpace
     sp = A.satisfy (\c -> c == ' ' || c == '\t')
     parseId = (parseDot *> pure Nothing) <|> (Just <$> word)
     parseDot = A.char '.'
diff --git a/test/SequenceFormats/EigenstratSpec.hs b/test/SequenceFormats/EigenstratSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SequenceFormats/EigenstratSpec.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE OverloadedStrings #-}
+module SequenceFormats.EigenstratSpec (spec) where
+
+import Control.Foldl (purely, list)
+import Control.Monad.IO.Class (liftIO)
+import Data.Vector (fromList)
+import Pipes (each, runEffect, (>->))
+import qualified Pipes.Prelude as P
+import Pipes.Safe (runSafeT)
+import SequenceFormats.Eigenstrat (readEigenstrat, writeEigenstrat, readBimFile,
+    EigenstratSnpEntry(..), EigenstratIndEntry(..), GenoLine, Sex(..), GenoEntry(..))
+import SequenceFormats.Utils (Chrom(..))
+import Test.Hspec
+
+spec :: Spec
+spec = do
+    testReadBimFile
+    testReadEigenstrat
+    testWriteEigenstrat
+
+mockDatEigenstratSnp :: [EigenstratSnpEntry]
+mockDatEigenstratSnp = [
+    EigenstratSnpEntry (Chrom "11") 0      0.000000 "rs0000" 'A' 'C',
+    EigenstratSnpEntry (Chrom "11") 100000 0.001000 "rs1111" 'A' 'G',
+    EigenstratSnpEntry (Chrom "11") 200000 0.002000 "rs2222" 'A' 'T',
+    EigenstratSnpEntry (Chrom "11") 300000 0.003000 "rs3333" 'C' 'A',
+    EigenstratSnpEntry (Chrom "11") 400000 0.004000 "rs4444" 'G' 'A',
+    EigenstratSnpEntry (Chrom "11") 500000 0.005000 "rs5555" 'T' 'A',
+    EigenstratSnpEntry (Chrom "11") 600000 0.006000 "rs6666" 'G' 'T']
+
+testReadBimFile :: Spec
+testReadBimFile = describe "readBimFile" $
+    it "should read a BIM file correctly" $ do
+        let esSnpProd = readBimFile "testDat/example.bim"
+        (runSafeT $ purely P.fold list esSnpProd) `shouldReturn` mockDatEigenstratSnp
+
+mockDatEigenstratInd :: [EigenstratIndEntry]
+mockDatEigenstratInd = [
+    EigenstratIndEntry "SAMPLE0" Female "Case",
+    EigenstratIndEntry "SAMPLE1" Male "Case",
+    EigenstratIndEntry "SAMPLE2" Female "Control",
+    EigenstratIndEntry "SAMPLE3" Male "Control",
+    EigenstratIndEntry "SAMPLE4" Female "Control"]
+
+mockDatEigenstratGeno :: [GenoLine]
+mockDatEigenstratGeno = [
+    fromList [Het, Het, Het, HomAlt, HomAlt],
+    fromList [HomAlt, Het, HomRef, Het, HomRef],
+    fromList [HomRef, Het, Het, HomAlt, Het],
+    fromList [HomAlt, Missing, Het, HomRef, HomRef],
+    fromList [HomRef, Het, Het, HomAlt, HomAlt],
+    fromList [HomAlt, HomAlt, Het, Missing, Het],
+    fromList [HomRef, HomRef, Het, Missing, Missing]]
+    
+testReadEigenstrat :: Spec
+testReadEigenstrat = describe "readEigenstrat" $ do
+    it "should read the correct eigenstrat file" $ do
+        let esSnpFile = "testDat/example.snp"
+            esIndFile = "testDat/example.ind"
+            esGenoFile = "testDat/example.eigenstratgeno"
+        (indEntries, esProd) <- runSafeT $ readEigenstrat esGenoFile esSnpFile esIndFile
+        indEntries `shouldBe` mockDatEigenstratInd 
+        snpGenoEntries <- runSafeT $ purely P.fold list esProd
+        (map fst snpGenoEntries) `shouldBe` mockDatEigenstratSnp 
+        (map snd snpGenoEntries) `shouldBe` mockDatEigenstratGeno
+
+testWriteEigenstrat :: Spec
+testWriteEigenstrat = describe "writeEigenstrat" $ do
+    it "should write and read back eigenstrat data correctly" $ do
+        let tmpGeno = "/tmp/eigenstratWriteTest.geno"
+            tmpSnp = "/tmp/eigenstratWriteTest.snp"
+            tmpInd = "/tmp/eigenstratWriteTest.ind"
+            testDatSnpProd = each mockDatEigenstratSnp
+            testDatGenoProd = each mockDatEigenstratGeno
+            testDatJointProd = P.zip testDatSnpProd testDatGenoProd
+        liftIO . runSafeT . runEffect $
+            testDatJointProd >-> writeEigenstrat tmpGeno tmpSnp tmpInd mockDatEigenstratInd
+        (indEntries, esProd) <- liftIO . runSafeT $ readEigenstrat tmpGeno tmpSnp tmpInd
+        indEntries `shouldBe` mockDatEigenstratInd 
+        snpGenoEntries <- liftIO . runSafeT $ purely P.fold list esProd
+        (map fst snpGenoEntries) `shouldBe` mockDatEigenstratSnp
diff --git a/test/SequenceFormats/FastaSpec.hs b/test/SequenceFormats/FastaSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SequenceFormats/FastaSpec.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE OverloadedStrings #-}
+module SequenceFormats.FastaSpec (spec) where
+
+import qualified Data.ByteString.Char8 as BS
+import qualified Pipes.Prelude as P
+import SequenceFormats.Fasta (loadFastaChrom)
+import SequenceFormats.Utils (Chrom(..))
+import System.IO (withFile, IOMode(..))
+import Test.Hspec
+
+spec :: Spec
+spec = do
+    testLoadFastaChrom
+
+testLoadFastaChrom :: Spec
+testLoadFastaChrom = around (withFile "testDat/example.fasta" ReadMode) $
+    describe "loadFastaChrom" $
+        it "should read the correct fasta string from file" $ \h -> do
+            fastaProd <- loadFastaChrom h (Chrom "chr3")
+            fastaString <- BS.unpack . BS.concat <$> P.toListM fastaProd
+            fastaString `shouldBe` testString
+  where
+    testString = "ACGACGACGACGGGGTTTAAAAAGGGTTTCCTCTCTCTCTGGG"
diff --git a/test/SequenceFormats/FreqSumSpec.hs b/test/SequenceFormats/FreqSumSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SequenceFormats/FreqSumSpec.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE OverloadedStrings #-}
+module SequenceFormats.FreqSumSpec (spec) where
+
+import SequenceFormats.FreqSum (readFreqSumFile, printFreqSumFile, FreqSumEntry(..), 
+    FreqSumHeader(..))
+import SequenceFormats.Utils (Chrom(..))
+
+import Control.Foldl (purely, list)
+import Pipes (each, runEffect, (>->))
+import qualified Pipes.Prelude as P
+import Pipes.Safe (runSafeT)
+import Test.Hspec
+
+spec :: Spec
+spec = do
+    testReadFreqSumFile
+    testPrintFreqSumFile
+
+testReadFreqSumFile :: Spec
+testReadFreqSumFile = describe "readFreqSumFile" $ do
+    (fsHeader, fsEntries) <- runIO . runSafeT $ do
+        (fsHeader_, fsProd_) <- readFreqSumFile "testDat/example.freqsum"
+        fsEntries_ <- purely P.fold list fsProd_
+        return (fsHeader_, fsEntries_)
+    it "should read the correct fs header" $
+        fsHeader `shouldBe` mockDatFsHeader 
+    it "should read the correct fs entries" $
+        fsEntries `shouldBe` mockDatFsEntries 
+
+testPrintFreqSumFile :: Spec
+testPrintFreqSumFile = describe "printFreqSumFile" $ do
+    let fn = "/tmp/freqSumWriteTest.txt"
+        testDatFsProd = each mockDatFsEntries
+    runIO . runSafeT . runEffect $ testDatFsProd >-> printFreqSumFile fn mockDatFsHeader
+    (fsHeader, fsEntries) <- runIO . runSafeT $ do
+        (fsHeader_, fsProd_) <- readFreqSumFile fn
+        fsEntries_ <- purely P.fold list fsProd_
+        return (fsHeader_, fsEntries_)
+    it "should read the correct fs header after writing" $
+        fsHeader `shouldBe` mockDatFsHeader 
+    it "should read the correct fs entries after writing" $
+        fsEntries `shouldBe` mockDatFsEntries 
+
+mockDatFsHeader :: FreqSumHeader 
+mockDatFsHeader = FreqSumHeader names numbers
+  where
+    names = ["SAMPLE0", "SAMPLE1", "SAMPLE2", "SAMPLE3", "SAMPLE4"]
+    numbers = [2, 2, 2, 1, 1]
+
+mockDatFsEntries :: [FreqSumEntry]
+mockDatFsEntries = [
+    FreqSumEntry (Chrom "11") 0      'A' 'C' [Just 1, Just 1,  Just 1, Just 1,  Just 1],
+    FreqSumEntry (Chrom "11") 100000 'A' 'G' [Just 2, Just 1,  Just 0, Just 0,  Just 0],
+    FreqSumEntry (Chrom "11") 200000 'A' 'T' [Just 0, Just 1,  Just 1, Just 1,  Just 1],
+    FreqSumEntry (Chrom "11") 300000 'C' 'A' [Just 2, Nothing, Just 1, Just 0,  Just 0],
+    FreqSumEntry (Chrom "11") 400000 'G' 'A' [Just 0, Just 1,  Just 1, Just 1,  Just 1],
+    FreqSumEntry (Chrom "11") 500000 'T' 'A' [Just 2, Just 2,  Just 1, Nothing, Just 1],
+    FreqSumEntry (Chrom "11") 600000 'G' 'T' [Just 0, Just 0,  Just 1, Nothing, Nothing]]
+
diff --git a/test/SequenceFormats/PileupSpec.hs b/test/SequenceFormats/PileupSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SequenceFormats/PileupSpec.hs
@@ -0,0 +1,24 @@
+module SequenceFormats.PileupSpec (spec) where
+
+import SequenceFormats.Pileup (readPileupFromFile, PileupRow(..))
+import SequenceFormats.Utils (Chrom(..))
+
+import Control.Foldl (purely, list)
+import qualified Pipes.Prelude as P
+import Pipes.Safe (runSafeT)
+import Test.Hspec
+
+spec :: Spec
+spec = testReadPileupFromFile
+
+testReadPileupFromFile :: Spec
+testReadPileupFromFile = describe "readPileupFromFile" $
+    it "should read correct data from file" $ do
+        let pProd = readPileupFromFile "testDat/example.pileup"
+        runSafeT (purely P.fold list pProd) `shouldReturn` mockDatPentries
+
+mockDatPentries :: [PileupRow]
+mockDatPentries = [
+    PileupRow (Chrom "1") 1000 'A' ["ACCT", "AAA", "ACCAACC"],
+    PileupRow (Chrom "1") 2000 'C' ["GGCA", "ACT", "ACCAACC"],
+    PileupRow (Chrom "2") 1000 'G' ["AGCT", "AAA", "ACCAACC"]]
diff --git a/test/SequenceFormats/RareAlleleHistogramSpec.hs b/test/SequenceFormats/RareAlleleHistogramSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SequenceFormats/RareAlleleHistogramSpec.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE OverloadedStrings #-}
+module SequenceFormats.RareAlleleHistogramSpec (spec) where
+
+import SequenceFormats.RareAlleleHistogram (RareAlleleHistogram(..), readHistogram, writeHistogramFile)
+
+import qualified Data.Map as Map
+import Test.Hspec
+
+spec :: Spec
+spec = do
+    testReadHistogram
+    testWriteHistogramFile
+
+testReadHistogram :: Spec
+testReadHistogram = describe "readHistogram" $
+    it "should read correct data" $
+        readHistogram "testDat/example.histogram.txt" `shouldReturn` mockHistogramDat
+
+testWriteHistogramFile :: Spec
+testWriteHistogramFile = describe "writeHistogramFile" $
+    it "should read the correct histogram after writing" $ do
+        let fn = "/tmp/histogramWriteTest.txt"
+        writeHistogramFile fn mockHistogramDat
+        readHistogram fn `shouldReturn`mockHistogramDat
+
+mockHistogramDat :: RareAlleleHistogram
+mockHistogramDat = RareAlleleHistogram names nVec 1 10 [] [] 1146826657 counts jnEstimates
+  where
+    names = ["EUR", "SEA", "SIB", "CHK", "SAM"]
+    nVec = [66, 42, 44, 8, 28]
+    counts = Map.fromList [
+        ([1,0,0,0,0], 773148),
+        ([0,1,0,0,0], 527207),
+        ([0,0,1,0,0], 368640),
+        ([0,0,0,0,1], 213918),
+        ([2,0,0,0,0], 158795),
+        ([0,2,0,0,0], 73241)]
+    jnEstimates = Just $ Map.fromList [
+        ([1,0,0,0,0], (6.7e-4, 3.6e-6)),
+        ([0,1,0,0,0], (4.5e-4, 2.5e-6)),
+        ([0,0,1,0,0], (3.2e-4, 2.4e-6)),
+        ([0,0,0,0,1], (1.8e-4, 3.6e-6)),
+        ([2,0,0,0,0], (1.3e-4, 1.4e-6)),
+        ([0,2,0,0,0], (6.3e-5, 7.3e-7))]
diff --git a/test/SequenceFormats/UtilsSpec.hs b/test/SequenceFormats/UtilsSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SequenceFormats/UtilsSpec.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE OverloadedStrings #-}
+module SequenceFormats.UtilsSpec (spec) where
+
+import SequenceFormats.Utils (Chrom(..))
+
+import Test.Hspec
+
+spec :: Spec
+spec = testChrom
+
+testChrom :: Spec
+testChrom = describe "Chrom" $ do
+    specify "2 should be smaller than 10" $
+        Chrom "2" < Chrom "10" `shouldBe` True
+    specify "chr2 should be smaller than 10" $
+        Chrom "chr2" < Chrom "10" `shouldBe` True
+    specify "2 should be smaller than chr10" $
+        Chrom "2" < Chrom "chr10" `shouldBe` True
+    specify "chr2 should be smaller than chr10" $
+        Chrom "chr2" < Chrom "chr10" `shouldBe` True
diff --git a/test/SequenceFormats/VCFSpec.hs b/test/SequenceFormats/VCFSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/SequenceFormats/VCFSpec.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE OverloadedStrings #-}
+module SequenceFormats.VCFSpec (spec) where
+
+import Control.Foldl (list, purely)
+import Pipes.Prelude (fold)
+import Pipes.Safe (runSafeT)
+import SequenceFormats.FreqSum (FreqSumEntry(..))
+import SequenceFormats.Utils (Chrom(..))
+import SequenceFormats.VCF (readVCFfromFile, getGenotypes, getDosages,
+    isTransversionSnp, vcfToFreqSumEntry, isBiallelicSnp, VCFheader(..), VCFentry(..))
+import Test.Hspec
+
+spec :: Spec
+spec = do
+    testReadVCFfromFile
+    testGetGenotypes
+    testGetDosages
+    testIsTransversionSnp
+    testVcfToFreqsumEntry
+    testIsBiallelicSnp
+
+testReadVCFfromFile :: Spec
+testReadVCFfromFile = describe "readVCFfromFile" $ do
+    (vcfH, vcfRows) <- runIO . runSafeT $ do
+        (vcfH_, vcfProd_) <- readVCFfromFile "testDat/example.vcf"
+        vcfRows_ <- purely fold list vcfProd_
+        return (vcfH_, vcfRows_)
+    let vcfHc = vcfHeaderComments vcfH
+    it "reads the correct header lines" $ do
+        vcfHc !! 0 `shouldBe` "##fileformat=VCFv4.2"
+        vcfHc !! 18 `shouldBe` "##bcftools_callCommand=call -c -v"
+    it "reads the correct sample names" $
+        vcfSampleNames vcfH `shouldBe` ["12880A", "12881A", "12883A", "12884A", "12885A"] 
+    it "reads the correct vcf genotype rows" $ do
+        vcfRows !! 0 `shouldBe` vcf1
+        vcfRows !! 6 `shouldBe` vcf7
+
+vcf1 :: VCFentry
+vcf1 = VCFentry (Chrom "1") 10492 Nothing "C" ["T"] 15.0302 Nothing ["DP=28", "PV4=1,1,0.30985,1"]
+  ["GT", "PL"] [["0/0", "0,3,37"], ["0/0", "0,6,67"], ["0/1", "51,0,28"], ["0/0", "0,54,255"],
+  ["0/0", "0,9,83"]]
+
+vcf7 :: VCFentry
+vcf7 = VCFentry (Chrom "2") 30923 (Just "rs12345") "G" [] 110.112 Nothing ["DP=5", "FQ=-28.9619"]
+  ["GT", "PL"] [["1/1", "0,0,0"], ["1/1", "0,0,0"], ["1/1", "40,6,0"], ["1/1", "105,9,0"], ["1/1", "0,0,0"]]
+
+testGetGenotypes :: Spec
+testGetGenotypes = describe "getGenotypes" $ do
+    it "should successfully read genotypes if GT format field is there" $
+        getGenotypes vcf1 `shouldBe` Right ["0/0", "0/0", "0/1", "0/0", "0/0"]
+    it "should yield Left err if GT format field isn't found" $
+        getGenotypes (vcf1 {vcfFormatString=["PL"]}) `shouldBe` Left "GT format field not found"
+
+testGetDosages :: Spec
+testGetDosages = describe "getDosages" $ do
+    it "should read correct dosages" $ do
+        getDosages vcf1 `shouldBe` Right [Just 0, Just 0, Just 1, Just 0, Just 0]
+        let vcf1' = vcf1 {vcfGenotypeInfo=[
+                ["0/0", "0,3,37"], ["0/0", "0,6,67"], [".", "51,0,28"], ["0/0", "0,54,255"],
+                ["0/0", "0,9,83"]]}
+        getDosages vcf1' `shouldBe` Right [Just 0, Just 0, Nothing, Just 0, Just 0]
+
+testIsTransversionSnp :: Spec
+testIsTransversionSnp = describe "isTransversionSnp" $ do
+    it "should reject triAllelic SNPs" $
+        isTransversionSnp "A" ["C", "T"] `shouldBe` False
+    it "should reject transitions" $ do
+        isTransversionSnp "A" ["G"] `shouldBe` False
+        isTransversionSnp "T" ["C"] `shouldBe` False
+    it "should accept transversions" $ do
+        isTransversionSnp "T" ["A"] `shouldBe` True
+        isTransversionSnp "C" ["G"] `shouldBe` True
+
+testVcfToFreqsumEntry :: Spec
+testVcfToFreqsumEntry = describe "vcfToFreqsumEntry" $
+    it "should convert correctly" $ do
+        let r = Right (FreqSumEntry (Chrom "1") 10492 'C' 'T' [Just 0, Just 0, Just 1, Just 0, Just 0])
+        vcfToFreqSumEntry vcf1 `shouldBe` r
+
+testIsBiallelicSnp :: Spec
+testIsBiallelicSnp = describe "isBiallelicSnp" $ do
+    it "should reject triAllelic" $
+        isBiallelicSnp "A" ["C", "T"] `shouldBe` False
+    it "should accept biallelic" $
+        isBiallelicSnp "A" ["T"] `shouldBe` True
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
