packages feed

rainbox-0.2.0.0: test/Rainbox/Box/PrimitivesTests.hs

module Rainbox.Box.PrimitivesTests where

import Control.Monad
import Control.Applicative
import Test.Tasty
import Test.Tasty.QuickCheck (testProperty)
import Test.QuickCheck
import System.Console.Rainbow
import qualified Data.Text as X
import qualified Test.Rainbow.Generators as G
import Rainbox.Box.Primitives
import Rainbox.Box (backgroundToTextSpec)

genText :: Gen X.Text
genText = fmap X.pack $ listOf c
  where
    c = elements ['0'..'Z']

genChunk :: Gen Chunk
genChunk = genText >>= G.chunk

genHeight :: Gen Height
genHeight = fmap Height $ frequency [(3, nonNeg), (1, neg)]
  where
    nonNeg = fmap getNonNegative arbitrarySizedIntegral
    neg = fmap (negate . abs) arbitrarySizedIntegral

genWidth :: Gen Width
genWidth = fmap Width $ frequency [(3, nonNeg), (1, neg)]
  where
    nonNeg = fmap abs arbitrarySizedIntegral
    neg = fmap (negate . abs) arbitrarySizedIntegral

genBackground :: Gen Background
genBackground = liftM2 Background G.colors8 G.colors256

-- | Generates blank Box.
genBlankBox :: Gen Box
genBlankBox = liftM3 blank genBackground rw cl
  where
    rw = fmap (Height . abs) arbitrarySizedIntegral
    cl = fmap (Width . abs) arbitrarySizedIntegral

-- | Generates a box using chunks.
genChunkBox :: Gen Box
genChunkBox = fmap chunks $ listOf genChunk

-- | Generates a box using catH.
genCatHBox :: Gen Box
genCatHBox = sized $ \s -> do
  bk <- genBackground
  av <- genAlignVert
  bs <- listOf (resize (s `div` 2) genBox)
  return $ catH bk av bs

-- | Generates a box using catV.
genCatVBox :: Gen Box
genCatVBox = sized $ \s -> do
  bk <- genBackground
  ah <- genAlignHoriz
  bs <- listOf (resize (s `div` 2) genBox)
  return $ catV bk ah bs

-- | Generates a random box.
genBox :: Gen Box
genBox = oneof [ genBlankBox, genCatHBox, genCatVBox, genChunkBox ]

genChunkLen :: Background -> Int -> Gen Chunk
genChunkLen bk l = do
  let ts = backgroundToTextSpec bk
  txt <- fmap X.pack $ vectorOf l (elements ['0'..'Z'])
  return $ Chunk ts txt

-- | Generates a box of text; its horizontal and vertical size
-- depends on the size parameter.
genTextBox :: Gen Box
genTextBox = do
  w <- fmap abs arbitrarySizedIntegral
  h <- fmap abs arbitrarySizedIntegral
  bk <- genBackground
  cks <- vectorOf h (genChunkLen bk w)
  let bxs = map (chunks . (:[])) cks
  bk' <- genBackground
  return $ catV bk' left bxs



-- # Alignment

genAlignVert :: Gen (Align Vert)
genAlignVert = elements
  [ center, top, bottom ]

genAlignHoriz :: Gen (Align Horiz)
genAlignHoriz = elements [ center, left, right ]

validBox :: Box -> Bool
validBox box = case unBox box of
  NoHeight i -> i > -1
  WithHeight rw -> case rw of
    [] -> False
    x:xs -> all (== width x) . map width $ xs

biggest :: Int -> Gen a -> Gen a
biggest m g = sized $ \s -> resize (min s m) g

data Inputs = Inputs
  { iChunks :: [Chunk]
  , iBackground :: Background
  , iHeight :: Height
  , iWidth :: Width
  , iVert :: Align Vert
  , iHoriz :: Align Horiz
  , iBoxes :: [Box]
  , iBox :: Box
  , iChunk :: Chunk
  } deriving Show

instance Arbitrary Inputs where
  arbitrary = Inputs
    <$> listOf genChunk
    <*> genBackground
    <*> genHeight
    <*> genWidth
    <*> genAlignVert
    <*> genAlignHoriz
    <*> listOf genBlankBox
    <*> genBlankBox
    <*> genChunk

tests :: TestTree
tests = testGroup "BoxTests"
  [ testGroup "blank"
    [ testProperty "makes valid Box" $ \i ->
      validBox $ blank (iBackground i) (iHeight i)
        (iWidth i)

    , testProperty "has right number of rows" $ \i ->
      let ht = unHeight . iHeight $ i
      in (== max 0 ht) . height $ blank (iBackground i)
            (iHeight i) (iWidth i)

    , testProperty "has right number of columns" $ \i ->
      let wt = unWidth . iWidth $ i
      in (== max 0 wt) . width $ blank (iBackground i)
            (iHeight i) (iWidth i)
    ]

  , testGroup "chunks"
    [ testProperty "makes valid Box" $
      validBox . chunks . iChunks

    , testProperty "makes Box whose height is 1" $
      (== 1) . height . chunks . iChunks

    , testProperty "makes Box with cols == number of characters" $ \i ->
      let cks = iChunks i
          nChars = sum . map X.length . map text $ cks
      in (== nChars) . width $ chunks cks
    ]

  , testGroup "catH"
    [ testProperty "makes valid Box" $ \i ->
      validBox $ catH (iBackground i) (iVert i) (iBoxes i)

    , testProperty "is as tall as tallest box" $ \i ->
      let h = maximum . (0 :) . map height $ bs
          bs = iBoxes i
      in (== h) . height $ catH (iBackground i) (iVert i) bs

    , testProperty "is as wide as sum of all widths" $ \i ->
      let s = sum . map width $ bs
          bs = iBoxes i
      in (== s) . width $ catH (iBackground i) (iVert i) bs
    ]

  , testGroup "catV"
    [ testProperty "makes a valid Box" $ \i ->
      validBox $ catV (iBackground i) (iHoriz i) (iBoxes i)

    , testProperty "is as tall as the sum of all heights" $ \i ->
      let h = sum . map height $ bs
          bs = iBoxes i
      in (== h) . height $ catV (iBackground i) (iHoriz i) bs

    , testProperty "is as wide as the widest box" $ \i ->
      let w = maximum . (0:) . map width $ bs
          bs = iBoxes i
      in (== w) . width $ catV (iBackground i) (iHoriz i) bs
    ]

  , testGroup "viewH"
    [ testProperty "makes a valid Box" $ \i ->
      validBox $ viewH (unWidth . iWidth $ i) (iHoriz i) (iBox i)

    , testProperty "number of rows does not change" $ \i ->
      let b = iBox i
      in (== height b) . height $ viewH (unWidth . iWidth $ i)
                                    (iHoriz i) b

    , testProperty "number of columns <= number requested" $ \i ->
      let c = unWidth . iWidth $ i
          tgt = max 0 c
      in (<= tgt) . width $ viewH c (iHoriz i) (iBox i)
    ]

  , testGroup "viewV"
    [ testProperty "makes a valid Box" $ \i ->
      validBox $ viewV (unHeight . iHeight $ i) (iVert i) (iBox i)

    , testProperty "width does not change" $ \i ->
      let b = iBox i
      in (== width b) . width $ viewV (unHeight . iHeight $ i)
                                    (iVert i) b

    , testProperty "number of rows <= number requested" $ \i ->
      let r = unHeight . iHeight $ i
          tgt = max 0 r
      in (<= tgt) . height $ viewV r (iVert i) (iBox i)
    ]
  ]