packages feed

RFC1751-1.0.0: tests/Spec.hs

import           Control.Monad
import           Data.Bits
import           Data.ByteString       (ByteString)
import qualified Data.ByteString       as BS
import           Data.Char
import           Data.List
import           Data.Maybe
import           Data.RFC1751
import           Data.Serialize
import           Data.Word
import           Numeric
import           Test.Hspec
import           Test.Hspec.QuickCheck

type TestVector = (ByteString, String)

main :: IO ()
main =
    hspec $ do
        describe "codec unit tests" $ do
            it "encodes keys" (forM_ vectors encodeTest)
            it "decodes keys" (forM_ vectors decodeTest)
            it "encodes lowercase keys" (forM_ vectorsLC encodeTest)
            it "encodes lowercase keys" (forM_ vectorsLC decodeTest)
            it "decodes bad checksums" (forM_ badChecksumVectors badChecksum)
            it
                "decodes bad lowercase checksums"
                (forM_ badChecksumVectorsLC badChecksum)
            it "decodes not in dictionary" (forM_ notDictVectors notDict)
            it
                "decodes lowercase not in dictionary"
                (forM_ notDictVectorsLC notDict)
        describe "codec property tests" $ do
            prop "encode/decode keys" decodeEncode
            prop "encode/decode lowercase keys" decodeEncodeLC
            prop "encode/decode double 64-bit keys" doubleWord64

badChecksumVectors :: [String]
badChecksumVectors =
    [ "RASH BUSH MILK LOOK BAD A AVID GAFF BAIT ROT POD LOVE"
    , "TAB BORE DUNK SURE COVE NORM PRY IF JOE MYRA GWEN TENT"
    , "BORN ROLL LOVE BEAR AGEE IFFY CUTS MASK MOOD FOWL ROME MIT"
    ]

badChecksumVectorsLC :: [String]
badChecksumVectorsLC =
    [ "rash bush milk look bad a avid gaff bait rot pod love"
    , "tab bore dunk sure cove norm pry if joe myra gwen tent"
    , "born roll love bear agee iffy cuts mask mood fowl rome mit"
    ]

badChecksum :: String -> Expectation
badChecksum s = mnemonicToKey s `shouldSatisfy` isNothing

notDictVectors :: [String]
notDictVectors =
    [ "PHON MEMO HOP NELL RET DEAF HURT YAWN FLAG MILE LEO LESK"
    , "JAG SUCH PER HASH FULL PHON DAN THEY CAIN BOND LEFT COCA"
    , "TENT TIER LIEU ROD URGE BOWL PATK HOOK FLEW ELY MAN OAK"
    , "TIE OLDY FEEL DOCK EWE PA EMIT HAVE HIS TOTE SWAN KTUH"
    ]

notDictVectorsLC :: [String]
notDictVectorsLC =
    [ "phon memo hop nell ret deaf hurt yawn flag mile leo lesk"
    , "jag such per hash full phon dan they cain bond left coca"
    , "tent tier lieu rod urge bowl patk hook flew ely man oak"
    , "tie oldy feel dock ewe pa emit have his tote swan ktuh"
    ]

notDict :: String -> Expectation
notDict s = mnemonicToKey s `shouldSatisfy` isNothing

integerToBS :: Integer -> ByteString
integerToBS 0 = BS.pack [0]
integerToBS i
    | i > 0     = BS.pack $ reverse $ unfoldr f i
    | otherwise = error "integerToBS not defined for negative values"
  where
    f 0 = Nothing
    f x = Just (fromInteger x :: Word8, x `shiftR` 8)

hexToBS :: String -> ByteString
hexToBS str
    | null str  = BS.empty
    | otherwise = BS.append z2 r2
  where
    (z,r) = span (== '0') $ filter (/= ' ') str
    z2    = BS.replicate (fromIntegral $ length z `div` 2) 0
    r1    = readHex r
    r2 | null r    = BS.empty
       | null r1   = error "cannot read hex"
       | otherwise = integerToBS $ fst $ head r1

vectors :: [TestVector]
vectors =
  [ ( hexToBS "CCAC 2AED 5910 56BE 4F90 FD44 1C53 4766"
    , "RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE"
    )
  , ( hexToBS "EFF8 1F9B FBC6 5350 920C DD74 16DE 8009"
    , "TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL"
    )
  ]

vectorsLC :: [TestVector]
vectorsLC =
  [ ( hexToBS "ccac 2aed 5910 56be 4f90 fd44 1c53 4766"
    , "rash bush milk look bad brim avid gaff bait rot pod love"
    )
  , ( hexToBS "eff8 1f9b fbc6 5350 920c dd74 16de 8009"
    , "trod mute tail warm char kong haag city bore o teal awl"
    )
  ]

encodeTest :: TestVector -> Expectation
encodeTest (bs, m) = mnemonicToKey m `shouldBe` Just bs

decodeTest :: TestVector -> Expectation
decodeTest (bs, hk) =
    map toLower <$> keyToMnemonic bs `shouldBe` Just (map toLower hk)

decodeEncode :: (Word64, Word64) -> Bool
decodeEncode (w1, w2) = (mnemonicToKey =<< keyToMnemonic bs) == Just bs
  where
    bs = encode w1 `BS.append` encode w2

decodeEncodeLC :: (Word64, Word64) -> Bool
decodeEncodeLC (w1, w2) =
    (mnemonicToKey =<< map toLower <$> keyToMnemonic bs) == Just bs
  where
    bs = encode w1 `BS.append` encode w2


doubleWord64 :: Word64 -> Bool
doubleWord64 w =
    maybe False (uncurry (==) . splitAt 6 . words) (keyToMnemonic bs)
  where
    bs = encode w `BS.append` encode w