packages feed

base32-z-bytestring-1.0.0.0: test/Test/Data/ByteString/Base32.hs

{-# LANGUAGE OverloadedStrings #-}
module Test.Data.ByteString.Base32 (tests) where

import           Data.ByteString.Base32.Z as Base32
import           Data.ByteString.Char8    as BC
import           Data.Char

import           System.IO.Unsafe         (unsafePerformIO)

import           Hedgehog
import qualified Hedgehog.Gen             as Gen
import qualified Hedgehog.Range           as Range
import           Test.Tasty
import           Test.Tasty.Hedgehog
import           Test.Tasty.Hspec


-- The use of 'unsafePerformIO' seems to be \"recommended\" by the library
-- itself: http://hackage.haskell.org/package/tasty-hspec-1.1.5.1/docs/Test-Tasty-Hspec.html#g:4
-- gulp!
tests :: TestTree
tests = testGroup "Z-Base32" [
    unsafePerformIO $ testSpec "encode" encodeSpec
  , unsafePerformIO $ testSpec "decode" decodeSpec
  , unsafePerformIO $ testSpec "decodeLenient" decodeLenientSpec
  , testProperty "encode roundtrips case sensitive" propEncodeRoundtrips
  , testProperty "encode roundtrips case insensitive" propEncodeCaseInsensitiveRoundtrips
  , testProperty "decode lenient roundtrips" propDecodeLenientRoundtrips
  , testProperty "decode lenient roundtrips (case insensitive)" propDecodeLenientCaseInsensitiveRoundtrips
  ]

{------------------------------------------------------------------------------
--- Specs
------------------------------------------------------------------------------}

encodeSpec :: Spec
encodeSpec =
    it "conform RFC examples" $ do
      encode ""       `shouldBe` ""
      encode "f"      `shouldBe` "ca"
      encode "fo"     `shouldBe` "c3zo"
      encode "foo"    `shouldBe` "c3zs6"
      encode "foob"   `shouldBe` "c3zs6ao"
      encode "fooba"  `shouldBe` "c3zs6aub"
      encode "foobar" `shouldBe` "c3zs6aubqe"

decodeSpec :: Spec
decodeSpec = do
    it "conform RFC examples" $ do
      decode ""           `shouldBe` Right ""
      decode "ca"         `shouldBe` Right "f"
      decode "c3zo"       `shouldBe` Right "fo"
      decode "c3zs6"      `shouldBe` Right "foo"
      decode "c3zs6ao"    `shouldBe` Right "foob"
      decode "c3zs6aub"   `shouldBe` Right "fooba"
      decode "c3zs6aubqe" `shouldBe` Right "foobar"
    it "fail gracefully if encoded data contains non alphabet chars" $ do
      decode "0======="         `shouldBe` Left "'0' is not base32 character"
      decode "AAAAAAAA0=======" `shouldBe` Left "'0' is not base32 character"

decodeLenientSpec :: Spec
decodeLenientSpec = do
    it "conform RFC examples" $ do
      decodeLenient ""           `shouldBe` Right ""
      decodeLenient "ca"         `shouldBe` Right "f"
      decodeLenient "c3zo"       `shouldBe` Right "fo"
      decodeLenient  "c3zs6"     `shouldBe` Right "foo"
      decodeLenient "c3zs6ao"    `shouldBe` Right "foob"
      decodeLenient "c3zs6aub"   `shouldBe` Right "fooba"
      decodeLenient "c3zs6aubqe" `shouldBe` Right "foobar"

    it "skip non alphabet chars" $ do
      decodeLenient "|"    `shouldBe` Right ""
      decodeLenient "M"    `shouldBe` Right ""
      decodeLenient "M|ca" `shouldBe` Right "["

{------------------------------------------------------------------------------
--- Properties
------------------------------------------------------------------------------}

propEncodeRoundtrips :: Property
propEncodeRoundtrips = property $ do
    bs <- forAll $ Gen.bytes (Range.linear 0 500)
    decode (encode bs) === Right bs

propEncodeCaseInsensitiveRoundtrips :: Property
propEncodeCaseInsensitiveRoundtrips = property $ do
    bs <- forAll $ Gen.utf8 (Range.linear 0 500) Gen.unicodeAll
    decode (BC.map toLower (encode bs)) === Right bs

propDecodeLenientRoundtrips :: Property
propDecodeLenientRoundtrips = property $ do
    bs <- forAll $ Gen.bytes (Range.linear 0 500)
    decodeLenient (encode bs) === Right bs

propDecodeLenientCaseInsensitiveRoundtrips :: Property
propDecodeLenientCaseInsensitiveRoundtrips = property $ do
    bs <- forAll $ Gen.utf8 (Range.linear 0 500) Gen.unicodeAll
    decodeLenient (BC.map toLower (encode bs)) === Right bs