net-spider-rpl-0.3.0.0: test/NetSpider/RPL/ContikiNGSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module NetSpider.RPL.ContikiNGSpec (main,spec) where
import Control.Monad.Logger (runWriterLoggingT, LogLevel(LevelWarn))
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Maybe (fromJust)
import Data.Text (Text)
import NetSpider.Found (FoundNode(..), FoundLink(..), LinkState(..))
import NetSpider.Timestamp (Timestamp, fromEpochMillisecond)
import System.IO (withFile, IOMode(ReadMode))
import System.Log.FastLogger (fromLogStr)
import Test.Hspec
import NetSpider.RPL.ContikiNG (parseFile, pCoojaLogHead', pSyslogHead, parseFileHandleM)
import NetSpider.RPL.FindingID (idFromText)
import qualified NetSpider.RPL.DIO as DIO
import qualified NetSpider.RPL.DAO as DAO
main :: IO ()
main = hspec spec
daoEntry :: Timestamp -> Text -> Maybe Word -> [(Text, Word)] -> DAO.FoundNodeDAO
daoEntry ts src mroute_num dests =
FoundNode
{ subjectNode = fromJust $ idFromText src,
foundAt = ts,
nodeAttributes = DAO.DAONode mroute_num,
neighborLinks = map toLink dests
}
where
toLink (dest, lifetime) =
FoundLink
{ targetNode = fromJust $ idFromText dest,
linkState = LinkToTarget,
linkAttributes = DAO.DAOLink lifetime
}
spec :: Spec
spec = do
describe "parseFile" $ do
specify "cooja log" $ do
let exp_dio =
FoundNode { subjectNode = fromJust $ idFromText "dio://[fd00::201:1:1:1]",
foundAt = fromEpochMillisecond 60382,
neighborLinks = exp_dio_links,
nodeAttributes = DIO.DIONode { DIO.rank = 128, DIO.dioInterval = 15 }
}
exp_dio_links =
[ FoundLink { targetNode = fromJust $ idFromText "dio://[fd00::202:2:2:2]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 299,
DIO.metric = Just 141
}
}
]
exp_daos = [ daoEntry (fromEpochMillisecond 60382)
"dao://[fd00::201:1:1:1]" (Just 2)
[("dao://[fd00::202:2:2:2]", 1740)]
]
(got_dios, got_daos) <- parseFile pCoojaLogHead' "test/data/cooja.log"
got_dios `shouldBe` [exp_dio]
got_daos `shouldBe` exp_daos
specify "syslog root log" $ do
let exp_timestamp = fromEpochMillisecond 1546968337000
exp_dio =
FoundNode
{ subjectNode = fromJust $ idFromText "dio://[fd00::212:1199:eebb:62c4]",
foundAt = exp_timestamp,
nodeAttributes = DIO.DIONode { DIO.rank = 128, DIO.dioInterval = 14 },
neighborLinks =
[ FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:1199:bbcc:4fdf]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 262,
DIO.metric = Just 128
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:1199:bbcc:d52d]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 263,
DIO.metric = Just 128
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:1199:bbcc:5e88]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 256,
DIO.metric = Just 128
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:1199:eebb:62fe]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 256,
DIO.metric = Just 129
}
}
]
}
exp_daos = [ daoEntry exp_timestamp
"dao://[fd00::212:1199:eebb:62c4]" (Just 5)
[ ("dao://[fd00::212:1199:bbcc:d52d]", 1080),
("dao://[fd00::212:1199:bbcc:4fdf]", 1260),
("dao://[fd00::212:1199:eebb:62fe]", 1440),
("dao://[fd00::212:1199:bbcc:5e88]", 1140)
]
]
pHead = pSyslogHead 2019 Nothing
(got_dios, got_daos) <- parseFile pHead "test/data/syslog_root.log"
got_dios `shouldBe` [exp_dio]
got_daos `shouldBe` exp_daos
specify "syslog nonroot" $ do
let exp_timestamp = fromEpochMillisecond 1547558149000
exp_dio =
FoundNode
{ subjectNode = fromJust $ idFromText "dio://[fd00::212:eeaa:0077:2f9c]",
foundAt = exp_timestamp,
nodeAttributes = DIO.DIONode { DIO.rank = 423, DIO.dioInterval = 16 },
neighborLinks =
[ FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:33cc:632a]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 256,
DIO.metric = Just 198
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:33ff:a874]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 256,
DIO.metric = Just 177
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:33cc:63d0]",
linkState = LinkToTarget,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.PreferredParent,
DIO.neighborRank = 272,
DIO.metric = Just 151
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:9977:13ba]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 283,
DIO.metric = Just 152
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:33cc:6350]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 400,
DIO.metric = Just 171
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:e88:db36]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 432,
DIO.metric = Just 166
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:9977:b4]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.OtherNeighbor,
DIO.neighborRank = 584,
DIO.metric = Just 65535
}
}
]
}
pHead = pSyslogHead 2019 Nothing
(got_dios, got_daos) <- parseFile pHead "test/data/syslog_nonroot.log"
got_dios `shouldBe` [exp_dio]
got_daos `shouldBe` []
specify "syslog sr tables" $ do
let exp_ts_jan = fromEpochMillisecond 1548843376000
exp_ts_feb = fromEpochMillisecond 1549373162000
exp_dio1 =
FoundNode
{ subjectNode = fromJust $ idFromText "dio://[fd00::222:5566:cc99:62c4]",
foundAt = exp_ts_jan,
nodeAttributes = DIO.DIONode { DIO.rank = 128, DIO.dioInterval = 15 },
neighborLinks = []
}
exp_dio2 =
FoundNode
{ subjectNode = fromJust $ idFromText "dio://[fd00::222:5566:cc99:62c4]",
foundAt = exp_ts_feb,
nodeAttributes = DIO.DIONode { DIO.rank = 128, DIO.dioInterval = 18 },
neighborLinks = exp_dio_links2
}
exp_dio_links2 =
[ FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::222:5566:ddee:4fdf]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 256,
DIO.metric = Just 128
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::222:5566:ddee:d52d]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 396,
DIO.metric = Just 128
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::222:5566:cc99:62fe]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 266,
DIO.metric = Just 137
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::222:5566:ddee:401e]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 384,
DIO.metric = Just 128
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::222:5566:ddee:5e88]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 406,
DIO.metric = Just 128
}
}
]
exp_daos = [ daoEntry exp_ts_feb
"dao://[fd00::222:5566:cc99:62c4]" (Just 6)
[ ("dao://[fd00::222:5566:ddee:4fdf]", 1140),
("dao://[fd00::222:5566:cc99:62fe]", 1380)
],
daoEntry exp_ts_feb
"dao://[fd00::222:5566:ddee:4fdf]" Nothing
[ ("dao://[fd00::222:5566:ddee:d52d]", 1380),
("dao://[fd00::222:5566:ddee:401e]", 1740),
("dao://[fd00::222:5566:ddee:5e88]", 1740)
]
]
pHead = pSyslogHead 2019 Nothing
(got_dios, got_daos) <- parseFile pHead "test/data/syslog_sr_tables.log"
got_dios `shouldBe` [exp_dio1, exp_dio2]
got_daos `shouldMatchList` exp_daos
specify "syslog_inf_rank" $ do
let exp_dio =
FoundNode
{ subjectNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:bcc:1008]",
foundAt = fromEpochMillisecond 1551104666000,
nodeAttributes = DIO.DIONode { DIO.rank = 65535, DIO.dioInterval = 12 },
neighborLinks = exp_links
}
exp_links =
[ FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:bcc:100a]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.OtherNeighbor,
DIO.neighborRank = 492,
DIO.metric = Just 601
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:9221:d51a]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.OtherNeighbor,
DIO.neighborRank = 422,
DIO.metric = Just 601
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::aaa:bbbb:bcc:d5e8]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.OtherNeighbor,
DIO.neighborRank = 65535,
DIO.metric = Just 133
}
}
]
pHead = pSyslogHead 2019 Nothing
(got_dios, got_daos) <- parseFile pHead "test/data/syslog_inf_rank.log"
got_dios `shouldBe` [exp_dio]
got_daos `shouldMatchList` []
specify "syslog_interleaving" $ do
let exp_dio =
FoundNode
{ subjectNode = fromJust $ idFromText "dio://[fd00::212:eeaa:0077:2f9c]",
foundAt = fromEpochMillisecond 1547558152000,
nodeAttributes = DIO.DIONode { DIO.rank = 332, DIO.dioInterval = 14 },
neighborLinks = exp_links
}
exp_links =
[ FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:33cc:63d0]",
linkState = LinkToTarget,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.PreferredParent,
DIO.neighborRank = 188,
DIO.metric = Just 144
}
},
FoundLink
{ targetNode = fromJust $ idFromText "dio://[fd00::212:eeaa:9977:13ba]",
linkState = LinkUnused,
linkAttributes = DIO.DIOLink { DIO.neighborType = DIO.ParentCandidate,
DIO.neighborRank = 263,
DIO.metric = Just 152
}
}
]
pHead = pSyslogHead 2019 Nothing
((got_dios, got_daos), got_logs) <- withFile "test/data/syslog_interleaving.log" ReadMode
(runWriterLoggingT . parseFileHandleM pHead)
got_dios `shouldBe` [exp_dio]
got_daos `shouldBe` []
-- Because the log file contains interleaving lines inside DIO
-- block, it should emit a warning message.
length got_logs `shouldBe` 1
let exp_msg = "Jan 15 13:15:49 hostname hoge[3392]: this is a log not related to ContikiNG."
(_, _, got_level, got_msg_str) = got_logs !! 0
got_msg = fromLogStr got_msg_str
got_level `shouldBe` LevelWarn
got_msg `shouldSatisfy` BS.isInfixOf exp_msg
got_msg `shouldSatisfy` BS.isInfixOf "DIO"