diff options
-rw-r--r-- | bittorrent.cabal | 6 | ||||
-rw-r--r-- | tests/Data/Torrent/InfoHashSpec.hs | 38 | ||||
-rw-r--r-- | tests/Data/Torrent/LayoutSpec.hs | 30 | ||||
-rw-r--r-- | tests/Data/Torrent/MagnetSpec.hs | 44 | ||||
-rw-r--r-- | tests/Data/Torrent/MetainfoSpec.hs | 78 | ||||
-rw-r--r-- | tests/Data/Torrent/PieceSpec.hs | 13 | ||||
-rw-r--r-- | tests/Data/TorrentSpec.hs | 139 | ||||
-rw-r--r-- | tests/Network/BitTorrent/DHT/MessageSpec.hs | 2 | ||||
-rw-r--r-- | tests/Network/BitTorrent/DHT/SessionSpec.hs | 2 | ||||
-rw-r--r-- | tests/Network/BitTorrent/Exchange/MessageSpec.hs | 2 | ||||
-rw-r--r-- | tests/Network/BitTorrent/Tracker/MessageSpec.hs | 2 |
11 files changed, 144 insertions, 212 deletions
diff --git a/bittorrent.cabal b/bittorrent.cabal index 9a86702d..6953816d 100644 --- a/bittorrent.cabal +++ b/bittorrent.cabal | |||
@@ -176,12 +176,8 @@ test-suite spec | |||
176 | other-modules: Spec | 176 | other-modules: Spec |
177 | Config | 177 | Config |
178 | 178 | ||
179 | Data.TorrentSpec | ||
179 | Data.Torrent.BitfieldSpec | 180 | Data.Torrent.BitfieldSpec |
180 | Data.Torrent.InfoHashSpec | ||
181 | Data.Torrent.LayoutSpec | ||
182 | Data.Torrent.MagnetSpec | ||
183 | Data.Torrent.MetainfoSpec | ||
184 | Data.Torrent.PieceSpec | ||
185 | Data.Torrent.ProgressSpec | 181 | Data.Torrent.ProgressSpec |
186 | Network.BitTorrent.Client.HandleSpec | 182 | Network.BitTorrent.Client.HandleSpec |
187 | Network.BitTorrent.CoreSpec | 183 | Network.BitTorrent.CoreSpec |
diff --git a/tests/Data/Torrent/InfoHashSpec.hs b/tests/Data/Torrent/InfoHashSpec.hs deleted file mode 100644 index 9accc741..00000000 --- a/tests/Data/Torrent/InfoHashSpec.hs +++ /dev/null | |||
@@ -1,38 +0,0 @@ | |||
1 | {-# OPTIONS -fno-warn-orphans #-} | ||
2 | module Data.Torrent.InfoHashSpec (spec) where | ||
3 | |||
4 | import Control.Applicative | ||
5 | import Data.ByteString as BS | ||
6 | import Data.Convertible | ||
7 | import System.FilePath | ||
8 | import Test.Hspec | ||
9 | import Test.QuickCheck | ||
10 | import Test.QuickCheck.Instances () | ||
11 | |||
12 | import Data.Torrent | ||
13 | |||
14 | instance Arbitrary InfoHash where | ||
15 | arbitrary = do | ||
16 | bs <- BS.pack <$> vectorOf 20 arbitrary | ||
17 | pure $ either (const (error "arbitrary infohash")) id $ safeConvert bs | ||
18 | |||
19 | type TestPair = (FilePath, String) | ||
20 | |||
21 | -- TODO add a few more torrents here | ||
22 | torrentList :: [TestPair] | ||
23 | torrentList = | ||
24 | [ ( "res" </> "dapper-dvd-amd64.iso.torrent" | ||
25 | , "0221caf96aa3cb94f0f58d458e78b0fc344ad8bf") | ||
26 | ] | ||
27 | |||
28 | infohashSpec :: (FilePath, String) -> Spec | ||
29 | infohashSpec (filepath, expectedHash) = do | ||
30 | it ("should match " ++ filepath) $ do | ||
31 | torrent <- fromFile filepath | ||
32 | let actualHash = show $ idInfoHash $ tInfoDict torrent | ||
33 | actualHash `shouldBe` expectedHash | ||
34 | |||
35 | spec :: Spec | ||
36 | spec = do | ||
37 | describe "info hash" $ do | ||
38 | mapM_ infohashSpec torrentList | ||
diff --git a/tests/Data/Torrent/LayoutSpec.hs b/tests/Data/Torrent/LayoutSpec.hs deleted file mode 100644 index a3fe7c02..00000000 --- a/tests/Data/Torrent/LayoutSpec.hs +++ /dev/null | |||
@@ -1,30 +0,0 @@ | |||
1 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
2 | {-# LANGUAGE StandaloneDeriving #-} | ||
3 | module Data.Torrent.LayoutSpec (spec) where | ||
4 | |||
5 | import Control.Applicative | ||
6 | import Test.Hspec | ||
7 | import Test.QuickCheck | ||
8 | import System.Posix.Types | ||
9 | |||
10 | import Data.Torrent | ||
11 | |||
12 | |||
13 | instance Arbitrary COff where | ||
14 | arbitrary = fromIntegral <$> (arbitrary :: Gen Int) | ||
15 | |||
16 | instance Arbitrary a => Arbitrary (FileInfo a) where | ||
17 | arbitrary = FileInfo <$> arbitrary <*> arbitrary <*> arbitrary | ||
18 | |||
19 | instance Arbitrary LayoutInfo where | ||
20 | arbitrary = oneof | ||
21 | [ SingleFile <$> arbitrary | ||
22 | , MultiFile <$> arbitrary <*> arbitrary | ||
23 | ] | ||
24 | |||
25 | spec :: Spec | ||
26 | spec = do | ||
27 | describe "accumPosition" $ do | ||
28 | it "" $ property $ \ p1 p2 p3 s1 s2 s3 -> | ||
29 | accumPositions [(p1, s1), (p2, s2), (p3, s3)] | ||
30 | `shouldBe` [(p1, (0, s1)), (p2, (s1, s2)), (p3, (s1 + s2, s3))] \ No newline at end of file | ||
diff --git a/tests/Data/Torrent/MagnetSpec.hs b/tests/Data/Torrent/MagnetSpec.hs deleted file mode 100644 index 838df570..00000000 --- a/tests/Data/Torrent/MagnetSpec.hs +++ /dev/null | |||
@@ -1,44 +0,0 @@ | |||
1 | {-# OPTIONS -fno-warn-orphans #-} | ||
2 | module Data.Torrent.MagnetSpec (spec) where | ||
3 | |||
4 | import Control.Applicative | ||
5 | import Data.Maybe | ||
6 | import Data.Monoid | ||
7 | import Test.Hspec | ||
8 | import Test.QuickCheck | ||
9 | import Test.QuickCheck.Instances () | ||
10 | import Network.URI | ||
11 | |||
12 | import Data.Torrent | ||
13 | import Data.Torrent.InfoHashSpec () | ||
14 | |||
15 | |||
16 | instance Arbitrary URIAuth where | ||
17 | arbitrary = URIAuth <$> arbitrary <*> arbitrary <*> arbitrary | ||
18 | |||
19 | instance Arbitrary URI where | ||
20 | arbitrary | ||
21 | = pure $ fromJust $ parseURI "http://ietf.org/1737.txt?a=1&b=h#123" | ||
22 | |||
23 | instance Arbitrary Magnet where | ||
24 | arbitrary = Magnet <$> arbitrary <*> arbitrary | ||
25 | <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary | ||
26 | <*> arbitrary <*> arbitrary <*> pure mempty | ||
27 | |||
28 | magnetEncoding :: Magnet -> IO () | ||
29 | magnetEncoding m = parseMagnet (renderMagnet m) `shouldBe` Just m | ||
30 | |||
31 | spec :: Spec | ||
32 | spec = do | ||
33 | describe "Magnet" $ do | ||
34 | it "properly encoded" $ property $ magnetEncoding | ||
35 | |||
36 | it "parse base32" $ do | ||
37 | let magnet = "magnet:?xt=urn:btih:CT76LXJDDCH5LS2TUHKH6EUJ3NYKX4Y6" | ||
38 | let ih = "CT76LXJDDCH5LS2TUHKH6EUJ3NYKX4Y6" | ||
39 | parseMagnet magnet `shouldBe` Just (nullMagnet ih) | ||
40 | |||
41 | it "parse base16" $ do | ||
42 | let magnet = "magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567" | ||
43 | let ih = "0123456789abcdef0123456789abcdef01234567" | ||
44 | parseMagnet magnet `shouldBe` Just (nullMagnet ih) | ||
diff --git a/tests/Data/Torrent/MetainfoSpec.hs b/tests/Data/Torrent/MetainfoSpec.hs deleted file mode 100644 index 1a8f97c7..00000000 --- a/tests/Data/Torrent/MetainfoSpec.hs +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | {-# LANGUAGE TypeSynonymInstances #-} | ||
2 | {-# OPTIONS -fno-warn-orphans #-} | ||
3 | module Data.Torrent.MetainfoSpec (spec) where | ||
4 | |||
5 | import Control.Applicative | ||
6 | import Data.ByteString as BS | ||
7 | import Data.ByteString.Lazy as BL | ||
8 | import Data.BEncode | ||
9 | import Data.Maybe | ||
10 | import Data.Time | ||
11 | import Network.URI | ||
12 | import Test.Hspec | ||
13 | import Test.QuickCheck | ||
14 | import Test.QuickCheck.Instances () | ||
15 | |||
16 | import Data.Torrent | ||
17 | import Data.Torrent.LayoutSpec () | ||
18 | import Network.BitTorrent.Core.NodeInfoSpec () | ||
19 | |||
20 | {----------------------------------------------------------------------- | ||
21 | -- Common | ||
22 | -----------------------------------------------------------------------} | ||
23 | |||
24 | data T a = T | ||
25 | |||
26 | prop_properBEncode :: Show a => BEncode a => Eq a | ||
27 | => T a -> a -> IO () | ||
28 | prop_properBEncode _ expected = actual `shouldBe` Right expected | ||
29 | where | ||
30 | actual = decode $ BL.toStrict $ encode expected | ||
31 | |||
32 | instance Arbitrary URI where | ||
33 | arbitrary = pure $ fromJust | ||
34 | $ parseURI "http://exsample.com:80/123365_asd" | ||
35 | |||
36 | {----------------------------------------------------------------------- | ||
37 | -- Instances | ||
38 | -----------------------------------------------------------------------} | ||
39 | |||
40 | instance Arbitrary HashList where | ||
41 | arbitrary = HashList <$> arbitrary | ||
42 | |||
43 | instance Arbitrary PieceInfo where | ||
44 | arbitrary = PieceInfo <$> arbitrary <*> arbitrary | ||
45 | |||
46 | instance Arbitrary InfoDict where | ||
47 | arbitrary = infoDictionary <$> arbitrary <*> arbitrary <*> arbitrary | ||
48 | |||
49 | pico :: Gen (Maybe NominalDiffTime) | ||
50 | pico = oneof | ||
51 | [ pure Nothing | ||
52 | , (Just . fromIntegral) <$> (arbitrary :: Gen Int) | ||
53 | ] | ||
54 | |||
55 | instance Arbitrary Torrent where | ||
56 | arbitrary = Torrent <$> arbitrary | ||
57 | <*> arbitrary <*> arbitrary <*> arbitrary | ||
58 | <*> pico <*> arbitrary <*> arbitrary | ||
59 | <*> arbitrary | ||
60 | <*> arbitrary <*> pure Nothing <*> arbitrary | ||
61 | |||
62 | {----------------------------------------------------------------------- | ||
63 | -- Spec | ||
64 | -----------------------------------------------------------------------} | ||
65 | |||
66 | spec :: Spec | ||
67 | spec = do | ||
68 | describe "FileInfo" $ do | ||
69 | it "properly bencoded" $ property $ | ||
70 | prop_properBEncode (T :: T (FileInfo BS.ByteString)) | ||
71 | |||
72 | describe "LayoutInfo" $ do | ||
73 | it "properly bencoded" $ property $ | ||
74 | prop_properBEncode (T :: T LayoutInfo) | ||
75 | |||
76 | describe "Torrent" $ do | ||
77 | it "property bencoded" $ property $ | ||
78 | prop_properBEncode (T :: T Torrent) | ||
diff --git a/tests/Data/Torrent/PieceSpec.hs b/tests/Data/Torrent/PieceSpec.hs deleted file mode 100644 index d3933396..00000000 --- a/tests/Data/Torrent/PieceSpec.hs +++ /dev/null | |||
@@ -1,13 +0,0 @@ | |||
1 | {-# OPTIONS_GHC -fno-warn-orphans #-} | ||
2 | module Data.Torrent.PieceSpec (spec) where | ||
3 | import Control.Applicative | ||
4 | import Test.Hspec | ||
5 | import Test.QuickCheck | ||
6 | import Data.Torrent | ||
7 | |||
8 | |||
9 | instance Arbitrary a => Arbitrary (Piece a) where | ||
10 | arbitrary = Piece <$> arbitrary <*> arbitrary | ||
11 | |||
12 | spec :: Spec | ||
13 | spec = return () \ No newline at end of file | ||
diff --git a/tests/Data/TorrentSpec.hs b/tests/Data/TorrentSpec.hs new file mode 100644 index 00000000..7186429e --- /dev/null +++ b/tests/Data/TorrentSpec.hs | |||
@@ -0,0 +1,139 @@ | |||
1 | {-# LANGUAGE TypeSynonymInstances #-} | ||
2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
3 | {-# LANGUAGE StandaloneDeriving #-} | ||
4 | {-# OPTIONS -fno-warn-orphans #-} | ||
5 | module Data.TorrentSpec (spec) where | ||
6 | import Control.Applicative | ||
7 | import Data.BEncode | ||
8 | import Data.ByteString as BS | ||
9 | import Data.ByteString.Lazy as BL | ||
10 | import Data.Convertible | ||
11 | import Data.Maybe | ||
12 | import Data.Monoid | ||
13 | import Data.Time | ||
14 | import Network.URI | ||
15 | import System.FilePath | ||
16 | import System.Posix.Types | ||
17 | import Test.Hspec | ||
18 | import Test.QuickCheck | ||
19 | import Test.QuickCheck.Instances () | ||
20 | |||
21 | import Data.Torrent | ||
22 | import Network.BitTorrent.Core.NodeInfoSpec () | ||
23 | |||
24 | |||
25 | pico :: Gen (Maybe NominalDiffTime) | ||
26 | pico = oneof | ||
27 | [ pure Nothing | ||
28 | , (Just . fromIntegral) <$> (arbitrary :: Gen Int) | ||
29 | ] | ||
30 | |||
31 | instance Arbitrary COff where | ||
32 | arbitrary = fromIntegral <$> (arbitrary :: Gen Int) | ||
33 | |||
34 | instance Arbitrary URIAuth where | ||
35 | arbitrary = URIAuth <$> arbitrary <*> arbitrary <*> arbitrary | ||
36 | |||
37 | instance Arbitrary URI where | ||
38 | arbitrary | ||
39 | = pure $ fromJust $ parseURI "http://ietf.org/1737.txt?a=1&b=h#123" | ||
40 | |||
41 | instance Arbitrary InfoHash where | ||
42 | arbitrary = do | ||
43 | bs <- BS.pack <$> vectorOf 20 arbitrary | ||
44 | pure $ either (const (error "arbitrary infohash")) id $ safeConvert bs | ||
45 | |||
46 | instance Arbitrary a => Arbitrary (FileInfo a) where | ||
47 | arbitrary = FileInfo <$> arbitrary <*> arbitrary <*> arbitrary | ||
48 | |||
49 | instance Arbitrary LayoutInfo where | ||
50 | arbitrary = oneof | ||
51 | [ SingleFile <$> arbitrary | ||
52 | , MultiFile <$> arbitrary <*> arbitrary | ||
53 | ] | ||
54 | |||
55 | instance Arbitrary a => Arbitrary (Piece a) where | ||
56 | arbitrary = Piece <$> arbitrary <*> arbitrary | ||
57 | |||
58 | instance Arbitrary HashList where | ||
59 | arbitrary = HashList <$> arbitrary | ||
60 | |||
61 | instance Arbitrary PieceInfo where | ||
62 | arbitrary = PieceInfo <$> arbitrary <*> arbitrary | ||
63 | |||
64 | instance Arbitrary InfoDict where | ||
65 | arbitrary = infoDictionary <$> arbitrary <*> arbitrary <*> arbitrary | ||
66 | |||
67 | instance Arbitrary Torrent where | ||
68 | arbitrary = Torrent <$> arbitrary | ||
69 | <*> arbitrary <*> arbitrary <*> arbitrary | ||
70 | <*> pico <*> arbitrary <*> arbitrary | ||
71 | <*> arbitrary | ||
72 | <*> arbitrary <*> pure Nothing <*> arbitrary | ||
73 | |||
74 | instance Arbitrary Magnet where | ||
75 | arbitrary = Magnet <$> arbitrary <*> arbitrary | ||
76 | <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary | ||
77 | <*> arbitrary <*> arbitrary <*> pure mempty | ||
78 | |||
79 | type TestPair = (FilePath, String) | ||
80 | |||
81 | -- TODO add a few more torrents here | ||
82 | torrentList :: [TestPair] | ||
83 | torrentList = | ||
84 | [ ( "res" </> "dapper-dvd-amd64.iso.torrent" | ||
85 | , "0221caf96aa3cb94f0f58d458e78b0fc344ad8bf") | ||
86 | ] | ||
87 | |||
88 | infohashSpec :: (FilePath, String) -> Spec | ||
89 | infohashSpec (filepath, expectedHash) = do | ||
90 | it ("should match " ++ filepath) $ do | ||
91 | torrent <- fromFile filepath | ||
92 | let actualHash = show $ idInfoHash $ tInfoDict torrent | ||
93 | actualHash `shouldBe` expectedHash | ||
94 | |||
95 | magnetEncoding :: Magnet -> IO () | ||
96 | magnetEncoding m = parseMagnet (renderMagnet m) `shouldBe` Just m | ||
97 | |||
98 | data T a = T | ||
99 | |||
100 | prop_properBEncode :: Show a => BEncode a => Eq a | ||
101 | => T a -> a -> IO () | ||
102 | prop_properBEncode _ expected = actual `shouldBe` Right expected | ||
103 | where | ||
104 | actual = decode $ BL.toStrict $ encode expected | ||
105 | |||
106 | spec :: Spec | ||
107 | spec = do | ||
108 | describe "info hash" $ do | ||
109 | mapM_ infohashSpec torrentList | ||
110 | |||
111 | describe "accumPosition" $ do | ||
112 | it "" $ property $ \ p1 p2 p3 s1 s2 s3 -> | ||
113 | accumPositions [(p1, s1), (p2, s2), (p3, s3)] | ||
114 | `shouldBe` [(p1, (0, s1)), (p2, (s1, s2)), (p3, (s1 + s2, s3))] | ||
115 | |||
116 | describe "FileInfo" $ do | ||
117 | it "properly bencoded" $ property $ | ||
118 | prop_properBEncode (T :: T (FileInfo BS.ByteString)) | ||
119 | |||
120 | describe "LayoutInfo" $ do | ||
121 | it "properly bencoded" $ property $ | ||
122 | prop_properBEncode (T :: T LayoutInfo) | ||
123 | |||
124 | describe "Torrent" $ do | ||
125 | it "property bencoded" $ property $ | ||
126 | prop_properBEncode (T :: T Torrent) | ||
127 | |||
128 | describe "Magnet" $ do | ||
129 | it "properly encoded" $ property $ magnetEncoding | ||
130 | |||
131 | it "parse base32" $ do | ||
132 | let magnet = "magnet:?xt=urn:btih:CT76LXJDDCH5LS2TUHKH6EUJ3NYKX4Y6" | ||
133 | let ih = "CT76LXJDDCH5LS2TUHKH6EUJ3NYKX4Y6" | ||
134 | parseMagnet magnet `shouldBe` Just (nullMagnet ih) | ||
135 | |||
136 | it "parse base16" $ do | ||
137 | let magnet = "magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567" | ||
138 | let ih = "0123456789abcdef0123456789abcdef01234567" | ||
139 | parseMagnet magnet `shouldBe` Just (nullMagnet ih) | ||
diff --git a/tests/Network/BitTorrent/DHT/MessageSpec.hs b/tests/Network/BitTorrent/DHT/MessageSpec.hs index 4ec875dd..3d886fea 100644 --- a/tests/Network/BitTorrent/DHT/MessageSpec.hs +++ b/tests/Network/BitTorrent/DHT/MessageSpec.hs | |||
@@ -17,9 +17,9 @@ import Test.Hspec | |||
17 | import Test.QuickCheck | 17 | import Test.QuickCheck |
18 | import System.Timeout | 18 | import System.Timeout |
19 | 19 | ||
20 | import Data.TorrentSpec () | ||
20 | import Network.BitTorrent.CoreSpec () | 21 | import Network.BitTorrent.CoreSpec () |
21 | import Network.BitTorrent.DHT.TokenSpec () | 22 | import Network.BitTorrent.DHT.TokenSpec () |
22 | import Data.Torrent.InfoHashSpec () | ||
23 | 23 | ||
24 | 24 | ||
25 | instance MonadLogger IO where | 25 | instance MonadLogger IO where |
diff --git a/tests/Network/BitTorrent/DHT/SessionSpec.hs b/tests/Network/BitTorrent/DHT/SessionSpec.hs index bb32cf0e..1fe1d08a 100644 --- a/tests/Network/BitTorrent/DHT/SessionSpec.hs +++ b/tests/Network/BitTorrent/DHT/SessionSpec.hs | |||
@@ -15,7 +15,7 @@ import Network.BitTorrent.DHT | |||
15 | import Network.BitTorrent.DHT.Message | 15 | import Network.BitTorrent.DHT.Message |
16 | import Network.BitTorrent.DHT.Session | 16 | import Network.BitTorrent.DHT.Session |
17 | 17 | ||
18 | import Data.Torrent.InfoHashSpec () | 18 | import Data.TorrentSpec () |
19 | import Network.BitTorrent.CoreSpec () | 19 | import Network.BitTorrent.CoreSpec () |
20 | import Network.BitTorrent.DHT.TokenSpec () | 20 | import Network.BitTorrent.DHT.TokenSpec () |
21 | 21 | ||
diff --git a/tests/Network/BitTorrent/Exchange/MessageSpec.hs b/tests/Network/BitTorrent/Exchange/MessageSpec.hs index 63d814ff..1395ba11 100644 --- a/tests/Network/BitTorrent/Exchange/MessageSpec.hs +++ b/tests/Network/BitTorrent/Exchange/MessageSpec.hs | |||
@@ -10,8 +10,8 @@ import Data.String | |||
10 | import Test.Hspec | 10 | import Test.Hspec |
11 | import Test.QuickCheck | 11 | import Test.QuickCheck |
12 | 12 | ||
13 | import Data.TorrentSpec () | ||
13 | import Data.Torrent.BitfieldSpec () | 14 | import Data.Torrent.BitfieldSpec () |
14 | import Data.Torrent.InfoHashSpec () | ||
15 | import Network.BitTorrent.CoreSpec () | 15 | import Network.BitTorrent.CoreSpec () |
16 | import Network.BitTorrent.Core () | 16 | import Network.BitTorrent.Core () |
17 | import Network.BitTorrent.Exchange.BlockSpec () | 17 | import Network.BitTorrent.Exchange.BlockSpec () |
diff --git a/tests/Network/BitTorrent/Tracker/MessageSpec.hs b/tests/Network/BitTorrent/Tracker/MessageSpec.hs index c56afd2a..439883a1 100644 --- a/tests/Network/BitTorrent/Tracker/MessageSpec.hs +++ b/tests/Network/BitTorrent/Tracker/MessageSpec.hs | |||
@@ -16,7 +16,7 @@ import Data.Maybe | |||
16 | import Test.Hspec | 16 | import Test.Hspec |
17 | import Test.QuickCheck | 17 | import Test.QuickCheck |
18 | 18 | ||
19 | import Data.Torrent.InfoHashSpec () | 19 | import Data.TorrentSpec () |
20 | import Data.Torrent.ProgressSpec () | 20 | import Data.Torrent.ProgressSpec () |
21 | import Network.BitTorrent.Core.PeerIdSpec () | 21 | import Network.BitTorrent.Core.PeerIdSpec () |
22 | import Network.BitTorrent.Core.PeerAddrSpec () | 22 | import Network.BitTorrent.Core.PeerAddrSpec () |