tomland-0.3: test/Test/Toml/Gen.hs
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
-- | This module contains all generators for @tomland@ testing.
module Test.Toml.Gen
( -- * Property
PropertyTest
, prop
-- * Generators
, genVal
, genKey
, genPrefixMap
, genToml
) where
import Control.Applicative (liftA2)
import Control.Monad (forM)
import GHC.Stack (HasCallStack)
import Hedgehog (MonadGen, PropertyT, property)
import Test.Tasty (TestName, TestTree)
import Test.Tasty.Hedgehog (testProperty)
import Toml.PrefixTree (pattern (:||), Key (..), Piece (..), PrefixMap, PrefixTree (..), fromList)
import Toml.Type (AnyValue (..), TOML (..), Value (..))
import qualified Data.HashMap.Strict as HashMap
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
----------------------------------------------------------------------------
-- Property test creator
----------------------------------------------------------------------------
type PropertyTest = [TestTree]
prop :: HasCallStack => TestName -> PropertyT IO () -> [TestTree]
prop testName = pure . testProperty testName . property
----------------------------------------------------------------------------
-- Common generators
----------------------------------------------------------------------------
type V = Int
genVal :: MonadGen m => m V
genVal = Gen.int (Range.constant 0 256)
-- TODO: Arrays and Date.
-- | Generates random value of 'AnyValue' type.
genAnyValue :: MonadGen m => m AnyValue
genAnyValue = do
let randB = Gen.bool
let randI = toInteger <$> Gen.int (Range.constantBounded @Int)
let randD = Gen.double $ Range.constant @Double (-1000000.0) 1000000.0
let randT = Gen.text (Range.constant 0 256) Gen.alphaNum
Gen.choice
[ AnyValue . Bool <$> randB
, AnyValue . Integer <$> randI
, AnyValue . Double <$> randD
, AnyValue . Text <$> randT
]
-- TODO: unicode support
genPiece :: MonadGen m => m Piece
genPiece = Piece <$> Gen.text (Range.constant 1 50) Gen.alphaNum
genKey :: MonadGen m => m Key
genKey = Key <$> Gen.nonEmpty (Range.constant 1 10) genPiece
genKeyAnyValue :: MonadGen m => m (Key, AnyValue)
genKeyAnyValue = liftA2 (,) genKey genAnyValue
genKeyAnyValueList :: MonadGen m => m [(Key, AnyValue)]
genKeyAnyValueList = Gen.list (Range.linear 0 10) genKeyAnyValue
-- Generates key-value pair for PrefixMap
genEntry :: MonadGen m => m (Piece, Key)
genEntry = do
key@(piece :|| _) <- genKey
pure (piece, key)
genPrefixMap :: MonadGen m => m (PrefixMap V)
genPrefixMap = do
entries <- Gen.list (Range.linear 0 10) genEntry
kvps <- forM entries $ \(piece, key) -> do
tree <- genPrefixTree key
pure (piece, tree)
pure $ HashMap.fromList kvps
genPrefixTree :: forall m . MonadGen m => Key -> m (PrefixTree V)
genPrefixTree key = Gen.recursive
-- list picker generator combinator
Gen.choice
-- non-recursive generators
[ Leaf key <$> genVal ]
-- recursive generators
[ genPrefixMap >>= genBranch ]
where
genBranch :: PrefixMap V -> m (PrefixTree V)
genBranch prefMap = do
prefVal <- Gen.maybe genVal
pure $ Branch key prefVal prefMap
genTableHeader :: MonadGen m => m (Key, TOML)
genTableHeader = do
k <- genKey
toml <- makeToml <$> genKeyAnyValueList
pure (k, toml)
where
makeToml :: [(Key, AnyValue)] -> TOML
makeToml kv = TOML (HashMap.fromList kv) mempty
genToml :: MonadGen m => m TOML
genToml = do
kv <- HashMap.fromList <$> genKeyAnyValueList
tables <- Gen.list (Range.linear 0 10) genTableHeader
pure $ TOML kv (fromList tables)