-- An example of generic Arbitrary instances with explicit generators
-- for some types of fields for which there is no 'Arbitrary' instance
-- or the existing one is unsatisfactory.
--
-- For example, Postgres can't handle strings containing NUL characters
-- see https://github.com/lpsmith/postgresql-simple/issues/223
-- so applications may want to generate data without them.

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}

import Data.Char (isAlphaNum)
import Data.Text as T (Text, pack, unpack)
import GHC.Generics (Generic)
import Test.QuickCheck

import Generic.Random

instance Arbitrary Text where
  arbitrary = pack <$> arbitrary
  shrink = fmap pack . shrink . unpack

data R = R                    -- Two constraints:
  { name :: Text              -- names and
  , address :: Text           -- addresses don't contain '\NUL'
  , id_ :: Text               -- IDs are alphanumeric
  } deriving (Show, Generic)

instance Arbitrary R where
  arbitrary = genericArbitrarySingleG gens
    where
      gens =
        (FieldGen (pack . filter isAlphaNum <$> scale (* 5) arbitrary)
          :: FieldGen "id_" Text) :+
        (pack . filter (/= '\NUL') <$> arbitrary)

  shrink = genericShrink

newtype Bugged a = Bugged a deriving Show

instance Arbitrary (Bugged R) where
  arbitrary = Bugged <$> genericArbitrarySingle
  shrink (Bugged r) = Bugged <$> shrink r

main :: IO ()
main = do
  sample (arbitrary :: Gen R)
  let prop_nameNullFree r = all (/= '\NUL') (unpack (name r))
      prop_idAlpha r = all isAlphaNum (unpack (id_ r))
      qc prop = quickCheckWith stdArgs{maxSuccess = 1000} prop
  qc prop_nameNullFree
  qc prop_idAlpha
  qc $ expectFailure $ \(Bugged r) -> prop_nameNullFree r
  qc $ expectFailure $ \(Bugged r) -> prop_idAlpha r
