willow-0.1.0.0: test/Test/Willow/Property/Encoding/Utf8.hs
{-|
Description:
Copyright: (c) 2020 Samuel May
License: MPL-2.0
Maintainer: ag.eitilt@gmail.com
Stability: experimental
Portability: portable
-}
module Test.Willow.Property.Encoding.Utf8
( tests
, genUtf8
) where
import qualified Data.ByteString as BS
import qualified Hedgehog as H
import qualified Hedgehog.Gen as H.G
import qualified Hedgehog.Range as H.R
import Hedgehog ( (===) )
import Web.Willow.Common.Encoding
import Test.Willow.Property.Common
tests :: H.Group
tests = packGroup "Web.Willow.Common.Encoding.Utf8"
[ utf8encodeRoundtrip
, utf8decodeRoundtrip
-- Single byte tests handled in "unit" due to limited number
, decodeEndOfStream
, decodeTwoByteInvalid
, decodeThreeByteInvalid
, decodeFourByteInvalid
, decodeOneByteLookup
, decodeTwoByteLookup
, decodeThreeByteLookup
, decodeFourByteLookup
]
genUtf8 :: H.Gen BS.ByteString
genUtf8 = H.G.utf8 (H.R.linear 0 64) (H.G.choice [H.G.latin1, H.G.unicode])
utf8encodeRoundtrip :: Test
utf8encodeRoundtrip = packTest "encode roundtrip" $ do
txt <- H.forAll genText
txt === fst (decodeUtf8' . fst $ encodeUtf8 txt)
utf8decodeRoundtrip :: Test
utf8decodeRoundtrip = packTest "decode roundtrip" $ do
bs <- H.forAll genUtf8
bs === fst (encodeUtf8 . fst $ decodeUtf8' bs)
decodeEndOfStream :: Test
decodeEndOfStream = packTest "incomplete character" $ do
char <- H.forAll $ H.G.choice
[ fmap pure . H.G.word8 $ H.R.linear 0xC2 0xDF
, do
b1 <- H.G.word8 $ H.R.linear 0xE0 0xEF
let (l2, h2) = case b1 of
0xE0 -> (0xA0, 0xBF)
0xED -> (0x80, 0x9F)
_ -> (0x80, 0xBF)
b2 <- H.G.word8 $ H.R.linear l2 h2
return [b1, b2]
, do
b1 <- H.G.word8 $ H.R.linear 0xF0 0xF4
let (l2, h2) = case b1 of
0xF0 -> (0x90, 0xBF)
0xF4 -> (0x80, 0x8F)
_ -> (0x80, 0xBF)
b2 <- H.G.word8 $ H.R.linear l2 h2
b3 <- H.G.word8 $ H.R.linear 0x80 0xBF
return [b1, b2, b3]
]
checkEndOfStream Utf8 char
decodeTwoByteInvalid :: Test
decodeTwoByteInvalid = packTest "invalid second byte" $ do
char <- H.forAll $ H.G.choice
[ do
b1 <- H.G.word8 $ H.R.linear 0xC2 0xF4
b2 <- H.G.choice $ map H.G.word8
[ H.R.linear 0x00 0x7F
, H.R.linear 0xC0 0xFF
]
return [b1, b2]
, do
b2 <- H.G.word8 $ H.R.linear 0x80 0x9F
return [0xE0, b2]
, do
b2 <- H.G.word8 $ H.R.linear 0xA0 0xBF
return [0xED, b2]
, do
b2 <- H.G.word8 $ H.R.linear 0x80 0x8F
return [0xF0, b2]
, do
b2 <- H.G.word8 $ H.R.linear 0x90 0xBF
return [0xF4, b2]
]
checkInvalidInit 1 Utf8 char
decodeThreeByteInvalid :: Test
decodeThreeByteInvalid = packTest "invalid third byte" $ do
b1 <- H.forAll . H.G.word8 $ H.R.linear 0xE0 0xF4
let (l2, h2) = case b1 of
0xE0 -> (0xA0, 0xBF)
0xED -> (0x80, 0x9F)
0xF0 -> (0x90, 0xBF)
0xF4 -> (0x80, 0x8F)
_ -> (0x80, 0xBF)
b2 <- H.forAll . H.G.word8 $ H.R.linear l2 h2
b3 <- H.forAll . H.G.choice $ map H.G.word8
[ H.R.linear 0x00 0x7F
, H.R.linear 0xC0 0xFF
]
checkInvalidInit 1 Utf8 [b1, b2, b3]
decodeFourByteInvalid :: Test
decodeFourByteInvalid = packTest "invalid fourth byte" $ do
b1 <- H.forAll . H.G.word8 $ H.R.linear 0xF0 0xF4
let (l2, h2) = case b1 of
0xF0 -> (0x90, 0xBF)
0xF4 -> (0x80, 0x8F)
_ -> (0x80, 0xBF)
b2 <- H.forAll . H.G.word8 $ H.R.linear l2 h2
b3 <- H.forAll . H.G.word8 $ H.R.linear 0x80 0xBF
b4 <- H.forAll . H.G.choice $ map H.G.word8
[ H.R.linear 0x00 0x7F
, H.R.linear 0xC0 0xFF
]
checkInvalidInit 1 Utf8 [b1, b2, b3, b4]
decodeOneByteLookup :: Test
decodeOneByteLookup = packTest "one-byte sequence" $ do
b1 <- H.forAll $ H.G.choice
[ H.G.word8 $ H.R.linear 0x00 0xC1
, H.G.word8 $ H.R.linear 0xF5 0xFF
]
checkTrailing Utf8 [b1]
decodeTwoByteLookup :: Test
decodeTwoByteLookup = packTest "two-byte sequence" $ do
b1 <- H.forAll . H.G.word8 $ H.R.linear 0xC2 0xDF
b2 <- H.forAll . H.G.word8 $ H.R.linear 0x80 0xBF
checkTrailingInit 1 Utf8 [b1, b2]
decodeThreeByteLookup :: Test
decodeThreeByteLookup = packTest "three-byte sequence" $ do
b1 <- H.forAll . H.G.word8 $ H.R.linear 0xE0 0xEF
let (l2, h2) = case b1 of
0xE0 -> (0xA0, 0xBF)
0xED -> (0x80, 0x9F)
_ -> (0x80, 0xBF)
b2 <- H.forAll . H.G.word8 $ H.R.linear l2 h2
b3 <- H.forAll . H.G.word8 $ H.R.linear 0x80 0xBF
checkTrailingInit 1 Utf8 [b1, b2, b3]
decodeFourByteLookup :: Test
decodeFourByteLookup = packTest "four-byte sequence" $ do
b1 <- H.forAll . H.G.word8 $ H.R.linear 0xF0 0xF4
let (l2, h2) = case b1 of
0xF0 -> (0x90, 0xBF)
0xF4 -> (0x80, 0x8F)
_ -> (0x80, 0xBF)
b2 <- H.forAll . H.G.word8 $ H.R.linear l2 h2
b3 <- H.forAll . H.G.word8 $ H.R.linear 0x80 0xBF
b4 <- H.forAll . H.G.word8 $ H.R.linear 0x80 0xBF
checkTrailingInit 1 Utf8 [b1, b2, b3, b4]