net-spider-0.4.2.0: test/NetSpider/WeaverSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module NetSpider.WeaverSpec (main,spec) where
import Data.Foldable (foldl')
import Data.Hashable (Hashable)
import Data.Int (Int64)
import Data.IORef (newIORef, readIORef, writeIORef, modifyIORef')
import Data.Maybe (fromJust)
import Data.Text (Text)
import Test.Hspec
import NetSpider.Found (FoundNode(..), FoundLink(..), LinkState(..))
import NetSpider.Query
( policyOverwrite, policyAppend,
foundNodePolicy, unifyLinkSamples
)
import NetSpider.Timestamp (fromEpochMillisecond)
import NetSpider.Weaver
( Weaver, newWeaver,
markAsVisited, addFoundNode,
isVisited, getFoundNodes, getSnapshot,
visitAllBoundaryNodes,
getBoundaryNodes
)
import SnapshotTestCase (SnapshotTestCase(..))
import qualified SnapshotTestCase
main :: IO ()
main = hspec spec
spec :: Spec
spec = describe "Weaver" $ do
spec_visitedNodes
spec_boundaryNodes
describe "SnapshotTestCase" $ do
mapM_ makeTestCase SnapshotTestCase.basics
spec_visitedNodes :: Spec
spec_visitedNodes = describe "visited nodes" $ do
specify "mark and add" $ do
let fn = FoundNode { subjectNode = 5,
foundAt = fromEpochMillisecond 100,
neighborLinks = [],
nodeAttributes = ()
}
w :: Weaver Int () ()
w = addFoundNode fn $ markAsVisited 10 $ markAsVisited 5 $ newWeaver policyOverwrite
isVisited 1 w `shouldBe` False
isVisited 5 w `shouldBe` True
isVisited 10 w `shouldBe` True
getFoundNodes 1 w `shouldBe` Nothing
getFoundNodes 5 w `shouldBe` Just [fn]
getFoundNodes 10 w `shouldBe` Just []
specify "add and mark" $ do
let fn = FoundNode { subjectNode = 5,
foundAt = fromEpochMillisecond 100,
neighborLinks = [],
nodeAttributes = ()
}
w :: Weaver Int () ()
w = markAsVisited 10 $ markAsVisited 5 $ addFoundNode fn $ newWeaver policyOverwrite
isVisited 1 w `shouldBe` False
isVisited 5 w `shouldBe` True
isVisited 10 w `shouldBe` True
getFoundNodes 1 w `shouldBe` Nothing
getFoundNodes 5 w `shouldBe` Just [fn]
getFoundNodes 10 w `shouldBe` Just []
describe "FoundNodePolicy for Weaver" $ do
let fn1 = FoundNode { subjectNode = 5,
foundAt = fromEpochMillisecond 100,
neighborLinks = [],
nodeAttributes = "foobar"
}
fn2 = FoundNode { subjectNode = 5,
foundAt = fromEpochMillisecond 150,
neighborLinks = [],
nodeAttributes = "quux"
}
specify "policyOverwrite should keep only the latest FoundNode" $ do
let w :: Weaver Int String ()
w = newWeaver policyOverwrite
(getFoundNodes 5 $ addFoundNode fn1 w)
`shouldBe` Just [fn1]
(getFoundNodes 5 $ addFoundNode fn2 w)
`shouldBe` Just [fn2]
(getFoundNodes 5 $ addFoundNode fn1 $ addFoundNode fn2 w)
`shouldBe` Just [fn2]
(getFoundNodes 5 $ addFoundNode fn2 $ addFoundNode fn1 w)
`shouldBe` Just [fn2]
specify "policyAppend should keep all FoundNodes" $ do
let w :: Weaver Int String ()
w = newWeaver policyAppend
(getFoundNodes 5 $ addFoundNode fn1 w)
`shouldBe` Just [fn1]
(getFoundNodes 5 $ addFoundNode fn2 w)
`shouldBe` Just [fn2]
(fromJust $ getFoundNodes 5 $ addFoundNode fn1 $ addFoundNode fn2 w)
`shouldMatchList` [fn1, fn2]
(fromJust $ getFoundNodes 5 $ addFoundNode fn2 $ addFoundNode fn1 w)
`shouldMatchList` [fn1, fn2]
shouldReturnSet :: (HasCallStack, Eq a, Show a) => IO [a] -> [a] -> IO ()
shouldReturnSet action expected = do
got <- action
got `shouldMatchList` expected
spec_boundaryNodes :: Spec
spec_boundaryNodes = describe "boundary nodes" $ do
let addFN ref_w fn = do
new_w <- fmap (addFoundNode fn) $ readIORef ref_w
writeIORef ref_w new_w
getBN ref_w = fmap getBoundaryNodes $ readIORef ref_w
makeFN :: Text -> [Text] -> Int64 -> FoundNode Text () ()
makeFN sub_n tar_ns time =
FoundNode { subjectNode = sub_n,
foundAt = fromEpochMillisecond time,
nodeAttributes = (),
neighborLinks = map makeFL tar_ns
}
makeFL tar_n = FoundLink { targetNode = tar_n, linkState = LinkToTarget, linkAttributes = () }
markV ref_w n = modifyIORef' ref_w $ markAsVisited n
specify "policyOverwrite" $ do
rweaver <- newIORef $ newWeaver policyOverwrite
let addFN' = addFN rweaver
getBN' = getBN rweaver
markV' = markV rweaver
getBN' `shouldReturnSet` []
addFN' (makeFN "n1" [] 100)
getBN' `shouldReturnSet` []
addFN' (makeFN "n1" ["n2"] 200)
getBN' `shouldReturnSet` ["n2"]
addFN' (makeFN "n2" ["n3", "n4", "n5", "n1"] 250)
getBN' `shouldReturnSet` ["n3", "n4", "n5"]
addFN' (makeFN "n3" ["n4"] 200)
getBN' `shouldReturnSet` ["n4", "n5"] -- should return unique node IDs
addFN' (makeFN "n5" ["n1", "n6", "n7", "n8", "n2"] 200)
getBN' `shouldReturnSet` ["n4", "n6", "n7", "n8"]
addFN' (makeFN "n5" ["n4", "n9"] 250)
getBN' `shouldReturnSet` ["n4", "n9"] -- overwrites the previous FoundNode
addFN' (makeFN "n5" ["n3", "n7"] 220)
getBN' `shouldReturnSet` ["n4", "n9"] -- get ignored.
markV' "n5"
getBN' `shouldReturnSet` ["n4", "n9"]
markV' "n9"
getBN' `shouldReturnSet` ["n4"]
specify "policyAppend" $ do
rweaver <- newIORef $ newWeaver policyAppend
let addFN' = addFN rweaver
getBN' = getBN rweaver
markV' = markV rweaver
getBN' `shouldReturnSet` []
addFN' (makeFN "n1" ["n2"] 200)
getBN' `shouldReturnSet` ["n2"]
addFN' (makeFN "n1" ["n3"] 100)
getBN' `shouldReturnSet` ["n2", "n3"]
addFN' (makeFN "n1" ["n4"] 300)
getBN' `shouldReturnSet` ["n2", "n3", "n4"]
addFN' (makeFN "n3" ["n5"] 200)
getBN' `shouldReturnSet` ["n2", "n4", "n5"]
markV' "n2"
getBN' `shouldReturnSet` ["n4", "n5"]
addAllFoundNodes :: (Eq n, Hashable n) => [FoundNode n na la] -> Weaver n na la -> Weaver n na la
addAllFoundNodes fns weaver = foldl' (\w fn -> addFoundNode fn w) weaver fns
makeTestCase :: SnapshotTestCase -> Spec
makeTestCase SnapshotTestCase { caseName = name, caseInput = input, caseQuery = query, caseAssert = assert } = do
specify name $ do
let init_w = newWeaver $ foundNodePolicy query
got_w = visitAllBoundaryNodes -- SnapshotTestCase assumes unlimited number of traverse steps. So, mark all nodes as visited.
$ addAllFoundNodes input init_w
got_graph = getSnapshot (unifyLinkSamples query) got_w
assert got_graph