1- module Data.Source.Spec (spec ) where
1+ {-# LANGUAGE NamedFieldPuns #-}
2+ module Data.Source.Spec (spec , testTree ) where
23
3- import Data.Char (chr )
4- import Data.Functor.Listable
54import Data.Range
65import Data.Source
76import Data.Span
87import qualified Data.Text as Text
8+
99import Test.Hspec
10- import Test.Hspec.LeanCheck
11- import Test.LeanCheck
10+
11+ import qualified Generators as Gen
12+ import qualified Hedgehog.Gen as Gen
13+ import Hedgehog ((===) )
14+ import qualified Hedgehog.Range
15+ import Hedgehog hiding (Range )
16+ import qualified Test.Tasty as Tasty
17+ import Test.Tasty.Hedgehog (testProperty )
18+
19+ prop :: HasCallStack => String -> (Source -> PropertyT IO () ) -> Tasty. TestTree
20+ prop desc f
21+ = testProperty desc
22+ . property
23+ $ forAll (Gen. source (Hedgehog.Range. linear 0 100 ))
24+ >>= f
25+
26+ testTree :: Tasty. TestTree
27+ testTree = Tasty. testGroup " Data.Source"
28+ [ Tasty. testGroup " sourceLineRanges"
29+ [ prop " produces 1 more range than there are newlines" $
30+ \ source -> length (sourceLineRanges source) === succ (Text. count " \n " (toText source))
31+
32+ , prop " produces exhaustive ranges" $
33+ \ source -> foldMap (`slice` source) (sourceLineRanges source) === source
34+ ]
35+
36+ , Tasty. testGroup " spanToRange"
37+ [ prop " computes single-line ranges" $ \ source -> do
38+ let ranges = sourceLineRanges source
39+ let spans = zipWith (\ i Range {.. } -> Span (Pos i 1 ) (Pos i (succ (end - start)))) [1 .. ] ranges
40+ fmap (spanToRange source) spans === ranges
41+
42+ , prop " computes multi-line ranges" $
43+ \ source ->
44+ spanToRange source (totalSpan source) === totalRange source
45+
46+ , prop " computes sub-line ranges" $
47+ \ s -> let source = " *" <> s <> " *" in
48+ spanToRange source (insetSpan (totalSpan source)) === insetRange (totalRange source)
49+
50+ , testProperty " inverse of rangeToSpan" . property $ do
51+ a <- forAll . Gen. source $ Hedgehog.Range. linear 0 100
52+ b <- forAll . Gen. source $ Hedgehog.Range. linear 0 100
53+ let s = a <> " \n " <> b in spanToRange s (totalSpan s) === totalRange s
54+ ]
55+
56+ , testProperty " rangeToSpan inverse of spanToRange" . property $ do
57+ a <- forAll . Gen. source $ Hedgehog.Range. linear 0 100
58+ b <- forAll . Gen. source $ Hedgehog.Range. linear 0 100
59+ let s = a <> " \n " <> b in rangeToSpan s (totalRange s) === totalSpan s
60+
61+ , Tasty. testGroup " totalSpan"
62+ [ testProperty " covers single lines" . property $ do
63+ n <- forAll $ Gen. int (Hedgehog.Range. linear 0 100 )
64+ totalSpan (fromText (Text. replicate n " *" )) === Span (Pos 1 1 ) (Pos 1 (max 1 (succ n)))
65+
66+ , testProperty " covers multiple lines" . property $ do
67+ n <- forAll $ Gen. int (Hedgehog.Range. linear 0 100 )
68+ totalSpan (fromText (Text. intersperse ' \n ' (Text. replicate n " *" ))) === Span (Pos 1 1 ) (Pos (max 1 n) (if n > 0 then 2 else 1 ))
69+ ]
70+
71+ ]
1272
1373spec :: Spec
1474spec = parallel $ do
15- describe " sourceLineRanges" $ do
16- prop " produces 1 more range than there are newlines" $
17- \ source -> length (sourceLineRanges source) `shouldBe` succ (Text. count " \n " (toText source))
18-
19- prop " produces exhaustive ranges" $
20- \ source -> foldMap (`slice` source) (sourceLineRanges source) `shouldBe` source
21-
22- describe " spanToRange" $ do
23- prop " computes single-line ranges" $
24- \ s -> let source = fromUTF8 s
25- spans = zipWith (\ i Range {.. } -> Span (Pos i 1 ) (Pos i (succ (end - start)))) [1 .. ] ranges
26- ranges = sourceLineRanges source in
27- spanToRange source <$> spans `shouldBe` ranges
28-
29- prop " computes multi-line ranges" $
30- \ source ->
31- spanToRange source (totalSpan source) `shouldBe` totalRange source
32-
33- prop " computes sub-line ranges" $
34- \ s -> let source = " *" <> s <> " *" in
35- spanToRange source (insetSpan (totalSpan source)) `shouldBe` insetRange (totalRange source)
36-
37- prop " inverse of rangeToSpan" $
38- \ a b -> let s = a <> " \n " <> b in spanToRange s (totalSpan s) `shouldBe` totalRange s
39-
40- describe " rangeToSpan" $ do
41- prop " inverse of spanToRange" $
42- \ a b -> let s = a <> " \n " <> b in rangeToSpan s (totalRange s) `shouldBe` totalSpan s
43-
44- describe " totalSpan" $ do
45- prop " covers single lines" $
46- \ n -> totalSpan (fromText (Text. replicate n " *" )) `shouldBe` Span (Pos 1 1 ) (Pos 1 (max 1 (succ n)))
47-
48- prop " covers multiple lines" $
49- \ n -> totalSpan (fromText (Text. intersperse ' \n ' (Text. replicate n " *" ))) `shouldBe` Span (Pos 1 1 ) (Pos (max 1 n) (if n > 0 then 2 else 1 ))
50-
5175 describe " newlineIndices" $ do
5276 it " finds \\ n" $
5377 let source = " a\n b" in
@@ -62,13 +86,6 @@ spec = parallel $ do
6286 let source = " hi\r }\r }\n xxx \r a" in
6387 newlineIndices source `shouldBe` [2 , 4 , 6 , 12 ]
6488
65- prop " preserves characters" . forAll (toTiers (list +| [chr 0xa0 .. chr 0x24f ])) $
66- \ c -> Text. unpack (toText (fromText (Text. singleton c))) `shouldBe` [c]
67-
68- prop " preserves strings" $
69- \ s -> fromText (toText s) `shouldBe` s
70-
71-
7289insetSpan :: Span -> Span
7390insetSpan sourceSpan = sourceSpan { spanStart = (spanStart sourceSpan) { posColumn = succ (posColumn (spanStart sourceSpan)) }
7491 , spanEnd = (spanEnd sourceSpan) { posColumn = pred (posColumn (spanEnd sourceSpan)) } }
0 commit comments