restman-0.7.2.2: test/Props/SkylightingProps.hs
module Props.SkylightingProps (tests) where
-- base
import Data.Bits ((.&.))
import Data.Word (Word8)
-- hedgehog
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
-- tasty / tasty-hedgehog
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Hedgehog (testProperty)
-- vty
import qualified Graphics.Vty.Attributes as VA
-- skylighting
import Skylighting.Types (ANSIColorLevel(..), Color(..))
-- restman
import Skylighting.Format.Vty
(attrColor, attrStyle, colorDistance, vtyStyleAttr)
tests :: TestTree
tests = testGroup "Props.Skylighting.Format.Vty"
[ colorDistanceProps
, attrStyleProps
, attrColorProps
, vtyStyleAttrProps
]
-- ---------------------------------------------------------------------------
-- Generators
-- ---------------------------------------------------------------------------
genWord8 :: Gen Word8
genWord8 = Gen.word8 Range.linearBounded
genColor :: Gen Color
genColor = RGB
<$> genWord8
<*> genWord8
<*> genWord8
genColorLevel :: Gen ANSIColorLevel
genColorLevel = Gen.element [ANSITrueColor, ANSI256Color, ANSI16Color]
-- ---------------------------------------------------------------------------
-- colorDistance properties
--
-- colorDistance :: Color -> Color -> Int16
-- Defined as the L1 (Manhattan) distance over RGB channels.
-- ---------------------------------------------------------------------------
-- Distance from a color to itself is zero.
prop_colorDistance_self_zero :: Property
prop_colorDistance_self_zero = property $ do
c <- forAll genColor
colorDistance c c === 0
-- Distance is symmetric: d(a,b) == d(b,a).
prop_colorDistance_symmetric :: Property
prop_colorDistance_symmetric = property $ do
a <- forAll genColor
b <- forAll genColor
colorDistance a b === colorDistance b a
-- Distance is always non-negative.
prop_colorDistance_nonneg :: Property
prop_colorDistance_nonneg = property $ do
a <- forAll genColor
b <- forAll genColor
assert (colorDistance a b >= 0)
-- Triangle inequality: d(a,c) <= d(a,b) + d(b,c).
prop_colorDistance_triangle :: Property
prop_colorDistance_triangle = property $ do
a <- forAll genColor
b <- forAll genColor
c <- forAll genColor
assert (colorDistance a c <= colorDistance a b + colorDistance b c)
colorDistanceProps :: TestTree
colorDistanceProps = testGroup "colorDistance"
[ testProperty "self distance is zero" prop_colorDistance_self_zero
, testProperty "symmetric" prop_colorDistance_symmetric
, testProperty "non-negative" prop_colorDistance_nonneg
, testProperty "triangle inequality" prop_colorDistance_triangle
]
-- ---------------------------------------------------------------------------
-- attrStyle properties
--
-- attrStyle :: Bool -> Bool -> Bool -> VA.Style
-- Each Bool selects whether bold, italic, or underline bits are set.
-- ---------------------------------------------------------------------------
-- When bold=True the bold bit is set; when False it is clear.
prop_attrStyle_bold_bit :: Property
prop_attrStyle_bold_bit = property $ do
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
let s = attrStyle b i u
(s .&. VA.bold /= 0) === b
-- When italic=True the italic bit is set; when False it is clear.
prop_attrStyle_italic_bit :: Property
prop_attrStyle_italic_bit = property $ do
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
let s = attrStyle b i u
(s .&. VA.italic /= 0) === i
-- When underline=True the underline bit is set; when False it is clear.
prop_attrStyle_underline_bit :: Property
prop_attrStyle_underline_bit = property $ do
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
let s = attrStyle b i u
(s .&. VA.underline /= 0) === u
-- All flags False gives the defaultStyleMask (no bits set by us).
prop_attrStyle_all_false_is_default :: Property
prop_attrStyle_all_false_is_default = property $
attrStyle False False False === VA.defaultStyleMask
-- Flags are independent: toggling one Bool does not affect the other bits.
prop_attrStyle_flags_independent :: Property
prop_attrStyle_flags_independent = property $ do
i <- forAll Gen.bool
u <- forAll Gen.bool
-- flip bold only
let withBold = attrStyle True i u
withoutBold = attrStyle False i u
-- italic and underline bits should be the same in both
(withBold .&. VA.italic) === (withoutBold .&. VA.italic)
(withBold .&. VA.underline) === (withoutBold .&. VA.underline)
attrStyleProps :: TestTree
attrStyleProps = testGroup "attrStyle"
[ testProperty "bold flag controls bold bit" prop_attrStyle_bold_bit
, testProperty "italic flag controls italic bit" prop_attrStyle_italic_bit
, testProperty "underline flag controls underline" prop_attrStyle_underline_bit
, testProperty "all False gives defaultStyleMask" prop_attrStyle_all_false_is_default
, testProperty "flags are independent" prop_attrStyle_flags_independent
]
-- ---------------------------------------------------------------------------
-- attrColor properties
--
-- attrColor :: ANSIColorLevel -> Maybe Color -> Maybe VA.Color
-- Nothing input always gives Nothing output (color level irrelevant).
-- Just input always gives Just output.
-- ---------------------------------------------------------------------------
-- Nothing maps to Nothing for all color levels.
prop_attrColor_nothing_to_nothing :: Property
prop_attrColor_nothing_to_nothing = property $ do
clv <- forAll genColorLevel
attrColor clv Nothing === Nothing
-- Just a color always produces Just a vty color (never Nothing).
prop_attrColor_just_to_just :: Property
prop_attrColor_just_to_just = property $ do
clv <- forAll genColorLevel
col <- forAll genColor
case attrColor clv (Just col) of
Nothing -> failure
Just _ -> success
-- For TrueColor and 256-color, identical RGB inputs yield identical outputs.
prop_attrColor_same_rgb_same_result :: Property
prop_attrColor_same_rgb_same_result = property $ do
clv <- forAll genColorLevel
col <- forAll genColor
attrColor clv (Just col) === attrColor clv (Just col)
attrColorProps :: TestTree
attrColorProps = testGroup "attrColor"
[ testProperty "Nothing -> Nothing for all levels" prop_attrColor_nothing_to_nothing
, testProperty "Just -> Just for all levels" prop_attrColor_just_to_just
, testProperty "same input gives same output" prop_attrColor_same_rgb_same_result
]
-- ---------------------------------------------------------------------------
-- vtyStyleAttr properties
--
-- vtyStyleAttr :: ANSIColorLevel -> Maybe Color -> Maybe Color
-- -> Bool -> Bool -> Bool -> VA.Attr
-- ---------------------------------------------------------------------------
-- fg Nothing -> attrForeColor is Default.
prop_vtyStyleAttr_fg_nothing_default :: Property
prop_vtyStyleAttr_fg_nothing_default = property $ do
clv <- forAll genColorLevel
bgc <- forAll (Gen.maybe genColor)
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
VA.attrForeColor (vtyStyleAttr clv Nothing bgc b i u) === VA.Default
-- fg Just -> attrForeColor is SetTo.
prop_vtyStyleAttr_fg_just_set :: Property
prop_vtyStyleAttr_fg_just_set = property $ do
clv <- forAll genColorLevel
fgc <- forAll genColor
bgc <- forAll (Gen.maybe genColor)
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
case VA.attrForeColor (vtyStyleAttr clv (Just fgc) bgc b i u) of
VA.SetTo _ -> success
_ -> failure
-- bg Nothing -> attrBackColor is Default.
prop_vtyStyleAttr_bg_nothing_default :: Property
prop_vtyStyleAttr_bg_nothing_default = property $ do
clv <- forAll genColorLevel
fgc <- forAll (Gen.maybe genColor)
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
VA.attrBackColor (vtyStyleAttr clv fgc Nothing b i u) === VA.Default
-- bg Just -> attrBackColor is SetTo.
prop_vtyStyleAttr_bg_just_set :: Property
prop_vtyStyleAttr_bg_just_set = property $ do
clv <- forAll genColorLevel
fgc <- forAll (Gen.maybe genColor)
bgc <- forAll genColor
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
case VA.attrBackColor (vtyStyleAttr clv fgc (Just bgc) b i u) of
VA.SetTo _ -> success
_ -> failure
-- attrURL is always Default.
prop_vtyStyleAttr_url_always_default :: Property
prop_vtyStyleAttr_url_always_default = property $ do
clv <- forAll genColorLevel
fgc <- forAll (Gen.maybe genColor)
bgc <- forAll (Gen.maybe genColor)
b <- forAll Gen.bool
i <- forAll Gen.bool
u <- forAll Gen.bool
VA.attrURL (vtyStyleAttr clv fgc bgc b i u) === VA.Default
vtyStyleAttrProps :: TestTree
vtyStyleAttrProps = testGroup "vtyStyleAttr"
[ testProperty "fg Nothing -> Default foreground" prop_vtyStyleAttr_fg_nothing_default
, testProperty "fg Just -> SetTo foreground" prop_vtyStyleAttr_fg_just_set
, testProperty "bg Nothing -> Default background" prop_vtyStyleAttr_bg_nothing_default
, testProperty "bg Just -> SetTo background" prop_vtyStyleAttr_bg_just_set
, testProperty "attrURL is always Default" prop_vtyStyleAttr_url_always_default
]