base64

Fast Haskell base64 encoding/decoding (docs.ppad.tech/base64).
git clone git://git.ppad.tech/base64.git
Log | Files | Refs | README | LICENSE

commit d4c704d005ceedbac7cb11b3b7abec818a22bdb2
parent c84cc9b184e71f455d0cd8d6b829f20f34bf232b
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 16 May 2026 11:38:19 -0230

test: property tests and RFC vectors

QuickCheck properties (5000 iters each) for decode-inverts-encode
and agreement with base64-bytestring on both encode and decode.
Unit test covers the seven RFC 4648 ยง10 vectors ("", "f", "fo",
"foo", "foob", "fooba", "foobar"), checking both directions.

Diffstat:
Atest/Main.hs | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+), 0 deletions(-)

diff --git a/test/Main.hs b/test/Main.hs @@ -0,0 +1,84 @@ +{-# OPTIONS_GHC -fno-warn-missing-signatures #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PackageImports #-} + +module Main where + +import qualified Data.ByteString as BS +import qualified "ppad-base64" Data.ByteString.Base64 as B64 +import qualified "base64-bytestring" Data.ByteString.Base64 as R0 +import Test.Tasty +import qualified Test.Tasty.QuickCheck as Q +import qualified Test.Tasty.HUnit as H + +newtype BS = BS BS.ByteString + deriving (Eq, Show) + +bytes :: Int -> Q.Gen BS.ByteString +bytes k = do + l <- Q.chooseInt (0, k) + v <- Q.vectorOf l Q.arbitrary + pure (BS.pack v) + +instance Q.Arbitrary BS where + arbitrary = do + b <- bytes 1024 + pure (BS b) + +decode_inverts_encode :: BS -> Bool +decode_inverts_encode (BS bs) = case B64.decode (B64.encode bs) of + Nothing -> False + Just b -> b == bs + +encode_matches_reference :: BS -> Bool +encode_matches_reference (BS bs) = + let us = B64.encode bs + r0 = R0.encode bs + in us == r0 + +decode_matches_reference :: BS -> Bool +decode_matches_reference (BS bs) = + let enc = R0.encode bs + us = B64.decode enc + r0 = R0.decode enc + in case us of + Nothing -> case r0 of + Left _ -> True + _ -> False + Just du -> case r0 of + Left _ -> False + Right d0 -> du == d0 + +case_rfc_vectors :: TestTree +case_rfc_vectors = H.testCase "RFC 4648 \167 10 vectors" $ do + let vectors = [ + ("", "") + , ("f", "Zg==") + , ("fo", "Zm8=") + , ("foo", "Zm9v") + , ("foob", "Zm9vYg==") + , ("fooba", "Zm9vYmE=") + , ("foobar", "Zm9vYmFy") + ] + check (input, expected) = do + H.assertEqual ("encode " <> show input) + expected (B64.encode input) + H.assertEqual ("decode " <> show expected) + (Just input) (B64.decode expected) + mapM_ check vectors + +main :: IO () +main = defaultMain $ + testGroup "ppad-base64" [ + testGroup "property tests" [ + Q.testProperty "decode . encode ~ id" $ + Q.withMaxSuccess 5000 decode_inverts_encode + , Q.testProperty "encode matches reference" $ + Q.withMaxSuccess 5000 encode_matches_reference + , Q.testProperty "decode matches reference" $ + Q.withMaxSuccess 5000 decode_matches_reference + ] + , testGroup "unit tests" [ + case_rfc_vectors + ] + ]