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])