diff options
author | James Crayne <jim.crayne@gmail.com> | 2019-09-28 13:43:29 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2020-01-01 19:27:53 -0500 |
commit | 11987749fc6e6d3e53ea737d46d5ab13a16faeb8 (patch) | |
tree | 5716463275c2d3e902889db619908ded2a73971c /dht/bittorrent/tests/Network/BitTorrent/Tracker | |
parent | add2c76bced51fde5e9917e7449ef52be70faf87 (diff) |
Factor out some new libraries
word64-map:
Data.Word64Map
network-addr:
Network.Address
tox-crypto:
Crypto.Tox
lifted-concurrent:
Control.Concurrent.Lifted.Instrument
Control.Concurrent.Async.Lifted.Instrument
psq-wrap:
Data.Wrapper.PSQInt
Data.Wrapper.PSQ
minmax-psq:
Data.MinMaxPSQ
tasks:
Control.Concurrent.Tasks
kad:
Network.Kademlia
Network.Kademlia.Bootstrap
Network.Kademlia.Routing
Network.Kademlia.CommonAPI
Network.Kademlia.Persistence
Network.Kademlia.Search
Diffstat (limited to 'dht/bittorrent/tests/Network/BitTorrent/Tracker')
7 files changed, 685 insertions, 0 deletions
diff --git a/dht/bittorrent/tests/Network/BitTorrent/Tracker/ListSpec.hs b/dht/bittorrent/tests/Network/BitTorrent/Tracker/ListSpec.hs new file mode 100644 index 00000000..bba9d0e2 --- /dev/null +++ b/dht/bittorrent/tests/Network/BitTorrent/Tracker/ListSpec.hs | |||
@@ -0,0 +1,40 @@ | |||
1 | module Network.BitTorrent.Tracker.ListSpec (spec) where | ||
2 | import Control.Exception | ||
3 | import Data.Default | ||
4 | import Data.Foldable as F | ||
5 | import Data.List as L | ||
6 | import Data.Maybe | ||
7 | import Network.URI | ||
8 | import Test.Hspec | ||
9 | |||
10 | import Data.Torrent | ||
11 | import Network.BitTorrent.Tracker.List | ||
12 | import Network.BitTorrent.Tracker.RPC | ||
13 | |||
14 | |||
15 | uris :: [URI] | ||
16 | uris = fmap (fromJust . parseURI . renderURI) [1..10 :: Int] | ||
17 | where | ||
18 | renderURI n = "http://" ++ show n ++ ".org" | ||
19 | |||
20 | list :: TrackerList () | ||
21 | list = trackerList def { tAnnounceList = Just [uris] } | ||
22 | |||
23 | spec :: Spec | ||
24 | spec = do | ||
25 | describe "TrackerList" $ do | ||
26 | it "shuffleTiers (may fail with very small probability)" $ do | ||
27 | list' <- shuffleTiers list | ||
28 | list' `shouldSatisfy` (/= list) | ||
29 | |||
30 | it "traverseAll" $ do | ||
31 | xs <- traverseAll (\ (uri, _) -> if uri == L.last uris | ||
32 | then throwIO (GenericException "") | ||
33 | else return ()) list | ||
34 | return () | ||
35 | |||
36 | it "traverseTiers" $ do | ||
37 | xs' <- traverseTiers (\ (uri, _) -> if uri == L.last uris then return () | ||
38 | else throwIO (GenericException "")) list | ||
39 | |||
40 | return () | ||
diff --git a/dht/bittorrent/tests/Network/BitTorrent/Tracker/MessageSpec.hs b/dht/bittorrent/tests/Network/BitTorrent/Tracker/MessageSpec.hs new file mode 100644 index 00000000..29854d58 --- /dev/null +++ b/dht/bittorrent/tests/Network/BitTorrent/Tracker/MessageSpec.hs | |||
@@ -0,0 +1,173 @@ | |||
1 | {-# LANGUAGE RecordWildCards #-} | ||
2 | {-# LANGUAGE ViewPatterns #-} | ||
3 | {-# LANGUAGE FlexibleInstances #-} | ||
4 | {-# OPTIONS -fno-warn-orphans #-} | ||
5 | module Network.BitTorrent.Tracker.MessageSpec | ||
6 | ( spec | ||
7 | , arbitrarySample | ||
8 | ) where | ||
9 | |||
10 | import Control.Applicative | ||
11 | import Control.Exception | ||
12 | import Data.BEncode as BE | ||
13 | import Data.ByteString.Lazy as BL | ||
14 | import Data.List as L | ||
15 | import Data.Maybe | ||
16 | import Test.Hspec | ||
17 | import Test.QuickCheck | ||
18 | |||
19 | import Data.TorrentSpec () | ||
20 | import Network.BitTorrent.Internal.ProgressSpec () | ||
21 | import Network.BitTorrent.Address () | ||
22 | import Network.BitTorrent.Address () | ||
23 | |||
24 | import Network.BitTorrent.Tracker.Message as Message | ||
25 | import Network.BitTorrent.Address | ||
26 | |||
27 | |||
28 | --prop_bencode :: Eq a => BEncode a => a -> Bool | ||
29 | --prop_bencode a = BE.decode (BL.toStrict (BE.encode a)) == return a | ||
30 | |||
31 | --prop_urlencode :: Eq a => URLDecoded a => URLEncoded a => a -> Bool | ||
32 | --prop_urlencode a = urlDecode (T.pack (urlEncode a)) == a | ||
33 | |||
34 | instance Arbitrary AnnounceEvent where | ||
35 | arbitrary = elements [minBound..maxBound] | ||
36 | |||
37 | instance Arbitrary AnnounceQuery where | ||
38 | arbitrary = AnnounceQuery | ||
39 | <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary | ||
40 | <*> arbitrary <*> arbitrary <*> arbitrary | ||
41 | |||
42 | instance Arbitrary (PeerList IP) where | ||
43 | arbitrary = frequency | ||
44 | [ (1, (PeerList . maybeToList) <$> arbitrary) | ||
45 | , (1, (CompactPeerList . maybeToList . fmap zeroPeerId) <$> arbitrary) | ||
46 | ] | ||
47 | |||
48 | shrink ( PeerList xs) = PeerList <$> shrink xs | ||
49 | shrink (CompactPeerList xs) = CompactPeerList <$> shrink xs | ||
50 | |||
51 | instance Arbitrary AnnounceInfo where | ||
52 | arbitrary = AnnounceInfo | ||
53 | <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary | ||
54 | <*> arbitrary <*> arbitrary | ||
55 | |||
56 | arbitrarySample :: Arbitrary a => IO a | ||
57 | arbitrarySample = L.head <$> sample' arbitrary | ||
58 | |||
59 | zeroPeerId :: PeerAddr a -> PeerAddr a | ||
60 | zeroPeerId addr = addr { peerId = Nothing } | ||
61 | |||
62 | spec :: Spec | ||
63 | spec = do | ||
64 | describe "AnnounceQuery" $ do | ||
65 | it "properly url encoded" $ property $ \ q -> | ||
66 | parseAnnounceQuery (renderAnnounceQuery q) | ||
67 | `shouldBe` Right q | ||
68 | |||
69 | describe "PeerList" $ do | ||
70 | context "Non compact" $ do | ||
71 | it "properly encoded (both ipv4 and ipv6)" $ do | ||
72 | BE.decode "ld2:ip7:1.2.3.44:porti80eed2:ip3:::14:porti8080eee" | ||
73 | `shouldBe` Right | ||
74 | (PeerList ["1.2.3.4:80", "[::1]:8080"] :: PeerList IPv4) | ||
75 | |||
76 | it "properly encoded (iso)" $ property $ \ xs -> | ||
77 | BE.decode (BL.toStrict (BE.encode (PeerList xs :: PeerList IPv4))) | ||
78 | `shouldBe` Right (PeerList xs :: PeerList IPv4) | ||
79 | |||
80 | context "Compact" $ do | ||
81 | it "properly encodes (ipv4)" $ do | ||
82 | BE.decode "12:\x1\x2\x3\x4\x1\x2\x9\x8\x7\x6\x1\x2" | ||
83 | `shouldBe` Right | ||
84 | (CompactPeerList ["1.2.3.4:258", "9.8.7.6:258"] :: PeerList IPv4) | ||
85 | |||
86 | it "properly encodes (ipv6)" $ do | ||
87 | BE.decode "18:\x1\x2\x3\x4\x5\x6\x7\x8\x1\x2\x3\x4\x5\x6\x7\x8\x1\x2" | ||
88 | `shouldBe` Right | ||
89 | (CompactPeerList ["[102:304:506:708:102:304:506:708]:258"] | ||
90 | :: PeerList IPv6) | ||
91 | |||
92 | it "properly encoded (ipv4, iso)" $ | ||
93 | property $ \ (fmap zeroPeerId -> xs) -> | ||
94 | BE.decode (BL.toStrict (BE.encode (CompactPeerList xs))) | ||
95 | `shouldBe` Right (CompactPeerList xs :: PeerList IPv4) | ||
96 | |||
97 | it "properly encoded (ipv6, iso)" $ | ||
98 | property $ \ (fmap zeroPeerId -> xs) -> | ||
99 | BE.decode (BL.toStrict (BE.encode (CompactPeerList xs))) | ||
100 | `shouldBe` Right (CompactPeerList xs :: PeerList IPv6) | ||
101 | |||
102 | describe "AnnounceInfo" $ do | ||
103 | it "parses minimal sample" $ do | ||
104 | "d8:intervali0e5:peerslee" | ||
105 | `shouldBe` | ||
106 | AnnounceInfo Nothing Nothing 0 Nothing (PeerList []) Nothing | ||
107 | |||
108 | it "parses optional fields" $ do | ||
109 | "d8:completei1e\ | ||
110 | \10:incompletei2e\ | ||
111 | \8:intervali3e\ | ||
112 | \12:min intervali4e\ | ||
113 | \5:peersle\ | ||
114 | \15:warning message3:str\ | ||
115 | \e" | ||
116 | `shouldBe` | ||
117 | AnnounceInfo (Just 1) (Just 2) 3 (Just 4) (PeerList []) (Just "str") | ||
118 | |||
119 | it "parses failed response" $ do | ||
120 | "d14:failure reason10:any reasone" | ||
121 | `shouldBe` | ||
122 | Message.Failure "any reason" | ||
123 | |||
124 | it "fail if no peer list present" $ do | ||
125 | evaluate ("d8:intervali0ee" :: AnnounceInfo) | ||
126 | `shouldThrow` | ||
127 | errorCall "fromString: unable to decode AnnounceInfo: \ | ||
128 | \required field `peers' not found" | ||
129 | |||
130 | it "parses `peer' list" $ do -- TODO | ||
131 | "d8:intervali0e\ | ||
132 | \5:peersl\ | ||
133 | \d2:ip7:1.2.3.4\ | ||
134 | \4:porti80e\ | ||
135 | \e\ | ||
136 | \d2:ip3:::1\ | ||
137 | \4:porti80e\ | ||
138 | \e\ | ||
139 | \e\ | ||
140 | \e" `shouldBe` | ||
141 | let xs = PeerList ["1.2.3.4:80", "[::1]:80"] in | ||
142 | AnnounceInfo Nothing Nothing 0 Nothing xs Nothing | ||
143 | |||
144 | it "parses `peers6' list" $ do | ||
145 | "d8:intervali0e\ | ||
146 | \5:peers0:\ | ||
147 | \6:peers60:\ | ||
148 | \e" `shouldBe` | ||
149 | AnnounceInfo Nothing Nothing 0 Nothing (CompactPeerList []) Nothing | ||
150 | |||
151 | it "fails on invalid combinations of the peer lists" $ do | ||
152 | BE.decode "d8:intervali0e\ | ||
153 | \5:peers0:\ | ||
154 | \6:peers6le\ | ||
155 | \e" | ||
156 | `shouldBe` (Left | ||
157 | "PeerList: the `peers6' field value should contain \ | ||
158 | \*compact* peer list" :: BE.Result AnnounceInfo) | ||
159 | |||
160 | BE.decode "d8:intervali0e\ | ||
161 | \5:peersle\ | ||
162 | \6:peers60:\ | ||
163 | \e" | ||
164 | `shouldBe` (Left | ||
165 | "PeerList: non-compact peer list provided, \ | ||
166 | \but the `peers6' field present" :: BE.Result AnnounceInfo) | ||
167 | |||
168 | it "properly bencoded (iso)" $ property $ \ info -> | ||
169 | BE.decode (BL.toStrict (BE.encode info)) | ||
170 | `shouldBe` Right (info :: AnnounceInfo) | ||
171 | |||
172 | describe "Scrape" $ do | ||
173 | return () | ||
diff --git a/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPC/HTTPSpec.hs b/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPC/HTTPSpec.hs new file mode 100644 index 00000000..e928f917 --- /dev/null +++ b/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPC/HTTPSpec.hs | |||
@@ -0,0 +1,95 @@ | |||
1 | {-# LANGUAGE RecordWildCards #-} | ||
2 | module Network.BitTorrent.Tracker.RPC.HTTPSpec (spec) where | ||
3 | import Control.Monad | ||
4 | import Data.Default | ||
5 | import Data.List as L | ||
6 | import Test.Hspec | ||
7 | |||
8 | import Network.BitTorrent.Internal.Progress | ||
9 | import Network.BitTorrent.Tracker.Message as Message | ||
10 | import Network.BitTorrent.Tracker.RPC.HTTP | ||
11 | |||
12 | import Network.BitTorrent.Tracker.TestData | ||
13 | import Network.BitTorrent.Tracker.MessageSpec hiding (spec) | ||
14 | |||
15 | |||
16 | validateInfo :: AnnounceQuery -> AnnounceInfo -> Expectation | ||
17 | validateInfo _ (Message.Failure reason) = do | ||
18 | error $ "validateInfo: " ++ show reason | ||
19 | validateInfo AnnounceQuery {..} AnnounceInfo {..} = do | ||
20 | return () | ||
21 | -- case respComplete <|> respIncomplete of | ||
22 | -- Nothing -> return () | ||
23 | -- Just n -> n `shouldBe` L.length (getPeerList respPeers) | ||
24 | |||
25 | isUnrecognizedScheme :: RpcException -> Bool | ||
26 | isUnrecognizedScheme (RequestFailed _) = True | ||
27 | isUnrecognizedScheme _ = False | ||
28 | |||
29 | isNotResponding :: RpcException -> Bool | ||
30 | isNotResponding (RequestFailed _) = True | ||
31 | isNotResponding _ = False | ||
32 | |||
33 | spec :: Spec | ||
34 | spec = parallel $ do | ||
35 | describe "Manager" $ do | ||
36 | describe "newManager" $ do | ||
37 | it "" $ pending | ||
38 | |||
39 | describe "closeManager" $ do | ||
40 | it "" $ pending | ||
41 | |||
42 | describe "withManager" $ do | ||
43 | it "" $ pending | ||
44 | |||
45 | describe "RPC" $ do | ||
46 | describe "announce" $ do | ||
47 | it "must fail on bad uri scheme" $ do | ||
48 | withManager def $ \ mgr -> do | ||
49 | q <- arbitrarySample | ||
50 | announce mgr "magnet://foo.bar" q | ||
51 | `shouldThrow` isUnrecognizedScheme | ||
52 | |||
53 | describe "scrape" $ do | ||
54 | it "must fail on bad uri scheme" $ do | ||
55 | withManager def $ \ mgr -> do | ||
56 | scrape mgr "magnet://foo.bar" [] | ||
57 | `shouldThrow` isUnrecognizedScheme | ||
58 | |||
59 | forM_ (L.filter isHttpTracker trackers) $ \ TrackerEntry {..} -> | ||
60 | context trackerName $ do | ||
61 | |||
62 | describe "announce" $ do | ||
63 | if tryAnnounce | ||
64 | then do | ||
65 | it "have valid response" $ do | ||
66 | withManager def $ \ mgr -> do | ||
67 | -- q <- arbitrarySample | ||
68 | let ih = maybe def L.head hashList | ||
69 | let q = AnnounceQuery ih "-HS0003-203534.37420" 6000 | ||
70 | (Progress 0 0 0) Nothing Nothing (Just Started) | ||
71 | info <- announce mgr trackerURI q | ||
72 | validateInfo q info | ||
73 | else do | ||
74 | it "should fail with RequestFailed" $ do | ||
75 | withManager def $ \ mgr -> do | ||
76 | q <- arbitrarySample | ||
77 | announce mgr trackerURI q | ||
78 | `shouldThrow` isNotResponding | ||
79 | |||
80 | describe "scrape" $ do | ||
81 | if tryScraping | ||
82 | then do | ||
83 | it "have valid response" $ do | ||
84 | withManager def $ \ mgr -> do | ||
85 | xs <- scrape mgr trackerURI [def] | ||
86 | L.length xs `shouldSatisfy` (>= 1) | ||
87 | else do | ||
88 | it "should fail with ScrapelessTracker" $ do | ||
89 | pending | ||
90 | |||
91 | when (not tryAnnounce) $ do | ||
92 | it "should fail with RequestFailed" $ do | ||
93 | withManager def $ \ mgr -> do | ||
94 | scrape mgr trackerURI [def] | ||
95 | `shouldThrow` isNotResponding | ||
diff --git a/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPC/UDPSpec.hs b/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPC/UDPSpec.hs new file mode 100644 index 00000000..73acb3fa --- /dev/null +++ b/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPC/UDPSpec.hs | |||
@@ -0,0 +1,144 @@ | |||
1 | {-# LANGUAGE RecordWildCards #-} | ||
2 | module Network.BitTorrent.Tracker.RPC.UDPSpec (spec, rpcOpts) where | ||
3 | import Control.Concurrent | ||
4 | import Control.Concurrent.Async | ||
5 | import Control.Exception | ||
6 | import Control.Monad | ||
7 | import Data.Default | ||
8 | import Data.List as L | ||
9 | import Data.Maybe | ||
10 | import Test.Hspec | ||
11 | |||
12 | import Network.BitTorrent.Address | ||
13 | import Network.BitTorrent.Tracker.Message as Message | ||
14 | |||
15 | import Network.BitTorrent.Tracker.TestData | ||
16 | import Network.BitTorrent.Tracker.MessageSpec hiding (spec) | ||
17 | import Network.BitTorrent.Tracker.RPC.UDP | ||
18 | |||
19 | |||
20 | validateInfo :: AnnounceQuery -> AnnounceInfo -> Expectation | ||
21 | validateInfo _ Message.Failure {} = error "validateInfo: failure" | ||
22 | validateInfo AnnounceQuery {..} AnnounceInfo {..} = do | ||
23 | respComplete `shouldSatisfy` isJust | ||
24 | respIncomplete `shouldSatisfy` isJust | ||
25 | respMinInterval `shouldSatisfy` isNothing | ||
26 | respWarning `shouldSatisfy` isNothing | ||
27 | peerList `shouldSatisfy` L.all (isNothing . peerId) | ||
28 | where | ||
29 | peerList = getPeerList respPeers | ||
30 | |||
31 | -- | Number of concurrent calls. | ||
32 | rpcCount :: Int | ||
33 | rpcCount = 100 | ||
34 | |||
35 | rpcOpts :: Options | ||
36 | rpcOpts = def | ||
37 | { optMinTimeout = 1 | ||
38 | , optMaxTimeout = 10 | ||
39 | } | ||
40 | |||
41 | isTimeoutExpired :: RpcException -> Bool | ||
42 | isTimeoutExpired (TimeoutExpired _) = True | ||
43 | isTimeoutExpired _ = False | ||
44 | |||
45 | isSomeException :: SomeException -> Bool | ||
46 | isSomeException _ = True | ||
47 | |||
48 | isIOException :: IOException -> Bool | ||
49 | isIOException _ = True | ||
50 | |||
51 | spec :: Spec | ||
52 | spec = parallel $ do | ||
53 | describe "newManager" $ do | ||
54 | it "should throw exception on zero optMaxPacketSize" $ do | ||
55 | let opts = def { optMaxPacketSize = 0 } | ||
56 | newManager opts `shouldThrow` isSomeException | ||
57 | |||
58 | it "should throw exception on zero optMinTimout" $ do | ||
59 | let opts = def { optMinTimeout = 0 } | ||
60 | newManager opts `shouldThrow` isSomeException | ||
61 | |||
62 | it "should throw exception on zero optMaxTimeout" $ do | ||
63 | let opts = def { optMaxTimeout = 0 } | ||
64 | newManager opts `shouldThrow` isSomeException | ||
65 | |||
66 | it "should throw exception on maxTimeout < minTimeout" $ do | ||
67 | let opts = def { optMinTimeout = 2, optMaxTimeout = 1 } | ||
68 | newManager opts `shouldThrow` isSomeException | ||
69 | |||
70 | it "should throw exception on zero optMultiplier" $ do | ||
71 | let opts = def { optMultiplier = 0 } | ||
72 | newManager opts `shouldThrow` isSomeException | ||
73 | |||
74 | describe "closeManager" $ do | ||
75 | it "unblock rpc calls" $ do | ||
76 | mgr <- newManager rpcOpts | ||
77 | _ <- forkIO $ do | ||
78 | threadDelay 10000000 | ||
79 | closeManager mgr | ||
80 | q <- arbitrarySample | ||
81 | announce mgr (trackerURI badTracker) q `shouldThrow` (== ManagerClosed) | ||
82 | |||
83 | it "announce throw exception after manager closed" $ do | ||
84 | mgr <- newManager rpcOpts | ||
85 | closeManager mgr | ||
86 | q <- arbitrarySample | ||
87 | announce mgr (trackerURI badTracker) q `shouldThrow` isIOException | ||
88 | |||
89 | it "scrape throw exception after manager closed" $ do | ||
90 | mgr <- newManager rpcOpts | ||
91 | closeManager mgr | ||
92 | scrape mgr (trackerURI badTracker) [def] `shouldThrow` isIOException | ||
93 | |||
94 | describe "withManager" $ do | ||
95 | it "closesManager at exit" $ do | ||
96 | mgr <- withManager rpcOpts return | ||
97 | scrape mgr (trackerURI badTracker) [def] `shouldThrow` isSomeException | ||
98 | |||
99 | describe "RPC" $ do | ||
100 | describe "announce" $ do | ||
101 | it "must fail on bad scheme" $ do | ||
102 | withManager rpcOpts $ \ mgr -> do | ||
103 | q <- arbitrarySample | ||
104 | announce mgr "magnet://a.com" q | ||
105 | `shouldThrow` (== UnrecognizedScheme "magnet:") | ||
106 | |||
107 | describe "scrape" $ do | ||
108 | it "must fail on bad scheme" $ do | ||
109 | withManager rpcOpts $ \ mgr -> do | ||
110 | scrape mgr "magnet://a.com" [] | ||
111 | `shouldThrow` (== UnrecognizedScheme "magnet:") | ||
112 | |||
113 | forM_ (L.filter isUdpTracker trackers) $ \ TrackerEntry {..} -> | ||
114 | context trackerName $ do | ||
115 | |||
116 | describe "announce" $ do | ||
117 | if tryAnnounce then do | ||
118 | it "have valid response" $ do | ||
119 | withManager rpcOpts $ \ mgr -> do | ||
120 | q <- arbitrarySample | ||
121 | announce mgr trackerURI q >>= validateInfo q | ||
122 | else do | ||
123 | it "should throw TimeoutExpired" $ do | ||
124 | withManager rpcOpts $ \ mgr -> do | ||
125 | q <- arbitrarySample | ||
126 | announce mgr trackerURI q `shouldThrow` isTimeoutExpired | ||
127 | |||
128 | describe "scrape" $ do | ||
129 | if tryScraping then do | ||
130 | it "have valid response" $ do | ||
131 | withManager rpcOpts $ \ mgr -> do | ||
132 | xs <- scrape mgr trackerURI [def] | ||
133 | L.length xs `shouldSatisfy` (>= 1) | ||
134 | else do | ||
135 | it "should throw TimeoutExpired" $ do | ||
136 | withManager rpcOpts $ \ mgr -> do | ||
137 | scrape mgr trackerURI [def] `shouldThrow` isTimeoutExpired | ||
138 | |||
139 | describe "Manager" $ do | ||
140 | when tryScraping $ do | ||
141 | it "should handle arbitrary intermixed concurrent queries" $ do | ||
142 | withManager rpcOpts $ \ mgr -> do | ||
143 | _ <- mapConcurrently (\ _ -> scrape mgr trackerURI [def]) [1..rpcCount] | ||
144 | return () | ||
diff --git a/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPCSpec.hs b/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPCSpec.hs new file mode 100644 index 00000000..dfc13a1e --- /dev/null +++ b/dht/bittorrent/tests/Network/BitTorrent/Tracker/RPCSpec.hs | |||
@@ -0,0 +1,79 @@ | |||
1 | {-# LANGUAGE RecordWildCards #-} | ||
2 | {-# OPTIONS_GHC -fno-warn-orphans #-} | ||
3 | module Network.BitTorrent.Tracker.RPCSpec (spec) where | ||
4 | import Control.Applicative | ||
5 | import Control.Monad | ||
6 | import Data.Default | ||
7 | import Data.List as L | ||
8 | import Test.Hspec | ||
9 | import Test.QuickCheck | ||
10 | |||
11 | import Network.BitTorrent.Tracker.RPC as RPC | ||
12 | |||
13 | import Network.BitTorrent.Tracker.TestData | ||
14 | import Network.BitTorrent.Tracker.MessageSpec hiding (spec) | ||
15 | import qualified Network.BitTorrent.Tracker.RPC.UDPSpec as UDP (rpcOpts) | ||
16 | |||
17 | |||
18 | instance Arbitrary SAnnounceQuery where | ||
19 | arbitrary = SAnnounceQuery <$> arbitrary <*> arbitrary | ||
20 | <*> arbitrary <*> arbitrary | ||
21 | |||
22 | rpcOpts :: Options | ||
23 | rpcOpts = def | ||
24 | { optUdpRPC = UDP.rpcOpts | ||
25 | } | ||
26 | |||
27 | matchUnrecognizedScheme :: String -> RpcException -> Bool | ||
28 | matchUnrecognizedScheme x (UnrecognizedScheme scheme) = x == scheme | ||
29 | matchUnrecognizedScheme _ _ = False | ||
30 | |||
31 | spec :: Spec | ||
32 | spec = parallel $ do | ||
33 | describe "Manager" $ do | ||
34 | describe "newManager" $ do | ||
35 | it "" $ pending | ||
36 | |||
37 | describe "closeManager" $ do | ||
38 | it "" $ pending | ||
39 | |||
40 | describe "withManager" $ do | ||
41 | it "" $ pending | ||
42 | |||
43 | describe "RPC" $ do | ||
44 | describe "announce" $ do | ||
45 | it "must fail on bad uri scheme" $ do | ||
46 | withManager rpcOpts def $ \ mgr -> do | ||
47 | q <- arbitrarySample | ||
48 | announce mgr "magnet://foo.bar" q | ||
49 | `shouldThrow` matchUnrecognizedScheme "magnet:" | ||
50 | |||
51 | describe "scrape" $ do | ||
52 | it "must fail on bad uri scheme" $ do | ||
53 | withManager rpcOpts def $ \ mgr -> do | ||
54 | scrape mgr "magnet://foo.bar" [] | ||
55 | `shouldThrow` matchUnrecognizedScheme "magnet:" | ||
56 | |||
57 | forM_ trackers $ \ TrackerEntry {..} -> | ||
58 | context trackerName $ do | ||
59 | |||
60 | describe "announce" $ do | ||
61 | if tryAnnounce then do | ||
62 | it "have valid response" $ do | ||
63 | withManager rpcOpts def $ \ mgr -> do | ||
64 | q <- arbitrarySample | ||
65 | _ <- announce mgr trackerURI q | ||
66 | return () | ||
67 | else do | ||
68 | it "should throw exception" $ do | ||
69 | pending | ||
70 | |||
71 | describe "scrape" $ do | ||
72 | if tryScraping then do | ||
73 | it "have valid response" $ do | ||
74 | withManager rpcOpts def $ \ mgr -> do | ||
75 | xs <- scrape mgr trackerURI [def] | ||
76 | L.length xs `shouldSatisfy` (>= 1) | ||
77 | else do | ||
78 | it "should throw exception" $ do | ||
79 | pending | ||
diff --git a/dht/bittorrent/tests/Network/BitTorrent/Tracker/SessionSpec.hs b/dht/bittorrent/tests/Network/BitTorrent/Tracker/SessionSpec.hs new file mode 100644 index 00000000..72936ee7 --- /dev/null +++ b/dht/bittorrent/tests/Network/BitTorrent/Tracker/SessionSpec.hs | |||
@@ -0,0 +1,61 @@ | |||
1 | module Network.BitTorrent.Tracker.SessionSpec (spec) where | ||
2 | import Control.Monad | ||
3 | import Data.Default | ||
4 | import Data.List as L | ||
5 | import Test.Hspec | ||
6 | |||
7 | import Data.Torrent | ||
8 | import Network.BitTorrent.Tracker.Message | ||
9 | import Network.BitTorrent.Tracker.List | ||
10 | import Network.BitTorrent.Tracker.RPC | ||
11 | import Network.BitTorrent.Tracker.Session | ||
12 | |||
13 | import Config | ||
14 | |||
15 | testSession :: Bool -> (Manager -> Session -> IO ()) -> IO () | ||
16 | testSession runEmpty action = do | ||
17 | t <- getTestTorrent | ||
18 | withManager def def $ \ m -> do | ||
19 | withSession m (idInfoHash (tInfoDict t)) (trackerList t) $ \ s -> | ||
20 | action m s | ||
21 | |||
22 | when runEmpty $ do | ||
23 | withSession m (idInfoHash (tInfoDict t)) def $ \ s -> | ||
24 | action m s | ||
25 | |||
26 | spec :: Spec | ||
27 | spec = do | ||
28 | describe "Session" $ do | ||
29 | it "start new session in paused state" $ do | ||
30 | testSession True $ \ _ s -> do | ||
31 | status <- getStatus s | ||
32 | status `shouldBe` Paused | ||
33 | |||
34 | describe "Query" $ do | ||
35 | it "change status after notify" $ do | ||
36 | testSession True $ \ m s -> do | ||
37 | notify m s Started | ||
38 | status <- getStatus s | ||
39 | status `shouldBe` Running | ||
40 | |||
41 | notify m s Stopped | ||
42 | stopped <- getStatus s | ||
43 | stopped `shouldBe` Paused | ||
44 | |||
45 | it "completed event do not change status" $ do | ||
46 | testSession True $ \ m s -> do | ||
47 | notify m s Completed | ||
48 | status <- getStatus s | ||
49 | status `shouldBe` Paused | ||
50 | |||
51 | testSession True $ \ m s -> do | ||
52 | notify m s Started | ||
53 | notify m s Completed | ||
54 | status <- getStatus s | ||
55 | status `shouldBe` Running | ||
56 | |||
57 | it "return non-empty list of peers" $ do | ||
58 | testSession False $ \ m s -> do | ||
59 | notify m s Started | ||
60 | peers <- askPeers m s | ||
61 | peers `shouldSatisfy` (not . L.null) | ||
diff --git a/dht/bittorrent/tests/Network/BitTorrent/Tracker/TestData.hs b/dht/bittorrent/tests/Network/BitTorrent/Tracker/TestData.hs new file mode 100644 index 00000000..b95e2df4 --- /dev/null +++ b/dht/bittorrent/tests/Network/BitTorrent/Tracker/TestData.hs | |||
@@ -0,0 +1,93 @@ | |||
1 | {-# LANGUAGE RecordWildCards #-} | ||
2 | {-# OPTIONS_GHC -fno-warn-orphans #-} | ||
3 | module Network.BitTorrent.Tracker.TestData | ||
4 | ( TrackerEntry (..) | ||
5 | , isUdpTracker | ||
6 | , isHttpTracker | ||
7 | , trackers | ||
8 | , badTracker | ||
9 | ) where | ||
10 | |||
11 | import Data.Maybe | ||
12 | import Data.String | ||
13 | import Network.URI | ||
14 | |||
15 | import Data.Torrent | ||
16 | |||
17 | |||
18 | data TrackerEntry = TrackerEntry | ||
19 | { -- | May be used to show tracker name in test suite report. | ||
20 | trackerName :: String | ||
21 | |||
22 | -- | Announce uri of the tracker. | ||
23 | , trackerURI :: URI | ||
24 | |||
25 | -- | Some trackers abadoned, so don't even try to announce. | ||
26 | , tryAnnounce :: Bool | ||
27 | |||
28 | -- | Some trackers do not support scraping, so we should not even | ||
29 | -- try to scrape them. | ||
30 | , tryScraping :: Bool | ||
31 | |||
32 | -- | Some trackers allow | ||
33 | , hashList :: Maybe [InfoHash] | ||
34 | } | ||
35 | |||
36 | isUdpTracker :: TrackerEntry -> Bool | ||
37 | isUdpTracker TrackerEntry {..} = uriScheme trackerURI == "udp:" | ||
38 | |||
39 | isHttpTracker :: TrackerEntry -> Bool | ||
40 | isHttpTracker TrackerEntry {..} = uriScheme trackerURI == "http:" | ||
41 | || uriScheme trackerURI == "https:" | ||
42 | |||
43 | instance IsString URI where | ||
44 | fromString str = fromMaybe err $ parseURI str | ||
45 | where | ||
46 | err = error $ "fromString: bad URI " ++ show str | ||
47 | |||
48 | trackerEntry :: URI -> TrackerEntry | ||
49 | trackerEntry uri = TrackerEntry | ||
50 | { trackerName = maybe "<unknown>" uriRegName (uriAuthority uri) | ||
51 | , trackerURI = uri | ||
52 | , tryAnnounce = False | ||
53 | , tryScraping = False | ||
54 | , hashList = Nothing | ||
55 | } | ||
56 | |||
57 | announceOnly :: String -> URI -> TrackerEntry | ||
58 | announceOnly name uri = (trackerEntry uri) | ||
59 | { trackerName = name | ||
60 | , tryAnnounce = True | ||
61 | } | ||
62 | |||
63 | announceScrape :: String -> URI -> TrackerEntry | ||
64 | announceScrape name uri = (announceOnly name uri) | ||
65 | { tryScraping = True | ||
66 | } | ||
67 | |||
68 | notWorking :: String -> URI -> TrackerEntry | ||
69 | notWorking name uri = (trackerEntry uri) | ||
70 | { trackerName = name | ||
71 | } | ||
72 | |||
73 | trackers :: [TrackerEntry] | ||
74 | trackers = | ||
75 | [ (announceOnly "LinuxTracker" | ||
76 | "http://linuxtracker.org:2710/00000000000000000000000000000000/announce") | ||
77 | { hashList = Just ["1c82a95b9e02bf3db4183da072ad3ef656aacf0e"] -- debian 7 | ||
78 | } | ||
79 | |||
80 | , (announceScrape "Arch" "http://tracker.archlinux.org:6969/announce") | ||
81 | { hashList = Just ["bc9ae647a3e6c3636de58535dd3f6360ce9f4621"] | ||
82 | } | ||
83 | |||
84 | , notWorking "rarbg" "udp://9.rarbg.com:2710/announce" | ||
85 | |||
86 | , announceScrape "OpenBitTorrent" "udp://tracker.openbittorrent.com:80/announce" | ||
87 | , announceScrape "PublicBT" "udp://tracker.publicbt.com:80/announce" | ||
88 | , notWorking "OpenBitTorrent" "http://tracker.openbittorrent.com:80/announce" | ||
89 | , notWorking "PublicBT" "http://tracker.publicbt.com:80/announce" | ||
90 | ] | ||
91 | |||
92 | badTracker :: TrackerEntry | ||
93 | badTracker = notWorking "rarbg" "udp://9.rarbg.com:2710/announce" \ No newline at end of file | ||