~alcinnz/fontconfig-pure

40a431c743763a5d24327b387fecf079dc74555a — Adrian Cochrane 7 months ago 1abac8a
fuzz-test MessagePack implementations.
M fontconfig-pure.cabal => fontconfig-pure.cabal +5 -2
@@ 87,7 87,8 @@ library
    build-depends:    base >=4.12 && <5, containers >=0.1 && <1, css-syntax,
            freetype2 >=0.2 && <0.3, hashable >=1.3 && <2, linear >=1.0.1 && <2,
            scientific, stylist-traits >=0.1.1 && <1, text, msgpack >= 1.0 && <2,
            vector >= 0.13 && <1, bytestring, stylist-traits, css-syntax
            vector >= 0.13 && <1, bytestring, stylist-traits, css-syntax,
            QuickCheck

    -- Directories containing source files.
    hs-source-dirs:   lib


@@ 144,4 145,6 @@ test-suite fontconfig-pure-test
    -- Test dependencies.
    build-depends:
        base ^>=4.17.0.0,
        fontconfig-pure
        fontconfig-pure,
        hspec, QuickCheck,
        msgpack

M lib/Graphics/Text/Font/Choose/CharSet.hs => lib/Graphics/Text/Font/Choose/CharSet.hs +8 -2
@@ 8,6 8,7 @@ import Data.Char (isHexDigit, ord, chr)
import Numeric (readHex)

import Data.MessagePack (MessagePack(..))
import Test.QuickCheck (Arbitrary(..))

-- | An FcCharSet is a set of Unicode characters.
type CharSet = IntSet


@@ 51,8 52,13 @@ diffDecompress :: Int -> [Int] -> [Int]
diffDecompress prev (x:xs) = let y = prev + x in y:diffDecompress y xs
diffDecompress _ [] = []

newtype CharSet' = CharSet' { unCharSet :: CharSet }
newtype CharSet' = CharSet' { unCharSet :: CharSet } deriving (Eq, Read, Show)
instance MessagePack CharSet' where
    toObject = toObject . diffCompress 0 . IntSet.toAscList . unCharSet
    toObject x = toObject $ diffCompress 0 $ IntSet.toAscList $ unCharSet x
    fromObject msg =
        CharSet' <$> IntSet.fromAscList <$> diffDecompress 0 <$> fromObject msg
instance Arbitrary CharSet' where
    arbitrary = do
        x <- arbitrary -- Ensure its non-empty, known failure!
        xs <- arbitrary
        return $ CharSet' $ IntSet.insert x xs

M lib/Graphics/Text/Font/Choose/LangSet.hs => lib/Graphics/Text/Font/Choose/LangSet.hs +7 -1
@@ 7,6 7,7 @@ import Data.Set (Set)
import qualified Data.Set as S

import Data.MessagePack (MessagePack(..))
import Test.QuickCheck (Arbitrary(..))
import Graphics.Text.Font.Choose.StrSet (StrSet)
import Graphics.Text.Font.Choose.CharSet (CharSet')



@@ 17,11 18,16 @@ import Graphics.Text.Font.Choose.Result
import Control.Exception (throw)

type LangSet = Set String
newtype LangSet' = LangSet' { unLangSet :: LangSet }
newtype LangSet' = LangSet' { unLangSet :: LangSet } deriving (Eq, Show, Read)

instance MessagePack LangSet' where
    toObject = toObject . S.toList . unLangSet
    fromObject msg = LangSet' <$> S.fromList <$> fromObject msg
instance Arbitrary LangSet' where
    arbitrary = do
        x <- arbitrary -- Ensure non-empty, known failure
        xs <- arbitrary
        return $ LangSet' $ S.insert x xs

data LangComparison = SameLang | SameTerritory | DifferentLang
i2cmp :: Int -> LangComparison

M lib/Graphics/Text/Font/Choose/Pattern.hs => lib/Graphics/Text/Font/Choose/Pattern.hs +13 -1
@@ 8,6 8,7 @@ module Graphics.Text.Font.Choose.Pattern(Pattern, Pattern'(..), module M, Bindin

import Data.Map as M
import Data.MessagePack (MessagePack(..), Object(..))
import Test.QuickCheck (Arbitrary(..), chooseEnum)
import Data.Hashable (Hashable(..))
import GHC.Generics (Generic)



@@ 24,12 25,13 @@ import Graphics.Text.Font.Choose.Weight
import Stylist (PropertyParser(..))
import Data.CSS.Syntax.Tokens (Token(..), NumericValue(..))
import Data.Text (Text, unpack)
import qualified Data.Text as Txt
import Data.Scientific (toRealFloat)
import Data.List (intercalate)
import Data.Maybe as Mb (listToMaybe, fromMaybe, mapMaybe)

type Pattern = Map Text [(Binding, Value)]
data Pattern' = Pattern' { unPattern :: Pattern }
data Pattern' = Pattern' { unPattern :: Pattern } deriving (Eq, Read, Show, Generic)
data Binding = Strong | Weak | Same deriving (Eq, Ord, Enum, Read, Show, Generic)

instance Hashable Binding where


@@ 43,6 45,16 @@ instance MessagePack Binding where
    toObject Weak = ObjectBool False
    toObject Same = ObjectNil

instance Hashable Pattern' where hash = hash . unPattern
instance MessagePack Pattern' where
    fromObject = fmap Pattern' . fromObject
    toObject = toObject . unPattern

instance Arbitrary Pattern' where
    arbitrary = Pattern' <$> M.mapKeys Txt.pack <$> arbitrary
instance Arbitrary Binding where
    arbitrary = chooseEnum (Strong, Same)

setValue :: ToValue v => Text -> Binding -> v -> Pattern -> Pattern
setValue key strength v self = setValues key strength [v] self
setValues :: ToValue v => Text -> Binding -> [v] -> Pattern -> Pattern

M lib/Graphics/Text/Font/Choose/Range.hs => lib/Graphics/Text/Font/Choose/Range.hs +8 -1
@@ 1,11 1,15 @@
{-# LANGUAGE DeriveGeneric #-}
module Graphics.Text.Font.Choose.Range(Range(..), iRange) where

import Data.MessagePack (MessagePack(..), Object(..))
import Test.QuickCheck (Arbitrary(..))
import GHC.Generics (Generic(..))
import Data.Hashable (Hashable(..))
import qualified Data.Vector as V
import qualified Data.IntMap as IM

-- | Matches a numeric range.
data Range = Range Double Double deriving (Eq, Show, Ord)
data Range = Range Double Double deriving (Eq, Read, Show, Ord, Generic)
-- | Matches an integral range.
iRange :: Int -> Int -> Range
iRange i j = toEnum i `Range` toEnum j


@@ 19,3 23,6 @@ instance MessagePack Range where
        | Just msg' <- fromObject msg =
            Just (IM.findWithDefault 0 0 msg' `Range` IM.findWithDefault 0 1 msg')
        | otherwise = Nothing
instance Arbitrary Range where
    arbitrary = uncurry Range <$> arbitrary
instance Hashable Range

M lib/Graphics/Text/Font/Choose/StrSet.hs => lib/Graphics/Text/Font/Choose/StrSet.hs +4 -1
@@ 4,9 4,12 @@ import Data.Set (Set)
import qualified Data.Set as S

import Data.MessagePack (MessagePack(..))
import Test.QuickCheck (Arbitrary(..))

newtype StrSet = StrSet { unStrSet :: Set String }
newtype StrSet = StrSet { unStrSet :: Set String } deriving (Eq, Show, Read)

instance MessagePack StrSet where
    toObject = toObject . S.toList . unStrSet
    fromObject msg = StrSet <$> S.fromList <$> fromObject msg
instance Arbitrary StrSet where
    arbitrary = StrSet <$> arbitrary

M lib/Graphics/Text/Font/Choose/Value.hs => lib/Graphics/Text/Font/Choose/Value.hs +22 -2
@@ 1,14 1,18 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, DeriveGeneric #-}
module Graphics.Text.Font.Choose.Value(Value(..), ToValue(..)) where

import Linear.Matrix (M22)
import Linear.V2 (V2(..))
import qualified Data.Vector as V
import Graphics.Text.Font.Choose.CharSet (CharSet, CharSet'(..))
--import FreeType.Core.Base (FT_Face(..))
import Graphics.Text.Font.Choose.LangSet (LangSet, LangSet'(..))
import Graphics.Text.Font.Choose.Range (Range)

import Data.MessagePack (MessagePack(..), Object(..))
import Test.QuickCheck (Arbitrary(..), oneof)
import GHC.Generics (Generic)
import Data.Hashable (Hashable(..))
import qualified Data.Text as Txt

-- | A dynamic type system for `Pattern`s.


@@ 21,8 25,9 @@ data Value = ValueVoid
    | ValueCharSet CharSet
--    | ValueFTFace FT_Face -- FIXME: Is it worth going through the trouble to bridge this?
    | ValueLangSet LangSet
    | ValueRange Range deriving (Eq, Show, Ord)
    | ValueRange Range deriving (Eq, Read, Show, Ord, Generic)

instance Hashable Value
instance MessagePack Value where
    toObject ValueVoid = ObjectNil
    toObject (ValueInt x) = ObjectInt x


@@ 41,6 46,7 @@ instance MessagePack Value where
    fromObject (ObjectDouble x) = Just $ ValueDouble x
    fromObject (ObjectStr x) = Just $ ValueString $ Txt.unpack x
    fromObject (ObjectBin _) = Nothing -- Would use for to transfer font faces via underlying bytes.
    fromObject (ObjectArray x) | V.null x = Nothing -- Ambiguous!
    fromObject msg
        | Just charset <- fromObject msg = Just $ ValueCharSet $ unCharSet charset
        | Just langset <- fromObject msg = Just $ ValueLangSet $ unLangSet langset


@@ 49,6 55,20 @@ instance MessagePack Value where
            -- [Double] decoding is overly generous, potentially conflicts with above.
            Just $ ValueMatrix $ V2 (V2 xx yx) (V2 xy yy)
        | otherwise = Nothing
instance Arbitrary Value where
    arbitrary = oneof [
        return ValueVoid,
        ValueInt <$> arbitrary,
        ValueDouble <$> arbitrary,
        ValueString <$> arbitrary,
        ValueBool <$> arbitrary,
        do
            (a, b, c, d) <- arbitrary
            return $ ValueMatrix $ V2 (V2 a b) (V2 c d),
        ValueCharSet <$> unCharSet <$> arbitrary,
        ValueLangSet <$> unLangSet <$> arbitrary,
        ValueRange <$> arbitrary
      ]

-- | Coerces compiletime types to runtime types.
class ToValue x where

D lib/MyLib.hs => lib/MyLib.hs +0 -4
@@ 1,4 0,0 @@
module MyLib (someFunc) where

someFunc :: IO ()
someFunc = putStrLn "someFunc"

M test/Main.hs => test/Main.hs +28 -1
@@ 1,4 1,31 @@
module Main (main) where

import Test.Hspec
import Test.Hspec.QuickCheck

import Data.MessagePack as MP
import Graphics.Text.Font.Choose

main :: IO ()
main = putStrLn "Test suite not yet implemented."
main = hspec $ do
    describe "Canary" $ do
        it "runs fine" $ do
            True `shouldBe` True
    describe "Roundtrips" $ do
        describe "converts MessagePack & back" $ do
            prop "CharSet" $ \x ->
                MP.unpack (MP.pack x) `shouldBe` Just (x :: CharSet')
            prop "FontSet" $ \x -> let y = Prelude.map unPattern x
                in MP.unpack (MP.pack y) `shouldBe` Just y
            prop "LangSet" $ \x ->
                MP.unpack (MP.pack x) `shouldBe` Just (x :: LangSet')
            prop "ObjectSet" $ \x ->
                MP.unpack (MP.pack x) `shouldBe` Just (x :: ObjectSet)
            prop "Pattern" $ \x ->
                MP.unpack (MP.pack x) `shouldBe` Just (x :: Pattern')
            prop "Range" $ \x ->
                MP.unpack (MP.pack x) `shouldBe` Just (x :: Range)
            prop "StrSet" $ \x ->
                MP.unpack (MP.pack x) `shouldBe` Just (x :: StrSet)
            prop "Value" $ \x ->
                MP.unpack (MP.pack x) `shouldBe` Just (x :: Value)