rtorrent-state-0.1.0.0: test/Data/Rtorrent/StateFileSpec.hs
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.Rtorrent.StateFileSpec where
import Control.Applicative ((<$>), (<*>), liftA2)
import Data.BEncode
import Data.ByteString (writeFile)
import Data.ByteString.Lazy (toStrict)
import Data.ByteString.UTF8 (ByteString, fromString)
import qualified Data.Map as M
import Data.Rtorrent.StateFile
import Data.Rtorrent.StateFile.Types
import Data.Typeable
import Prelude hiding (writeFile)
import System.Directory
import System.FilePath
import System.IO.Temp
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck hiding (Result)
-- | Just like 'withRtorrentFiles' but allows us to specify the number
-- of files to use. When 0 or negative, no files are created.
withNRtorrentFiles ∷ Int → StateMod → (FilePath → IO a) → IO a
withNRtorrentFiles i f g = do
t ← getTemporaryDirectory
withTempDirectory t "rtorrent-state-test-dir" $ \d → do
mapM_ (\x → makeStateFile $ d </> show x ++ ".rtorrent") [1 .. i]
g d
where
genStateFileContent ∷ IO ByteString
genStateFileContent = generate arbitrary >>= return . encodeState' . f
makeStateFile ∷ FilePath → IO ()
makeStateFile p = genStateFileContent >>= writeFile p
-- | Produces a temporary directory with some temporary, generated
-- .rtorrent files for the functions that need it.
withRtorrentFiles ∷ StateMod -- ^ Modifies the 'StateFile' before
-- writing in case we need specific
-- conditions
→ (FilePath → IO a) → IO a
withRtorrentFiles f g =
generate arbitrary >>= \(Positive i) → withNRtorrentFiles i f g
-- | IO version of 'shouldSatisfy'.
shouldSatisfyIO ∷ IO a → (a → Bool) → Expectation
t `shouldSatisfyIO` p = p <$> t `shouldReturn` True
spec ∷ Spec
spec = describe "StateFile" $ do
modifyMaxSuccess (const 10) . prop "decode . encode = id" $
\s@(StateFile {}) → let e = toStrict $ encode s
in decode e `shouldBe` Right s
prop "overFiles and parse files work with 0 files"
. withNRtorrentFiles 0 id $ \d →
afterRunning d id `shouldSatisfyIO` const True
prop "can stop all torrents" . withRtorrentFiles startTorrent $ \d →
afterRunning d stopTorrent `shouldSatisfyIO` allStopped
it "can stop 10000 torrents" . once
. withNRtorrentFiles 10000 startTorrent $ \d →
afterRunning d stopTorrent `shouldSatisfyIO` allStopped
prop "can start all torrents" . withRtorrentFiles stopTorrent $ \d →
afterRunning d startTorrent `shouldSatisfyIO` allStarted
where
allSatisfy ∷ M.Map FilePath (Result StateFile) -- ^ parsed files
→ (Result StateFile → Bool) -- ^ predicate
→ Bool
allSatisfy m p = all (== True) . map p $ M.elems m
-- similar to allSatisfy but fails if a parsing failure has occured
allAre ∷ M.Map FilePath (Result StateFile)
→ (StateFile → Bool)
→ Bool
allAre m p = allSatisfy m (\case Right x → p x
Left _ → False )
allStopped, allStarted ∷ M.Map FilePath (Result StateFile) → Bool
allStopped m = allAre m ((== 0) . _state)
allStarted m = allAre m ((== 1) . _state)
afterRunning d f = overFilesIn d f >> parseFiles d
instance Arbitrary ByteStringUtf8 where
arbitrary = BS8 . fromString <$> arbitrary
instance Arbitrary CustomSeeding where
arbitrary = liftA2 CustomSeeding arbitrary arbitrary
instance Arbitrary StateFile where
arbitrary = do
(cdl, cds, cdul, cdus) ← (,,,) <$> arbitrary <*> arbitrary <*> arbitrary
<*> arbitrary
(cd, cw, com) ← (,,) <$> arbitrary <*> arbitrary <*> arbitrary
(cl, cs) ← (,) <$> arbitrary <*> arbitrary
cust ← arbitrary
(c1, c2, c3, c4, c5, dir) ← (,,,,,) <$> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary <*> arbitrary
<*> arbitrary
(hash, ign, k) ← (,,) <$> arbitrary <*> arbitrary <*> arbitrary
lf ← arbitrary
(p, st, stch, stco) ← (,,,) <$> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary
(tn, tf) ← (,) <$> arbitrary <*> arbitrary
(tsf, tss, totu) ← (,,) <$> arbitrary <*> arbitrary <*> arbitrary
vs ← listOf arbitrary
return StateFile { _chokeHeuristicsDownLeech = cdl
, _chokeHeuristicsDownSeed = cds
, _chokeHeuristicsUpLeech = cdul
, _chokeHeuristicsUpSeed = cdus
, _chunksDone = cd
, _chunksWanted = cw
, _complete = com
, _connectionLeech = cl
, _connectionSeed = cs
, _custom = cust
, _custom1 = c1
, _custom2 = c2
, _custom3 = c3
, _custom4 = c4
, _custom5 = c5
, _directory = dir
, _hashing = hash
, _ignoreCommands = ign
, _key = k
, _loadedFile = lf
, _priority = p
, _state = st
, _stateChanged = stch
, _stateCounter = stco
, _throttleName = tn
, _tiedToFile = tf
, _timestampFinished = tsf
, _timestampStarted = tss
, _totalUploaded = totu
, _views = vs
}
newtype RtorrentFileContent = RTFC ByteString
deriving (Show, Eq, Read, Typeable)
instance Arbitrary RtorrentFileContent where
arbitrary = RTFC . toStrict . encode <$> (arbitrary ∷ Gen StateFile)