svfactor-0.1: test/Data/Svfactor/RoundTripsParsePrint.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
module Data.Svfactor.RoundTripsParsePrint (test_Roundtrips) where
import Control.Lens ((&), (.~))
import Data.ByteString (ByteString)
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.UTF8 as UTF8
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Vector as V
import Hedgehog ((===), Property, Gen, forAll, property)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.Tasty (TestName, TestTree, testGroup)
import Test.Tasty.Hedgehog (testProperty)
import Test.Tasty.HUnit ((@?=), testCase)
import Text.Parser.Char (CharParsing)
import Text.Trifecta (parseByteString)
import Data.Svfactor.Parse (trifectaResultToEither)
import Data.Svfactor.Generators
import Data.Svfactor.Syntax (Sv, Headedness, SpacedField, comma)
import Data.Svfactor.Parse (defaultParseOptions, headedness, encodeString, separatedValues)
import Data.Svfactor.Parse.Internal (spacedField)
import Data.Svfactor.Print (defaultPrintOptions, printSvText)
import Data.Svfactor.Print.Internal (printSpaced)
import Data.Svfactor.Text.Space (HorizontalSpace (Space, Tab), Spaces)
test_Roundtrips :: TestTree
test_Roundtrips =
testGroup "Round trips" [
csvRoundTrip
, fieldRoundTrip
]
printAfterParseRoundTrip :: (forall m. CharParsing m => m a) -> (a -> ByteString) -> TestName -> ByteString -> TestTree
printAfterParseRoundTrip parser display name s =
testCase name $
fmap display (trifectaResultToEither $ parseByteString parser mempty s) @?= Right s
fieldRoundTrip :: TestTree
fieldRoundTrip =
let sep = comma
test =
printAfterParseRoundTrip
(spacedField sep UTF8.fromString :: CharParsing m => m (SpacedField ByteString))
(LBS.toStrict . BSB.toLazyByteString . printSpaced defaultPrintOptions)
in testGroup "field" [
test "empty" ""
, test "unquoted" "wobble"
, test "unquoted with space" " wiggle "
, test "single quoted" "'tortoise'"
, test "single quoted with space" " 'turtle' "
, test "single quoted with escape outer" "\'\'\'c\'\'\'"
, test "single quoted with escape in the middle" "\' The char \'\'c\'\' is nice.\'"
, test "double quoted" "\"honey badger\""
, test "double quoted with space" " \" sun bear\" "
, test "double quoted with escape outer" "\"\"\"laser\"\"\""
, test "double quoted with escape in the middle" "\"John \"\"The Duke\"\" Wayne\""
]
csvRoundTrip :: TestTree
csvRoundTrip = testProperty "roundtrip" prop_csvRoundTrip
prop_csvRoundTrip :: Property
prop_csvRoundTrip =
let genSpace :: Gen HorizontalSpace
genSpace = Gen.element [Space, Tab]
genSpaces :: Gen Spaces
genSpaces = V.fromList <$> Gen.list (Range.linear 0 10) genSpace
genText :: Gen Text
genText = Gen.text (Range.linear 1 100) Gen.alphaNum
gen = genSvWithHeadedness (pure comma) genSpaces genText
mkOpts h = defaultParseOptions & headedness .~ h & encodeString .~ Text.pack
parseCsv :: CharParsing m => Headedness -> m (Sv Text)
parseCsv = separatedValues . mkOpts
parse h = parseByteString (parseCsv h) mempty
in property $ do
(c,h) <- forAll gen
trifectaResultToEither (fmap printSvText (parse h (printSvText c))) === pure (printSvText c)