pipes-csv-1.3.0: test/test-pipes-csv.hs
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-unused-do-bind -fno-warn-orphans -fno-warn-unused-imports -fno-warn-unused-binds #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (
main
) where
import Test.Framework as TF
import Test.Framework.Providers.HUnit (testCase)
import Test.HUnit
import Control.Applicative
import Control.Monad
import qualified Data.ByteString.Char8 as C
import qualified Data.Vector as DV
import Data.Csv ((.:), (.!), FromNamedRecord(..), FromRecord(..),
Record, HasHeader(..))
import Pipes.ByteString (stdin, ByteString)
import Pipes.Csv (decode, decodeByName)
import Pipes
import qualified Pipes.Prelude as P
-- helper functions
right :: (Monad m) => Pipe (Either a b) b m r
right = loop
where
loop = await >>= \s -> case s of
Left _ -> loop
Right v -> yield v >> loop
-- decode to vector
decoder :: Monad m
=> Producer ByteString m ()
-> Producer (Either String Record) m ()
decoder = decode NoHeader
decoderStream =
[ ""
, "1"
, "\n"
, "1,2\n"
, "1,2,3\n"
, "\n" -- ignores empty line
, "no line end needed on last chunk"
]
decoderResult =
[
["1"]
, ["1","2"]
, ["1","2","3"]
, ["no line end needed on last chunk"]
]
testDecoder :: Assertion
testDecoder =
map DV.fromList decoderResult @=?
P.toList (decoder (each $ map C.pack decoderStream) >-> right)
ioDecoder = runEffect $
for (decoder (each $ map C.pack decoderStream)) (lift . print)
-- decode to Int tuple
decoderTuple :: Monad m
=> Producer ByteString m ()
-> Producer (Either String (Int, Int)) m ()
decoderTuple = decode NoHeader
decoderTupleStream =
[ ""
, "1,2"
, "\n"
, "1,2\n"
, "2,3\n"
, "\n" -- ignores empty line
, "one,two\n"
, "no line end needed on last chunk\n"
, "0,0"
]
decoderTupleResult =
[ Right (1,2)
, Right (1,2)
, Right (2,3)
, Left "expected Int, got \"one\" (Failed reading: takeWhile1)"
, Left "cannot unpack array of length 1 into a pair. Input record: fromList [\"no line end needed on last chunk\"]"
, Right (0,0)
]
testDecoderTuple :: Assertion
testDecoderTuple =
decoderTupleResult @=?
P.toList (decoderTuple (each $ map C.pack decoderTupleStream))
ioDecoderTuple = runEffect $
for (decoderTuple (each $ map C.pack decoderTupleStream)) (lift . print)
-- (unnamed) Person Record
data Person = Person String Int
deriving (Show, Eq)
instance FromRecord Person where
parseRecord v =
Person <$> v .! 0
<*> v .! 1
decoderPerson :: Monad m
=> Producer ByteString m ()
-> Producer (Either String Person) m ()
decoderPerson = decode NoHeader
decoderPersonStream =
[ ""
, "Jack,21\nJill,55\n"
, "Jimbo,21,beyond this is discarded,and this\n"
, "Bill,1\n"
, "Bill"
, "y"
, ","
, "2"
, "\n"
, "Sue,22abc\n" -- consumes 22 and discards "abc"
-- , "zz\n" -- throws an exception
, "no space allowed, 99\n"
, "eof,99"
]
decoderPersonResult =
[ Right (Person "Jack" 21)
, Right (Person "Jill" 55)
, Right (Person "Jimbo" 21)
, Right (Person "Bill" 1)
, Right (Person "Billy" 2)
, Right (Person "Sue" 22)
, Left "expected Int, got \" 99\" (Failed reading: takeWhile1)"
, Right (Person "eof" 99)
]
testDecoderPerson :: Assertion
testDecoderPerson =
decoderPersonResult @=?
P.toList (decoderPerson (each $ map C.pack decoderPersonStream))
ioDecoderPerson = runEffect $
for (decoderPerson (each $ map C.pack decoderPersonStream)) (lift . print)
-- data record version of Person
data Person' = Person' {
name :: String
, age :: Int
} deriving (Show)
instance FromRecord Person' where
parseRecord v =
Person' <$> v .! 0
<*> v .! 1
decoderPerson' :: Monad m
=> Producer ByteString m ()
-> Producer (Either String Person') m ()
decoderPerson' = decode NoHeader
ioDecoderPerson' = runEffect $
for (decoderPerson' (each $ map C.pack decoderPersonStream)) (lift . print)
decoderPerson'Result = [21,55,21,1,2,22,99]
testDecoderPerson' :: Assertion
testDecoderPerson' =
decoderPerson'Result @=?
P.toList (decoderPerson' (each $ map C.pack decoderPersonStream)
>-> right >-> P.map age)
-- named person record
instance FromNamedRecord Person where
parseNamedRecord p =
Person <$> p .: "name"
<*> p .: "age"
decoderNamedPerson :: Monad m
=> Producer ByteString m ()
-> Producer (Either String Person) m ()
decoderNamedPerson = decodeByName
decoderNamedPersonStream = "Name,Age\n" : decoderPersonStream
ioDecoderNamedPerson = runEffect $
for (decoderNamedPerson (each $ map C.pack decoderNamedPersonStream))
(lift . print)
-- test frame
main :: IO ()
main = defaultMain tests
tests :: [TF.Test]
tests =
[
testGroup "Pipes.Csv." decodeTests
]
decodeTests = [ testCase "HUnit" testDecoder
, testCase "HUnit" testDecoderTuple
, testCase "HUnit" testDecoderPerson
, testCase "HUnit" testDecoderPerson'
]