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