packages feed

cursor-gen-0.0.0.0: test/Cursor/ListSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE RankNTypes #-}

module Cursor.ListSpec
    ( spec
    ) where

import Test.Hspec
import Test.QuickCheck
import Test.Validity

import Control.Monad

import Cursor.List
import Cursor.List.Gen ()

spec :: Spec
spec = do
    eqSpec @(ListCursor Int)
    functorSpec @ListCursor
    genValidSpec @(ListCursor Double)
    describe "emptyListCursor" $
        it "is valid" $ shouldBeValid (emptyListCursor @Int)
    describe "makeListCursor" $
        it "produces valid list cursors" $
        producesValidsOnValids (makeListCursor @Double)
    describe "makeListCursorWithSelection" $
        it "produces valid list cursors" $
        producesValidsOnValids2 (makeListCursorWithSelection @Double)
    describe "rebuildListCursor" $ do
        it "produces valid lists" $
            producesValidsOnValids (rebuildListCursor @Double)
        it "is the inverse of makeListCursor" $
            inverseFunctions (makeListCursor @Int) rebuildListCursor
        it "is the inverse of makeListCursorWithSelection for any index" $
            forAllUnchecked $ \i ->
                inverseFunctionsIfFirstSucceeds
                    (makeListCursorWithSelection @Int i)
                    rebuildListCursor
    describe "listCursorNull" $
        it "produces valid bools" $
        producesValidsOnValids (listCursorNull @Double)
    describe "listCursorLength" $
        it "produces valid bools" $
        producesValidsOnValids (listCursorLength @Double)
    describe "listCursorIndex" $
        it "produces valid indices" $
        producesValidsOnValids (listCursorIndex @Double)
    describe "listCursorSelectPrev" $ do
        it "produces valid cursors" $
            producesValidsOnValids (listCursorSelectPrev @Double)
        it "is a movement" $ isMovementM listCursorSelectPrev
        it "selects the previous position" pending
    describe "listCursorSelectNext" $ do
        it "produces valid cursors" $
            producesValidsOnValids (listCursorSelectNext @Double)
        it "is a movement" $ isMovementM listCursorSelectNext
        it "selects the next position" pending
    describe "listCursorSelectIndex" $ do
        it "produces valid cursors" $
            producesValidsOnValids2 (listCursorSelectIndex @Double)
        it "is a movement" $
            forAllUnchecked $ \ix -> isMovement (listCursorSelectIndex ix)
        it "selects the position at the given index" pending
    describe "listCursorPrevItem" $ do
        it "produces valid items" $
            producesValidsOnValids (listCursorPrevItem @Double)
        it "returns the item before the position" pending
    describe "listCursorNextItem" $ do
        it "produces valid items" $
            producesValidsOnValids (listCursorNextItem @Double)
        it "returns the item after the position" pending
    describe "listCursorSelectStart" $ do
        it "produces valid cursors" $
            producesValidsOnValids (listCursorSelectStart @Double)
        it "is a movement" $ isMovement listCursorSelectStart
        it "is idempotent" $ idempotentOnValid (listCursorSelectStart @Double)
        it "selects the starting position" pending
    describe "listCursorSelectEnd" $ do
        it "produces valid cursors" $
            producesValidsOnValids (listCursorSelectEnd @Double)
        it "is a movement" $ isMovement listCursorSelectEnd
        it "is idempotent" $ idempotentOnValid (listCursorSelectEnd @Double)
        it "selects the end position" pending
    describe "listCursorInsert" $ do
        it "produces valids" $
            forAllValid $ \d ->
                producesValidsOnValids (listCursorInsert @Double d)
        it "inserts an item before the cursor" $ pending
    describe "listCursorAppend" $ do
        it "produces valids" $
            forAllValid $ \d ->
                producesValidsOnValids (listCursorAppend @Double d)
        it "inserts an item after the cursor" $ pending
    describe "listCursorRemove" $ do
        it "produces valids" $ validIfSucceedsOnValid (listCursorRemove @Double)
        it "removes an item before the cursor" $ pending
    describe "listCursorDelete" $ do
        it "produces valids" $ validIfSucceedsOnValid (listCursorDelete @Double)
        it "removes an item before the cursor" $ pending
    describe "listCursorSplit" $ do
        it "produces valids" $ producesValidsOnValids (listCursorSplit @Double)
        it
            "produces two list cursors that rebuild to the rebuilding of the original" $
            forAllValid $ \lc ->
                let (lc1, lc2) = listCursorSplit (lc :: ListCursor Double)
                 in (rebuildListCursor lc1 ++ rebuildListCursor lc2) `shouldBe`
                    rebuildListCursor lc
    describe "listCursorCombine" $ do
        it "produces valids" $
            producesValidsOnValids2 (listCursorCombine @Double)
        it
            "produces a list that rebuilds to the rebuilding of the original two cursors" $
            forAllValid $ \lc1 ->
                forAllValid $ \lc2 ->
                    let lc = listCursorCombine lc1 (lc2 :: ListCursor Double)
                     in rebuildListCursor lc `shouldBe`
                        (rebuildListCursor lc1 ++ rebuildListCursor lc2)

isMovementM :: (forall a. ListCursor a -> Maybe (ListCursor a)) -> Property
isMovementM func =
    forAllValid $ \lec ->
        case func (lec :: ListCursor Int) of
            Nothing -> pure () -- Fine
            Just lec' ->
                let ne = rebuildListCursor lec
                    ne' = rebuildListCursor lec'
                 in unless (ne == ne') $
                    expectationFailure $
                    unlines
                        [ "Cursor before:\n" ++ show lec
                        , "List before:  \n" ++ show ne
                        , "Cursor after: \n" ++ show lec'
                        , "List after:   \n" ++ show ne'
                        ]

isMovement :: (forall a. ListCursor a -> ListCursor a) -> Property
isMovement func =
    forAllValid $ \lec ->
        rebuildListCursor (lec :: ListCursor Int) `shouldBe`
        rebuildListCursor (func lec)