packages feed

wordify-0.1.0.0: test/Tests/LetterBagTest.hs

module Tests.LetterBagTest where

    import Test.QuickCheck (Property, quickCheck)
    import Test.QuickCheck.Monadic as Q (assert, monadicIO, pick, pre, run)
    import Wordify.Rules.LetterBag
    import Wordify.Rules.LetterBag.Internal
    import Wordify.Rules.Tile
    import Tests.Utils
    import System.IO (hPutStr, hFlush, hPutStrLn, hClose)
    import Test.HUnit.Base as H
    import Wordify.Rules.ScrabbleError
    import Data.Map
    import Data.Maybe
    import qualified Data.List as L
    import Tests.Instances

    bagFromTilesProperty :: [Tile] -> Property
    bagFromTilesProperty inputTiles = monadicIO $
      do
        bag <- run $ bagFromTiles inputTiles
        let LetterBag resultingTiles numTiles generator validLetters = bag
        Q.assert $ numTiles == (length inputTiles) && resultingTiles == inputTiles

    shuffleProperty :: LetterBag -> Bool
    shuffleProperty bag =
      let shuffled = shuffleBag bag
      in if (bagSize bag < 10) then sameTiles bag shuffled else bagIsShuffled bag shuffled && sameTiles bag shuffled

      where
        sameTiles originalBag shuffledBag = bagSize originalBag == (length $ (tiles originalBag) `L.intersect` (tiles shuffledBag))
        bagIsShuffled originalBag shuffledBag = not $ originalBag == shuffledBag

    shuffleTwiceProperty :: LetterBag -> Bool
    shuffleTwiceProperty bag = if (bagSize bag < 10) then True else not $ bag1 == bag2 && bagSize bag1 == bagSize bag2
      where
        bag1 = shuffleBag bag
        bag2 = shuffleBag bag1

    takeLettersProperty :: LetterBag -> Int -> Bool
    takeLettersProperty letterBag numTake =
        if (originalBagSize < numTake) then takeLetters letterBag numTake == Nothing
         else
          takeLetters letterBag numTake == Just (expectedTiles, expectedBag)
        where
            LetterBag originalBagTiles originalBagSize gen validLetters = letterBag
            expectedTiles = L.take numTake originalBagTiles
            expectedBag = LetterBag (L.drop numTake originalBagTiles) (originalBagSize - numTake) gen validLetters

    exchangeLettersProperty :: LetterBag -> [Tile] -> Bool
    exchangeLettersProperty letterBag toExchange =
        let exchangeResult = exchangeLetters letterBag toExchange
        in case exchangeResult of
                Nothing -> originalNumTiles == 0
                Just (given, LetterBag newTiles newNumTiles newGenerator validLetters) ->
                 (originalNumTiles == newNumTiles)
                  && length given == length toExchange
                   && forAll (\tile -> (getCount tile newTileCounts) == (getCount tile originalTileCounts) + (getCount tile exchangedCounts) - (getCount tile givenCounts) ) allTiles

                    where
                      allTiles = given ++ newTiles ++ originalTiles
                      givenCounts = countMap given
                      newTileCounts = countMap newTiles
                      originalTileCounts = countMap originalTiles
                      exchangedCounts = countMap toExchange
                      countMap xs = fromListWith (+) [(x, 1) | x <- xs]
                      getCount key m = findWithDefault 0 key m

                      forAll condition list = L.null $ L.filter (not . condition) list
            where
                LetterBag originalTiles originalNumTiles generator validLetters = letterBag

    makeBagInvalidlyFormattedBag :: Assertion
    makeBagInvalidlyFormattedBag =
      withTempFile $ \ filePath handle -> do
        let invalidStr = "A 2 2 3 4" -- Erroneous extra number
        hPutStrLn handle invalidStr
        hFlush handle
        hClose handle
        letterBag <- makeBag filePath

        case letterBag of
          Left (MalformedLetterBagFile _) -> return ()
          x -> H.assertFailure $ "Input with invalidly formatted bag unexpectedly succeeded: " ++ show x

    makeBagTestSuccess :: Assertion
    makeBagTestSuccess =
        withTempFile $ \ filePath handle -> do
          let letters = ['A' .. ]
          let values = [1 .. 5]
          let distributions = [1 .. 5]
          let inputLines = unlines $ zipWith3 (\letter value distribution -> L.intersperse ' ' $ letter : (show value) ++ (show distribution)) letters values distributions
          hPutStrLn handle "_ 2" -- 2 Blank tiles
          hPutStr handle inputLines
          hFlush handle
          hClose handle
          letterBag <- makeBag filePath

          case letterBag of
            Left _ -> H.assertFailure "makeBag returned an error"
            Right (LetterBag tiles numTiles generator validLetters) -> do
              let expectedLetters = zipWith3 (\letter value distribution -> replicate distribution $ Letter letter value  ) letters values distributions
              let expectedBlanks = replicate 2 $ Blank Nothing
              let expectedTiles = concat $ expectedBlanks : expectedLetters

              H.assertBool "Letter bag contains expected letters" $ expectedTiles `L.intersect` tiles == expectedTiles
              H.assertBool "Letter bag contains expected number of letters" $ (length expectedTiles) == (length tiles)

    makeBagInvalidPath :: Assertion
    makeBagInvalidPath =
     do
      letterBag <- makeBag "this is an invalid file path"
      case letterBag of
        Left (LetterBagFileNotOpenable _) -> return ()
        _ -> H.assertFailure "Unexpected success"

      return ()