irc-dcc-2.0.0: test/Network/IRC/DCCTest.hs
{-# LANGUAGE TemplateHaskell #-}
module Network.IRC.DCCTest where
import Network.IRC.DCC.Internal
import Control.Monad (replicateM)
import Data.ByteString.Char8 (pack)
import Data.IP (IPv4, toIPv4)
import Network.Socket (PortNumber)
import Path (mkAbsFile, mkRelFile)
import Test.Hspec.Attoparsec
import Test.Tasty (TestTree)
import Test.Tasty.Hspec (describe, it, testSpec)
import Test.Tasty.QuickCheck (Arbitrary (..), Gen, choose,
elements, forAll, oneof, property)
instance Arbitrary Token where
arbitrary = Token . pack <$> arbitrary
instance Arbitrary PortNumber where
arbitrary = fromIntegral <$> (choose (1, 65535) :: Gen Int)
instance Arbitrary IPv4 where
arbitrary = toIPv4 <$> replicateM 4 (choose (0, 255))
instance Arbitrary Path where
arbitrary = elements [ Rel Simple $(mkRelFile "filename")
, Rel Simple $(mkRelFile "filename.txt")
, Rel Simple $(mkRelFile "file\8195name.txt")
, Rel Simple $(mkRelFile "\128110\127997")
, Abs Simple $(mkAbsFile "/home/user/dirname/filename.txt")
, Rel Quoted $(mkRelFile "filename.txt")
, Rel Quoted $(mkRelFile "file name.txt")
, Rel Quoted $(mkRelFile "file\8195 name.txt")
, Rel Quoted $(mkRelFile "\128110\127997")
, Abs Quoted $(mkAbsFile "/home/user/dir name/filename.txt")
]
instance Arbitrary FileOffset where
arbitrary = FileOffset <$> arbitrary
instance Arbitrary DccChat where
arbitrary = oneof [ Chat <$> arbitrary <*> arbitrary
, ChatWhiteboard <$> arbitrary <*> arbitrary
]
instance Arbitrary DccSend where
arbitrary = oneof [ Send <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
, SendReverseServer <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
]
instance Arbitrary DccResume where
arbitrary = oneof [ Resume <$> arbitrary <*> arbitrary <*> arbitrary
, ResumeReverse <$> arbitrary <*> arbitrary <*> arbitrary
]
instance Arbitrary DccAccept where
arbitrary = oneof [ Accept <$> arbitrary <*> arbitrary <*> arbitrary
, AcceptReverse <$> arbitrary <*> arbitrary <*> arbitrary
]
instance Arbitrary DccSendReverseClient where
arbitrary = SendReverseClient
<$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
spec :: IO TestTree
spec = testSpec "DCC message serialization" $ do
describe "Parsing single elements" $ do
describe "Network byte order IP" $ do
describe "[SUCCESS]" $ do
it "parse . encode == id" $ property $ \ip ->
ipToBigEndianBS ip ~> ipBigEndian `parseSatisfies` (== ip)
it "At beginning of stream" $
pack "0abcd" ~?> ipBigEndian
`leavesUnconsumed` pack "abcd"
describe "[FAILURE]" $ do
it "Parse < 0.0.0.0" $
forAll (choose (minBound, -1 :: Int)) $ \ip ->
ipBigEndian `shouldFailOn` pack (show ip)
it "Parse > 255.255.255.255" $
forAll (choose (4294967296, maxBound :: Int)) $ \ip ->
ipBigEndian `shouldFailOn` pack (show ip)
it "Non-digits" $
ipBigEndian `shouldFailOn` pack "abcd"
it "When not at beginning of stream" $
ipBigEndian `shouldFailOn` pack " 0"
describe "File name" $ do
describe "[SUCCESS]" $ do
it "parse . encode == id" $ property $ \name ->
pathToBS name ~> path `parseSatisfies` (== name)
it "At beginning of stream" $
pack "filename 122350" ~?> path
`leavesUnconsumed` pack " 122350"
-- TODO Not sure exactly what to do with this yet. On Unix the whole thing
-- could be a file name, while on windows it is obviously an absolute path
-- it "File name from absolute unix path" $
-- ("c:\\Users\\user\\filename.txt" ~> path
-- `shouldParse` $(mkRelFile "filename.txt")
describe "[FAILURE]" $ do
it "ASCII filename with space" $
pack "file name.txt" ~?> path
`leavesUnconsumed` pack " name.txt"
it "Not at beginning of stream" $
path `shouldFailOn` pack " filename"
describe "Token" $
describe "[SUCCESS]" $
it "parse . encode == id" $ property $ \t ->
tokenToBS t ~> token `parseSatisfies` (== t)
describe "TCP port" $ do
describe "[SUCCESS]" $
it "parse . encode == id" $ property $ \p ->
tcpPortToBS p ~> tcpPort `parseSatisfies` (== p)
describe "[FAILURE]" $ do
it "Parse <= 0" $
forAll (choose (minBound, 0 :: Int)) $ \p ->
tcpPort `shouldFailOn` pack (show p)
it "Parse > 65535" $
forAll (choose (65535, maxBound :: Int)) $ \p ->
tcpPort `shouldFailOn` pack (show p)
describe "File offset" $ do
describe "[SUCCESS]" $
it "parse . encode == id" $ property $ \o ->
fileOffsetToBS o ~> fileOffset `parseSatisfies` (== o)
describe "[FAILURE]" $
it "Parse < 0" $
forAll (choose (minBound, -1 :: Int)) $ \o ->
fileOffset `shouldFailOn` pack (show o)
describe "DCC commands" $ do
describe "DCC CHAT" $
describe "[SUCCESS]" $
it "fromCtcp . toCtcp == id" $ property $ \cmd ->
fromCtcp (toCtcp cmd) == Right (cmd :: DccChat)
describe "DCC CLOSE" $
describe "[SUCCESS]" $
it "fromCtcp . toCtcp == id" $
fromCtcp (toCtcp Close) == Right Close
describe "DCC SEND" $
describe "[SUCCESS]" $
it "fromCtcp . toCtcp == id" $ property $ \cmd ->
fromCtcp (toCtcp cmd) == Right (cmd :: DccSend)
describe "DCC RESUME" $
describe "[SUCCESS]" $
it "fromCtcp . toCtcp == id" $ property $ \cmd ->
fromCtcp (toCtcp cmd) == Right (cmd :: DccResume)
describe "DCC ACCEPT" $
describe "[SUCCESS]" $
it "fromCtcp . toCtcp == id" $ property $ \cmd ->
fromCtcp (toCtcp cmd) == Right (cmd :: DccAccept)
describe "DCC SEND (reverse client)" $
describe "[SUCCESS]" $
it "fromCtcp . toCtcp == id" $ property $ \cmd ->
fromCtcp (toCtcp cmd) == Right (cmd :: DccSendReverseClient)