-- Derived from @primitive@ package. {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} {- HLINT ignore "Redundant compare" -} import Control.Monad (when, unless) import Data.Array.Byte (ByteArray) import Data.Function (on) import Data.Proxy (Proxy(..)) import Data.Semigroup (Semigroup(..), stimesMonoid) import Data.Word (Word8) import GHC.Exts (IsList(..)) import Language.Haskell.TH.Syntax (lift) import Test.QuickCheck.Classes.Base (eqLaws, ordLaws, monoidLaws, showLaws, isListLaws, semigroupLaws, semigroupMonoidLaws, Laws(..)) import Test.Tasty (defaultMain, testGroup, TestTree) import Test.Tasty.QuickCheck (testProperty, (===), Property, NonNegative(..), property, Arbitrary(..)) main :: IO () main = do testByteArray defaultMain $ testGroup "ByteArray" [ testProperty "equality" byteArrayEqProp , testProperty "reflexivity" byteArrayReflexivityProp , testProperty "compare" byteArrayCompareProp , lawsToTest (eqLaws (Proxy :: Proxy MyByteArray)) , lawsToTest (ordLaws (Proxy :: Proxy MyByteArray)) , lawsToTest (monoidLaws (Proxy :: Proxy MyByteArray)) , lawsToTest (showLaws (Proxy :: Proxy MyByteArray)) , lawsToTest (isListLaws (Proxy :: Proxy MyByteArray)) , lawsToTest (semigroupLaws (Proxy :: Proxy MyByteArray)) , lawsToTest (semigroupMonoidLaws (Proxy :: Proxy MyByteArray)) , testProperty "stimes" byteArrayStimesProp , testProperty "lift" byteArrayLiftProp ] byteArrayCompareProp :: [Word8] -> [Word8] -> Property byteArrayCompareProp xs ys = compareLengthFirst xs ys === compare (mkByteArray xs) (mkByteArray ys) byteArrayEqProp :: [Word8] -> [Word8] -> Property byteArrayEqProp xs ys = (compareLengthFirst xs ys == EQ) === (mkByteArray xs == mkByteArray ys) byteArrayReflexivityProp :: [Word8] -> Property byteArrayReflexivityProp xs = property $ let ba = mkByteArray xs in ba == ba && compare ba ba == EQ compareLengthFirst :: [Word8] -> [Word8] -> Ordering compareLengthFirst xs ys = (compare `on` length) xs ys <> compare xs ys byteArrayStimesProp :: NonNegative Int -> MyByteArray -> Property byteArrayStimesProp (NonNegative n) (MyByteArray xs) = stimes n xs === stimesMonoid n xs byteArrayLiftProp :: Property byteArrayLiftProp = let ba = mkByteArray [0,1,2,3,4,5] in ba === $(lift (fromList [0,1,2,3,4,5] :: ByteArray)) lawsToTest :: Laws -> TestTree lawsToTest (Laws name pairs) = testGroup name (map (uncurry testProperty) pairs) testByteArray :: IO () testByteArray = do let arr1 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8]) arr2 = mkByteArray ([0xde, 0xad, 0xbe, 0xef] :: [Word8]) arr3 = mkByteArray ([0xde, 0xad, 0xbe, 0xee] :: [Word8]) arr4 = mkByteArray ([0xde, 0xad, 0xbe, 0xdd] :: [Word8]) arr5 = mkByteArray ([0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xdd] :: [Word8]) arr6 = mkByteArray ([0xde, 0xad, 0x00, 0x01, 0xb0] :: [Word8]) when (show arr1 /= "[0xde, 0xad, 0xbe, 0xef]") $ fail $ "ByteArray Show incorrect: " ++ show arr1 when (show arr6 /= "[0xde, 0xad, 0x00, 0x01, 0xb0]") $ fail $ "ByteArray Show incorrect: " ++ show arr6 unless (arr1 > arr3) $ fail "ByteArray Ord incorrect" unless (arr1 == arr2) $ fail "ByteArray Eq incorrect" unless (mappend arr1 arr4 == arr5) $ fail "ByteArray Monoid mappend incorrect" unless (mappend arr1 (mappend arr3 arr4) == mappend (mappend arr1 arr3) arr4) $ fail "ByteArray Monoid mappend not associative" unless (mconcat [arr1, arr2, arr3, arr4, arr5] == (arr1 <> arr2 <> arr3 <> arr4 <> arr5)) $ fail "ByteArray Monoid mconcat incorrect" unless (stimes (3 :: Int) arr4 == (arr4 <> arr4 <> arr4)) $ fail "ByteArray Semigroup stimes incorrect" newtype MyByteArray = MyByteArray ByteArray deriving (Eq, Ord, Semigroup, Monoid, Show) instance IsList MyByteArray where type Item MyByteArray = Item ByteArray fromList = MyByteArray . fromList toList (MyByteArray ba) = toList ba instance Arbitrary MyByteArray where arbitrary = MyByteArray . mkByteArray <$> arbitrary mkByteArray :: [Word8] -> ByteArray mkByteArray = fromList