{-# OPTIONS_HADDOCK show-extensions #-}

-- |
-- Module      : Test.Fluent.Assertions.List
-- Description : Set of assertions for List type
-- Copyright   : (c) Pawel Nosal, 2021
-- License     : MIT
-- Maintainer  : p.nosal1986@gmail.com
-- Stability   : experimental
--
-- This library aims to provide a set of combinators to assert List type.
module Test.Fluent.Assertions.List where

import Data.List (isPrefixOf)
import GHC.Stack (HasCallStack)
import Test.Fluent.Assertions (Assertion, forceError, simpleAssertion)
import Test.Fluent.Diff (pretty)

-- | assert if given list has same length as expected list
--
-- @
--  assertThat [1..10] $ shouldHaveSameSizeAs [0..10]
-- @
shouldHaveSameSizeAs :: HasCallStack => [a] -> Assertion [a]
shouldHaveSameSizeAs :: [a] -> Assertion [a]
shouldHaveSameSizeAs [a]
expected = ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
predicate [a] -> String
forall (t :: * -> *) a. Foldable t => t a -> String
message
  where
    predicate :: t a -> Bool
predicate t a
given = t a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t a
given Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
expected
    message :: t a -> String
message t a
given =
      String
"should have same leght, but they don't. Given: "
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show (t a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t a
given)
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
" is not equal to expected "
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show ([a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
expected)

-- | verify if the given list has a length lower or equal to expected value
--
-- @
--  assertThat [1..10] $ shouldHaveSizeLowerOrEqual 10
-- @
shouldHaveSizeLowerOrEqual :: HasCallStack => Int -> Assertion [a]
shouldHaveSizeLowerOrEqual :: Int -> Assertion [a]
shouldHaveSizeLowerOrEqual Int
expected = ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
predicate [a] -> String
forall (t :: * -> *) a. Foldable t => t a -> String
message
  where
    predicate :: t a -> Bool
predicate t a
given = t a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t a
given Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
expected
    message :: t a -> String
message t a
given =
      String
"the lenght of given list is "
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show (t a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t a
given)
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
", but should be lower or equal "
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show Int
expected

-- | verify if the given list has expected prefix
--
-- @
--  assertThat [1..10] $ shouldStartWith [0..4]
-- @
shouldStartWith :: (Eq a, Show a, HasCallStack) => [a] -> Assertion [a]
shouldStartWith :: [a] -> Assertion [a]
shouldStartWith [a]
expected = Assertion [a] -> Assertion [a]
forall a. Assertion a -> Assertion a
forceError (Int -> Assertion [a]
forall a. HasCallStack => Int -> Assertion [a]
shouldHaveSizeLowerOrEqual Int
expectedLenght) Assertion [a] -> Assertion [a] -> Assertion [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
predicate [a] -> String
forall a. Show a => [a] -> String
message
  where
    predicate :: [a] -> Bool
predicate = ([a]
expected [a] -> [a] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf`)
    expectedLenght :: Int
expectedLenght = [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
expected
    actualPrefix :: [a] -> [a]
actualPrefix [a]
x = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
expectedLenght [a]
x
    message :: [a] -> String
message [a]
x =
      String
"should start with "
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [a] -> String
forall a. Show a => a -> String
show [a]
expected
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
", but it start with "
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [a] -> String
forall a. Show a => a -> String
show ([a] -> [a]
forall a. [a] -> [a]
actualPrefix [a]
x)

-- | verify if the given list does not start with prefix
--
-- @
--  assertThat [1..10] $ shouldNotStartWith [1..4]
-- @
shouldNotStartWith :: (Eq a, Show a, HasCallStack) => [a] -> Assertion [a]
shouldNotStartWith :: [a] -> Assertion [a]
shouldNotStartWith [a]
expected = ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
predicate [a] -> String
forall p. p -> String
message
  where
    predicate :: [a] -> Bool
predicate = Bool -> Bool
not (Bool -> Bool) -> ([a] -> Bool) -> [a] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a]
expected [a] -> [a] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf`)
    message :: p -> String
message p
_ =
      String
"should not start with "
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [a] -> String
forall a. Show a => a -> String
show [a]
expected
        String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
", but it does"

shouldBeSameAs :: (Eq a, HasCallStack, Show a) => [a] -> Assertion [a]
shouldBeSameAs :: [a] -> Assertion [a]
shouldBeSameAs [a]
expected = ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
predicate [a] -> String
message
  where
    predicate :: [a] -> Bool
predicate = ([a] -> [a] -> Bool
forall a. Eq a => a -> a -> Bool
== [a]
expected)
    message :: [a] -> String
message [a]
given = String
"given list should be same as expected list, but is not.\n" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [a] -> [a] -> String
forall a. Show a => a -> a -> String
pretty [a]
given [a]
expected

shouldContain :: (Eq a, HasCallStack, Show a) => a -> Assertion [a]
shouldContain :: a -> Assertion [a]
shouldContain a
expected = ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
predicate [a] -> String
forall p. p -> String
message
  where
    predicate :: [a] -> Bool
predicate = a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem a
expected
    message :: p -> String
message p
_ = String
"given list should contain element " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> a -> String
forall a. Show a => a -> String
show a
expected String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
", but it doesn't."

shouldNotContain :: (Eq a, HasCallStack, Show a) => a -> Assertion [a]
shouldNotContain :: a -> Assertion [a]
shouldNotContain a
expected = ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
predicate [a] -> String
forall p. p -> String
message
  where
    predicate :: [a] -> Bool
predicate = a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
notElem a
expected
    message :: p -> String
message p
_ = String
"given list should not contain element " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> a -> String
forall a. Show a => a -> String
show a
expected String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
", but it doesn."

-- | verify if the given list contains same elements as expected list in any order
--
-- @
--  assertThat [1..10] $ shouldNotStartWith [1..4]
-- @
shouldHaveSameElements :: (HasCallStack, Eq a, Show a) => [a] -> Assertion [a]
shouldHaveSameElements :: [a] -> Assertion [a]
shouldHaveSameElements [a]
expected = Assertion [a] -> Assertion [a]
forall a. Assertion a -> Assertion a
forceError ([a] -> Assertion [a]
forall a. HasCallStack => [a] -> Assertion [a]
shouldHaveSameSizeAs [a]
expected) Assertion [a] -> Assertion [a] -> Assertion [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> Bool) -> ([a] -> String) -> Assertion [a]
forall a.
HasCallStack =>
(a -> Bool) -> (a -> String) -> Assertion a
simpleAssertion [a] -> Bool
forall (t :: * -> *). Foldable t => t a -> Bool
predicate [a] -> String
errorMessage
  where
    predicate :: t a -> Bool
predicate t a
given = (a -> Bool) -> t a -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [a]
expected) t a
given
    inGiven :: t a -> [a]
inGiven t a
given = (a -> Bool) -> [a] -> [a]
forall a. (a -> Bool) -> [a] -> [a]
filter (a -> t a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` t a
given) [a]
expected
    inExpected :: [a] -> [a]
inExpected [a]
given = (a -> Bool) -> [a] -> [a]
forall a. (a -> Bool) -> [a] -> [a]
filter (a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [a]
expected) [a]
given
    errorMessage :: [a] -> String
errorMessage [a]
given = String
"two lists should have same elements but:\n " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
message
      where
        message :: String
message = case ([a] -> [a]
forall (t :: * -> *). Foldable t => t a -> [a]
inGiven [a]
given, [a] -> [a]
inExpected [a]
given) of
          ([], []) -> String
"bug in shouldHaveSameElements assertion, please report this to the maintainer"
          ([a]
xs, [a]
ys) -> String
"given list don't have " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [a] -> String
forall a. Show a => a -> String
show [a]
xs String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\nexpected list don't have " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [a] -> String
forall a. Show a => a -> String
show [a]
ys