|
1 | | -module Data.Source.Spec (spec) where |
| 1 | +{-# LANGUAGE NamedFieldPuns #-} |
| 2 | +module Data.Source.Spec (spec, testTree) where |
2 | 3 |
|
3 | 4 | import Data.Char (chr) |
4 | | -import Data.Functor.Listable |
5 | 5 | import Data.Range |
6 | 6 | import Data.Source |
7 | 7 | import Data.Span |
8 | 8 | import qualified Data.Text as Text |
| 9 | + |
| 10 | +import Data.Functor.Listable |
9 | 11 | import Test.Hspec |
10 | 12 | import Test.Hspec.LeanCheck |
11 | 13 | import Test.LeanCheck |
12 | 14 |
|
| 15 | +-- This file deals with Range values, which is unfortunate because |
| 16 | +-- Hedgehog has its own Range type. We solve this by importing |
| 17 | +-- everything qualified. |
| 18 | +import qualified Generators as Gen |
| 19 | +import Hedgehog ((===)) |
| 20 | +import qualified Hedgehog.Range |
| 21 | +import qualified Hedgehog |
| 22 | +import qualified Test.Tasty as Tasty |
| 23 | +import qualified Test.Tasty.Hedgehog as Tasty |
| 24 | + |
| 25 | +testTree :: Tasty.TestTree |
| 26 | +testTree = Tasty.testGroup "Data.Source.spanToRange" |
| 27 | + [ Tasty.testProperty "computes single-line ranges" prop_computes_single_line_ranges |
| 28 | + ] |
| 29 | + |
| 30 | +prop_computes_single_line_ranges = Hedgehog.property $ do |
| 31 | + source <- Hedgehog.forAll . Gen.source $ Hedgehog.Range.linear 0 100 |
| 32 | + let ranges = sourceLineRanges source |
| 33 | + spanFromRangeWithIndex i Range{start, end} = Span (Pos i 1) (Pos i (end - start + 1)) |
| 34 | + spans = zipWith spanFromRangeWithIndex [1..] ranges |
| 35 | + fmap (spanToRange source) spans === ranges |
| 36 | + |
13 | 37 | spec :: Spec |
14 | 38 | spec = 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 | | - |
51 | | - describe "newlineIndices" $ do |
52 | | - it "finds \\n" $ |
53 | | - let source = "a\nb" in |
54 | | - newlineIndices source `shouldBe` [1] |
55 | | - it "finds \\r" $ |
56 | | - let source = "a\rb" in |
57 | | - newlineIndices source `shouldBe` [1] |
58 | | - it "finds \\r\\n" $ |
59 | | - let source = "a\r\nb" in |
60 | | - newlineIndices source `shouldBe` [2] |
61 | | - it "finds intermixed line endings" $ |
62 | | - let source = "hi\r}\r}\n xxx \r a" in |
63 | | - newlineIndices source `shouldBe` [2, 4, 6, 12] |
64 | | - |
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 |
| 39 | + describe "sourceLineRanges" $ pure () |
| 40 | + -- prop "produces 1 more range than there are newlines" $ |
| 41 | + -- \ source -> length (sourceLineRanges source) `shouldBe` succ (Text.count "\n" (toText source)) |
| 42 | + |
| 43 | + -- prop "produces exhaustive ranges" $ |
| 44 | + -- \ source -> foldMap (`slice` source) (sourceLineRanges source) `shouldBe` source |
| 45 | + |
| 46 | + -- describe "spanToRange" $ do |
| 47 | + |
| 48 | + -- prop "computes multi-line ranges" $ |
| 49 | + -- \ source -> |
| 50 | + -- spanToRange source (totalSpan source) `shouldBe` totalRange source |
| 51 | + |
| 52 | + -- prop "computes sub-line ranges" $ |
| 53 | + -- \ s -> let source = "*" <> s <> "*" in |
| 54 | + -- spanToRange source (insetSpan (totalSpan source)) `shouldBe` insetRange (totalRange source) |
| 55 | + |
| 56 | + -- prop "inverse of rangeToSpan" $ |
| 57 | + -- \ a b -> let s = a <> "\n" <> b in spanToRange s (totalSpan s) `shouldBe` totalRange s |
| 58 | + |
| 59 | + -- describe "rangeToSpan" $ do |
| 60 | + -- prop "inverse of spanToRange" $ |
| 61 | + -- \ a b -> let s = a <> "\n" <> b in rangeToSpan s (totalRange s) `shouldBe` totalSpan s |
| 62 | + |
| 63 | + -- describe "totalSpan" $ do |
| 64 | + -- prop "covers single lines" $ |
| 65 | + -- \ n -> totalSpan (fromText (Text.replicate n "*")) `shouldBe` Span (Pos 1 1) (Pos 1 (max 1 (succ n))) |
| 66 | + |
| 67 | + -- prop "covers multiple lines" $ |
| 68 | + -- \ 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)) |
| 69 | + |
| 70 | + -- describe "newlineIndices" $ do |
| 71 | + -- it "finds \\n" $ |
| 72 | + -- let source = "a\nb" in |
| 73 | + -- newlineIndices source `shouldBe` [1] |
| 74 | + -- it "finds \\r" $ |
| 75 | + -- let source = "a\rb" in |
| 76 | + -- newlineIndices source `shouldBe` [1] |
| 77 | + -- it "finds \\r\\n" $ |
| 78 | + -- let source = "a\r\nb" in |
| 79 | + -- newlineIndices source `shouldBe` [2] |
| 80 | + -- it "finds intermixed line endings" $ |
| 81 | + -- let source = "hi\r}\r}\n xxx \r a" in |
| 82 | + -- newlineIndices source `shouldBe` [2, 4, 6, 12] |
| 83 | + |
| 84 | + -- prop "preserves characters" . forAll (toTiers (list +| [chr 0xa0..chr 0x24f])) $ |
| 85 | + -- \ c -> Text.unpack (toText (fromText (Text.singleton c))) `shouldBe` [c] |
| 86 | + |
| 87 | + -- prop "preserves strings" $ |
| 88 | + -- \ s -> fromText (toText s) `shouldBe` s |
70 | 89 |
|
71 | 90 |
|
72 | 91 | insetSpan :: Span -> Span |
|
0 commit comments