asif-6.0.4: test/Arbor/File/Format/Asif/Format/SegmentValueSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Arbor.File.Format.Asif.Format.SegmentValueSpec
( spec
) where
import Arbor.File.Format.Asif.Data.Ip
import Arbor.File.Format.Asif.Format as F
import Arbor.File.Format.Asif.Type
import Arbor.File.Format.Asif.Whatever
import Arbor.TestUtils
import Control.Lens
import Data.Semigroup ((<>))
import Data.Thyme.Time.Core
import HaskellWorks.Hspec.Hedgehog
import Hedgehog
import Test.Hspec
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Lazy.Char8 as LC8
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
{-# ANN module ("HLint: ignore Redundant do" :: String) #-}
spec :: Spec
spec = describe "Arbor.File.Format.Asif.Format.RawValueSpec" $ do
it "should parse strings" $ require $ property $ do
bs <- forAll $ Gen.list (Range.linear 0 20) (genLazyBS (Range.linear 0 512) Gen.alphaNum)
let body = mconcat $ (<> "\0") <$> bs
let seg = segment body $ metaFormat (Known F.StringZ)
segmentValues seg === fmap SString bs
it "should parse chars" $ require $ property $ do
bs <- forAll $ genLazyBS (Range.linear 0 512) Gen.latin1
let expected = LC8.unpack bs <&> SChar
let seg = segment bs $ metaFormat (Known F.Char)
segmentValues seg === expected
it "should parse time (micros)" $ require $ property $ do
ts <- forAll $ Gen.list (Range.linear 0 20) (Gen.int64 (Range.linear 0 maxBound))
let body = ts <&> BB.int64LE & mconcat & BB.toLazyByteString
let times = ts ^.. each . from microseconds . to posixSecondsToUTCTime
let seg = segment body $ metaFormat (Known F.TimeMicros64LE)
segmentValues seg === fmap STime times
it "should parse time (millis)" $ require $ property $ do
ts <- forAll $ Gen.list (Range.linear 0 20) (Gen.int64 (Range.linear 0 maxBound) <&> (`div` 1000))
let body = ts <&> BB.int64LE & mconcat & BB.toLazyByteString
let times = ts ^.. each . to (*1000) . from microseconds . to posixSecondsToUTCTime
let seg = segment body $ metaFormat (Known F.TimeMillis64LE)
segmentValues seg === fmap STime times
it "should parse int64" $ require $ property $ do
testMany F.Int64LE SInt64 BB.int64LE (Gen.int64 (Range.linear 0 maxBound))
it "should parse int32" $ require $ property $ do
testMany F.Int32LE SInt32 BB.int32LE (Gen.int32 (Range.linear 0 maxBound))
it "should parse int16" $ require $ property $ do
testMany F.Int16LE SInt16 BB.int16LE (Gen.int16 (Range.linear 0 maxBound))
it "should parse int8" $ require $ property $ do
testMany F.Int8 SInt8 BB.int8 (Gen.int8 (Range.linear 0 maxBound))
it "should parse word64" $ require $ property $ do
testMany F.Word64LE SWord64 BB.word64LE (Gen.word64 (Range.linear 0 maxBound))
it "should parse word32" $ require $ property $ do
testMany F.Word32LE SWord32 BB.word32LE (Gen.word32 (Range.linear 0 maxBound))
it "should parse word16" $ require $ property $ do
testMany F.Word16LE SWord16 BB.word16LE (Gen.word16 (Range.linear 0 maxBound))
it "should parse word8" $ require $ property $ do
testMany F.Word8 SWord8 BB.word8 (Gen.word8 (Range.linear 0 maxBound))
it "should parse BitString" $ require $ property $
let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict
in testOne F.BitString SBitString BB.lazyByteString gen
it "should parse Binary" $ require $ property $
let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict
in testOne F.Binary SBinary BB.lazyByteString gen
it "should parse Bitmap" $ require $ property $
let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict
in testOne F.Bitmap SBitmap BB.lazyByteString gen
it "should parse repeated BitString" $ require $ property $ do
n <- forAll $ Gen.int (Range.linear 1 32)
vs <- forAll $ Gen.list (Range.linear 0 20) (Gen.bytes (Range.singleton n) <&> LBS.fromStrict)
let seg = segment (mconcat vs) $ metaFormat (Known (Repeat (fromIntegral n) F.BitString))
segmentValues seg === fmap SBitString vs
it "should parse repeated chars" $ require $ property $ do
n <- forAll $ Gen.int (Range.linear 1 32)
vs <- forAll $ Gen.list (Range.linear 0 20) (Gen.string (Range.singleton n) Gen.alphaNum)
let seg = segment (LC8.pack $ mconcat vs) $ metaFormat (Known (Repeat (fromIntegral n) F.Char))
segmentValues seg === fmap (SList . fmap SChar) vs
it "should parse repeated ips" $ require $ property $ do
n <- forAll $ Gen.int (Range.linear 1 32)
k <- forAll $ Gen.int (Range.linear 0 20)
vs <- forAll $ Gen.list (Range.singleton (n*k)) (Gen.word32 Range.linearBounded)
let body = vs <&> BB.word32LE & mconcat & BB.toLazyByteString
let seg = segment body $ metaFormat (Known (Repeat (fromIntegral n) F.Ipv4))
let expected = vs <&> (SIpv4 . word32ToIpv4) & chunksOf n <&> SList
segmentValues seg === expected
genLazyBS :: MonadGen m => Range Int -> m Char -> m LBS.ByteString
genLazyBS rng gen = Gen.utf8 rng gen <&> LBS.fromStrict
testOne :: (Monad m, Show a)
=> Format
-> (a -> SegmentValue)
-> (a -> BB.Builder)
-> Gen a
-> PropertyT m ()
testOne fmt wrap put gen = do
v <- forAll gen
let body = put v & BB.toLazyByteString
let seg = segment body $ metaFormat (Known fmt)
segmentValues seg === [wrap v]
testMany :: (Monad m, Show a)
=> Format
-> (a -> SegmentValue)
-> (a -> BB.Builder)
-> Gen a
-> PropertyT m ()
testMany fmt wrap put gen = do
vs <- forAll $ Gen.list (Range.linear 0 20) gen
let body = vs <&> put & mconcat & BB.toLazyByteString
let seg = segment body $ metaFormat (Known fmt)
segmentValues seg === fmap wrap vs