packages feed

elocrypt-0.6.0: test/Test/ElocryptTest.hs

{-# LANGUAGE TemplateHaskell #-}
module Test.ElocryptTest where

import Control.Monad
import Control.Monad.Random hiding (next)
import Data.Maybe
import System.Random hiding (next)
import Test.QuickCheck
import Test.Tasty
import Test.Tasty.QuickCheck (testProperty)
import Test.Tasty.TH

import Data.Elocrypt
import Data.Elocrypt.Trigraph
import Test.Elocrypt.Instances

tests :: TestTree
tests = $(testGroupGenerator)

prop_genPasswordShouldBeUnique :: GreaterThan2 Int -> Bool -> StdGen -> Bool
prop_genPasswordShouldBeUnique (GT2 len) caps gen
  = p /= fst (genPassword len caps gen')
  where (p, gen') = genPassword len caps gen

prop_genPasswordsShouldBeUnique :: GreaterThan2 Int -> Bool -> StdGen -> Bool
prop_genPasswordsShouldBeUnique (GT2 len) caps gen
  = p /= ps
  where (p:ps:_) = fst (genPasswords len caps gen)

prop_newPasswordShouldBeLength :: Int -> Bool -> StdGen -> Property
prop_newPasswordShouldBeLength len caps gen = len > 0 ==>
                                              length (newPassword len caps gen) == len

prop_newPasswordShouldConsistOfAlphabet :: Int -> Bool -> StdGen -> Bool
prop_newPasswordShouldConsistOfAlphabet len caps gen
  = all ((flip elem) alphabet) (newPassword len caps gen)

prop_newPasswordsShouldBeUnique :: GreaterThan2 Int -> Bool -> StdGen -> Bool
prop_newPasswordsShouldBeUnique (GT2 len) caps gen
  = p /= ps
  where (p:ps:_) = newPasswords len caps gen

prop_newPasswordShouldHaveLen :: Int -> Bool -> StdGen -> Property
prop_newPasswordShouldHaveLen len caps gen
  = len >= 0 ==> length (newPassword len caps gen) == len

prop_first2ShouldHaveLength2 :: StdGen -> Bool
prop_first2ShouldHaveLength2 g = length (evalRand first2 g) == 2

prop_nextShouldSkip0Weights :: AlphaChar -> AlphaChar -> StdGen -> Property
prop_nextShouldSkip0Weights (Alpha c1) (Alpha c2) gen = isCandidate next' [c1, c2]
  where next' = evalRand (next . reverse $ [c1, c2]) gen

prop_lastNShouldSkip0Weights :: AlphaChar -> AlphaChar -> Int -> StdGen -> Property
prop_lastNShouldSkip0Weights (Alpha c1) (Alpha c2) len gen
  = len > 0 ==> lastNShouldSkip0Weights' lastN'
  where f2 = [c1, c2]
        lastN' = evalRand (lastN len [c2, c1]) gen

lastNShouldSkip0Weights' :: String -> Property
lastNShouldSkip0Weights' (p:ps:pss:psss) = isCandidate p [pss, ps] .&&.
                                           lastNShouldSkip0Weights' (ps:pss:psss)
lastNShouldSkip0Weights' _ = property True

-- Utility functions
isCandidate :: Char -> String -> Property
isCandidate c (s:ss:sss) = hasCandidates f2 ==>
                           isJust $ elem c `liftM` findNextCandidates f2
  where f2 = [s, ss]

isCandidate _ _ = undefined -- This really shouldn't ever happen

hasCandidates :: String -> Bool
hasCandidates s = (not . null) `liftM` findNextCandidates s == Just True

findNextCandidates :: String -> Maybe [Char]
findNextCandidates (c1:c2:_) = map fst `liftM` frequency
  where f2 = [c1, c2]
        frequency = (filter ((/=0) . snd) . zip ['a'..'z']) `liftM` findFrequency f2

findNextCandidates _ = Nothing  -- This really shouldn't ever happen