content: {-# LANGUAGE OverloadedStrings #-}
  module Source.Test
  ( testTree
  ) where
  
  import qualified Data.Text as Text
  import           Hedgehog hiding (Range)
  import qualified Hedgehog.Gen as Gen
  import qualified Hedgehog.Range as Range
  import qualified Source.Source as Source
  import           Source.Span
  import qualified Test.Tasty as Tasty
  import           Test.Tasty.HUnit
  import           Test.Tasty.Hedgehog (testPropertyNamed)
  
  
  source :: MonadGen m => Range.Range Int -> m Source.Source
  source r = Gen.frequency [ (1, empty), (20, nonEmpty) ] where
    empty    = pure mempty
    nonEmpty = Source.fromUTF8 <$> Gen.utf8 r (Gen.frequency [ (1, pure '\r'), (1, pure '\n'), (20, Gen.unicode) ])
  
  testTree :: Tasty.TestTree
  testTree = Tasty.testGroup "Source.Source"
    [ Tasty.testGroup "lineRanges"
      [ testPropertyNamed "produces 1 more range than there are newlines" "Source_lineRanges_produces_1_more_range_than_there_are_newlines" . property $ do
          source <- forAll (source (Range.linear 0 100))
          summarize source
          length (Source.lineRanges source) === length (Text.splitOn "\r\n" (Source.toText source) >>= Text.splitOn "\r" >>= Text.splitOn "\n")
  
      , testPropertyNamed "produces exhaustive ranges" "Source_lineRanges_produces_exhaustive_ranges" . property $ do
          source <- forAll (source (Range.linear 0 100))
          summarize source
          foldMap (Source.slice source) (Source.lineRanges source) === source
      ]
  
    , Tasty.testGroup "totalSpan"
      [ testPropertyNamed "covers single lines" "Source_totalSpan_covers_single_lines" . property $ do
          n <- forAll $ Gen.int (Range.linear 0 100)
          Source.totalSpan (Source.fromText (Text.replicate n "*")) === Span (Pos 1 1) (Pos 1 (max 1 (succ n)))
  
      , testPropertyNamed "covers multiple lines" "Source_totalSpan_covers_multiple_lines" . property $ do
          n <- forAll $ Gen.int (Range.linear 0 100)
          Source.totalSpan (Source.fromText (Text.intersperse '\n' (Text.replicate n "*"))) === Span (Pos 1 1) (Pos (max 1 n) (if n > 0 then 2 else 1))
      ]
  
    , Tasty.testGroup "newlineIndices"
      [ testCase "finds \\n" $ Source.newlineIndices "a\nb" @?= [1]
      , testCase "finds \\r" $ Source.newlineIndices "a\rb" @?= [1]
      , testCase "finds \\r\\n" $ Source.newlineIndices "a\r\nb" @?= [2]
      , testCase "finds intermixed line endings" $ Source.newlineIndices "hi\r}\r}\n xxx \r a" @?= [2, 4, 6, 12]
      ]
    ]
  
  summarize :: Source.Source -> PropertyT IO ()
  summarize src = do
    let lines = Source.lines src
    -- FIXME: this should be using cover (reverted in 1b427b995), but that leads to flaky tests: hedgehog’s 'cover' implementation fails tests instead of warning, and currently has no equivalent to 'checkCoverage'.
    classify "empty"          $ Source.null src
    classify "single-line"    $ length lines == 1
    classify "multiple lines" $ length lines >  1
  