packages feed

mqtt-0.1.0.0: test/EncodingTest.hs

{-# LANGUAGE OverloadedStrings #-}
module EncodingTest (tests) where

import qualified Data.Binary.Get         as SG
import qualified Data.ByteString.Builder as BS
import qualified Data.ByteString.Lazy    as BSL
import           Prelude                 hiding (head)
import           Test.Tasty
import           Test.Tasty.HUnit
import           Test.Tasty.QuickCheck   as QC

import           Network.MQTT.Message
import qualified Network.MQTT.Message    as Topic

tests :: TestTree
tests = testGroup "Encoding / Decoding"
  [ testClientPacket
  , testServerPacket
  , testLengthEncoding
  ]

testClientPacket :: TestTree
testClientPacket = QC.testProperty "clientMessageBuilder <-> clientPacketParser" $ \msg->
  msg === SG.runGet clientPacketParser (BS.toLazyByteString $ clientPacketBuilder msg)

testServerPacket :: TestTree
testServerPacket = QC.testProperty "serverMessageBuilder <-> serverPacketParser" $ \msg->
  msg === SG.runGet serverPacketParser (BS.toLazyByteString $ serverPacketBuilder msg)

instance Arbitrary ClientPacket where
  arbitrary = oneof
    [ arbitraryConnect
    , arbitraryPublish
    , arbitrarySubscribe
    , arbitraryUnsubscribe
    , ClientPublishAcknowledged <$> arbitrary
    , ClientPublishReceived     <$> arbitrary
    , ClientPublishRelease      <$> arbitrary
    , ClientPublishComplete     <$> arbitrary
    , pure ClientPingRequest
    , pure ClientDisconnect
    ]
    where
      arbitraryConnect = ClientConnect
        <$> elements [ "", "client-identifier" ]
        <*> arbitrary
        <*> arbitrary
        <*> arbitrary
        <*> oneof [ pure Nothing, (Just .) . (,)
          <$> elements [ "", "username" ]
          <*> oneof [ pure Nothing, Just <$> elements [ Password "", Password "password" ] ] ]
      arbitraryPublish = do
        msg <- arbitrary
        dup <- Duplicate <$> if msgQoS msg == QoS0 then pure False else arbitrary
        pid <- PacketIdentifier <$> if msgQoS msg == QoS0 then pure (-1) else choose (0, 65535)
        pure (ClientPublish pid dup msg)
      arbitrarySubscribe = ClientSubscribe
        <$> arbitrary
        <*> listOf1 ((,) <$> arbitrary <*> arbitrary )
      arbitraryUnsubscribe = ClientUnsubscribe
        <$> arbitrary
        <*> listOf1 arbitrary

instance Arbitrary ServerPacket where
  arbitrary = oneof
    [ ServerConnectionAccepted      <$> arbitrary
    , ServerConnectionRejected      <$> arbitrary
    , do
        msg <- arbitrary
        dup <- Duplicate <$> if msgQoS msg == QoS0 then pure False else arbitrary
        pid <- PacketIdentifier <$> if msgQoS msg == QoS0 then pure (-1) else choose (0, 65535)
        pure (ServerPublish pid dup msg)
    , ServerPublishAcknowledged     <$> arbitrary
    , ServerPublishReceived         <$> arbitrary
    , ServerPublishRelease          <$> arbitrary
    , ServerPublishComplete         <$> arbitrary
    , ServerSubscribeAcknowledged   <$> arbitrary <*> listOf1 arbitrary
    , ServerUnsubscribeAcknowledged <$> arbitrary
    , pure ServerPingResponse
    ]

instance Arbitrary Message where
  arbitrary = Message
    <$> arbitrary
    <*> arbitrary
    <*> arbitrary
    <*> elements [ "", "shortTopic", Payload $ BSL.replicate 345 23 ]

instance Arbitrary PacketIdentifier where
  arbitrary = PacketIdentifier <$> choose (0, 65535)

instance Arbitrary SessionPresent where
  arbitrary = SessionPresent <$> arbitrary

instance Arbitrary CleanSession where
  arbitrary = CleanSession <$> arbitrary

instance Arbitrary Retain where
  arbitrary = Retain <$> arbitrary

instance Arbitrary KeepAliveInterval where
  arbitrary = KeepAliveInterval <$> arbitrary

instance Arbitrary RejectReason where
  arbitrary = elements
    [ UnacceptableProtocolVersion
    , IdentifierRejected
    , ServerUnavailable
    , BadUsernameOrPassword
    , NotAuthorized
    ]

instance Arbitrary QoS where
  arbitrary = elements [ QoS0, QoS1, QoS2 ]

instance Arbitrary Topic.Topic where
  arbitrary = elements
    [ "a"
    , "/"
    , "//"
    , "a/b/c"
    , "a/nyːnɔʃk/c"
    , "   /"
    , "   /   /"
    , "$SYS/grampfglompf"
    ]

instance Arbitrary Topic.Filter where
  arbitrary = elements
    [ "#"
    , "+/#"
    , "a/a/a/a/a/a/a/a/adasdas//asda/+/+/    /#"
    , "$SYS/#"
    ]

testLengthEncoding :: TestTree
testLengthEncoding =
  testGroup "lengthParser, lengthBuilder"
  [ testCase "p [193,2] == 321" $ assertEqual "" 321
      ( SG.runGet lengthParser $ BSL.pack [193,2] )

  , testCase "p [0x00] == 0" $ assertEqual "" 0
      ( SG.runGet lengthParser (BSL.pack [0x00]) )

  , testCase "p [0x7f] == 127" $ assertEqual "" 127
      ( SG.runGet lengthParser (BSL.pack [0x7f]) )

  , testCase "p [0x80, 0x01] == 128" $ assertEqual "" 128
      ( SG.runGet lengthParser (BSL.pack [0x80, 0x01]) )

  , testCase "p [0xff, 0x7f] == 16383" $ assertEqual "" 16383
      ( SG.runGet lengthParser (BSL.pack [0xff, 0x7f]) )

  , testCase "p [0x80, 0x80, 0x01] == 16384" $ assertEqual "" 16384
      ( SG.runGet lengthParser (BSL.pack [0x80, 0x80, 0x01]) )

  , testCase "p [0xff, 0xff, 0x7f] == 2097151" $ assertEqual "" 2097151
      ( SG.runGet lengthParser (BSL.pack [0xff, 0xff, 0x7f]) )

  , testCase "p [0x80, 0x80, 0x80, 0x01] == 2097152" $ assertEqual "" 2097152
      ( SG.runGet lengthParser (BSL.pack [0x80, 0x80, 0x80, 0x01]) )

  , testCase "p [0xff, 0xff, 0xff, 0x7f] == 268435455" $ assertEqual "" 268435455
      ( SG.runGet lengthParser (BSL.pack [0xff, 0xff, 0xff, 0x7f]) )

  , testCase "p [0xff, 0xff, 0xff, 0xff] == invalid" $ assertEqual ""
      ( Left ("", 4, "lengthParser: invalid input") )
      ( SG.runGetOrFail lengthParser (BSL.pack [0xff, 0xff, 0xff, 0xff]) )

  , QC.testProperty "lengthParser . lengthBuilder == id" $
      \i -> let i' = i `mod` 268435455
            in  i' == SG.runGet lengthParser (BS.toLazyByteString (lengthBuilder i'))
  ]