{-# language NamedFieldPuns #-} {-# language NumericUnderscores #-} {-# language TypeApplications #-} import Asn.Ber (Value(..),Class(..),Contents(..)) import Data.Word (Word32) import Test.QuickCheck.Arbitrary (Arbitrary(..)) import Test.QuickCheck.Gen (Gen) import Test.Tasty (TestTree,defaultMain,testGroup) import Test.Tasty.QuickCheck ((===)) import Test.Tasty.QuickCheck (testProperty) import qualified Asn.Ber as Ber import qualified Asn.Oid as Oid import qualified Asn.Ber.Encode as Ber import qualified Data.Bytes as Bytes import qualified Data.Primitive as Prim import qualified Data.Primitive.SmallArray as SA import qualified Data.Text.Short as TS import qualified GHC.Exts as Exts import qualified Test.QuickCheck.Gen as Gen import qualified Test.QuickCheck as QC main :: IO () main = defaultMain tests tests :: TestTree tests = testGroup "encoder-decoder" [ testProperty "encoded value is decodable" $ \val -> let bs = Ber.encode val val' = Ber.decode bs in val' === Right val ] instance Arbitrary Value where arbitrary = do Gen.oneof [aUniversal, anUnresolved] -- TODO where anUnresolved = do tagClass <- Gen.oneof $ pure <$> [Application, ContextSpecific, Private] tagNumber <- Gen.oneof $ Gen.chooseBoundedIntegral <$> [(0,30), (31,65535)] contents <- Unresolved <$> arbitrary pure Value{tagClass,tagNumber,contents} aUniversal = do let tagClass = Universal Gen.oneof [ do let tagNumber = 0x02 contents <- Integer <$> arbitrary pure Value{tagClass,tagNumber,contents} , do let tagNumber = 0x17 contents <- UtcTime <$> QC.choose (-100_000_000,1_500_000_000) pure Value{tagClass,tagNumber,contents} , do let tagNumber = 0x01 contents <- Boolean <$> arbitrary pure Value{tagClass,tagNumber,contents} , do contents <- OctetString <$> arbitrary let tagNumber = 0x04 pure Value{tagClass,tagNumber,contents} , do let tagNumber = 0x03 contents <- BitString <$> Gen.chooseBoundedIntegral (0,7) <*> arbitrary pure Value{tagClass,tagNumber,contents} , do let tagNumber = 0x05 let contents = Null pure Value{tagClass,tagNumber,contents} , do let tagNumber = 0x06 a <- Gen.chooseBoundedIntegral (0,2) b <- Gen.chooseBoundedIntegral (0,39) rest <- arbitrary @[Word32] let contents = ObjectIdentifier (Oid.Oid (Exts.fromList (a:b:rest))) pure Value{tagClass,tagNumber,contents} , do let tagNumber = 0x0C contents <- Utf8String <$> arbitrary pure Value{tagClass,tagNumber,contents} , do let tagNumber = 0x13 chars <- Gen.listOf $ Gen.elements $ concat [ ['A'..'Z'] , ['a'..'z'] , ['0'..'9'] , " '()+,-./:=?" ] let contents = PrintableString $ TS.fromString chars pure Value{tagClass,tagNumber,contents} -- UtcTime -- TODO ] aConstructed = do tagClass <- arbitrary tagNumber <- arbitrary contents <- Constructed . SA.smallArrayFromList <$> Gen.sized go pure Value{tagClass,tagNumber,contents} where go :: Int -> Gen [Value] go 0 = pure [] go n = Gen.resize (n `div` 10) $ Gen.listOf arbitrary instance Arbitrary Class where arbitrary = Gen.oneof $ pure <$> [ Universal , Application , ContextSpecific , Private ] instance Arbitrary Bytes.Bytes where arbitrary = Bytes.fromLatinString <$> arbitrary instance Arbitrary TS.ShortText where arbitrary = TS.fromString <$> arbitrary