Misc refresh and cleanup, added some QuickCheck props

master
Levi Pearson 2014-06-12 03:33:09 -06:00
parent 45eb76c8af
commit fd141d09fb
10 changed files with 827 additions and 712 deletions

View File

@ -23,26 +23,26 @@ executable pipes-irc-server
, Pipes.IRC.Server.Types
, Pipes.IRC.Server.MessageHandler
-- other-extensions:
build-depends: base >= 4.6 && < 4.7
, mtl >= 2.1 && < 3
, errors >= 1.4 && < 2
, mmorph >= 1 && < 2
, containers >= 0.5 && < 1
, bytestring >= 0.10 && < 0.11
, text >= 0.11.3 && < 0.12
, attoparsec >= 0.10 && < 0.11
, network >= 2.4 && < 2.5
, pipes >= 4 && < 5
, pipes-concurrency >= 2 && < 3
, pipes-bytestring >= 1.0 && < 2
, pipes-parse >= 2.0 && < 3
, pipes-attoparsec >= 0.3 && < 1
, pipes-network >= 0.6 && < 1
, stm >= 2 && < 3
, time >= 1.4 && < 1.5
, async >= 2 && < 3
, free >= 3 && < 4
, lens >= 3 && < 4
build-depends: base
, mtl
, errors
, mmorph
, containers
, bytestring
, text
, attoparsec
, network
, pipes
, pipes-concurrency
, pipes-bytestring
, pipes-parse
, pipes-attoparsec
, pipes-network
, stm
, time
, async
, free
, lens
hs-source-dirs: src
default-language: Haskell2010
@ -50,21 +50,23 @@ executable pipes-irc-server
test-suite tests
type: exitcode-stdio-1.0
main-is: Main.hs
build-depends: base >= 4.6 && < 4.7
, mtl >= 2.1 && < 3
, containers >= 0.5 && < 1
, bytestring >= 0.10 && < 0.11
, text >= 0.11.3 && < 0.12
, attoparsec >= 0.10 && < 0.11
, pipes >= 4 && < 5
, pipes-concurrency >= 2 && < 3
, pipes-bytestring >= 1.0 && < 2
, pipes-parse >= 2.0 && < 3
, pipes-attoparsec >= 0.3 && < 1
, pipes-network >= 0.6 && < 1
, stm >= 2 && < 3
, async >= 2 && < 3
, free >= 3 && < 4
build-depends: base
, mtl
, containers
, bytestring
, text
, attoparsec
, pipes
, pipes-concurrency
, pipes-bytestring
, pipes-parse
, pipes-attoparsec
, pipes-network
, stm
, async
, free
, lens
, time
build-depends: tasty
, tasty-hspec
, tasty-hunit

View File

@ -35,10 +35,10 @@ version = "0.1a"
parseMessage :: Producer ByteString IO ()
-> Producer (Either ByteString IrcMessage) IO ()
parseMessage prod = do
void $ for (parseMany parseMsgOrLine prod) $ \res ->
void $ for (parsed parseMsgOrLine prod) $ \res ->
case res of
(_, Left _) -> yield $ Left "ERROR Bad Parse"
(_, Right val) -> yield $ Right val
(Left _) -> yield $ Left "ERROR Bad Parse"
(Right val) -> yield $ Right val
return ()
renderMessage :: Pipe IrcMessage ByteString IO ()
@ -153,7 +153,8 @@ idlePinger srv cid =
atomically $ do
conns <- readTVar (srv ^. ircConnections)
PC.send (conns ! cid ^. out) pingMsg
void $ PC.send (conns ! cid ^. out) pingMsg
return ()
threadDelay oneMinute

View File

@ -15,6 +15,7 @@ module Pipes.IRC.Server.Channel
, chanSigil
, chanUserSigil
, chanSetPass
, chanClearPass
, chanHasPass
, chanCheckPass
, chanAddOper
@ -74,21 +75,21 @@ chanAddUser, chanDelUser :: NickKey -- ^ user to add to channel
-- | Add a user to the channel's list of users. This does not change
-- the set of set of channels on the user or perform any checking;
-- this just performs the low-level change to the channel.
chanAddUser un = chanUsers %~ insert un
chanAddUser uname = chanUsers %~ insert uname
-- | Delete a user from the channel's list of users. This does not
-- change the set of channels on the user or perform any checking;
-- this just performs the low-level change to the channel.
chanDelUser un = (chanUsers %~ delete un)
. (chanOpers %~ delete un)
. (chanVoices %~ delete un)
chanDelUser uname = (chanUsers %~ delete uname)
. (chanOpers %~ delete uname)
. (chanVoices %~ delete uname)
-- | Is the user owning the given nick on the channel? Returns 'True'
-- if the user is in fact on the channel.
chanHasUser :: NickKey -- ^ user to check presence of on the channel
-> IrcChannel -- ^ channel in which to look for the user
-> Bool -- ^ 'True' if the user is in the channel
chanHasUser un ch = member un (ch ^. chanUsers)
chanHasUser uname ch = member uname (ch ^. chanUsers)
-- | Set the topic of discussion in the channel. This does not do any
-- permission or size checking; it just performs the low-level action.
@ -123,8 +124,8 @@ chanSigil ch | chanHasModeFlag Secret ch = "@"
chanUserSigil :: NickKey -- ^ nickname of user of which to find sigil
-> IrcChannel -- ^ channel the user may have status in
-> ByteString -- ^ the sigil associated with the user's status
chanUserSigil un ch | member un $ ch ^. chanOpers = "@"
| member un $ ch ^. chanVoices = "+"
chanUserSigil uname ch | member uname $ ch ^. chanOpers = "@"
| member uname $ ch ^. chanVoices = "+"
| otherwise = ""
-- | Set a password key that must be given with a JOIN command in
@ -135,6 +136,12 @@ chanSetPass :: PassKey -- ^ password key to set for a channel
-> IrcChannel -- ^ channel with the password set
chanSetPass pwd = chanPass .~ Just pwd
-- | Clear password key for the channel. This does not do permission or
-- error checking, it just performs the low-level operation.
chanClearPass :: IrcChannel -- ^ channel to clear the password key
-> IrcChannel -- ^ channel with the password cleared
chanClearPass = chanPass .~ Nothing
-- | Does the channel have a password key set? Returns 'True' if the
-- channel does have a password key set.
chanHasPass :: IrcChannel -- ^ channel on which to look for password key
@ -158,12 +165,12 @@ chanAddOper, chanDelOper :: NickKey -- ^ nickname of user
-- | Add the given nickname as an operator on the channel. This does
-- not perform any permission or error checks, it just performs the
-- low-level operation.
chanAddOper un = chanOpers %~ insert un
chanAddOper uname = chanOpers %~ insert uname
-- | Remove the given nickname as an operator on the channel. This
-- does not perform any permission or error checks, it just performs
-- the low-level operation.
chanDelOper un = chanOpers %~ delete un
chanDelOper uname = chanOpers %~ delete uname
chanAddVoice, chanDelVoice :: NickKey -- ^ nickname of user
-> IrcChannel -- ^ channel to change voice on
@ -172,12 +179,12 @@ chanAddVoice, chanDelVoice :: NickKey -- ^ nickname of user
-- | Give voice status to the given nickname on the channel. This
-- does not perform any permission or error checks, it just performs
-- the low-level operation.
chanAddVoice un = chanVoices %~ insert un
chanAddVoice uname = chanVoices %~ insert uname
-- | Remove voice status from the given nickname on the channel. This
-- does not perform any permission or error checks, it just performs
-- the low-level operation.
chanDelVoice un = chanVoices %~ delete un
chanDelVoice uname = chanVoices %~ delete uname
chanAddInvite, chanDelInvite :: NickKey -- ^ nickname of user
-> IrcChannel -- ^ channel to change invitation
@ -187,12 +194,12 @@ chanAddInvite, chanDelInvite :: NickKey -- ^ nickname of user
-- This will allow the user to join when the channel is invite-only.
-- This does not perform any permission or error checks, it just does
-- the operation.
chanAddInvite un = chanInvites %~ insert un
chanAddInvite uname = chanInvites %~ insert uname
-- | Remove record of the invitation for the nickname to join the
-- channel. This does not perform permission or error checks, it just
-- does the low-level operation.
chanDelInvite un = chanInvites %~ delete un
chanDelInvite uname = chanInvites %~ delete uname
-- | Change the nick of a user on the channel. This will replace the
-- old name with the news name in the member list, invitation list,
@ -223,7 +230,7 @@ chanChangeNick old new ch = (chOps . chVoice . chInvite . chUsers) ch
chanUserIsOper :: NickKey -- ^ nickname to check
-> IrcChannel -- ^ channel to check
-> Bool -- ^ 'True' if nickname has ops on channel
chanUserIsOper un ch = member un $ ch ^. chanOpers
chanUserIsOper uname ch = member uname $ ch ^. chanOpers
-- | Does the nickname have voice on the channel? Returns 'True' if
-- the user has the voice mode flag. This does not consider any other
@ -231,7 +238,7 @@ chanUserIsOper un ch = member un $ ch ^. chanOpers
chanUserHasVoice :: NickKey -- ^ nickname to check
-> IrcChannel -- ^ channel to check
-> Bool -- ^ 'True' if nickname has voice on channel
chanUserHasVoice un ch = member un $ ch ^. chanVoices
chanUserHasVoice uname ch = member uname $ ch ^. chanVoices
-- | Has the nickname been invited to the channel? Returns 'True' if
-- the user has been invited. This does not consider any other
@ -239,7 +246,7 @@ chanUserHasVoice un ch = member un $ ch ^. chanVoices
chanUserIsInvited :: NickKey -- ^ nickname to check
-> IrcChannel -- ^ channel to check
-> Bool -- ^ 'True' if nickname has been invited
chanUserIsInvited un ch = member un $ ch ^. chanInvites
chanUserIsInvited uname ch = member uname $ ch ^. chanInvites
-- | Is the nickname allowed to speak on the channel? Returns 'True'
-- if the relevant permission checks all pass. The checks consider
@ -248,11 +255,11 @@ chanUserIsInvited un ch = member un $ ch ^. chanInvites
chanUserMaySpeak :: NickKey -- ^ nickname to check
-> IrcChannel -- ^ channel to check
-> Bool -- ^ 'True' if nickname may speak on channel
chanUserMaySpeak un ch
| (chanHasUser un ch || not (chanHasModeFlag NoOutsideMsgs ch))
chanUserMaySpeak uname ch
| (chanHasUser uname ch || not (chanHasModeFlag NoOutsideMsgs ch))
&& not (chanHasModeFlag Moderated ch) = True
| chanUserIsOper un ch = True
| chanUserHasVoice un ch = True
| chanUserIsOper uname ch = True
| chanUserHasVoice uname ch = True
| otherwise = False
-- | Is the nickname allowed to join the channel? Returns 'True' if
@ -261,9 +268,9 @@ chanUserMaySpeak un ch
chanUserMayJoin :: NickKey -- ^ nickname to check
-> IrcChannel -- ^ channel to check
-> Bool -- ^ 'True' if nickname may join the channel
chanUserMayJoin un ch
chanUserMayJoin uname ch
| not $ chanHasModeFlag InviteOnly ch = True
| chanUserIsInvited un ch = True
| chanUserIsInvited uname ch = True
| otherwise = False
-- | Is the nickname allowed to set the topic for the channel?
@ -271,8 +278,8 @@ chanUserMayJoin un ch
chanUserMaySetTopic :: NickKey -- ^ nickname to check
-> IrcChannel -- ^ channel to check
-> Bool -- ^ 'True' if nickname may set the topic
chanUserMaySetTopic un ch
chanUserMaySetTopic uname ch
| not (chanHasModeFlag TopicOperOnly ch) &&
chanHasUser un ch = True
| chanUserIsOper un ch = True
chanHasUser uname ch = True
| chanUserIsOper uname ch = True
| otherwise = False

View File

@ -4,6 +4,7 @@ module Pipes.IRC.Server.EventHandler
( ircEventHandler )
where
import Control.Applicative ((<$>))
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Lens
@ -25,12 +26,10 @@ ircEventHandler srv evt =
case evt of
Close connId -> do
outConns <- readTVarIO $ srv ^. ircConnections
case M.lookup connId outConns of
Just IrcConnection{..} -> return False
_ -> return True
return $ isJust (M.lookup connId outConns)
Msg {..} -> do
outConns <- readTVarIO $ srv ^. ircConnections
let os = fmap _out $ DM.mapMaybe (`M.lookup` outConns) _outDest
let os = _out <$> DM.mapMaybe (`M.lookup` outConns) _outDest
sendToMany _outMsg os
logOutMsg _outMsg _outDest
return True

View File

@ -97,7 +97,7 @@ channelTargets chname = do
let cUsers chan = S.elems (S.delete mynick $ chan ^. chanUsers)
let chmap = srv ^. ircChannels
case M.lookup chname chmap of
Just chan -> fmap catMaybes $ forM (cUsers chan) userTarget
Just chan -> catMaybes <$> forM (cUsers chan) userTarget
_ -> do tellNumeric err_nosuchnick [chname, ":No such nick/channel"]
return []

View File

@ -5,7 +5,7 @@ module Pipes.IRC.Server.MessageHandler
( ircMessageHandler )
where
import Control.Applicative (pure, (<$>), (<|>))
import Control.Applicative ((<|>))
import Control.Error
import Control.Lens
import Control.Monad.RWS

View File

@ -53,9 +53,9 @@ ircDelUser nn srv =
uchans = S.elems (usr ^. userChannels)
ichans = S.elems (usr ^. userInvites)
in
(ircUsers %~ M.delete nn) .
(ircChannels %~ alterAtKeys (ircPartChan nn) uchans) .
(ircChannels %~ adjustAtKeys (chanDelInvite nn) ichans)
(ircUsers %~ M.delete nn)
. (ircChannels %~ alterAtKeys (ircPartChan nn) uchans)
. (ircChannels %~ adjustAtKeys (chanDelInvite nn) ichans)
-- | Check whether a user with the given nickname is known by the
-- server. This only checks for fully-registered users; the nick
@ -79,14 +79,16 @@ ircJoin :: NickKey -- ^ nickname of the joining user
-> ChanKey -- ^ name of the channel to join
-> IrcServer -- ^ server to perform the join on
-> IrcServer -- ^ new server with join completed
ircJoin un cn = (ircChannels %~ M.alter alterChan cn)
. (ircUsers %~ M.adjust (userAddChan cn) un)
ircJoin uname cn = (ircChannels %~ M.alter alterChan cn)
. (ircUsers %~ M.adjust (userAddChan cn) uname)
where
alterChan mChan = Just $ chanAddUser un (fromMaybe (newChannel un) mChan)
alterChan mChan = Just $
chanAddUser uname (fromMaybe (newChannel uname) mChan)
-- Helper 'alter' function for ircPart, not exported
ircPartChan :: NickKey -> Maybe IrcChannel -> Maybe IrcChannel
ircPartChan un (Just chan) = case chanDelUser un chan of
ircPartChan uname (Just chan) =
case chanDelUser uname chan of
IrcChannel{ _chanUsers = us }
| us == S.empty -> Nothing
chan' -> Just chan'
@ -99,8 +101,8 @@ ircPart :: NickKey -- ^ nickname of parting user
-> ChanKey -- ^ name of the channel to part from
-> IrcServer -- ^ server to perform the part on
-> IrcServer -- ^ new server with part completed
ircPart un cn srv =
srv & (ircChannels %~ (M.alter $ ircPartChan un) cn)
ircPart uname cn srv =
srv & (ircChannels %~ (M.alter $ ircPartChan uname) cn)
& (ircUsers %~ adjustAtKeys (userDelInvite cn) iusers)
where
chan = (srv ^. ircChannels) ! cn
@ -112,8 +114,8 @@ ircInvite :: NickKey -- ^ nickname of user to invite
-> ChanKey -- ^ name of channel user is invited to
-> IrcServer -- ^ server to perform the invitation on
-> IrcServer -- ^ new server with invite completed
ircInvite un cn = (ircChannels %~ M.adjust (chanAddInvite un) cn)
. (ircUsers %~ M.adjust (userAddInvite cn) un)
ircInvite uname cn = (ircChannels %~ M.adjust (chanAddInvite uname) cn)
. (ircUsers %~ M.adjust (userAddInvite cn) uname)
-- | Determine whether the user with the given nickname is disallowed
-- from joining the channel due to the 'InviteOnly' flag and lack of

View File

@ -62,7 +62,7 @@ data IrcUserMode = Away -- ^ the user is marked as away from keyboard
| Oper -- ^ the user is an IRC operator
| LocalOper -- ^ the user is a local server operator
| ServerNotices -- ^ the user receives server notices
deriving (Show, Eq, Enum, Ord)
deriving (Show, Eq, Enum, Ord, Bounded)
-- | An 'IrcUser' record tracks the state of a registered user that is not
-- channel-specific.
@ -90,7 +90,7 @@ data IrcChanModeFlags = Anonymous -- ^ all communication is anonymized
| Private -- ^ channel shows up as private in list
| Secret -- ^ channel does not appear in list
| TopicOperOnly -- ^ topic may only be set by ops
deriving (Show, Eq, Enum, Ord)
deriving (Show, Eq, Enum, Ord, Bounded)
-- | An 'IrcChannel' record tracks the state of a channel.
data IrcChannel =

View File

@ -4,26 +4,26 @@ module Main where
import Test.Tasty
import Test.Tasty.Hspec as HS
--import Test.Tasty.HUnit as HU
--import Test.Tasty.QuickCheck as QC
--import Test.Tasty.SmallCheck as SC
import Test.Tasty.QuickCheck as QC
import Data.Attoparsec.ByteString.Char8 as P
import Data.ByteString.Char8 as C8
import Control.Applicative
import qualified Data.ByteString.Char8 as C8
import Data.List
import Data.Ord
import Data.Monoid
import Pipes.IRC.Message.Parse
import Pipes.IRC.Message.Render
import Pipes.IRC.Message.Types
import ParseTests
import Pipes.IRC.Server.Channel
import Pipes.IRC.Server.Server
import Pipes.IRC.Server.Types
import Pipes.IRC.Server.User
main :: IO ()
main = defaultMain tests
tests :: TestTree
tests = testGroup "Tests" [specs]
--tests = testGroup "Tests" [specs, properties, unitTests]
tests = testGroup "Tests" [specs, userProperties, chanProperties]
-- Hspec Tests
@ -33,617 +33,8 @@ specs = testGroup "Specifications"
-- , HS.testCase "Message Rendering" msgRenderSpec
]
msgParseSpec :: Spec
msgParseSpec = do
describe "Parsing" $ do
describe "parseMsgOrLine" $ do
it "succeeds parsing an empty line, returning a Left value" $
pMsgOrLine "\r\n"
`shouldBe`
Right (Left "\r\n")
it "succeeds parsing an IRC message, returning a Right value" $
pMsgOrLine "PRIVMSG #haskell :Hi, guys!\r\n"
`shouldBe`
Right
(Right (IrcMessage Nothing (Left PRIVMSG) ["#haskell", "Hi, guys!"]))
describe "parseIrcMessage" $ do
context "Messages with no prefix" $ do
it "matches with no parameters" $
pMsg "NAMES\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NAMES) [])
it "matches with one parameter (without spaces)" $
pMsg "NICK WiZ\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NICK) ["WiZ"])
it "matches with one parameter (with spaces)" $
pMsg "QUIT :Goodbye, cruel world!\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left QUIT) ["Goodbye, cruel world!"])
context "Messages with server name prefix" $ do
it "matches with server name prefixes" $
pMsg ":foo.domain.com ERROR :Oh no!\r\n"
`shouldBe`
Right (IrcMessage (Just (Left "foo.domain.com"))
(Left ERROR)
["Oh no!"] )
it "matches with hyphenated server name prefixes" $
pMsg ":my-domain.org ERROR :Oh no!\r\n"
`shouldBe`
Right (IrcMessage (Just (Left "my-domain.org"))
(Left ERROR)
["Oh no!"] )
context "Messages with nickname prefix" $ do
it "matches with just nick" $
pMsg ":WiZ PRIVMSG #haskell :Hello\r\n"
`shouldBe`
Right (IrcMessage (Just (Right (NickName "WiZ" Nothing Nothing)))
(Left PRIVMSG)
["#haskell", "Hello"] )
it "matches with nick and user" $
pMsg ":WiZ!wiz PRIVMSG #haskell :Hello\r\n"
`shouldBe`
Right (IrcMessage (Just (Right (NickName "WiZ" (Just "wiz") Nothing)))
(Left PRIVMSG)
["#haskell", "Hello"] )
it "matches with nick, user, and host" $
pMsg ":WiZ!wiz@wiz-host.com PRIVMSG #haskell :Hello\r\n"
`shouldBe`
Right (IrcMessage (Just (Right
(NickName
"WiZ"
(Just "wiz")
(Just "wiz-host.com"))))
(Left PRIVMSG)
["#haskell", "Hello"] )
context "Examples from RFC1459" $ do
it "matches PASS example" $
pMsg "PASS secretpasswordhere\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PASS) ["secretpasswordhere"])
it "matches NICK example 1" $
pMsg "NICK Wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NICK) ["Wiz"])
it "matches NICK example 2" $
pMsg ":WiZ NICK Kilroy\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left NICK) ["Kilroy"])
it "matches USER example 1" $
pMsg "USER guest tolmoon tolsun :Ronnie Reagan\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left USER)
["guest", "tolmoon", "tolsun", "Ronnie Reagan"])
it "matches USER example 2" $
pMsg ":testnick USER guest tolmoon tolsun :Ronnie Reagan\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "testnick" Nothing Nothing)))
(Left USER) ["guest", "tolmoon", "tolsun", "Ronnie Reagan"])
it "matches SERVER example 1" $
pMsg "SERVER test.oulu.fi 1 :[tolsun.oulu.fi] Experimental server\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SERVER)
["test.oulu.fi", "1", "[tolsun.oulu.fi] Experimental server"])
it "matches SERVER example 2" $
pMsg ":tolsun.oulu.fi SERVER csd.bu.edu 5 :BU Central Server\r\n"
`shouldBe`
Right (IrcMessage
(Just (Left "tolsun.oulu.fi"))
(Left SERVER) ["csd.bu.edu", "5", "BU Central Server"])
it "matches OPER example" $
pMsg "OPER foo bar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left OPER) ["foo", "bar"])
it "matches QUIT example" $
pMsg "QUIT :Gone to have lunch\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left QUIT) ["Gone to have lunch"])
it "matches SQUIT example 1" $
pMsg "SQUIT tolsun.oulu.fi :Bad Link ?\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SQUIT)
["tolsun.oulu.fi", "Bad Link ?"])
it "matches SQUIT example 2" $
pMsg ":Trillian SQUIT cm22.eng.umd.edu :Server out of control\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Trillian" Nothing Nothing)))
(Left SQUIT) ["cm22.eng.umd.edu", "Server out of control"])
it "matches JOIN example 1" $
pMsg "JOIN #foobar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foobar"])
it "matches JOIN example 2" $
pMsg "JOIN &foo fubar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["&foo", "fubar"])
it "matches JOIN example 3" $
pMsg "JOIN #foo,&bar fubar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foo,&bar", "fubar"])
it "matches JOIN example 4" $
pMsg "JOIN #foo,&bar fubar,foobar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foo,&bar", "fubar,foobar"])
it "matches JOIN example 5" $
pMsg "JOIN #foo,#bar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foo,#bar"])
it "matches JOIN example 6" $
pMsg ":WiZ JOIN #Twilight_zone\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left JOIN) ["#Twilight_zone"])
it "matches PART example 1" $
pMsg "PART #twilight_zone\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PART) ["#twilight_zone"])
it "matches PART example 2" $
pMsg "PART #oz-ops,&group5\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PART) ["#oz-ops,&group5"])
it "matches MODE example 1" $
pMsg "MODE #Finnish +im\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Finnish", "+im"])
it "matches MODE example 2" $
pMsg "MODE #Finnish +o Kilroy\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Finnish", "+o", "Kilroy"])
it "matches MODE example 3" $
pMsg "MODE #Finnish +v Wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Finnish", "+v", "Wiz"])
it "matches MODE example 4" $
pMsg "MODE #Fins -s\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Fins", "-s"])
it "matches MODE example 5" $
pMsg "MODE #42 +k oulu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#42", "+k", "oulu"])
it "matches MODE example 6" $
pMsg "MODE #eu-opers +l 10\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#eu-opers", "+l", "10"])
it "matches MODE example 7" $
pMsg "MODE &oulu +b\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["&oulu", "+b"])
it "matches MODE example 8" $
pMsg "MODE &oulu +b *!*@*\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["&oulu", "+b", "*!*@*"])
it "matches MODE example 9" $
pMsg "MODE &oulu +b *!*@*.edu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["&oulu", "+b", "*!*@*.edu"])
it "matches MODE example 10" $
pMsg "MODE WiZ -w\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["WiZ", "-w"])
it "matches MODE example 11" $
pMsg ":Angel MODE Angel +i\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left MODE) ["Angel", "+i"])
it "matches MODE example 12" $
pMsg "MODE WiZ -o\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["WiZ", "-o"])
it "matches TOPIC example 1" $
pMsg ":WiZ TOPIC #test :New topic\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left TOPIC) ["#test", "New topic"])
it "matches TOPIC example 2" $
pMsg "TOPIC #test :another topic\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TOPIC) ["#test", "another topic"])
it "matches TOPIC example 3" $
pMsg "TOPIC #test\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TOPIC) ["#test"])
it "matches NAMES example 1" $
pMsg "NAMES #twilight_zone,#42\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NAMES) ["#twilight_zone,#42"])
it "matches NAMES example 2" $
pMsg "NAMES\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NAMES) [])
it "matches LIST example 1" $
pMsg "LIST\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left LIST) [])
it "matches LIST example 2" $
pMsg "LIST #twilight_zone,#42\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left LIST) ["#twilight_zone,#42"])
it "matches INVITE example 1" $
pMsg ":Angel INVITE Wiz #Dust\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left INVITE) ["Wiz", "#Dust"])
it "matches INVITE example 2" $
pMsg "INVITE Wiz #Twilight_Zone\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left INVITE) ["Wiz", "#Twilight_Zone"])
it "matches KICK example 1" $
pMsg "KICK &Melbourne Matthew\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left KICK) ["&Melbourne", "Matthew"])
it "matches KICK example 2" $
pMsg "KICK #Finnish John :Speaking English\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left KICK)
["#Finnish", "John", "Speaking English"])
it "matches KICK example 3" $
pMsg ":WiZ KICK #Finnish John\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left KICK) ["#Finnish", "John"])
it "matches VERSION example 1" $
pMsg ":WiZ VERSION *.se\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left VERSION) ["*.se"])
it "matches VERSION example 2" $
pMsg "VERSION tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left VERSION) ["tolsun.oulu.fi"])
it "matches STATS example 1" $
pMsg "STATS m\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left STATS) ["m"])
it "matches STATS example 2" $
pMsg ":Wiz STATS c eff.org\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Wiz" Nothing Nothing)))
(Left STATS) ["c", "eff.org"])
it "matches LINKS example 1" $
pMsg "LINKS *.au\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left LINKS) ["*.au"])
it "matches LINKS example 2" $
pMsg ":WiZ LINKS *.bu.edu *.edu\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left LINKS) ["*.bu.edu", "*.edu"])
it "matches TIME example 1" $
pMsg "TIME tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TIME) ["tolsun.oulu.fi"])
it "matches TIME example 2" $
pMsg ":Angel TIME *.au\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left TIME) ["*.au"])
it "matches CONNECT example 1" $
pMsg "CONNECT tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left CONNECT) ["tolsun.oulu.fi"])
it "matches CONNECT example 2" $
pMsg ":WiZ CONNECT eff.org 6667 csd.bu.edu\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left CONNECT) ["eff.org", "6667", "csd.bu.edu"])
it "matches TRACE example 1" $
pMsg "TRACE *.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TRACE) ["*.oulu.fi"])
it "matches TRACE example 2" $
pMsg ":WiZ TRACE AngelDust\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left TRACE) ["AngelDust"])
it "matches ADMIN example 1" $
pMsg "ADMIN tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left ADMIN) ["tolsun.oulu.fi"])
it "matches ADMIN example 2" $
pMsg ":WiZ ADMIN *.edu\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left ADMIN) ["*.edu"])
it "matches INFO example 1" $
pMsg "INFO csd.bu.edu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left INFO) ["csd.bu.edu"])
it "matches INFO example 2" $
pMsg ":Avalon INFO *.fi\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Avalon" Nothing Nothing)))
(Left INFO) ["*.fi"])
it "matches INFO example 3" $
pMsg "INFO Angel\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left INFO) ["Angel"])
it "matches PRIVMSG example 1" $
pMsg ":Angel PRIVMSG Wiz :Hello are you receiving this message ?\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left PRIVMSG)
["Wiz", "Hello are you receiving this message ?"])
it "matches PRIVMSG example 2" $
pMsg "PRIVMSG Angel :yes I'm receiving it!\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["Angel", "yes I'm receiving it!"])
it "matches PRIVMSG example 3" $
pMsg "PRIVMSG jto@tolsun.oulu.fi :Hello !\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["jto@tolsun.oulu.fi", "Hello !"])
it "matches PRIVMSG example 4" $
pMsg "PRIVMSG $*.fi :Server tolsun.oulu.fi rebooting.\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["$*.fi", "Server tolsun.oulu.fi rebooting."])
it "matches PRIVMSG example 5" $
pMsg "PRIVMSG #*.edu :NSFNet is undergoing work, expect interruptions\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["#*.edu", "NSFNet is undergoing work, expect interruptions"])
it "matches WHO example 1" $
pMsg "WHO *.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHO) ["*.fi"])
it "matches WHO example 2" $
pMsg "WHO jto* o\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHO) ["jto*", "o"])
it "matches WHOIS example 1" $
pMsg "WHOIS wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOIS) ["wiz"])
it "matches WHOIS example 2" $
pMsg "WHOIS eff.org trillian\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOIS) ["eff.org", "trillian"])
it "matches WHOWAS example 1" $
pMsg "WHOWAS Wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOWAS) ["Wiz"])
it "matches WHOWAS example 2" $
pMsg "WHOWAS Mermaid 9\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOWAS) ["Mermaid", "9"])
it "matches WHOWAS example 3" $
pMsg "WHOWAS Trillian 1 *.edu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOWAS) ["Trillian", "1", "*.edu"])
it "matches KILL example" $
pMsg "KILL David :(csd.bu.edu <- tolsun.oulu.fi)\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left KILL)
["David", "(csd.bu.edu <- tolsun.oulu.fi)"])
it "matches PING example 1" $
pMsg "PING tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PING) ["tolsun.oulu.fi"])
it "matches PING example 2" $
pMsg "PING WiZ\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PING) ["WiZ"])
it "matches PONG example" $
pMsg "PONG csd.bu.edu tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PONG)
["csd.bu.edu", "tolsun.oulu.fi"])
it "matches ERROR example" $
pMsg "ERROR :Server *.fi already exists\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left ERROR)
["Server *.fi already exists"])
it "matches AWAY example 1" $
pMsg "AWAY :Gone to lunch. Back in 5\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left AWAY)
["Gone to lunch. Back in 5"])
it "matches AWAY example 2" $
pMsg ":WiZ AWAY\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left AWAY) [])
it "matches REHASH example" $
pMsg "REHASH\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left REHASH) [])
it "matches RESTART example" $
pMsg "RESTART\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left RESTART) [])
it "matches SUMMON example 1" $
pMsg "SUMMON jto\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SUMMON) ["jto"])
it "matches SUMMON example 2" $
pMsg "SUMMON jto tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SUMMON) ["jto", "tolsun.oulu.fi"])
it "matches USERS example 1" $
pMsg "USERS eff.org\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left USERS) ["eff.org"])
it "matches USERS example 2" $
pMsg ":John USERS tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "John" Nothing Nothing)))
(Left USERS) ["tolsun.oulu.fi"])
it "matches WALLOPS example" $
pMsg ":csd.bu.edu WALLOPS :Connect '*.uiuc.edu 6667' from Joshua\r\n"
`shouldBe`
Right (IrcMessage
(Just (Left "csd.bu.edu"))
(Left WALLOPS) ["Connect '*.uiuc.edu 6667' from Joshua"])
it "matches USERHOST example" $
pMsg "USERHOST Wiz Michael Marty p\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left USERHOST)
["Wiz", "Michael", "Marty", "p"])
it "matches ISON example" $
pMsg "ISON phone trillian WiZ jarlek Avalon Angel Monstah\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left ISON)
["phone", "trillian", "WiZ", "jarlek", "Avalon"
, "Angel", "Monstah"])
where
pMsgOrLine = parseOnly parseMsgOrLine
pMsg = parseOnly parseIrcMessage
msgRenderSpec :: Spec
msgRenderSpec = undefined
-- QuickCheck and SmallCheck properties
{-
properties :: TestTree
properties = testGroup "Properties" [qcProps, scProps]
scProps :: TestTree
scProps = testGroup "(Checked by SmallCheck)"
[ SC.testProperty "sort == sort . reverse" $
\list -> sort (list :: [Int]) == sort (reverse list)
, SC.testProperty "Fermat's little theorem" $
\x -> ((x :: Integer)^7 - x) `mod` 7 == 0
-- the following property does not hold
, SC.testProperty "Fermat's last theorem" $
\x y z n ->
(n :: Integer) >= 3 SC.==> x^n + y^n /= (z^n :: Integer)
]
qcProps = testGroup "(checked by QuickCheck)"
[ QC.testProperty "sort == sort . reverse" $
\list -> sort (list :: [Int]) == sort (reverse list)
, QC.testProperty "Fermat's little theorem" $
\x -> ((x :: Integer)^7 - x) `mod` 7 == 0
-- the following property does not hold
, QC.testProperty "Fermat's last theorem" $
\x y z n ->
(n :: Integer) >= 3 QC.==> x^n + y^n /= (z^n :: Integer)
]
-- HUnit tests
unitTests = testGroup "Unit tests"
@ -655,3 +46,119 @@ unitTests = testGroup "Unit tests"
[1, 2, 3] `compare` [1,2,2] @?= LT
]
-}
instance Arbitrary C8.ByteString where
arbitrary = C8.pack <$> listOf1 (elements ['a'..'z'])
instance Arbitrary IrcUser where
arbitrary = newUser <$> arbitrary
<*> suchThat arbitrary (>0)
instance Arbitrary IrcUserMode where
arbitrary = elements [minBound .. maxBound]
-- * User properties
userProperties :: TestTree
userProperties = testGroup "User Properties" [userqcProps]
userqcProps :: TestTree
userqcProps = testGroup "(checked by QuickCheck)"
[ QC.testProperty "userDelChan . userAddChan == id" $
\user chanKey -> (userDelChan chanKey . userAddChan chanKey) user == user
, QC.testProperty "userDelMode . userAddMode == id" $
\user mode -> (userDelMode mode . userAddMode mode) user == user
, QC.testProperty "userDelInvite . userAddInvite == id" $
\user inv -> (userDelInvite inv . userAddInvite inv) user == user
, QC.testProperty "userHasMode . userAddMode == True" $
\u m -> (userHasMode m . userAddMode m) u
, QC.testProperty "userInChan . userAddChan == True" $
\u ck -> (userInChan ck . userAddChan ck) u
, QC.testProperty "adding and removing multiple channels" manyChans
, QC.testProperty "adding and removing many modes" manyModes
, QC.testProperty "adding and removing many invites" manyInvites
]
manyChans :: IrcUser -> [ChanKey] -> Bool
manyChans u ks =
let cs = nub ks
scs = sort cs
newu = foldr userAddChan u cs
newu' = foldr userDelChan newu scs
in newu' == u && all (`userInChan` newu) cs
manyModes :: IrcUser -> [IrcUserMode] -> Bool
manyModes u ms =
let ms' = nub ms
sms = sort ms'
newu = foldr userAddMode u ms'
newu' = foldr userDelMode newu sms
in newu' == u && all (`userHasMode` newu) ms'
manyInvites :: IrcUser -> [ChanKey] -> Bool
manyInvites u ks = let cs = nub ks
scs = sort cs
newu = foldr userAddInvite u cs
newu' = foldr userDelInvite newu scs
in newu' == u
-- * Channel properties
instance Arbitrary IrcChannel where
arbitrary = newChannel <$> arbitrary
instance Arbitrary IrcChanModeFlags where
arbitrary = elements [minBound .. maxBound]
chanProperties :: TestTree
chanProperties = testGroup "Channel Properties" [chanqcProps]
chanqcProps :: TestTree
chanqcProps = testGroup "(checked by QuickCheck)" [
QC.testProperty "add/del modeFlags" $
\c m -> (chanDelModeFlag m . chanAddModeFlag m) c == c
, QC.testProperty "add/del users" $
\c u -> not (chanHasUser u c) ==>
(chanDelUser u . chanAddUser u) c == c
, QC.testProperty "set/check pass" $
\c p -> (chanClearPass . chanSetPass p) c == c
, QC.testProperty "add/del oper" $
\c u -> not (chanHasUser u c) ==>
(chanDelOper u . chanAddOper u) c == c
, QC.testProperty "add/del voice" $
\c u -> (chanDelVoice u . chanAddVoice u) c == c
, QC.testProperty "add/del invite" $
\c u -> (chanDelInvite u . chanAddInvite u) c == c
, QC.testProperty "nick change" nameChange
]
instance Monoid Bool where
mappend = (&&)
mempty = True
nameChange :: IrcChannel -> [NickKey] -> NickKey -> NickKey -> Property
nameChange c ns n n' =
let ns' = nub ns
newc = foldr (\x -> chanAddInvite x
.chanAddVoice x
.chanAddOper x
.chanAddUser x) c (n:ns')
newc' = chanChangeNick n n' newc
newc'' = foldr chanDelUser newc' ns'
in n' `notElem` ns && n /= n' ==>
mconcat [ chanHasUser n'
, chanUserIsInvited n'
, chanUserHasVoice n'
, chanUserIsOper n'] newc''

597
tests/ParseTests.hs Normal file
View File

@ -0,0 +1,597 @@
{-# LANGUAGE OverloadedStrings #-}
module ParseTests where
import Test.Tasty
import Test.Tasty.Hspec as HS
import Data.Attoparsec.ByteString.Char8 as P
import Data.ByteString.Char8 as C8
import Data.List
import Data.Ord
import Pipes.IRC.Message.Parse
import Pipes.IRC.Message.Render
import Pipes.IRC.Message.Types
msgParseSpec :: Spec
msgParseSpec = do
describe "Parsing" $ do
describe "parseMsgOrLine" $ do
it "succeeds parsing an empty line, returning a Left value" $
pMsgOrLine "\r\n"
`shouldBe`
Right (Left "\r\n")
it "succeeds parsing an IRC message, returning a Right value" $
pMsgOrLine "PRIVMSG #haskell :Hi, guys!\r\n"
`shouldBe`
Right
(Right (IrcMessage Nothing (Left PRIVMSG) ["#haskell", "Hi, guys!"]))
describe "parseIrcMessage" $ do
context "Messages with no prefix" $ do
it "matches with no parameters" $
pMsg "NAMES\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NAMES) [])
it "matches with one parameter (without spaces)" $
pMsg "NICK WiZ\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NICK) ["WiZ"])
it "matches with one parameter (with spaces)" $
pMsg "QUIT :Goodbye, cruel world!\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left QUIT) ["Goodbye, cruel world!"])
context "Messages with server name prefix" $ do
it "matches with server name prefixes" $
pMsg ":foo.domain.com ERROR :Oh no!\r\n"
`shouldBe`
Right (IrcMessage (Just (Left "foo.domain.com"))
(Left ERROR)
["Oh no!"] )
it "matches with hyphenated server name prefixes" $
pMsg ":my-domain.org ERROR :Oh no!\r\n"
`shouldBe`
Right (IrcMessage (Just (Left "my-domain.org"))
(Left ERROR)
["Oh no!"] )
context "Messages with nickname prefix" $ do
it "matches with just nick" $
pMsg ":WiZ PRIVMSG #haskell :Hello\r\n"
`shouldBe`
Right (IrcMessage (Just (Right (NickName "WiZ" Nothing Nothing)))
(Left PRIVMSG)
["#haskell", "Hello"] )
it "matches with nick and user" $
pMsg ":WiZ!wiz PRIVMSG #haskell :Hello\r\n"
`shouldBe`
Right (IrcMessage (Just (Right (NickName "WiZ" (Just "wiz") Nothing)))
(Left PRIVMSG)
["#haskell", "Hello"] )
it "matches with nick, user, and host" $
pMsg ":WiZ!wiz@wiz-host.com PRIVMSG #haskell :Hello\r\n"
`shouldBe`
Right (IrcMessage (Just (Right
(NickName
"WiZ"
(Just "wiz")
(Just "wiz-host.com"))))
(Left PRIVMSG)
["#haskell", "Hello"] )
context "Examples from RFC1459" $ do
it "matches PASS example" $
pMsg "PASS secretpasswordhere\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PASS) ["secretpasswordhere"])
it "matches NICK example 1" $
pMsg "NICK Wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NICK) ["Wiz"])
it "matches NICK example 2" $
pMsg ":WiZ NICK Kilroy\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left NICK) ["Kilroy"])
it "matches USER example 1" $
pMsg "USER guest tolmoon tolsun :Ronnie Reagan\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left USER)
["guest", "tolmoon", "tolsun", "Ronnie Reagan"])
it "matches USER example 2" $
pMsg ":testnick USER guest tolmoon tolsun :Ronnie Reagan\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "testnick" Nothing Nothing)))
(Left USER) ["guest", "tolmoon", "tolsun", "Ronnie Reagan"])
it "matches SERVER example 1" $
pMsg "SERVER test.oulu.fi 1 :[tolsun.oulu.fi] Experimental server\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SERVER)
["test.oulu.fi", "1", "[tolsun.oulu.fi] Experimental server"])
it "matches SERVER example 2" $
pMsg ":tolsun.oulu.fi SERVER csd.bu.edu 5 :BU Central Server\r\n"
`shouldBe`
Right (IrcMessage
(Just (Left "tolsun.oulu.fi"))
(Left SERVER) ["csd.bu.edu", "5", "BU Central Server"])
it "matches OPER example" $
pMsg "OPER foo bar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left OPER) ["foo", "bar"])
it "matches QUIT example" $
pMsg "QUIT :Gone to have lunch\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left QUIT) ["Gone to have lunch"])
it "matches SQUIT example 1" $
pMsg "SQUIT tolsun.oulu.fi :Bad Link ?\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SQUIT)
["tolsun.oulu.fi", "Bad Link ?"])
it "matches SQUIT example 2" $
pMsg ":Trillian SQUIT cm22.eng.umd.edu :Server out of control\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Trillian" Nothing Nothing)))
(Left SQUIT) ["cm22.eng.umd.edu", "Server out of control"])
it "matches JOIN example 1" $
pMsg "JOIN #foobar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foobar"])
it "matches JOIN example 2" $
pMsg "JOIN &foo fubar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["&foo", "fubar"])
it "matches JOIN example 3" $
pMsg "JOIN #foo,&bar fubar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foo,&bar", "fubar"])
it "matches JOIN example 4" $
pMsg "JOIN #foo,&bar fubar,foobar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foo,&bar", "fubar,foobar"])
it "matches JOIN example 5" $
pMsg "JOIN #foo,#bar\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left JOIN) ["#foo,#bar"])
it "matches JOIN example 6" $
pMsg ":WiZ JOIN #Twilight_zone\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left JOIN) ["#Twilight_zone"])
it "matches PART example 1" $
pMsg "PART #twilight_zone\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PART) ["#twilight_zone"])
it "matches PART example 2" $
pMsg "PART #oz-ops,&group5\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PART) ["#oz-ops,&group5"])
it "matches MODE example 1" $
pMsg "MODE #Finnish +im\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Finnish", "+im"])
it "matches MODE example 2" $
pMsg "MODE #Finnish +o Kilroy\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Finnish", "+o", "Kilroy"])
it "matches MODE example 3" $
pMsg "MODE #Finnish +v Wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Finnish", "+v", "Wiz"])
it "matches MODE example 4" $
pMsg "MODE #Fins -s\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#Fins", "-s"])
it "matches MODE example 5" $
pMsg "MODE #42 +k oulu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#42", "+k", "oulu"])
it "matches MODE example 6" $
pMsg "MODE #eu-opers +l 10\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["#eu-opers", "+l", "10"])
it "matches MODE example 7" $
pMsg "MODE &oulu +b\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["&oulu", "+b"])
it "matches MODE example 8" $
pMsg "MODE &oulu +b *!*@*\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["&oulu", "+b", "*!*@*"])
it "matches MODE example 9" $
pMsg "MODE &oulu +b *!*@*.edu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["&oulu", "+b", "*!*@*.edu"])
it "matches MODE example 10" $
pMsg "MODE WiZ -w\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["WiZ", "-w"])
it "matches MODE example 11" $
pMsg ":Angel MODE Angel +i\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left MODE) ["Angel", "+i"])
it "matches MODE example 12" $
pMsg "MODE WiZ -o\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left MODE) ["WiZ", "-o"])
it "matches TOPIC example 1" $
pMsg ":WiZ TOPIC #test :New topic\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left TOPIC) ["#test", "New topic"])
it "matches TOPIC example 2" $
pMsg "TOPIC #test :another topic\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TOPIC) ["#test", "another topic"])
it "matches TOPIC example 3" $
pMsg "TOPIC #test\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TOPIC) ["#test"])
it "matches NAMES example 1" $
pMsg "NAMES #twilight_zone,#42\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NAMES) ["#twilight_zone,#42"])
it "matches NAMES example 2" $
pMsg "NAMES\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left NAMES) [])
it "matches LIST example 1" $
pMsg "LIST\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left LIST) [])
it "matches LIST example 2" $
pMsg "LIST #twilight_zone,#42\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left LIST) ["#twilight_zone,#42"])
it "matches INVITE example 1" $
pMsg ":Angel INVITE Wiz #Dust\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left INVITE) ["Wiz", "#Dust"])
it "matches INVITE example 2" $
pMsg "INVITE Wiz #Twilight_Zone\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left INVITE) ["Wiz", "#Twilight_Zone"])
it "matches KICK example 1" $
pMsg "KICK &Melbourne Matthew\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left KICK) ["&Melbourne", "Matthew"])
it "matches KICK example 2" $
pMsg "KICK #Finnish John :Speaking English\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left KICK)
["#Finnish", "John", "Speaking English"])
it "matches KICK example 3" $
pMsg ":WiZ KICK #Finnish John\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left KICK) ["#Finnish", "John"])
it "matches VERSION example 1" $
pMsg ":WiZ VERSION *.se\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left VERSION) ["*.se"])
it "matches VERSION example 2" $
pMsg "VERSION tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left VERSION) ["tolsun.oulu.fi"])
it "matches STATS example 1" $
pMsg "STATS m\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left STATS) ["m"])
it "matches STATS example 2" $
pMsg ":Wiz STATS c eff.org\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Wiz" Nothing Nothing)))
(Left STATS) ["c", "eff.org"])
it "matches LINKS example 1" $
pMsg "LINKS *.au\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left LINKS) ["*.au"])
it "matches LINKS example 2" $
pMsg ":WiZ LINKS *.bu.edu *.edu\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left LINKS) ["*.bu.edu", "*.edu"])
it "matches TIME example 1" $
pMsg "TIME tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TIME) ["tolsun.oulu.fi"])
it "matches TIME example 2" $
pMsg ":Angel TIME *.au\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left TIME) ["*.au"])
it "matches CONNECT example 1" $
pMsg "CONNECT tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left CONNECT) ["tolsun.oulu.fi"])
it "matches CONNECT example 2" $
pMsg ":WiZ CONNECT eff.org 6667 csd.bu.edu\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left CONNECT) ["eff.org", "6667", "csd.bu.edu"])
it "matches TRACE example 1" $
pMsg "TRACE *.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left TRACE) ["*.oulu.fi"])
it "matches TRACE example 2" $
pMsg ":WiZ TRACE AngelDust\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left TRACE) ["AngelDust"])
it "matches ADMIN example 1" $
pMsg "ADMIN tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left ADMIN) ["tolsun.oulu.fi"])
it "matches ADMIN example 2" $
pMsg ":WiZ ADMIN *.edu\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left ADMIN) ["*.edu"])
it "matches INFO example 1" $
pMsg "INFO csd.bu.edu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left INFO) ["csd.bu.edu"])
it "matches INFO example 2" $
pMsg ":Avalon INFO *.fi\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Avalon" Nothing Nothing)))
(Left INFO) ["*.fi"])
it "matches INFO example 3" $
pMsg "INFO Angel\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left INFO) ["Angel"])
it "matches PRIVMSG example 1" $
pMsg ":Angel PRIVMSG Wiz :Hello are you receiving this message ?\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "Angel" Nothing Nothing)))
(Left PRIVMSG)
["Wiz", "Hello are you receiving this message ?"])
it "matches PRIVMSG example 2" $
pMsg "PRIVMSG Angel :yes I'm receiving it!\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["Angel", "yes I'm receiving it!"])
it "matches PRIVMSG example 3" $
pMsg "PRIVMSG jto@tolsun.oulu.fi :Hello !\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["jto@tolsun.oulu.fi", "Hello !"])
it "matches PRIVMSG example 4" $
pMsg "PRIVMSG $*.fi :Server tolsun.oulu.fi rebooting.\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["$*.fi", "Server tolsun.oulu.fi rebooting."])
it "matches PRIVMSG example 5" $
pMsg "PRIVMSG #*.edu :NSFNet is undergoing work, expect interruptions\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PRIVMSG)
["#*.edu", "NSFNet is undergoing work, expect interruptions"])
it "matches WHO example 1" $
pMsg "WHO *.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHO) ["*.fi"])
it "matches WHO example 2" $
pMsg "WHO jto* o\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHO) ["jto*", "o"])
it "matches WHOIS example 1" $
pMsg "WHOIS wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOIS) ["wiz"])
it "matches WHOIS example 2" $
pMsg "WHOIS eff.org trillian\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOIS) ["eff.org", "trillian"])
it "matches WHOWAS example 1" $
pMsg "WHOWAS Wiz\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOWAS) ["Wiz"])
it "matches WHOWAS example 2" $
pMsg "WHOWAS Mermaid 9\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOWAS) ["Mermaid", "9"])
it "matches WHOWAS example 3" $
pMsg "WHOWAS Trillian 1 *.edu\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left WHOWAS) ["Trillian", "1", "*.edu"])
it "matches KILL example" $
pMsg "KILL David :(csd.bu.edu <- tolsun.oulu.fi)\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left KILL)
["David", "(csd.bu.edu <- tolsun.oulu.fi)"])
it "matches PING example 1" $
pMsg "PING tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PING) ["tolsun.oulu.fi"])
it "matches PING example 2" $
pMsg "PING WiZ\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PING) ["WiZ"])
it "matches PONG example" $
pMsg "PONG csd.bu.edu tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left PONG)
["csd.bu.edu", "tolsun.oulu.fi"])
it "matches ERROR example" $
pMsg "ERROR :Server *.fi already exists\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left ERROR)
["Server *.fi already exists"])
it "matches AWAY example 1" $
pMsg "AWAY :Gone to lunch. Back in 5\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left AWAY)
["Gone to lunch. Back in 5"])
it "matches AWAY example 2" $
pMsg ":WiZ AWAY\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "WiZ" Nothing Nothing)))
(Left AWAY) [])
it "matches REHASH example" $
pMsg "REHASH\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left REHASH) [])
it "matches RESTART example" $
pMsg "RESTART\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left RESTART) [])
it "matches SUMMON example 1" $
pMsg "SUMMON jto\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SUMMON) ["jto"])
it "matches SUMMON example 2" $
pMsg "SUMMON jto tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left SUMMON) ["jto", "tolsun.oulu.fi"])
it "matches USERS example 1" $
pMsg "USERS eff.org\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left USERS) ["eff.org"])
it "matches USERS example 2" $
pMsg ":John USERS tolsun.oulu.fi\r\n"
`shouldBe`
Right (IrcMessage
(Just (Right (NickName "John" Nothing Nothing)))
(Left USERS) ["tolsun.oulu.fi"])
it "matches WALLOPS example" $
pMsg ":csd.bu.edu WALLOPS :Connect '*.uiuc.edu 6667' from Joshua\r\n"
`shouldBe`
Right (IrcMessage
(Just (Left "csd.bu.edu"))
(Left WALLOPS) ["Connect '*.uiuc.edu 6667' from Joshua"])
it "matches USERHOST example" $
pMsg "USERHOST Wiz Michael Marty p\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left USERHOST)
["Wiz", "Michael", "Marty", "p"])
it "matches ISON example" $
pMsg "ISON phone trillian WiZ jarlek Avalon Angel Monstah\r\n"
`shouldBe`
Right (IrcMessage Nothing (Left ISON)
["phone", "trillian", "WiZ", "jarlek", "Avalon"
, "Angel", "Monstah"])
where
pMsgOrLine = parseOnly parseMsgOrLine
pMsg = parseOnly parseIrcMessage
msgRenderSpec :: Spec
msgRenderSpec = undefined