summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-12-09 06:37:28 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-12-09 06:37:28 +0400
commitb00f17874babc0a63a501a4fb33f4f9c8b7d5c7d (patch)
treef945b2fa2ada038a927debc3ba0b8f8fff8733ab
parentc147b181094937d780b93ca82fb8604deeb9a7cd (diff)
Add stats method to PeerMessage class
-rw-r--r--bittorrent.cabal3
-rw-r--r--src/Network/BitTorrent/Exchange/Message.hs124
-rw-r--r--src/Network/BitTorrent/Exchange/Wire.hs61
-rw-r--r--tests/Data/Torrent/BitfieldSpec.hs13
-rw-r--r--tests/Network/BitTorrent/Exchange/BlockSpec.hs16
-rw-r--r--tests/Network/BitTorrent/Exchange/MessageSpec.hs47
6 files changed, 210 insertions, 54 deletions
diff --git a/bittorrent.cabal b/bittorrent.cabal
index 3ad1d7e4..dbb3c3be 100644
--- a/bittorrent.cabal
+++ b/bittorrent.cabal
@@ -152,7 +152,8 @@ test-suite spec
152 type: exitcode-stdio-1.0 152 type: exitcode-stdio-1.0
153 hs-source-dirs: tests 153 hs-source-dirs: tests
154 main-is: Spec.hs 154 main-is: Spec.hs
155 other-modules: Data.Torrent.InfoHashSpec 155 other-modules: Data.Torrent.BitfieldSpec
156 Data.Torrent.InfoHashSpec
156 Data.Torrent.LayoutSpec 157 Data.Torrent.LayoutSpec
157 Data.Torrent.MagnetSpec 158 Data.Torrent.MagnetSpec
158 Data.Torrent.MetainfoSpec 159 Data.Torrent.MetainfoSpec
diff --git a/src/Network/BitTorrent/Exchange/Message.hs b/src/Network/BitTorrent/Exchange/Message.hs
index 38a8ac33..b3100269 100644
--- a/src/Network/BitTorrent/Exchange/Message.hs
+++ b/src/Network/BitTorrent/Exchange/Message.hs
@@ -46,10 +46,15 @@ module Network.BitTorrent.Exchange.Message
46 , handshakeSize 46 , handshakeSize
47 , handshakeMaxSize 47 , handshakeMaxSize
48 48
49 -- * Stats
50 , ByteCount
51 , ByteStats (..)
52 , byteLength
53
49 -- * Messages 54 -- * Messages
50 , Message (..) 55 , Message (..)
51 , PeerMessage (..)
52 , defaultKeepAliveInterval 56 , defaultKeepAliveInterval
57 , PeerMessage (..)
53 58
54 -- ** Core messages 59 -- ** Core messages
55 , StatusUpdate (..) 60 , StatusUpdate (..)
@@ -292,6 +297,47 @@ defaultHandshake :: InfoHash -> PeerId -> Handshake
292defaultHandshake = Handshake def def 297defaultHandshake = Handshake def def
293 298
294{----------------------------------------------------------------------- 299{-----------------------------------------------------------------------
300-- Stats
301-----------------------------------------------------------------------}
302
303-- | Number of bytes.
304type ByteCount = Int
305
306-- | Summary of encoded message byte layout can be used to collect
307-- stats about message flow in both directions. This data can be
308-- retrieved using 'stats' function.
309data ByteStats = ByteStats
310 { -- | Number of bytes used to help encode 'control' and 'payload'
311 -- bytes: message size, message ID's, etc
312 overhead :: {-# UNPACK #-} !ByteCount
313
314 -- | Number of bytes used to exchange peers state\/options: piece
315 -- and block indexes, infohash, port numbers, peer ID\/IP, etc.
316 , control :: {-# UNPACK #-} !ByteCount
317
318 -- | Number of payload bytes: torrent data blocks and infodict
319 -- metadata.
320 , payload :: {-# UNPACK #-} !ByteCount
321 } deriving Show
322
323-- | Empty byte sequences.
324instance Default ByteStats where
325 def = ByteStats 0 0 0
326
327-- | Monoid under addition.
328instance Monoid ByteStats where
329 mempty = def
330 mappend a b = ByteStats
331 { overhead = overhead a + overhead b
332 , control = control a + control b
333 , payload = payload a + payload b
334 }
335
336-- | Sum of the all byte sequences.
337byteLength :: ByteStats -> Int
338byteLength ByteStats {..} = overhead + control + payload
339
340{-----------------------------------------------------------------------
295-- Regular messages 341-- Regular messages
296-----------------------------------------------------------------------} 342-----------------------------------------------------------------------}
297 343
@@ -311,6 +357,17 @@ class PeerMessage a where
311 requires :: a -> Maybe Extension 357 requires :: a -> Maybe Extension
312 requires _ = Nothing 358 requires _ = Nothing
313 359
360 -- | Get sizes of overhead\/control\/payload byte sequences of
361 -- binary message representation without encoding message to binary
362 -- bytestring.
363 --
364 -- This function should obey one law:
365 --
366 -- * 'byteLength' ('stats' msg) == 'BL.length' ('encode' msg)
367 --
368 stats :: a -> ByteStats
369 stats _ = ByteStats 4 0 0
370
314{----------------------------------------------------------------------- 371{-----------------------------------------------------------------------
315-- Status messages 372-- Status messages
316-----------------------------------------------------------------------} 373-----------------------------------------------------------------------}
@@ -337,6 +394,9 @@ instance PeerMessage StatusUpdate where
337 envelop _ = Status 394 envelop _ = Status
338 {-# INLINE envelop #-} 395 {-# INLINE envelop #-}
339 396
397 stats _ = ByteStats 4 1 0
398 {-# INLINE stats #-}
399
340{----------------------------------------------------------------------- 400{-----------------------------------------------------------------------
341-- Available messages 401-- Available messages
342-----------------------------------------------------------------------} 402-----------------------------------------------------------------------}
@@ -361,12 +421,14 @@ instance Pretty Available where
361 421
362instance PeerMessage Available where 422instance PeerMessage Available where
363 envelop _ = Available 423 envelop _ = Available
364
365-- | BITFIELD message.
366instance PeerMessage Bitfield where
367 envelop c = envelop c . Bitfield
368 {-# INLINE envelop #-} 424 {-# INLINE envelop #-}
369 425
426 stats (Have _) = ByteStats (4 + 1) 4 0
427 stats (Bitfield bf) = ByteStats (4 + 1) (q + trailing) 0
428 where
429 trailing = if r == 0 then 0 else 1
430 (q, r) = quotRem (totalCount bf) 8
431
370{----------------------------------------------------------------------- 432{-----------------------------------------------------------------------
371-- Transfer messages 433-- Transfer messages
372-----------------------------------------------------------------------} 434-----------------------------------------------------------------------}
@@ -395,15 +457,9 @@ instance PeerMessage Transfer where
395 envelop _ = Transfer 457 envelop _ = Transfer
396 {-# INLINE envelop #-} 458 {-# INLINE envelop #-}
397 459
398-- | REQUEST message. 460 stats (Request _ ) = ByteStats (4 + 1) (3 * 4) 0
399instance PeerMessage BlockIx where 461 stats (Piece pi ) = ByteStats (4 + 1) (4 + 4 + blockSize pi) 0
400 envelop c = envelop c . Request 462 stats (Cancel _ ) = ByteStats (4 + 1) (3 * 4) 0
401 {-# INLINE envelop #-}
402
403-- | PIECE message.
404instance PeerMessage (Block BL.ByteString) where
405 envelop c = envelop c . Piece
406 {-# INLINE envelop #-}
407 463
408{----------------------------------------------------------------------- 464{-----------------------------------------------------------------------
409-- Fast messages 465-- Fast messages
@@ -424,11 +480,12 @@ data FastMessage =
424 -- amount of IO. 480 -- amount of IO.
425 | SuggestPiece !PieceIx 481 | SuggestPiece !PieceIx
426 482
427 -- | Notifies a requesting peer that its request will not be satisfied. 483 -- | Notifies a requesting peer that its request will not be
484 -- satisfied.
428 | RejectRequest !BlockIx 485 | RejectRequest !BlockIx
429 486
430 -- | This is an advisory messsage meaning "if you ask for this 487 -- | This is an advisory messsage meaning \"if you ask for this
431 -- piece, I'll give it to you even if you're choked." Used to 488 -- piece, I'll give it to you even if you're choked.\" Used to
432 -- shorten starting phase. 489 -- shorten starting phase.
433 | AllowedFast !PieceIx 490 | AllowedFast !PieceIx
434 deriving (Show, Eq) 491 deriving (Show, Eq)
@@ -447,6 +504,12 @@ instance PeerMessage FastMessage where
447 requires _ = Just ExtFast 504 requires _ = Just ExtFast
448 {-# INLINE requires #-} 505 {-# INLINE requires #-}
449 506
507 stats HaveAll = ByteStats 4 1 0
508 stats HaveNone = ByteStats 4 1 0
509 stats (SuggestPiece _) = ByteStats 5 4 0
510 stats (RejectRequest _) = ByteStats 5 12 0
511 stats (AllowedFast _) = ByteStats 5 4 0
512
450{----------------------------------------------------------------------- 513{-----------------------------------------------------------------------
451-- Extension protocol 514-- Extension protocol
452-----------------------------------------------------------------------} 515-----------------------------------------------------------------------}
@@ -588,7 +651,7 @@ extHandshakeId = 0
588 651
589-- | Default 'Request' queue size. 652-- | Default 'Request' queue size.
590defaultQueueLength :: Int 653defaultQueueLength :: Int
591defaultQueueLength = 0 654defaultQueueLength = 1
592 655
593-- | All fields are empty. 656-- | All fields are empty.
594instance Default ExtendedHandshake where 657instance Default ExtendedHandshake where
@@ -619,6 +682,7 @@ instance BEncode ExtendedHandshake where
619instance Pretty ExtendedHandshake where 682instance Pretty ExtendedHandshake where
620 pretty = PP.text . show 683 pretty = PP.text . show
621 684
685-- | NOTE: Approximated 'stats'.
622instance PeerMessage ExtendedHandshake where 686instance PeerMessage ExtendedHandshake where
623 envelop c = envelop c . EHandshake 687 envelop c = envelop c . EHandshake
624 {-# INLINE envelop #-} 688 {-# INLINE envelop #-}
@@ -626,6 +690,9 @@ instance PeerMessage ExtendedHandshake where
626 requires _ = Just ExtExtended 690 requires _ = Just ExtExtended
627 {-# INLINE requires #-} 691 {-# INLINE requires #-}
628 692
693 stats _ = ByteStats (4 + 1 + 1) 100 {- is it ok? -} 0 -- FIXME
694 {-# INLINE stats #-}
695
629-- | Set default values and the specified 'ExtendedCaps'. 696-- | Set default values and the specified 'ExtendedCaps'.
630nullExtendedHandshake :: ExtendedCaps -> ExtendedHandshake 697nullExtendedHandshake :: ExtendedCaps -> ExtendedHandshake
631nullExtendedHandshake caps = ExtendedHandshake 698nullExtendedHandshake caps = ExtendedHandshake
@@ -721,6 +788,7 @@ instance Pretty ExtendedMetadata where
721 pretty (MetadataReject pix ) = "Reject" <+> PP.int pix 788 pretty (MetadataReject pix ) = "Reject" <+> PP.int pix
722 pretty (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval 789 pretty (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval
723 790
791-- | NOTE: Approximated 'stats'.
724instance PeerMessage ExtendedMetadata where 792instance PeerMessage ExtendedMetadata where
725 envelop c = envelop c . EMetadata (remoteMessageId ExtMetadata c) 793 envelop c = envelop c . EMetadata (remoteMessageId ExtMetadata c)
726 {-# INLINE envelop #-} 794 {-# INLINE envelop #-}
@@ -728,6 +796,14 @@ instance PeerMessage ExtendedMetadata where
728 requires _ = Just ExtExtended 796 requires _ = Just ExtExtended
729 {-# INLINE requires #-} 797 {-# INLINE requires #-}
730 798
799 stats (MetadataRequest _) = ByteStats (4 + 1 + 1) {- ~ -} 25 0
800 stats (MetadataData pi t) = ByteStats (4 + 1 + 1) {- ~ -} 41 $
801 BS.length (Data.pieceData pi)
802 stats (MetadataReject _) = ByteStats (4 + 1 + 1) {- ~ -} 25 0
803 stats (MetadataUnknown _) = ByteStats (4 + 1 + 1) {- ? -} 0 0
804
805-- | All 'Piece's in 'MetadataData' messages MUST have size equal to
806-- this value. The last trailing piece can be shorter.
731metadataPieceSize :: Int 807metadataPieceSize :: Int
732metadataPieceSize = 16 * 1024 808metadataPieceSize = 16 * 1024
733 809
@@ -791,6 +867,10 @@ instance PeerMessage ExtendedMessage where
791 requires _ = Just ExtExtended 867 requires _ = Just ExtExtended
792 {-# INLINE requires #-} 868 {-# INLINE requires #-}
793 869
870 stats (EHandshake hs) = stats hs
871 stats (EMetadata _ msg) = stats msg
872 stats (EUnknown _ msg) = ByteStats (4 + 1 + 1) (BS.length msg) 0
873
794{----------------------------------------------------------------------- 874{-----------------------------------------------------------------------
795-- The message datatype 875-- The message datatype
796-----------------------------------------------------------------------} 876-----------------------------------------------------------------------}
@@ -849,6 +929,14 @@ instance PeerMessage Message where
849 requires (Fast _) = Just ExtFast 929 requires (Fast _) = Just ExtFast
850 requires (Extended _) = Just ExtExtended 930 requires (Extended _) = Just ExtExtended
851 931
932 stats KeepAlive = ByteStats 4 0 0
933 stats (Status m) = stats m
934 stats (Available m) = stats m
935 stats (Transfer m) = stats m
936 stats (Port _) = ByteStats 5 2 0
937 stats (Fast m) = stats m
938 stats (Extended m) = stats m
939
852-- | PORT message. 940-- | PORT message.
853instance PeerMessage PortNumber where 941instance PeerMessage PortNumber where
854 envelop _ = Port 942 envelop _ = Port
diff --git a/src/Network/BitTorrent/Exchange/Wire.hs b/src/Network/BitTorrent/Exchange/Wire.hs
index a6ee35d8..fe4086bc 100644
--- a/src/Network/BitTorrent/Exchange/Wire.hs
+++ b/src/Network/BitTorrent/Exchange/Wire.hs
@@ -33,9 +33,12 @@ module Network.BitTorrent.Exchange.Wire
33 , getExtCaps 33 , getExtCaps
34 34
35 -- ** Messaging 35 -- ** Messaging
36 , recvMessage
37 , sendMessage
38
36 , validate 39 , validate
37 , validateBoth 40 , validateBoth
38 , keepStats 41 , trackStats
39 42
40 -- ** Stats 43 -- ** Stats
41 , ConnectionStats (..) 44 , ConnectionStats (..)
@@ -70,12 +73,16 @@ import Text.PrettyPrint.Class
70import Data.Torrent.InfoHash 73import Data.Torrent.InfoHash
71import Network.BitTorrent.Core 74import Network.BitTorrent.Core
72import Network.BitTorrent.Exchange.Message 75import Network.BitTorrent.Exchange.Message
76import Data.Torrent
77import Data.Torrent.Piece
78import Data.BEncode as BE
73 79
74-- TODO handle port message? 80-- TODO handle port message?
75-- TODO handle limits? 81-- TODO handle limits?
76-- TODO filter not requested PIECE messages 82-- TODO filter not requested PIECE messages
77-- TODO metadata piece request flood protection 83-- TODO metadata piece request flood protection
78-- TODO piece request flood protection 84-- TODO piece request flood protection
85-- TODO protect against flood attacks
79{----------------------------------------------------------------------- 86{-----------------------------------------------------------------------
80-- Exceptions 87-- Exceptions
81-----------------------------------------------------------------------} 88-----------------------------------------------------------------------}
@@ -130,50 +137,34 @@ isWireFailure _ = return ()
130-- Stats 137-- Stats
131-----------------------------------------------------------------------} 138-----------------------------------------------------------------------}
132 139
133type ByteCount = Int 140data FlowStats = FlowStats
134 141 { messageBytes :: {-# UNPACK #-} !ByteStats
135data MessageStats = MessageStats 142 , messageCount :: {-# UNPACK #-} !Int
136 { overhead :: {-# UNPACK #-} !ByteCount 143 -- msgTypes :: Map MessageType Int
137 , payload :: {-# UNPACK #-} !ByteCount
138 } deriving Show 144 } deriving Show
139 145
140instance Default MessageStats where 146-- | Note that this is stats is completely different from Progress:
141 def = MessageStats 0 0 147-- TODO explain why.
142
143instance Monoid MessageStats where
144 mempty = mempty
145 mappend a b = MessageStats
146 { overhead = overhead a + overhead b
147 , payload = payload a + payload b
148 }
149
150
151messageSize :: MessageStats -> Int
152messageSize MessageStats {..} = overhead + payload
153
154messageStats :: Message -> MessageStats
155messageStats = undefined
156
157data ConnectionStats = ConnectionStats 148data ConnectionStats = ConnectionStats
158 { incomingFlow :: !MessageStats 149 { incomingFlow :: !ByteStats
159 , outcomingFlow :: !MessageStats 150 , outcomingFlow :: !ByteStats
160 } deriving Show 151 } deriving Show
161 152
162instance Default ConnectionStats where 153instance Default ConnectionStats where
163 def = ConnectionStats def def 154 def = ConnectionStats def def
164 155
165addStats :: ChannelSide -> MessageStats -> ConnectionStats -> ConnectionStats 156addStats :: ChannelSide -> ByteStats -> ConnectionStats -> ConnectionStats
166addStats ThisPeer x s = s { outcomingFlow = outcomingFlow s <> x } 157addStats ThisPeer x s = s { outcomingFlow = outcomingFlow s <> x }
167addStats RemotePeer x s = s { incomingFlow = incomingFlow s <> x } 158addStats RemotePeer x s = s { incomingFlow = incomingFlow s <> x }
168 159
169recvBytes :: ConnectionStats -> Int 160recvBytes :: ConnectionStats -> Int
170recvBytes = messageSize . incomingFlow 161recvBytes = byteLength . incomingFlow
171 162
172sentBytes :: ConnectionStats -> Int 163sentBytes :: ConnectionStats -> Int
173sentBytes = messageSize . outcomingFlow 164sentBytes = byteLength . outcomingFlow
174 165
175wastedBytes :: ConnectionStats -> Int 166wastedBytes :: ConnectionStats -> Int
176wastedBytes (ConnectionStats _in out) = overhead _in + overhead out 167wastedBytes (ConnectionStats _in out) = overhead _in + overhead out
177 168
178payloadBytes :: ConnectionStats -> Int 169payloadBytes :: ConnectionStats -> Int
179payloadBytes (ConnectionStats _in out) = payload _in + payload out 170payloadBytes (ConnectionStats _in out) = payload _in + payload out
@@ -184,11 +175,11 @@ payloadBytes (ConnectionStats _in out) = payload _in + payload out
184 175
185data Connection = Connection 176data Connection = Connection
186 { connCaps :: !Caps 177 { connCaps :: !Caps
187 , connExtCaps :: !(IORef ExtendedCaps)
188 , connTopic :: !InfoHash 178 , connTopic :: !InfoHash
189 , connRemotePeerId :: !PeerId 179 , connRemotePeerId :: !PeerId
190 , connThisPeerId :: !PeerId 180 , connThisPeerId :: !PeerId
191 , connStats :: !(IORef ConnectionStats) 181 , connStats :: !(IORef ConnectionStats)
182 , connExtCaps :: !(IORef ExtendedCaps)
192 } 183 }
193 184
194instance Pretty Connection where 185instance Pretty Connection where
@@ -278,7 +269,7 @@ askStats :: (ConnectionStats -> a) -> Wire a
278askStats f = f <$> getStats 269askStats f = f <$> getStats
279 270
280putStats :: ChannelSide -> Message -> Wire () 271putStats :: ChannelSide -> Message -> Wire ()
281putStats side msg = modifyRef connStats (addStats side (messageStats msg)) 272putStats side msg = modifyRef connStats (addStats side (stats msg))
282 273
283 274
284getConnection :: Wire Connection 275getConnection :: Wire Connection
@@ -301,8 +292,8 @@ validateBoth action = do
301 action 292 action
302 validate ThisPeer 293 validate ThisPeer
303 294
304keepStats :: Wire () 295trackStats :: Wire ()
305keepStats = do 296trackStats = do
306 mmsg <- await 297 mmsg <- await
307 case mmsg of 298 case mmsg of
308 Nothing -> return () 299 Nothing -> return ()
@@ -329,7 +320,7 @@ extendedHandshake caps = do
329 sendMessage $ nullExtendedHandshake caps 320 sendMessage $ nullExtendedHandshake caps
330 msg <- recvMessage 321 msg <- recvMessage
331 case msg of 322 case msg of
332 Extended (EHandshake ExtendedHandshake {..}) -> 323 Extended (EHandshake ExtendedHandshake {..}) -> do
333 setExtCaps $ ehsCaps <> caps 324 setExtCaps $ ehsCaps <> caps
334 _ -> protocolError HandshakeRefused 325 _ -> protocolError HandshakeRefused
335 326
@@ -356,10 +347,10 @@ connectWire hs addr extCaps wire =
356 statsRef <- newIORef def 347 statsRef <- newIORef def
357 runWire wire' sock $ Connection 348 runWire wire' sock $ Connection
358 { connCaps = caps 349 { connCaps = caps
359 , connExtCaps = extCapsRef
360 , connTopic = hsInfoHash hs 350 , connTopic = hsInfoHash hs
361 , connRemotePeerId = hsPeerId hs' 351 , connRemotePeerId = hsPeerId hs'
362 , connThisPeerId = hsPeerId hs 352 , connThisPeerId = hsPeerId hs
353 , connExtCaps = extCapsRef
363 , connStats = statsRef 354 , connStats = statsRef
364 } 355 }
365 356
diff --git a/tests/Data/Torrent/BitfieldSpec.hs b/tests/Data/Torrent/BitfieldSpec.hs
new file mode 100644
index 00000000..093f6f19
--- /dev/null
+++ b/tests/Data/Torrent/BitfieldSpec.hs
@@ -0,0 +1,13 @@
1{-# OPTIONS -fno-warn-orphans #-}
2module Data.Torrent.BitfieldSpec (spec) where
3import Control.Applicative
4import Test.Hspec
5import Test.QuickCheck
6
7import Data.Torrent.Bitfield
8
9instance Arbitrary Bitfield where
10 arbitrary = fromBitmap <$> arbitrary
11
12spec :: Spec
13spec = return () \ No newline at end of file
diff --git a/tests/Network/BitTorrent/Exchange/BlockSpec.hs b/tests/Network/BitTorrent/Exchange/BlockSpec.hs
new file mode 100644
index 00000000..0712a21d
--- /dev/null
+++ b/tests/Network/BitTorrent/Exchange/BlockSpec.hs
@@ -0,0 +1,16 @@
1module Network.BitTorrent.Exchange.BlockSpec (spec) where
2import Control.Applicative
3import Test.Hspec
4import Test.QuickCheck
5
6import Network.BitTorrent.Exchange.Block
7
8
9instance Arbitrary a => Arbitrary (Block a) where
10 arbitrary = Block <$> arbitrary <*> arbitrary <*> arbitrary
11
12instance Arbitrary BlockIx where
13 arbitrary = BlockIx <$> arbitrary <*> arbitrary <*> arbitrary
14
15spec :: Spec
16spec = return () \ No newline at end of file
diff --git a/tests/Network/BitTorrent/Exchange/MessageSpec.hs b/tests/Network/BitTorrent/Exchange/MessageSpec.hs
index 38a20112..5d332eaa 100644
--- a/tests/Network/BitTorrent/Exchange/MessageSpec.hs
+++ b/tests/Network/BitTorrent/Exchange/MessageSpec.hs
@@ -2,6 +2,7 @@ module Network.BitTorrent.Exchange.MessageSpec (spec) where
2import Control.Applicative 2import Control.Applicative
3import Control.Exception 3import Control.Exception
4import Data.ByteString as BS 4import Data.ByteString as BS
5import Data.ByteString.Lazy as BL
5import Data.Default 6import Data.Default
6import Data.List as L 7import Data.List as L
7import Data.Set as S 8import Data.Set as S
@@ -10,9 +11,11 @@ import Data.String
10import Test.Hspec 11import Test.Hspec
11import Test.QuickCheck 12import Test.QuickCheck
12 13
14import Data.Torrent.BitfieldSpec ()
13import Data.Torrent.InfoHashSpec () 15import Data.Torrent.InfoHashSpec ()
14import Network.BitTorrent.CoreSpec () 16import Network.BitTorrent.CoreSpec ()
15import Network.BitTorrent.Core 17import Network.BitTorrent.Core
18import Network.BitTorrent.Exchange.BlockSpec ()
16import Network.BitTorrent.Exchange.Message 19import Network.BitTorrent.Exchange.Message
17 20
18instance Arbitrary Extension where 21instance Arbitrary Extension where
@@ -28,6 +31,45 @@ instance Arbitrary Handshake where
28 arbitrary = Handshake <$> arbitrary <*> arbitrary 31 arbitrary = Handshake <$> arbitrary <*> arbitrary
29 <*> arbitrary <*> arbitrary 32 <*> arbitrary <*> arbitrary
30 33
34instance Arbitrary StatusUpdate where
35 arbitrary = frequency
36 [ (1, Choking <$> arbitrary)
37 , (1, Interested <$> arbitrary)
38 ]
39
40instance Arbitrary Available where
41 arbitrary = frequency
42 [ (1, Have <$> arbitrary)
43 , (1, Bitfield <$> arbitrary)
44 ]
45
46instance Arbitrary Transfer where
47 arbitrary = frequency
48 [ (1, Request <$> arbitrary)
49 , (1, Piece <$> arbitrary)
50 , (1, Cancel <$> arbitrary)
51 ]
52
53instance Arbitrary FastMessage where
54 arbitrary = frequency
55 [ (1, pure HaveAll)
56 , (1, pure HaveNone)
57 , (1, SuggestPiece <$> arbitrary)
58 , (1, RejectRequest <$> arbitrary)
59 , (1, AllowedFast <$> arbitrary)
60 ]
61
62instance Arbitrary Message where
63 arbitrary = frequency
64 [ (1, pure KeepAlive)
65 , (1, Status <$> arbitrary)
66 , (1, Available <$> arbitrary)
67 , (1, Transfer <$> arbitrary)
68 , (1, Fast <$> arbitrary)
69 ]
70
71-- TODO test extension protocol
72
31spec :: Spec 73spec :: Spec
32spec = do 74spec = do
33 describe "Caps" $ do 75 describe "Caps" $ do
@@ -38,6 +80,11 @@ spec = do
38 S.fromList (fromCaps (toCaps (S.toList extSet) :: Caps)) 80 S.fromList (fromCaps (toCaps (S.toList extSet) :: Caps))
39 `shouldBe` extSet 81 `shouldBe` extSet
40 82
83 describe "ByteStats" $ do
84 it "preserve size" $ property $ \ msg ->
85 byteLength (stats msg) `shouldBe`
86 fromIntegral (BS.length (S.encode (msg :: Message)))
87
41 describe "ProtocolString" $ do 88 describe "ProtocolString" $ do
42 it "fail to construct invalid string" $ do 89 it "fail to construct invalid string" $ do
43 let str = L.replicate 500 'x' 90 let str = L.replicate 500 'x'