packages feed

vformat-0.9.0.0: test/ArgFmtSpec.hs

module ArgFmtSpec ( spec ) where


import           Data.Maybe
import           Test.Hspec
import           Test.Hspec.QuickCheck
import           Test.QuickCheck

import           Text.Format


spec :: Spec
spec = describe "read" $ do
  prop "all" $ \(TestArgFmt (cs, fmt)) -> read cs == fmt


newtype TestArgFmt = TestArgFmt (String, ArgFmt) deriving Show

instance Arbitrary TestArgFmt where
  arbitrary = do
      (pad, align) <- genAlign
      sign <- genSign
      alternate <- genAlternate
      signAware <- genSignAware
      width <- genWidth
      sep <- genNumSep
      precision <- genPrecision
      specs <- genSpecs

      let cs1 = [x | (Just x) <- [ fst pad, fst align, fst sign, fst alternate
                                 , fst signAware
                                 ]
                ]
          cs2 = fromMaybe "" $ fst width
          cs3 = fromMaybe "" $ sequence [fst sep]
          cs4 = fromMaybe "" $ fst precision
          cs = concat [cs1, cs2, cs3, cs4, specs]
          fmt = ArgFmt (snd align) (snd pad) (snd sign) (snd alternate)
                       (snd signAware) (snd width) (snd sep) (snd precision)
                       specs
      return $ TestArgFmt (cs, fmt)
    where
      genAlign :: Gen ((Maybe Char, Char), (Maybe Char, FmtAlign))
      genAlign = do
        align <- elements [ (Nothing, AlignNone), (Just '<', AlignLeft)
                          , (Just '>', AlignRight), (Just '^', AlignCenter)
                          , (Just '=', AlignSign)
                          ]
        c <- maybe (return Nothing) (\_ -> arbitrary) $ fst align
        return (maybe (Nothing, ' ') (\c -> (Just c, c)) c, align)

      genPad :: Gen (Maybe Char, Char)
      genPad = do

        oneof $ [ return (Nothing, ' ')
                       , (\c -> (Just c, c)) <$> arbitrary
                       ]

      genSign :: Gen (Maybe Char, FmtSign)
      genSign = elements [ (Nothing, SignNone), (Just '+', SignPlus)
                         , (Just '-', SignMinus), (Just ' ', SignSpace)
                         ]

      genAlternate :: Gen (Maybe Char, Bool)
      genAlternate = elements [(Nothing, False), (Just '#', True)]

      genSignAware :: Gen (Maybe Char, Bool)
      genSignAware = elements [(Nothing, False), (Just '0', True)]

      genWidth :: Gen (Maybe String, Either Int ArgKey)
      genWidth = oneof [genEmptyWP, genLeftWP False, genRightWP False]

      genNumSep :: Gen (Maybe Char, FmtNumSep)
      genNumSep = elements [ (Nothing, NumSepNone), (Just '_', NumSepDash)
                           , (Just ',', NumSepComma)
                           ]

      genPrecision :: Gen (Maybe String, Either Int ArgKey)
      genPrecision = oneof [genEmptyWP, genLeftWP True, genRightWP True]

      genEmptyWP :: Gen (Maybe String, Either Int ArgKey)
      genEmptyWP = return (Nothing, Left (-1))

      genLeftWP :: Bool -> Gen (Maybe String, Either Int ArgKey)
      genLeftWP dot = do
        Positive x <- arbitrary
        let cs = if dot then ('.' : show x) else show x
        return (Just cs, Left x)

      genRightWP :: Bool -> Gen (Maybe String, Either Int ArgKey)
      genRightWP dot = do
        cs <- ('x' :) <$> suchThat arbitrary (all (not . (flip elem "!{}")))
        let cs1 = (if dot then ".{" else "{") ++ cs ++ "}"
        return (Just cs1, Right $ read cs)

      genSpecs :: Gen String
      genSpecs = elements [ "", "s"
                          , "b", "c", "d", "o", "x", "X"
                          , "e", "E", "f", "F", "g", "G", "%"
                          , "%Y-%m-%d"
                          ]