opml-conduit-0.2.0.0: Text/OPML/Arbitrary.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
-- | External 'Arbitrary' instances used by OPML types.
-- All instances are defined through the 'OpmlGen' wrapper to avoid conflicts.
module Text.OPML.Arbitrary where
-- {{{ Imports
import Data.Char
import Data.List.NonEmpty
import Data.Maybe
import Data.MonoTraversable (Element)
import Data.NonNull
import Data.Sequences (SemiSequence)
import Data.Text (Text, find)
import Data.Time.Clock
import Data.Version
import GHC.Generics
import Network.URI
import Test.QuickCheck
import Test.QuickCheck.Instances ()
-- }}}
newtype OpmlGen a = OpmlGen { unwrap :: a }
deriving instance (Generic a) => Generic (OpmlGen a)
instance (Arbitrary (OpmlGen a)) => Arbitrary (OpmlGen (Maybe a)) where
arbitrary = do
a <- arbitrary :: Gen (Maybe ())
(OpmlGen result) <- arbitrary
return . OpmlGen $ maybe Nothing (const $ Just result) a
-- | OPML version may only be @1.0@, @1.1@ or @2.0@
instance Arbitrary (OpmlGen Version) where
arbitrary = OpmlGen <$> (Version <$> elements [ [1, 0], [1, 1], [2, 0] ] <*> pure [])
shrink (OpmlGen (Version a b)) = OpmlGen <$> (Version <$> shrink a <*> shrink b)
-- | Reasonable enough 'URI' generator.
instance Arbitrary (OpmlGen URI) where
arbitrary = OpmlGen <$> (URI <$> genUriScheme <*> (unwrap <$> arbitrary) <*> genUriPath <*> genUriQuery <*> genUriFragment)
where genUriPath = ("/" ++) <$> listOf1 genAlphaNum
genUriQuery = oneof [return "", ("?" ++) <$> listOf1 genAlphaNum]
genUriFragment = oneof [return "", ("#" ++) <$> listOf1 genAlphaNum]
genUriScheme = (++ ":") <$> listOf1 (choose('a', 'z'))
-- shrink = genericShrink
-- | Reasonable enough 'URIAuth' generator.
instance Arbitrary (OpmlGen URIAuth) where
arbitrary = do
userInfo <- oneof [return "", (++ "@") <$> listOf1 genAlphaNum]
regName <- listOf1 genAlphaNum
port <- oneof [return "", (\x -> ":" ++ x) . show <$> choose(1 :: Int, 65535)]
return . OpmlGen $ URIAuth userInfo regName port
-- shrink = genericShrink
-- | Generates 'UTCTime' with rounded seconds.
instance Arbitrary (OpmlGen UTCTime) where
arbitrary = do
(UTCTime d s) <- arbitrary
return . OpmlGen $ UTCTime d (fromIntegral (round s :: Int))
-- shrink = genericShrink
-- | Generates 'OutlineBase''s categories.
-- This generator makes sure that the result has no @,@ nor @/@ characters, since those are used as separators.
instance Arbitrary (OpmlGen [NonEmpty (NonNull Text)]) where
arbitrary = OpmlGen <$> listOf genCategoryPath
where genCategory = genNonNull `suchThat` (isNothing . find (\c -> c == ',' || c == '/') . toNullable)
genCategoryPath = (:|) <$> genCategory <*> listOf genCategory
-- shrink = genericShrink
-- | Alpha-numeric generator.
genAlphaNum :: Gen Char
genAlphaNum = oneof [choose('a', 'z'), suchThat arbitrary isDigit]
-- | Non-empty mono-foldable
genNonNull :: (SemiSequence a, Arbitrary (Element a), Arbitrary a) => Gen (NonNull a)
genNonNull = ncons <$> arbitrary <*> arbitrary