@@ -16,9 +16,12 @@ file.
1616module Main (main) where
1717
1818--------------------------------------------------------------------------------
19+import Control.Monad
20+import Data.ByteString (ByteString)
1921import qualified Data.ByteString as BS
2022import Data.Csv hiding (Record, NamedRecord, record)
2123import qualified Data.Vector as V
24+import System.IO.Streams (InputStream, OutputStream)
2225import qualified System.IO.Streams as Streams
2326import qualified System.IO.Streams.Csv as CSV
2427import Test.QuickCheck.Monadic (monadicIO, run, assert)
@@ -53,34 +56,49 @@ header :: Header
5356header = V.fromList ["a", "b", "c"]
5457
5558--------------------------------------------------------------------------------
56−roundTrip :: [Record] -> IO [Record]
57−roundTrip recs = do
58− -- Encode to ByteString.
59− encSource <- Streams.fromList (map NamedRecord recs)
60− (collector, flush) <- Streams.listOutputStream
61− encoder <- CSV.encodeStreamByName header collector
62− Streams.connect encSource encoder
59+-- | Given a list of records generated by QuickCheck, encode those
60+-- records into a CSV ByteString then decode them back into records.
61+roundTrip :: (InputStream ByteString -> IO (InputStream a)) -- ^ Decoder.
62+ -> (OutputStream ByteString -> IO (OutputStream a)) -- ^ Encoder.
63+ -> [a] -- ^ Records.
64+ -> IO [a]
65+roundTrip is os recs = do
66+ -- Encode records to a ByteString.
67+ sourceList <- Streams.fromList recs
68+ (collector, encoded) <- Streams.listOutputStream
69+ encoder <- os collector
70+ Streams.connect sourceList encoder
6371
6472 -- Decode from ByteString.
65− decoder <- fmap BS.concat flush >>=
66− Streams.fromByteString >>=
67− CSV.decodeStreamByName >>=
68− CSV.onlyValidRecords
69−
70− (outRecs, flush') <- Streams.listOutputStream
71− Streams.connect decoder outRecs
72− fmap (map record) flush'
73+ decoder <- fmap BS.concat encoded >>= Streams.fromByteString >>= is
74+ (decodeStream, decoded) <- Streams.listOutputStream
75+ Streams.connect decoder decodeStream
76+ decoded
7377
7478--------------------------------------------------------------------------------
7579prop_namedRoundTrip :: [Record] -> Property
7680prop_namedRoundTrip recsIn = not (null recsIn) ==> monadicIO $ do
77− recsOut <- run $ roundTrip recsIn
78− assert $ recsIn == recsOut
81+ recsOut <- run $ roundTrip is os (map NamedRecord recsIn)
82+ assert $ recsIn == map record recsOut
83+ where
84+ is = CSV.decodeStreamByName >=> CSV.onlyValidRecords
85+ os = CSV.encodeStreamByName header
86+
87+--------------------------------------------------------------------------------
88+prop_indexedRoundTrip :: [Record] -> Property
89+prop_indexedRoundTrip recsIn = not (null recsIn) ==> monadicIO $ do
90+ recsOut <- run $ roundTrip is os recsIn
91+ assert $ recsIn == recsOut
92+ where
93+ is = CSV.decodeStream NoHeader >=> CSV.onlyValidRecords
94+ os = CSV.encodeStream
7995
8096--------------------------------------------------------------------------------
8197tests :: TestTree
82−tests = testGroup "Tests" [ QC.testProperty "namedRoundTrip" $ prop_namedRoundTrip
83− ]
98+tests = testGroup "Tests"
99+ [ QC.testProperty "namedRoundTrip" $ prop_namedRoundTrip
100+ , QC.testProperty "indexedRoundTrip" $ prop_indexedRoundTrip
101+ ]
84102
85103--------------------------------------------------------------------------------
86104main :: IO ()