packages feed

opml-conduit-0.1.0.0: Text/OPML/Arbitrary.hs

{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE FlexibleContexts   #-}
{-# LANGUAGE FlexibleInstances  #-}
{-# 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.Maybe
import           Data.NotEmpty
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

-- | Alpha-numeric generator.
genAlphaNum :: Gen Char
genAlphaNum = oneof [choose('a', 'z'), suchThat arbitrary isDigit]

-- | 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 = (\x -> x ++ ":") <$> listOf1 (choose('a', 'z'))
  -- shrink = genericShrink

-- | Reasonable enough 'URIAuth' generator.
instance Arbitrary (OpmlGen URIAuth) where
  arbitrary = do
    userInfo <- oneof [return "", fmap (\x -> x ++ "@") $ 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 [[NE Text]]) where
  arbitrary = OpmlGen <$> listOf (listOf1 $ arbitrary `suchThat` (isNothing . find (\c -> c == ',' || c == '/') . original))
  shrink = genericShrink