packages feed

balkon-0.3.0.0: test/Data/Text/ParagraphLayout/Internal/LinePaginationSpec.hs

module Data.Text.ParagraphLayout.Internal.LinePaginationSpec (spec) where

import Control.Monad (forM_)
import Data.Int (Int32)

import Test.Hspec
import Data.Text.ParagraphLayout.Internal.LinePagination

emptyLines :: [Int32]
emptyLines = []

-- TODO: For rich text, add tests with unequal line heights.
tenLines :: [Int32]
tenLines = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]

spec :: Spec
spec = do

    -- Lower level function.
    -- Must prevent overflow and meet orphan/widow constraints at all times.
    describe "bestSplit" $ do

        describe "emptyLines, orphans = 1, widows = 1" $ do
            let ls = emptyLines
            let page = bestSplit 1 1

            ([-30, -5, 0, 5, 30, 90, 100, 110] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " returns empty lists") $
                    page h ls `shouldBe`
                        ([], [])

        describe "tenLines, orphans = 1, widows = 1" $ do
            let ls = tenLines
            let page = bestSplit 1 1

            ([-30, -5, 0, 5] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts all in second list") $
                    page h ls `shouldBe`
                        ([], [10, 10, 10, 10, 10, 10, 10, 10, 10, 10])

            ([30, 35] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts 3 in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10], [10, 10, 10, 10, 10, 10, 10])

            ([100, 110] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts all in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

        describe "tenLines, orphans = 3, widows = 4" $ do
            let ls = tenLines
            let page = bestSplit 3 4
            -- Acceptable page breaks:
            --  -  0 + 10
            --  -  3 +  7
            --  -  4 +  6
            --  -  5 +  5
            --  -  6 +  4
            --  - 10 +  0

            ([0, 10, 15, 25] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " ensures 3 orphans") $
                    page h ls `shouldBe`
                        ([], [10, 10, 10, 10, 10, 10, 10, 10, 10, 10])

            ([30, 35] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts 3 in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10], [10, 10, 10, 10, 10, 10, 10])

            ([40, 45] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts 4 in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10, 10], [10, 10, 10, 10, 10, 10])

            ([60, 75, 90] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " ensures 4 widows") $
                    page h ls `shouldBe`
                        ([10, 10, 10, 10, 10, 10], [10, 10, 10, 10])

            ([100, 110] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts all in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

        describe "tenLines, orphans = 6, widows = 4" $ do
            let ls = tenLines
            let page = bestSplit 6 4
            -- Acceptable page breaks:
            --  -  0 + 10
            --  -  6 +  4
            --  - 10 +  0

            ([0, 10, 15, 35, 50, 55] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " ensures 6 orphans") $
                    page h ls `shouldBe`
                        ([], [10, 10, 10, 10, 10, 10, 10, 10, 10, 10])

            ([60, 65, 85, 95] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts 6 in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10, 10, 10, 10], [10, 10, 10, 10])

            ([100, 110] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts all in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

        describe "tenLines, orphans = 6, widows = 5" $ do
            let ls = tenLines
            let page = bestSplit 6 5
            -- Acceptable page breaks:
            --  -  0 + 10
            --  - 10 +  0

            ([0, 10, 60, 65, 85, 95] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts all in second list") $
                    page h ls `shouldBe`
                        ([], [10, 10, 10, 10, 10, 10, 10, 10, 10, 10])

            ([100, 110] :: [Int32]) `forM_` \ h ->
                it ("maxHeight = " ++ show h ++ " puts all in first list") $
                    page h ls `shouldBe`
                        ([10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

    -- Higher level function.
    -- Must return a non-empty prefix if input was non-empty.
    -- May only break orphan/widow constraints or overflow if unavoidable.
    describe "paginateLines" $ do

        describe "emptyLines, orphans = 1, widows = 1" $ do
            let ls = emptyLines
            let page = paginateLines 1 1

            it "continues page with no lines when space is zero" $
                page 0 0 ls `shouldBe`
                        (Continue, [], [])

            it "tolerates negative current page height" $
                page (-30) 0 ls `shouldBe`
                        (Continue, [], [])

            it "tolerates negative next page height" $
                page 0 (-30) ls `shouldBe`
                        (Continue, [], [])

            it "continues page with no lines when space is equal" $
                page 50 50 ls `shouldBe`
                        (Continue, [], [])

            it "continues page with no lines when next page has more space" $
                page 50 100 ls `shouldBe`
                        (Continue, [], [])

        describe "tenLines, orphans = 1, widows = 1" $ do
            let ls = tenLines
            let page = paginateLines 1 1

            it "puts all lines on current page if possible" $
                page 200 200 ls `shouldBe`
                    (Continue, [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

            it "puts as many lines on current page as possible" $
                page 60 200 ls `shouldBe`
                    (Continue, [10, 10, 10, 10, 10, 10], [10, 10, 10, 10])

            it "starts at next page if not enough room" $
                page 5 200 ls `shouldBe`
                    (Break, [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

            it "starts at next page and handles breaking there" $
                page 5 70 ls `shouldBe`
                    (Break, [10, 10, 10, 10, 10, 10, 10], [10, 10, 10])

            it "overflows on current page" $
                page 5 5 ls `shouldBe`
                    (Continue, [10], [10, 10, 10, 10, 10, 10, 10, 10, 10])

            it "overflows on next page if it has more room" $
                page 5 6 ls `shouldBe`
                    (Break, [10], [10, 10, 10, 10, 10, 10, 10, 10, 10])

            it "tolerates negative current page height" $
                page (-30) 0 ls `shouldBe`
                    (Break, [10], [10, 10, 10, 10, 10, 10, 10, 10, 10])

            it "tolerates negative next page height" $
                page 0 (-30) ls `shouldBe`
                    (Continue, [10], [10, 10, 10, 10, 10, 10, 10, 10, 10])

        describe "tenLines, orphans = 5, widows = 3" $ do
            let ls = tenLines
            let page = paginateLines 5 3
            -- Acceptable page breaks:
            --  -  0 + 10
            --  -  5 +  5
            --  -  6 +  4
            --  -  7 +  3
            --  - 10 +  0

            it "puts all lines on current page if possible" $
                page 200 200 ls `shouldBe`
                    (Continue, [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

            it "puts as many lines on current page as possible" $
                page 60 200 ls `shouldBe`
                    (Continue, [10, 10, 10, 10, 10, 10], [10, 10, 10, 10])

            it "starts at next page if not enough room" $
                page 5 200 ls `shouldBe`
                    (Break, [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

            it "starts at next page and handles breaking there" $
                page 5 70 ls `shouldBe`
                    (Break, [10, 10, 10, 10, 10, 10, 10], [10, 10, 10])

            it "overflows on current page" $
                page 5 5 ls `shouldBe`
                    (Continue, [10], [10, 10, 10, 10, 10, 10, 10, 10, 10])

            it "overflows on next page if it has more room" $
                page 5 6 ls `shouldBe`
                    (Break, [10], [10, 10, 10, 10, 10, 10, 10, 10, 10])

            -- Behaviour affected by orphans/widows:

            it "breaks early to meet widows constraint" $
                page 80 200 ls `shouldBe`
                    (Continue, [10, 10, 10, 10, 10, 10, 10], [10, 10, 10])

            it "breaks at start to meet orphans constraint" $
                page 45 200 ls `shouldBe`
                    (Break, [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [])

            it "starts at next page and meets widows constraint there" $
                page 5 80 ls `shouldBe`
                    (Break, [10, 10, 10, 10, 10, 10, 10], [10, 10, 10])

            it "continues page and violates impossible constraints" $
                page 45 46 ls `shouldBe`
                    (Continue, [10, 10, 10, 10], [10, 10, 10, 10, 10, 10])

            it "breaks page and violates impossible constraints" $
                page 5 36 ls `shouldBe`
                    (Break, [10, 10, 10], [10, 10, 10, 10, 10, 10, 10])