content: {-# LANGUAGE CPP, ScopedTypeVariables, DataKinds, TypeSynonymInstances #-}
  module Main ( main ) where
  
  #if MIN_VERSION_base(4,8,0)
  #define HAS_NATURAL
  #endif
  
  #if MIN_VERSION_base(4,7,0)
  #define HAS_FIXED_CONSTRUCTOR
  #endif
  
  import           Control.Applicative
  import           Control.Exception                    as C (SomeException,
                                                              catch, evaluate)
  import           Control.Monad                        (unless, liftM2)
  import qualified Data.ByteString                      as B
  import qualified Data.ByteString.Lazy                 as L
  import qualified Data.ByteString.Lazy.Internal        as L
  #if MIN_VERSION_bytestring(0,10,4)
  import           Data.ByteString.Short                (ShortByteString)
  #endif
  import           Data.Int
  import           Data.Ratio
  import           Data.Typeable
  import           System.IO.Unsafe
  
  import           Data.Orphans ()
  
  #ifdef HAS_NATURAL
  import           Numeric.Natural
  #endif
  
  import           GHC.Fingerprint
  
  import qualified Data.Fixed as Fixed
  
  import           Test.Framework
  import           Test.Framework.Providers.QuickCheck2
  import           Test.QuickCheck hiding (total)
  
  import qualified Action                               (tests)
  import           Arbitrary                            ()
  import           Data.Binary
  import           Data.Binary.Get
  import           Data.Binary.Put
  import qualified Data.Binary.Class as Class
  
  
  ------------------------------------------------------------------------
  
  roundTrip :: (Eq a, Binary a) => a -> (L.ByteString -> L.ByteString) -> Bool
  roundTrip a f = a ==
      {-# SCC "decode.refragment.encode" #-} decode (f (encode a))
  
  roundTripWith ::  Eq a => (a -> Put) -> Get a -> a -> Property
  roundTripWith putter getter x =
      forAll positiveList $ \xs ->
      x == runGet getter (refragment xs (runPut (putter x)))
  
  -- make sure that a test fails
  mustThrowError :: B a
  mustThrowError a = unsafePerformIO $
      C.catch (do _ <- C.evaluate a
                  return False)
              (\(_e :: SomeException) -> return True)
  
  -- low level ones:
  --
  -- Words
  
  prop_Word8 :: Word8 -> Property
  prop_Word8 = roundTripWith putWord8 getWord8
  
  prop_Word16be :: Word16 -> Property
  prop_Word16be = roundTripWith putWord16be getWord16be
  
  prop_Word16le :: Word16 -> Property
  prop_Word16le = roundTripWith putWord16le getWord16le
  
  prop_Word16host :: Word16 -> Property
  prop_Word16host = roundTripWith putWord16host getWord16host
  
  prop_Word32be :: Word32 -> Property
  prop_Word32be = roundTripWith putWord32be getWord32be
  
  prop_Word32le :: Word32 -> Property
  prop_Word32le = roundTripWith putWord32le getWord32le
  
  prop_Word32host :: Word32 -> Property
  prop_Word32host = roundTripWith putWord32host getWord32host
  
  prop_Word64be :: Word64 -> Property
  prop_Word64be = roundTripWith putWord64be getWord64be
  
  prop_Word64le :: Word64 -> Property
  prop_Word64le = roundTripWith putWord64le getWord64le
  
  prop_Word64host :: Word64 -> Property
  prop_Word64host = roundTripWith putWord64host getWord64host
  
  prop_Wordhost :: Word -> Property
  prop_Wordhost = roundTripWith putWordhost getWordhost
  
  -- Ints
  
  prop_Int8 :: Int8 -> Property
  prop_Int8 = roundTripWith putInt8 getInt8
  
  prop_Int16be :: Int16 -> Property
  prop_Int16be = roundTripWith putInt16be getInt16be
  
  prop_Int16le :: Int16 -> Property
  prop_Int16le = roundTripWith putInt16le getInt16le
  
  prop_Int16host :: Int16 -> Property
  prop_Int16host = roundTripWith putInt16host getInt16host
  
  prop_Int32be :: Int32 -> Property
  prop_Int32be = roundTripWith putInt32be getInt32be
  
  prop_Int32le :: Int32 -> Property
  prop_Int32le = roundTripWith putInt32le getInt32le
  
  prop_Int32host :: Int32 -> Property
  prop_Int32host = roundTripWith putInt32host getInt32host
  
  prop_Int64be :: Int64 -> Property
  prop_Int64be = roundTripWith putInt64be getInt64be
  
  prop_Int64le :: Int64 -> Property
  prop_Int64le = roundTripWith putInt64le getInt64le
  
  prop_Int64host :: Int64 -> Property
  prop_Int64host = roundTripWith putInt64host getInt64host
  
  prop_Inthost :: Int -> Property
  prop_Inthost = roundTripWith putInthost getInthost
  
  -- Floats and Doubles
  
  prop_Floatbe :: Float -> Property
  prop_Floatbe = roundTripWith putFloatbe getFloatbe
  
  prop_Floatle :: Float -> Property
  prop_Floatle = roundTripWith putFloatle getFloatle
  
  prop_Floathost :: Float -> Property
  prop_Floathost = roundTripWith putFloathost getFloathost
  
  prop_Doublebe :: Double -> Property
  prop_Doublebe = roundTripWith putDoublebe getDoublebe
  
  prop_Doublele :: Double -> Property
  prop_Doublele = roundTripWith putDoublele getDoublele
  
  prop_Doublehost :: Double -> Property
  prop_Doublehost = roundTripWith putDoublehost getDoublehost
  
  #if MIN_VERSION_base(4,10,0)
  testTypeable :: Test
  testTypeable = testProperty "TypeRep" prop_TypeRep
  
  prop_TypeRep :: TypeRep -> Property
  prop_TypeRep = roundTripWith Class.put Class.get
  
  atomicTypeReps :: [TypeRep]
  atomicTypeReps =
      [ typeRep (Proxy :: Proxy ())
      , typeRep (Proxy :: Proxy String)
      , typeRep (Proxy :: Proxy Int)
      , typeRep (Proxy :: Proxy (,))
      , typeRep (Proxy :: Proxy ((,) (Maybe Int)))
      , typeRep (Proxy :: Proxy Maybe)
      , typeRep (Proxy :: Proxy 'Nothing)
      , typeRep (Proxy :: Proxy 'Left)
      , typeRep (Proxy :: Proxy "Hello")
      , typeRep (Proxy :: Proxy 42)
      , typeRep (Proxy :: Proxy '[1,2,3,4])
      , typeRep (Proxy :: Proxy ('Left Int))
      , typeRep (Proxy :: Proxy (Either Int String))
      , typeRep (Proxy :: Proxy (() -> ()))
      ]
  
  instance Arbitrary TypeRep where
      arbitrary = oneof (map pure atomicTypeReps)
  #else
  testTypeable :: Test
  testTypeable = testGroup "Skipping Typeable tests" []
  #endif
  
  -- done, partial and fail
  
  -- | Test partial results.
  -- May or may not use the whole input, check conditions for the different
  -- outcomes.
  prop_partial :: L.ByteString -> Property
  prop_partial lbs = forAll (choose (0, L.length lbs * 2)) $ \skipN ->
    let result = pushChunks (runGetIncremental decoder) lbs
        decoder = do
          s <- getByteString (fromIntegral skipN)
          return (L.fromChunks [s])
    in case result of
         Partial _ -> L.length lbs < skipN
         Done unused _pos value ->
           and [ L.length value == skipN
               , L.append value (L.fromChunks [unused]) == lbs
               ]
         Fail _ _ _ -> False
  
  -- | Fail a decoder and make sure the result is sane.
  prop_fail :: L.ByteString -> String -> Property
  prop_fail lbs msg = forAll (choose (0, L.length lbs)) $ \pos ->
    let result = pushChunks (runGetIncremental decoder) lbs
        decoder = do
          -- use part of the input...
          _ <- getByteString (fromIntegral pos)
          -- ... then fail
          fail msg
    in case result of
       Fail unused pos' msg' ->
         and [ pos == pos'
             , msg == msg'
             , L.length lbs - pos == fromIntegral (B.length unused)
             , L.fromChunks [unused] `L.isSuffixOf` lbs
             ]
       _ -> False -- wuut?
  
  -- read negative length
  prop_getByteString_negative :: Int -> Property
  prop_getByteString_negative n =
    n < 1 ==>
      runGet (getByteString n) L.empty == B.empty
  
  
  prop_bytesRead :: L.ByteString -> Property
  prop_bytesRead lbs =
    forAll (makeChunks 0 totalLength) $ \chunkSizes ->
    let result = pushChunks (runGetIncremental decoder) lbs
        decoder = do
          -- Read some data and invoke bytesRead several times.
          -- Each time, check that the values are what we expect.
          flip mapM_ chunkSizes $ \(total, step) -> do
            _ <- getByteString (fromIntegral step)
            n <- bytesRead
            unless (n == total) $ fail "unexpected position"
          bytesRead
    in case result of
         Done unused pos value ->
           and [ value == totalLength
               , pos == value
               , B.null unused
               ]
         Partial _ -> False
         Fail _ _ _ -> False
    where
      totalLength = L.length lbs
      makeChunks total i
        | i == 0 = return []
        | otherwise = do
            n <- choose (0,i)
            let total' = total + n
            rest <- makeChunks total' (i - n)
            return ((total',n):rest)
  
  
  -- | We're trying to guarantee that the Decoder will not ask for more input
  -- with Partial if it has been given Nothing once.
  -- In this test we're making the decoder return 'Partial' to get more
  -- input, and to get knownledge of the current position using 'BytesRead'.
  -- Both of these operations, when used with the <|> operator, result internally
  -- in that the decoder return with Partial and BytesRead multiple times,
  -- in which case we need to keep track of if the user has passed Nothing to a
  -- Partial in the past.
  prop_partialOnlyOnce :: Property
  prop_partialOnlyOnce = property $
    let result = runGetIncremental (decoder <|> decoder)
        decoder = do
          0 <- bytesRead
          _ <- getWord8 -- this will make the decoder return with Partial
          return "shouldn't get here"
    in case result of
         -- we expect Partial followed by Fail
         Partial k -> case k Nothing of -- push down a Nothing
                        Fail _ _ _ -> True
                        Partial _ -> error $ "partial twice! oh noes!"
                        Done _ _ _ -> error $ "we're not supposed to be done."
         _ -> error $ "not partial, error!"
  
  -- read too much
  prop_readTooMuch :: (Eq a, Binary a) => a -> Bool
  prop_readTooMuch x = mustThrowError $ x == a && x /= b
    where
      -- encode 'a', but try to read 'b' too
      (a,b) = decode (encode x)
      _types = [a,b]
  
  -- In binary-0.5 the Get monad looked like
  --
  -- > data S = S {-# UNPACK #-} !B.ByteString
  -- >            L.ByteString
  -- >            {-# UNPACK #-} !Int64
  -- >
  -- > newtype Get a = Get { unGet :: S -> (# a, S #) }
  --
  -- with a helper function
  --
  -- > mkState :: L.ByteString -> Int64 -> S
  -- > mkState l = case l of
  -- >     L.Empty      -> S B.empty L.empty
  -- >     L.Chunk x xs -> S x xs
  --
  -- Note that mkState is strict in its first argument. This goes wrong in this
  -- function:
  --
  -- > getBytes :: Int -> Get B.ByteString
  -- > getBytes n = do
  -- >     S s ss bytes <- traceNumBytes n $ get
  -- >     if n <= B.length s
  -- >         then do let (consume,rest) = B.splitAt n s
  -- >                 put $! S rest ss (bytes + fromIntegral n)
  -- >                 return $! consume
  -- >         else
  -- >               case L.splitAt (fromIntegral n) (s `join` ss) of
  -- >                 (consuming, rest) ->
  -- >                     do let now = B.concat . L.toChunks $ consuming
  -- >                        put $ mkState rest (bytes + fromIntegral n)
  -- >                        -- forces the next chunk before this one is returned
  -- >                        if (B.length now < n)
  -- >                          then
  -- >                             fail "too few bytes"
  -- >                          else
  -- >                             return now
  --
  -- Consider the else-branch of this function; suppose we ask for n bytes;
  -- the call to L.splitAt gives us a lazy bytestring 'consuming' of precisely @n@
  -- bytes (unless we don't have enough data, in which case we fail); but then
  -- the strict evaluation of mkState on 'rest' means we look ahead too far.
  --
  -- Although this is all done completely differently in binary-0.7 it is
  -- important that the same bug does not get introduced in some other way. The
  -- test is basically the same test that already exists in this test suite,
  -- verifying that
  --
  -- > decode . refragment . encode == id
  --
  -- However, we use a different 'refragment', one that introduces an exception
  -- as the tail of the bytestring after rechunking. If we don't look ahead too
  -- far then this should make no difference, but if we do then this will throw
  -- an exception (for instance, in binary-0.5, this will throw an exception for
  -- certain rechunkings, but not for others).
  --
  -- To make sure that the property holds no matter what refragmentation we use,
  -- we test exhaustively for a single chunk, and all ways to break the string
  -- into 2, 3 and 4 chunks.
  prop_lookAheadIndepOfChunking :: (Eq a, Binary a) => a -> Property
  prop_lookAheadIndepOfChunking testInput =
     forAll (testCuts (L.length (encode testInput))) $
       roundTrip testInput . rechunk
    where
      testCuts :: forall a. (Num a, Enum a) => a -> Gen [a]
      testCuts len = elements $ [ [] ]
                             ++ [ [i]
                                | i <- [0 .. len] ]
                             ++ [ [i, j]
                                | i <- [0 .. len]
                                , j <- [0 .. len - i] ]
                             ++ [ [i, j, k]
                                | i <- [0 .. len]
                                , j <- [0 .. len - i]
                                , k <- [0 .. len - i - j] ]
  
      -- Rechunk a bytestring, leaving the tail as an exception rather than Empty
      rechunk :: forall a. Integral a => [a] -> L.ByteString -> L.ByteString
      rechunk cuts = fromChunks . cut cuts . B.concat . L.toChunks
        where
          cut :: [a] -> B.ByteString -> [B.ByteString]
          cut []     bs = [bs]
          cut (i:is) bs = let (bs0, bs1) = B.splitAt (fromIntegral i) bs
                          in bs0 : cut is bs1
  
          fromChunks :: [B.ByteString] ->  L.ByteString
          fromChunks []       = error "Binary should not have to ask for this chunk!"
          fromChunks (bs:bss) = L.Chunk bs (fromChunks bss)
  
  -- String utilities
  
  prop_getLazyByteString :: L.ByteString -> Property
  prop_getLazyByteString lbs = forAll (choose (0, 2 * L.length lbs)) $ \len ->
    let result = pushChunks (runGetIncremental decoder) lbs
        decoder = getLazyByteString len
    in case result of
         Done unused _pos value ->
           and [ value == L.take len lbs
               , L.fromChunks [unused] == L.drop len lbs
               ]
         Partial _ -> len > L.length lbs
         _ -> False
  
  prop_getLazyByteStringNul :: Word16 -> [Int] -> Property
  prop_getLazyByteStringNul count0 fragments = count >= 0 ==>
    forAll (choose (0, count)) $ \pos ->
    let lbs = case L.splitAt pos (L.replicate count 65) of
                (start,end) -> refragment fragments $ L.concat [start, L.singleton 0, end]
        result = pushEndOfInput $ pushChunks (runGetIncremental getLazyByteStringNul) lbs
    in case result of
         Done unused pos' value ->
           and [ value == L.take pos lbs
               , pos + 1 == pos' -- 1 for the NUL
               , L.fromChunks [unused] == L.drop (pos + 1) lbs
               ]
         _ -> False
    where
    count = fromIntegral count0 -- to make the generated numbers a bit smaller
  
  -- | Same as prop_getLazyByteStringNul, but without any NULL in the string.
  prop_getLazyByteStringNul_noNul :: Word16 -> [Int] -> Property
  prop_getLazyByteStringNul_noNul count0 fragments = count >= 0 ==>
    let lbs = refragment fragments $ L.replicate count 65
        result = pushEndOfInput $ pushChunks (runGetIncremental getLazyByteStringNul) lbs
    in case result of
         Fail _ _ _ -> True
         _ -> False
    where
    count = fromIntegral count0 -- to make the generated numbers a bit smaller
  
  prop_getRemainingLazyByteString :: L.ByteString -> Property
  prop_getRemainingLazyByteString lbs = property $
    let result = pushEndOfInput $ pushChunks (runGetIncremental getRemainingLazyByteString) lbs
    in case result of
      Done unused pos value ->
        and [ value == lbs
            , B.null unused
            , fromIntegral pos == L.length lbs
            ]
      _ -> False
  
  -- sanity:
  
  invariant_lbs :: L.ByteString -> Bool
  invariant_lbs (L.Empty)      = True
  invariant_lbs (L.Chunk x xs) = not (B.null x) && invariant_lbs xs
  
  prop_invariant :: (Binary a) => a -> Bool
  prop_invariant = invariant_lbs . encode
  
  -- refragment a lazy bytestring's chunks
  refragment :: [Int] -> L.ByteString -> L.ByteString
  refragment [] lbs = lbs
  refragment (x:xs) lbs =
      let x' = fromIntegral . (+1) . abs $ x
          rest = refragment xs (L.drop x' lbs) in
      L.append (L.fromChunks [B.concat . L.toChunks . L.take x' $ lbs]) rest
  
  -- check identity of refragmentation
  prop_refragment :: L.ByteString -> [Int] -> Bool
  prop_refragment lbs xs = lbs == refragment xs lbs
  
  -- check that refragmention still hold invariant
  prop_refragment_inv :: L.ByteString -> [Int] -> Bool
  prop_refragment_inv lbs xs = invariant_lbs $ refragment xs lbs
  
  main :: IO ()
  main = defaultMain tests
  
  ------------------------------------------------------------------------
  
  genInteger :: Gen Integer
  genInteger = do
    b <- arbitrary
    if b then genIntegerSmall else genIntegerSmall
  
  genIntegerSmall :: Gen Integer
  genIntegerSmall = arbitrary
  
  genIntegerBig :: Gen Integer
  genIntegerBig = do
    x <- arbitrarySizedIntegral :: Gen Integer
    -- arbitrarySizedIntegral generates numbers smaller than
    -- (maxBound :: Word32), so let's make them bigger to better test
    -- the Binary instance.
    return (x + fromIntegral (maxBound :: Word32))
  
  #ifdef HAS_NATURAL
  genNatural :: Gen Natural
  genNatural = do
    b <- arbitrary
    if b then genNaturalSmall else genNaturalBig
  
  genNaturalSmall :: Gen Natural
  genNaturalSmall = arbitrarySizedNatural
  
  genNaturalBig :: Gen Natural
  genNaturalBig = do
    x <- arbitrarySizedNatural :: Gen Natural
    -- arbitrarySizedNatural generates numbers smaller than
    -- (maxBound :: Word64), so let's make them bigger to better test
    -- the Binary instance.
    return (x + fromIntegral (maxBound :: Word64))
  #endif
  
  ------------------------------------------------------------------------
  
  genFingerprint :: Gen Fingerprint
  genFingerprint = liftM2 Fingerprint arbitrary arbitrary
  
  ------------------------------------------------------------------------
  
  #ifdef HAS_FIXED_CONSTRUCTOR
  
  fixedPut :: forall a. Fixed.HasResolution a => Fixed.Fixed a -> Put
  fixedPut x = put (truncate (x * fromInteger (Fixed.resolution (undefined :: Maybe a))) :: Integer)
  
  fixedGet :: forall a. Fixed.HasResolution a => Get (Fixed.Fixed a)
  fixedGet = (\x -> fromInteger x / fromInteger (Fixed.resolution (undefined :: Maybe a))) `liftA` get
  
  -- | Serialise using base >=4.7 and <4.7 methods agree
  prop_fixed_ser :: Fixed.Fixed Fixed.E3 -> Bool
  prop_fixed_ser x = runPut (put x) == runPut (fixedPut x)
  
  -- | Serialised with base >=4.7, unserialised with base <4.7 method roundtrip
  prop_fixed_constr_resolution :: Fixed.Fixed Fixed.E3 -> Bool
  prop_fixed_constr_resolution x = runGet fixedGet (runPut (put x)) == x
  
  -- | Serialised with base <4.7, unserialised with base >=4.7 method roundtrip
  prop_fixed_resolution_constr :: Fixed.Fixed Fixed.E3 -> Bool
  prop_fixed_resolution_constr x = runGet get (runPut (fixedPut x)) == x
  
  #endif
  
  ------------------------------------------------------------------------
  
  type T a = a -> Property
  type B a = a -> Bool
  
  p :: (Testable p) => p -> Property
  p = property
  
  test    :: (Eq a, Binary a) => a -> Property
  test a  = forAll positiveList (roundTrip a . refragment)
  
  test' :: (Show a, Arbitrary a) => String -> (a -> Property) -> ([a] -> Property) -> Test
  test' desc prop propList =
    testGroup desc [
      testProperty desc prop,
      testProperty ("[" ++ desc ++ "]") propList
    ]
  
  testWithGen :: (Show a, Eq a, Binary a) => String -> Gen a -> Test
  testWithGen desc gen =
    testGroup desc [
      testProperty desc (forAll gen test),
      testProperty ("[" ++ desc ++ "]") (forAll (listOf gen) test)
    ]
  
  positiveList :: Gen [Int]
  positiveList = fmap (filter (/=0) . map abs) $ arbitrary
  
  tests :: [Test]
  tests =
          [ testGroup "Utils"
              [ testProperty "refragment id" (p prop_refragment)
              , testProperty "refragment invariant" (p prop_refragment_inv)
              ]
  
          , testGroup "Boundaries"
              [ testProperty "read to much"         (p (prop_readTooMuch :: B Word8))
              , testProperty "read negative length" (p (prop_getByteString_negative :: T Int))
              , -- Arbitrary test input
                let testInput :: [Int] ; testInput = [0 .. 10]
                in testProperty "look-ahead independent of chunking" (p (prop_lookAheadIndepOfChunking testInput))
              ]
  
          , testGroup "Partial"
              [ testProperty "partial" (p prop_partial)
              , testProperty "fail"    (p prop_fail)
              , testProperty "bytesRead" (p prop_bytesRead)
              , testProperty "partial only once" (p prop_partialOnlyOnce)
              ]
  
          , testGroup "Model"
              Action.tests
  
          , testGroup "Primitives"
              [ testProperty "Word8"      (p prop_Word8)
              , testProperty "Word16be"   (p prop_Word16be)
              , testProperty "Word16le"   (p prop_Word16le)
              , testProperty "Word16host" (p prop_Word16host)
              , testProperty "Word32be"   (p prop_Word32be)
              , testProperty "Word32le"   (p prop_Word32le)
              , testProperty "Word32host" (p prop_Word32host)
              , testProperty "Word64be"   (p prop_Word64be)
              , testProperty "Word64le"   (p prop_Word64le)
              , testProperty "Word64host" (p prop_Word64host)
              , testProperty "Wordhost"   (p prop_Wordhost)
                -- Int
              , testProperty "Int8"       (p prop_Int8)
              , testProperty "Int16be"    (p prop_Int16be)
              , testProperty "Int16le"    (p prop_Int16le)
              , testProperty "Int16host"  (p prop_Int16host)
              , testProperty "Int32be"    (p prop_Int32be)
              , testProperty "Int32le"    (p prop_Int32le)
              , testProperty "Int32host"  (p prop_Int32host)
              , testProperty "Int64be"    (p prop_Int64be)
              , testProperty "Int64le"    (p prop_Int64le)
              , testProperty "Int64host"  (p prop_Int64host)
              , testProperty "Inthost"    (p prop_Inthost)
                -- Float/Double
              , testProperty "Floatbe"    (p prop_Floatbe)
              , testProperty "Floatle"    (p prop_Floatle)
              , testProperty "Floathost"  (p prop_Floathost)
              , testProperty "Doublebe"   (p prop_Doublebe)
              , testProperty "Doublele"   (p prop_Doublele)
              , testProperty "Doublehost" (p prop_Doublehost)
              ]
  
          , testGroup "String utils"
              [ testProperty "getLazyByteString"          prop_getLazyByteString
              , testProperty "getLazyByteStringNul"       prop_getLazyByteStringNul
              , testProperty "getLazyByteStringNul No Null" prop_getLazyByteStringNul_noNul
              , testProperty "getRemainingLazyByteString" prop_getRemainingLazyByteString
              ]
  
          , testGroup "Using Binary class, refragmented ByteString"
              [ test' "()"          (test :: T ()         ) test
              , test' "Bool"        (test :: T Bool       ) test
              , test' "Char"        (test :: T Char       ) test
              , test' "Ordering"    (test :: T Ordering   ) test
              , test' "Ratio Int"   (test :: T (Ratio Int)) test
  
              , test' "Word"        (test :: T Word  ) test
              , test' "Word8"       (test :: T Word8 ) test
              , test' "Word16"      (test :: T Word16) test
              , test' "Word32"      (test :: T Word32) test
              , test' "Word64"      (test :: T Word64) test
  
              , test' "Int"         (test :: T Int  ) test
              , test' "Int8"        (test :: T Int8 ) test
              , test' "Int16"       (test :: T Int16) test
              , test' "Int32"       (test :: T Int32) test
              , test' "Int64"       (test :: T Int64) test
  
              , testWithGen "Integer mixed" genInteger
              , testWithGen "Integer small" genIntegerSmall
              , testWithGen "Integer big"   genIntegerBig
  
              , test' "Fixed"       (test :: T (Fixed.Fixed Fixed.E3) ) test
  #ifdef HAS_NATURAL
              , testWithGen "Natural mixed" genNatural
              , testWithGen "Natural small" genNaturalSmall
              , testWithGen "Natural big"   genNaturalBig
  #endif
              , testWithGen "GHC.Fingerprint" genFingerprint
  
              , test' "Float"       (test :: T Float ) test
              , test' "Double"      (test :: T Double) test
  
              , test' "((), ())"            (test :: T ((), ())            ) test
              , test' "(Word8, Word32)"     (test :: T (Word8, Word32)     ) test
              , test' "(Int8, Int32)"       (test :: T (Int8,  Int32)      ) test
              , test' "(Int32, [Int])"      (test :: T (Int32, [Int])      ) test
              , test' "Maybe Int8"          (test :: T (Maybe Int8)        ) test
              , test' "Either Int8 Int16"   (test :: T (Either Int8 Int16) ) test
  
              , test' "(Int, ByteString)"
                      (test     :: T (Int, B.ByteString)   ) test
              , test' "[(Int, ByteString)]"
                      (test     :: T [(Int, B.ByteString)] ) test
  
              , test' "(Maybe Int64, Bool, [Int])"
                      (test :: T (Maybe Int64, Bool, [Int])) test
              , test' "(Maybe Word8, Bool, [Int], Either Bool Word8)"
                      (test :: T (Maybe Word8, Bool, [Int], Either Bool Word8)) test
              , test' "(Maybe Word16, Bool, [Int], Either Bool Word16, Int)"
                      (test :: T (Maybe Word16, Bool, [Int], Either Bool Word16, Int)) test
  
              , test' "(Int,Int,Int,Int,Int,Int)"
                        (test :: T (Int,Int,Int,Int,Int,Int)) test
              , test' "(Int,Int,Int,Int,Int,Int,Int)"
                        (test :: T (Int,Int,Int,Int,Int,Int,Int)) test
              , test' "(Int,Int,Int,Int,Int,Int,Int,Int)"
                        (test :: T (Int,Int,Int,Int,Int,Int,Int,Int)) test
              , test' "(Int,Int,Int,Int,Int,Int,Int,Int,Int)"
                        (test :: T (Int,Int,Int,Int,Int,Int,Int,Int,Int)) test
              , test' "(Int,Int,Int,Int,Int,Int,Int,Int,Int,Int)"
                        (test :: T (Int,Int,Int,Int,Int,Int,Int,Int,Int,Int)) test
  
              , test' "B.ByteString" (test :: T B.ByteString) test
              , test' "L.ByteString" (test :: T L.ByteString) test
  #if MIN_VERSION_bytestring(0,10,4)
              , test' "ShortByteString" (test :: T ShortByteString) test
  #endif
              ]
  
          , testGroup "Invariants" $ map (uncurry testProperty)
              [ ("B.ByteString invariant",   p (prop_invariant :: B B.ByteString                 ))
              , ("[B.ByteString] invariant", p (prop_invariant :: B [B.ByteString]               ))
              , ("L.ByteString invariant",   p (prop_invariant :: B L.ByteString                 ))
              , ("[L.ByteString] invariant", p (prop_invariant :: B [L.ByteString]               ))
  #if MIN_VERSION_bytestring(0,10,4)
              , ("ShortByteString invariant",  p (prop_invariant :: B ShortByteString            ))
              , ("[ShortByteString] invariant", p (prop_invariant :: B [ShortByteString]         ))
  #endif
              ]
  #ifdef HAS_FIXED_CONSTRUCTOR
          , testGroup "Fixed"
              [ testProperty "Serialisation same"       $ p prop_fixed_ser
              , testProperty "MkFixed -> HasResolution" $ p prop_fixed_constr_resolution
              , testProperty "HasResolution -> MkFixed" $ p prop_fixed_resolution_constr
              ]
  #endif
          , testTypeable
          ]
  