seqn-0.1.0.0: test/Seq.hs
{-# OPTIONS_GHC -Wno-orphans #-} -- Arbitrary instances
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Seq (seqTests) where
import Prelude hiding (unzip3, zip3, unzip, zip, break, concatMap, drop, dropWhile, filter, lookup, replicate, reverse, splitAt, scanl, scanr, span, take, takeWhile, zipWith, zipWith3)
import qualified Control.Applicative as Ap
import Data.Coerce (coerce)
import Control.Monad.Fix (MonadFix(..))
import qualified Data.Foldable as F
import Data.Foldable (toList)
import qualified Data.Foldable.WithIndex as IFo
import Data.Functor.Identity (Identity(..))
import qualified Data.Functor.WithIndex as IFu
import qualified Data.List as L
import Data.Proxy (Proxy(..))
import Data.Semigroup (stimes)
import qualified Data.Traversable.WithIndex as ITr
import Test.Tasty
import Test.Tasty.QuickCheck hiding (generate)
import qualified Test.QuickCheck.Classes.Base as QLaws
import Test.QuickCheck.Poly (A, B, C, OrdA)
import Data.Seqn.Seq
import qualified Data.Seqn.Internal.Seq as SeqInternal
import qualified Data.Seqn.Internal.Tree as TreeInternal
import ListExtra (unsnocL)
import qualified ListLikeTests as LL
import TestUtil (Sqrt1(..), tastyLaws, (.:), (.:.), ListLike(..))
seqTests :: TestTree
seqTests = testGroup "Data.Seqn.Seq"
[ testGroup "properties"
[
LL.listLike @(Seq A)
-- Construct
, LL.empty @(Seq A) empty
, LL.singleton @(Seq A) singleton
, LL.replicate @(Seq A) replicate
, LL.replicateA @(Seq A) replicateA
, LL.generate @(Seq A) generate
, LL.generateA @(Seq A) generateA
, LL.unfoldr @(Seq A) unfoldr
, LL.unfoldl @(Seq A) unfoldl
, LL.unfoldrM @(Seq A) unfoldrM
, LL.unfoldlM @(Seq A) unfoldlM
, (LL.<>) @(Seq A) (<>)
, LL.stimes @(Seq A) stimes
, LL.mconcat @(Seq A) mconcat
, LL.concatMap @(Seq A) concatMap
, testProperty "mfix" $ \n ->
toList (mkMfix n) === fmap (Solo . L.replicate 10) [0 .. n-1]
, LL.read @(Seq Int)
-- Convert
, LL.toRevList @(Seq A) toRevList
, LL.show @(Seq A)
-- Fold
, LL.foldMap @(Seq A) foldMap
, LL.foldMap' @(Seq A) F.foldMap'
, LL.foldr @(Seq A) foldr
, LL.foldl @(Seq A) F.foldl
, LL.foldl' @(Seq A) F.foldl'
, LL.foldr' @(Seq A) F.foldr'
, LL.ifoldMap @(Seq A) IFo.ifoldMap
, LL.ifoldMap' @(Seq A) IFo.ifoldMap'
, LL.ifoldr @(Seq A) IFo.ifoldr
, LL.ifoldl @(Seq A) IFo.ifoldl
, LL.ifoldr' @(Seq A) IFo.ifoldr'
, LL.ifoldl' @(Seq A) IFo.ifoldl'
-- Index
, LL.lookup @(Seq A) lookup
, LL.index @(Seq A) index
, LL.update @(Seq A) update
, LL.adjust @(Seq A) adjust
, LL.insertAt @(Seq A) insertAt
, LL.deleteAt @(Seq A) deleteAt
-- Slice
, LL.cons @(Seq A) cons
, LL.snoc @(Seq A) snoc
, LL.uncons @(Seq A) uncons
, LL.unsnoc @(Seq A) unsnoc
, LL.take @(Seq A) take
, LL.drop @(Seq A) drop
, LL.slice @(Seq A) slice
, LL.splitAt @(Seq A) splitAt
, LL.takeEnd @(Seq A) takeEnd
, LL.dropEnd @(Seq A) dropEnd
, LL.splitAtEnd @(Seq A) splitAtEnd
, LL.tails @(Seq A) tails
, LL.inits @(Seq A) inits
, LL.chunksOf @(Seq A) chunksOf
-- Filter
, LL.filter @(Seq A) filter
, LL.mapMaybe @(Seq A) @(Seq B) mapMaybe
, LL.mapEither @(Seq A) @(Seq B) @(Seq C) mapEither
, LL.filterM @(Seq A) filterA
, LL.mapMaybeM @(Seq A) @(Seq B) mapMaybeA
, LL.mapEitherM @(Seq A) @(Seq B) @(Seq C) mapEitherA
, LL.takeWhile @(Seq A) takeWhile
, LL.dropWhile @(Seq A) dropWhile
, LL.span @(Seq A) span
, LL.break @(Seq A) break
, LL.takeWhileEnd @(Seq A) takeWhileEnd
, LL.dropWhileEnd @(Seq A) dropWhileEnd
, LL.spanEnd @(Seq A) spanEnd
, LL.breakEnd @(Seq A) breakEnd
-- Transform
, LL.map @(Seq A) @(Seq B) fmap
, LL.liftA2 @(Sqrt1 Seq A) @(Sqrt1 Seq B) @(Sqrt1 Seq C) (coerce (Ap.liftA2 @Seq @A @B @C))
, (LL.<*) @(Seq A) @(Seq B) (<*)
, (LL.*>) @(Seq A) @(Seq B) (*>)
, LL.bind @(Seq A) @(Seq B) (>>=)
, LL.traverse @(Seq A) @(Seq B) traverse
, LL.imap @(Seq A) @(Seq B) IFu.imap
, LL.itraverse @(Seq A) @(Seq B) ITr.itraverse
, LL.reverse @(Seq A) reverse
, LL.intersperse @(Seq A) intersperse
, LL.scanl @(Seq A) @(Seq B) scanl
, LL.scanr @(Seq A) @(Seq B) scanr
, LL.sort @(Seq OrdA) sort
, LL.sortBy @(Seq A) sortBy
-- Search and test
, LL.eq @(Seq A) (==)
, LL.cmp @(Seq OrdA) compare
, LL.findEnd @(Seq A) findEnd
, LL.findIndex @(Seq A) findIndex
, LL.findIndexEnd @(Seq A) findIndexEnd
, LL.infixIndices @(Seq A) infixIndices
, LL.binarySearchFind @(Seq OrdA) binarySearchFind
, LL.isPrefixOf @(Seq A) isPrefixOf
, LL.isSuffixOf @(Seq A) isSuffixOf
, LL.isInfixOf @(Seq A) isInfixOf
, LL.isSubsequenceOf @(Seq A) isSubsequenceOf
-- Zip and unzip
, LL.zip @(Seq A) @(Seq B) zip
, LL.zip3 @(Seq A) @(Seq B) @(Seq C) zip3
, LL.zipWith @(Seq A) @(Seq B) @(Seq C) zipWith
, LL.zipWith3 @(Seq A) @(Seq B) @(Seq C) @(Seq A) zipWith3
, LL.zipWithM @(Seq A) @(Seq B) @(Seq C) zipWithM
, LL.zipWith3M @(Seq A) @(Seq B) @(Seq C) @(Seq A) zipWith3M
, LL.unzip @(Seq (A, B)) unzip
, LL.unzip3 @(Seq (A, B, C)) unzip3
, LL.unzipWith @(Seq A) @(Seq B) @(Seq C) unzipWith
, LL.unzipWith3 @(Seq A) @(Seq B) @(Seq C) @(Seq A) unzipWith3
]
, testGroup "valid"
[
-- Arbitrary
testProperty "arbitrary" $ id @(Seq A)
-- Construct
, testProperty "fromList" $ fromList @A
, testProperty "fromRevList" $ fromRevList @A
, testProperty "replicate" $ replicate @A
, testProperty "generate" $ \n -> generate n . applyFun @Int @A
, testProperty "unfoldr" $ unfoldr (L.uncons @A)
, testProperty "unfoldl" $ unfoldl (unsnocL @A)
, testProperty "<>" $ (<>) @(Seq A)
, testProperty "stimes" $ stimes @(Seq A) @Int
, testProperty "mconcat" $ mconcat @(Seq A)
, testProperty "concatMap" $ concatMap @[] . applyFun @A @(Seq B)
, testProperty "mfix" mkMfix
-- Index
, testProperty "adjust" $ adjust . applyFun @A
, testProperty "update" $ update @A
, testProperty "insertAt" $ insertAt @A
, testProperty "deleteAt" $ deleteAt @A
-- Slice
, testProperty "cons" $ cons @A
, testProperty "snoc" $ snoc @A
, testProperty "uncons" $ fmap snd . uncons @A
, testProperty "unsnoc" $ fmap fst . unsnoc @A
, testProperty "take" $ take @A
, testProperty "drop" $ drop @A
, testProperty "slice" $ slice @A
, testProperty "splitAt" $ splitAt @A
, testProperty "takeEnd" $ takeEnd @A
, testProperty "dropEnd" $ dropEnd @A
, testProperty "splitAtEnd" $ splitAtEnd @A
, testProperty "tails" $ tails @A
, testProperty "tails each" $ toList . tails @A
, testProperty "inits" $ inits @A
, testProperty "inits each" $ toList . inits @A
, testProperty "chunksOf" $ chunksOf @A
, testProperty "chunksOf each" $ toList .: chunksOf @A
-- Transform
, testProperty "fmap" $ fmap @Seq . applyFun @A @B
, testProperty "liftA2" $ unSqrt1 .:. Ap.liftA2 @(Sqrt1 Seq) . applyFun2 @A @B @C
, testProperty "<*" $ (<*) @Seq @A @B
, testProperty "*>" $ (*>) @Seq @A @B
, testProperty ">>=" $ flip ((>>=) @Seq) . applyFun @A @(Seq B)
, testProperty "traverse" $ runIdentity .: traverse @Seq . applyFun @A @(Identity B)
, testProperty "imap" $ IFu.imap @_ @Seq . applyFun2 @_ @A @B
, testProperty "itraverse" $ runIdentity .: ITr.itraverse @_ @Seq . applyFun2 @_ @A @(Identity B)
, testProperty "reverse" $ reverse @A
, testProperty "intersperse" $ intersperse @A
, testProperty "scanl" $ scanl . applyFun2 @A @B
, testProperty "scanr" $ scanr . applyFun2 @A @B
, testProperty "sort" $ sort @OrdA
, testProperty "sortBy" $ sortBy @OrdA compare
-- Filter
, testProperty "filter" $ filter . applyFun @A
, testProperty "mapMaybe" $ mapMaybe . applyFun @A @(Maybe B)
, testProperty "mapEither" $ mapEither . applyFun @A @(Either B C)
, testProperty "takeWhile" $ takeWhile . applyFun @A
, testProperty "dropWhile" $ dropWhile . applyFun @A
, testProperty "span" $ span . applyFun @A
, testProperty "break" $ break . applyFun @A
, testProperty "takeWhileEnd" $ takeWhileEnd . applyFun @A
, testProperty "dropWhileEnd" $ dropWhileEnd . applyFun @A
, testProperty "spanEnd" $ spanEnd . applyFun @A
, testProperty "breakEnd" $ breakEnd . applyFun @A
-- Zip
, testProperty "zipWith" $ zipWith . applyFun2 @A @B @C
, testProperty "zipWith3" $ zipWith3 . applyFun3 @A @B @A @C
, testProperty "unzipWith" $ unzipWith . applyFun @A @(B,C)
, testProperty "unzipWith3" $ unzipWith3 . applyFun @A @(B,C,B)
-- Random
, testProperty "random transforms" $ \tf (xs :: Seq A) ->
let xs' = unTransform tf xs
in classify (not (null xs')) "non-empty" xs'
]
, testGroup "laws" $ map tastyLaws $
let p = Proxy @Seq
sqrtp = Proxy @(Sqrt1 Seq)
pa = Proxy @(Seq A)
porda = Proxy @(Seq OrdA)
pint = Proxy @(Seq Int)
in
[ QLaws.eqLaws pa
, QLaws.ordLaws porda
, QLaws.isListLaws pa
, QLaws.semigroupLaws pa
, QLaws.monoidLaws pa
, QLaws.showLaws pa
, QLaws.showReadLaws pint
, QLaws.foldableLaws p
, QLaws.traversableLaws p
, QLaws.functorLaws p
, QLaws.applicativeLaws sqrtp
, QLaws.alternativeLaws p
, QLaws.monadLaws sqrtp
, QLaws.monadPlusLaws p
, QLaws.monadZipLaws p
]
]
instance Arbitrary a => Arbitrary (Seq a) where
arbitrary = oneof
[ fromList <$> arbitrary
, fromRevList <$> arbitrary
, randomStructure
]
where
randomStructure = sized $ \n -> do
n' <- choose (0,n)
if n' == 0
then pure empty
else Ap.liftA2 SeqInternal.Tree arbitrary (go (n'-1))
where
go 0 = pure TreeInternal.Tip
go n = do
ln <- choose (0,n-1)
Ap.liftA3 TreeInternal.link arbitrary (go ln) (go (n-1-ln))
shrink = fmap fromList . shrink . F.toList
newtype Transform a = Transform { unTransform :: Seq a -> Seq a }
instance Show (Transform a) where
show _ = "Transform"
instance (Arbitrary a, CoArbitrary a) => Arbitrary (Transform a) where
arbitrary = Transform . foldr (.) id <$> listOf tf
where
tf = oneof
[ (fst .) . splitAt <$> arbitrary
, (snd .) . splitAt <$> arbitrary
, (<>) <$> arbitrary
, flip (<>) <$> arbitrary
, insertAt <$> arbitrary <*> arbitrary
, deleteAt <$> arbitrary
, fmap <$> arbitrary
, fmap cons arbitrary
, fmap (flip snoc) arbitrary
, pure (maybe empty snd . uncons)
, pure (maybe empty fst . unsnoc)
, filter <$> arbitrary
, mapMaybe <$> arbitrary
]
instance Show a => Testable (Seq a) where
property t =
counterexample ("Invalid: " ++ SeqInternal.debugShowsPrec 0 t "") $
SeqInternal.valid t
instance (Testable a, Testable b, a ~ Seq x, b ~ Seq y) => Testable (a, b) where
property (a, b) = property a .&&. property b
instance
(Testable a, Testable b, Testable c, a ~ Seq x, b ~ Seq y, c ~ Seq z) =>
Testable (a, b, c) where
property (a, b, c) = property a .&&. property b .&&. property c
instance (Testable a, a ~ Seq x) => Testable [a] where
property = conjoin
instance ListLike (Seq a) where
type E (Seq a) = a
fromL = fromList
toL = toList
-- map (replicate 10) [0..n-1]
mkMfix :: Int -> Seq (Solo [Int])
mkMfix n =
fmap (fmap (L.take 10))
(mfix (\ ~(Solo is) -> generate n (\i -> Solo (i : is))))
-- In Data.Tuple since 4.15
data Solo a = Solo a
deriving (Eq, Show, Functor)