packages feed

elocrypt-1.0.0: test/IntegTest/Elocrypt/PasswordTest.hs

{-# LANGUAGE TemplateHaskell #-}
module IntegTest.Elocrypt.PasswordTest where

import Data.List
import Data.Maybe
import Control.Monad

import Test.Proctest
import Test.Proctest.Assertions
import Test.QuickCheck
import Test.Tasty hiding (Timeout)
import Test.Tasty.QuickCheck (testProperty)
import Test.Tasty.TH

import Test.Elocrypt.Instances

tests :: TestTree
tests = $(testGroupGenerator)

elocrypt = "elocrypt"

prop_printsPasswordsWithSpecifiedLength :: CliArgs -> Property
prop_printsPasswordsWithSpecifiedLength (CliArgs args)
  = isJust (getPosParam args) ==>
    ioProperty $ do
      (in', out', err', p) <- run' elocrypt args
      response <- readHandle out'

      let len    = read . fromJust . getPosParam $ args
          words' = words response

      return (all ((==) len . length) words')

prop_printsNothingWhenSpecifiedLengthIsZero :: CliArgs -> Property
prop_printsNothingWhenSpecifiedLengthIsZero (CliArgs args)
  = isNothing (getPosParam args) ==>
    ioProperty $ do
      let args' = args ++ ["0"]

      (in', out', err', p) <- run' elocrypt args'
      response <- readHandle out'
      return (response == "")

prop_printsLongPasswords :: GreaterThan79 Int -> Property
prop_printsLongPasswords (GT79 a)
  = ioProperty $ do
      (in', out', err', p) <- run' elocrypt [show a]
      response <- readHandle out'
      return . all (==1) . map (length . words) . lines $ response

prop_printsSpecifiedNumberOfPasswords :: CliArgs -> Property
prop_printsSpecifiedNumberOfPasswords (CliArgs args)
  = isJust (getArg "-n" args) ==>
    ioProperty $ do
      (in', out', err', p) <- run' elocrypt args
      response <- readHandle out'

      let number = read . fromJust . getArg "-n" $ args
          words' = words response

      return (number == length words')

prop_printsMultiplePasswordsPerLine :: CliArgs -> Property
prop_printsMultiplePasswordsPerLine (CliArgs args)
  = (read . fromMaybe "8" . getPosParam $ args) <= 38 ==>
    ioProperty $ do
      (in', out', err', p) <- run' elocrypt args
      response <- readHandle out'

      return $
        all (>1) . tail . reverse . map (length . words) . lines $ response

-- Utility functions
run' :: FilePath -> [String] -> IO (Handle, Handle, Handle, ProcessHandle)
run' exe args = do
  res@(in', out', err', p) <- run exe args
  sleep'
  assertExitedSuccess (seconds 2) p
  return res

readHandle :: Handle -> IO String
readHandle = (<$>) asUtf8Str . waitOutput (seconds 2) 5000

assertExitedSuccess :: Timeout -> ProcessHandle -> IO Bool
assertExitedSuccess t = liftM (== ExitSuccess) . assertExitedTimeout t

assertExitedFailure :: Timeout -> ProcessHandle -> IO Bool
assertExitedFailure t = liftM not . assertExitedSuccess t

sleep' :: IO ()
sleep' = sleep (seconds 0.0001)

getArg :: String -> [String] -> Maybe String
getArg prefix args = (tail . dropWhile (not . elem')) `liftM` arg
  where arg = find (isPrefixOf prefix) args
        elem' = (flip elem) [' ', '=']

getPosParam :: [String] -> Maybe String
getPosParam = find $ (/= '-') . head