packages feed

fortran-src-0.7.0: test/Language/Fortran/Rewriter/InternalSpec.hs

{-# LANGUAGE OverloadedStrings #-}

module Language.Fortran.Rewriter.InternalSpec
  ( spec
  )
where

import qualified Data.ByteString.Lazy.Char8    as BC
import           Test.Hspec
import           Control.Exception.Base         ( evaluate )
import           Language.Fortran.Rewriter.Internal

spec :: Spec
spec = do
  describe "SourceLocation" $ it "Initialize SourceLocation" $ do
    let sl = SourceLocation 0 0
    sl `shouldBe` SourceLocation 0 0
  describe "SourceRange" $ it "Initialize SourceRange" $ do
    let sr = SourceRange (SourceLocation 0 0) (SourceLocation 0 5)
    sr `shouldBe` SourceRange (SourceLocation 0 0) (SourceLocation 0 5)
  describe "RChar" $ do
    it "Initialize RChar" $ do
      let rch = RChar (Just 'a') False (SourceLocation 0 0) ""
      rch `shouldBe` RChar (Just 'a') False (SourceLocation 0 0) ""
    it "Convert line to [RChar]" $ do
      let line   = "Line"
          rcharl = toRCharList line
      rcharl
        `shouldBe` [ RChar (Just 'L') False (SourceLocation 0 0) ""
                   , RChar (Just 'i') False (SourceLocation 0 1) ""
                   , RChar (Just 'n') False (SourceLocation 0 2) ""
                   , RChar (Just 'e') False (SourceLocation 0 3) ""
                   , RChar Nothing    False (SourceLocation 0 4) ""
                   ]
    it "Convert multiline string to [RChar]" $ do
      let multiLine = "fi\nl\nes"
          rcharl    = toRCharList multiLine
      rcharl
        `shouldBe` [ RChar (Just 'f')  False (SourceLocation 0 0) ""
                   , RChar (Just 'i')  False (SourceLocation 0 1) ""
                   , RChar (Just '\n') False (SourceLocation 0 2) ""
                   , RChar (Just 'l')  False (SourceLocation 1 0) ""
                   , RChar (Just '\n') False (SourceLocation 1 1) ""
                   , RChar (Just 'e')  False (SourceLocation 2 0) ""
                   , RChar (Just 's')  False (SourceLocation 2 1) ""
                   , RChar Nothing     False (SourceLocation 2 2) ""
                   ]
    it "Mark range in line (in the middle)" $ do
      let line   = toRCharList "Long Line"
          range  = SourceRange (SourceLocation 0 1) (SourceLocation 0 5)
          rcharl = markRChars line range
      rcharl
        `shouldBe` [ RChar (Just 'L') False (SourceLocation 0 0) ""
                   , RChar (Just 'o') True  (SourceLocation 0 1) ""
                   , RChar (Just 'n') True  (SourceLocation 0 2) ""
                   , RChar (Just 'g') True  (SourceLocation 0 3) ""
                   , RChar (Just ' ') True  (SourceLocation 0 4) ""
                   , RChar (Just 'L') False (SourceLocation 0 5) ""
                   , RChar (Just 'i') False (SourceLocation 0 6) ""
                   , RChar (Just 'n') False (SourceLocation 0 7) ""
                   , RChar (Just 'e') False (SourceLocation 0 8) ""
                   , RChar Nothing    False (SourceLocation 0 9) ""
                   ]
    it "Mark range in line (the whole line)" $ do
      let line   = toRCharList "Long Line"
          range  = SourceRange (SourceLocation 0 0) (SourceLocation 0 9)
          rcharl = markRChars line range
      rcharl
        `shouldBe` [ RChar (Just 'L') True  (SourceLocation 0 0) ""
                   , RChar (Just 'o') True  (SourceLocation 0 1) ""
                   , RChar (Just 'n') True  (SourceLocation 0 2) ""
                   , RChar (Just 'g') True  (SourceLocation 0 3) ""
                   , RChar (Just ' ') True  (SourceLocation 0 4) ""
                   , RChar (Just 'L') True  (SourceLocation 0 5) ""
                   , RChar (Just 'i') True  (SourceLocation 0 6) ""
                   , RChar (Just 'n') True  (SourceLocation 0 7) ""
                   , RChar (Just 'e') True  (SourceLocation 0 8) ""
                   , RChar Nothing    False (SourceLocation 0 9) ""
                   ]
    it "Mark range in multiline string (in the middle)" $ do
      let line   = toRCharList "Li\nn\nes12"
          range  = SourceRange (SourceLocation 0 1) (SourceLocation 2 2)
          rcharl = markRChars line range
      rcharl
        `shouldBe` [ RChar (Just 'L')  False (SourceLocation 0 0) ""
                   , RChar (Just 'i')  True  (SourceLocation 0 1) ""
                   , RChar (Just '\n') True  (SourceLocation 0 2) ""
                   , RChar (Just 'n')  True  (SourceLocation 1 0) ""
                   , RChar (Just '\n') True  (SourceLocation 1 1) ""
                   , RChar (Just 'e')  True  (SourceLocation 2 0) ""
                   , RChar (Just 's')  True  (SourceLocation 2 1) ""
                   , RChar (Just '1')  False (SourceLocation 2 2) ""
                   , RChar (Just '2')  False (SourceLocation 2 3) ""
                   , RChar Nothing     False (SourceLocation 2 4) ""
                   ]
    it "Mark range in multiline string (the whole multiline)" $ do
      let multiline = toRCharList "Li\nn\nes12"
          range     = SourceRange (SourceLocation 0 0) (SourceLocation 2 4)
          rcharl    = markRChars multiline range
      rcharl
        `shouldBe` [ RChar (Just 'L')  True  (SourceLocation 0 0) ""
                   , RChar (Just 'i')  True  (SourceLocation 0 1) ""
                   , RChar (Just '\n') True  (SourceLocation 0 2) ""
                   , RChar (Just 'n')  True  (SourceLocation 1 0) ""
                   , RChar (Just '\n') True  (SourceLocation 1 1) ""
                   , RChar (Just 'e')  True  (SourceLocation 2 0) ""
                   , RChar (Just 's')  True  (SourceLocation 2 1) ""
                   , RChar (Just '1')  True  (SourceLocation 2 2) ""
                   , RChar (Just '2')  True  (SourceLocation 2 3) ""
                   , RChar Nothing     False (SourceLocation 2 4) ""
                   ]
    it "Set replacement string (SourceLocation)" $ do
      let line   = toRCharList "Line"
          sl     = SourceLocation 0 1
          repl   = "repl"
          rchars = setReplacementStringSL line sl repl False
      rchars
        `shouldBe` [ RChar (Just 'L') False (SourceLocation 0 0) ""
                   , RChar (Just 'i') False (SourceLocation 0 1) "repl"
                   , RChar (Just 'n') False (SourceLocation 0 2) ""
                   , RChar (Just 'e') False (SourceLocation 0 3) ""
                   , RChar Nothing    False (SourceLocation 0 4) ""
                   ]
    it "Set replacement string (SourceRange)" $ do
      let line   = toRCharList "Line"
          range  = SourceRange (SourceLocation 0 1) (SourceLocation 0 3)
          repl   = "repl"
          rchars = setReplacementStringSR line range repl False
      rchars
        `shouldBe` [ RChar (Just 'L') False (SourceLocation 0 0) ""
                   , RChar (Just 'i') False (SourceLocation 0 1) "repl"
                   , RChar (Just 'n') False (SourceLocation 0 2) ""
                   , RChar (Just 'e') False (SourceLocation 0 3) ""
                   , RChar Nothing    False (SourceLocation 0 4) ""
                   ]
    it "Evaluate single line (empty replacement)" $ do
      let rchars =
            [ RChar (Just 'L') False (SourceLocation 0 0) ""
            , RChar (Just 'i') False (SourceLocation 0 1) ""
            , RChar (Just 'n') False (SourceLocation 0 2) ""
            , RChar (Just 'e') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "Line"
    it "Evaluate single line (one-to-none replacement)" $ do
      let rchars =
            [ RChar (Just 'L') True  (SourceLocation 0 0) ""
            , RChar (Just 'i') False (SourceLocation 0 1) ""
            , RChar (Just 'n') False (SourceLocation 0 2) ""
            , RChar (Just 'e') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "ine"
    it "Evaluate single line (many-to-none replacement)" $ do
      let rchars =
            [ RChar (Just 'L') True  (SourceLocation 0 0) ""
            , RChar (Just 'i') True  (SourceLocation 0 1) ""
            , RChar (Just 'n') True  (SourceLocation 0 2) ""
            , RChar (Just 'q') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "q"
    it "Evaluate single line (many-to-none replacement)" $ do
      let rchars =
            [ RChar (Just 'L') True  (SourceLocation 0 0) ""
            , RChar (Just 'i') True  (SourceLocation 0 1) ""
            , RChar (Just 'n') True  (SourceLocation 0 2) ""
            , RChar (Just 'e') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "e"
    it "Evaluate single line (one-to-one replacement)" $ do
      let rchars =
            [ RChar (Just 'L') True  (SourceLocation 0 0) "T"
            , RChar (Just 'i') False (SourceLocation 0 1) ""
            , RChar (Just 'n') False (SourceLocation 0 2) ""
            , RChar (Just 'e') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "Tine"
    it "Evaluate single line (many-to-one replacement)" $ do
      let rchars =
            [ RChar (Just 'L') True  (SourceLocation 0 0) "T"
            , RChar (Just 'i') True  (SourceLocation 0 1) ""
            , RChar (Just 'n') True  (SourceLocation 0 2) ""
            , RChar (Just 'e') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "Te"
    it "Evaluate single line (one-to-many replacement)" $ do
      let rchars =
            [ RChar (Just 'L') True  (SourceLocation 0 0) "Argent"
            , RChar (Just 'i') False (SourceLocation 0 1) ""
            , RChar (Just 'n') False (SourceLocation 0 2) ""
            , RChar (Just 'e') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "Argentine"
    it "Evaluate single line (many-to-many replacement)" $ do
      let rchars =
            [ RChar (Just 'L') True  (SourceLocation 0 0) "Pineappl"
            , RChar (Just 'i') True  (SourceLocation 0 1) ""
            , RChar (Just 'n') True  (SourceLocation 0 2) ""
            , RChar (Just 'e') False (SourceLocation 0 3) ""
            , RChar Nothing    False (SourceLocation 0 4) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "Pineapple"
    it "Evaluate multiline" $ do
      let rchars =
            [ RChar (Just 'L')  True  (SourceLocation 0 0) "Formula"
            , RChar (Just 'i')  True  (SourceLocation 0 1) ""
            , RChar (Just '\n') True  (SourceLocation 0 2) ""
            , RChar (Just '\n') False (SourceLocation 0 3) ""
            , RChar (Just '1')  False (SourceLocation 0 4) ""
            , RChar Nothing     False (SourceLocation 0 5) ""
            ]
          result = evaluateRChars rchars
      result `shouldBe` "Formula\n1"
  describe "Chunk" $ do
    it "Take next chunk (no replacements)" $ do
      let line  = toRCharList "a very long text"
          chunk = nextChunk line
      chunk `shouldBe` (line, [])
    it "Take next chunk (replacement in the middle)" $ do
      let line =
            [ RChar (Just 'L') False (SourceLocation 0 0) ""
            , RChar (Just 'i') False (SourceLocation 0 1) ""
            , RChar (Just 'n') True  (SourceLocation 0 2) "repl"
            , RChar (Just 'e') True  (SourceLocation 0 3) ""
            , RChar (Just 'O') True  (SourceLocation 0 4) ""
            , RChar (Just 'f') False (SourceLocation 0 5) ""
            , RChar Nothing    False (SourceLocation 0 6) ""
            ]
          chunk = nextChunk line
      chunk `shouldBe` (take 2 line, drop 2 line)
    it "Take next chunk (replacement in the begining)" $ do
      let line =
            [ RChar (Just 'D') True  (SourceLocation 0 0) "repl"
            , RChar (Just 'o') True  (SourceLocation 0 1) ""
            , RChar (Just 'g') False (SourceLocation 0 2) ""
            , RChar Nothing    False (SourceLocation 0 3) ""
            ]
          chunk = nextChunk line
      chunk `shouldBe` (take 1 line, drop 1 line)
    it "Take next chunk (deletion in the begining)" $ do
      let line =
            [ RChar (Just 'D') True  (SourceLocation 0 0) ""
            , RChar (Just 'o') True  (SourceLocation 0 1) ""
            , RChar (Just 'g') False (SourceLocation 0 2) ""
            , RChar Nothing    False (SourceLocation 0 3) ""
            ]
          chunk = nextChunk line
      chunk `shouldBe` (take 1 line, drop 1 line)
    it "Take next chunk (with a new line)" $ do
      let multiline = toRCharList "some text with\na new line"
          chunk     = nextChunk multiline
      chunk `shouldBe` (take 15 multiline, drop 15 multiline)
    it "Take all chunks (single chunk)" $ do
      let line   = toRCharList "A single chunk"
          chunks = allChunks line
      chunks `shouldBe` [line]
    it "Take all chunks (chunks separated by a new line)" $ do
      let multiline = toRCharList "Two \n chunks"
          chunks    = allChunks multiline
      chunks `shouldBe` [take 5 multiline, drop 5 multiline]
    it "Take all chunks (chunks separated by a new line and deletion marks)"
      $ do
          let multiline =
                [ RChar (Just 'L')  False (SourceLocation 0 0) ""
                , RChar (Just 'i')  False (SourceLocation 0 1) ""
                , RChar (Just '\n') False (SourceLocation 0 2) ""
                , RChar (Just 'e')  True  (SourceLocation 1 0) "repl"
                , RChar (Just 't')  True  (SourceLocation 1 1) ""
                , RChar (Just 'f')  False (SourceLocation 2 0) ""
                , RChar Nothing     False (SourceLocation 2 1) ""
                ]
              chunks = allChunks multiline
          chunks
            `shouldBe` [ [ RChar (Just 'L')  False (SourceLocation 0 0) ""
                         , RChar (Just 'i')  False (SourceLocation 0 1) ""
                         , RChar (Just '\n') False (SourceLocation 0 2) ""
                         ]
                       , [RChar (Just 'e') True (SourceLocation 1 0) "repl"]
                       , [RChar (Just 't') True (SourceLocation 1 1) ""]
                       , [ RChar (Just 'f') False (SourceLocation 2 0) ""
                         , RChar Nothing    False (SourceLocation 2 1) ""
                         ]
                       ]
    it "Evaluate chunks (no replacements; short single line)" $ do
      let chunks = allChunks $ toRCharList "Line"
          str    = evaluateChunks chunks
      str `shouldBe` "Line"
    it "Evaluate chunks (no replacements; short multiline)" $ do
      let chunks = allChunks $ toRCharList "Multi\nline"
          str    = evaluateChunks chunks
      str `shouldBe` "Multi\nline"
    it "Evaluate chunks (no replacements; long single line)" $ do
      let rchars = toRCharList $ BC.replicate 90 'a'
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str `shouldBe` BC.replicate 90 'a'
    it "Evaluate chunks (no replacements; long multiline)" $ do
      let ostr =
            BC.replicate 20 'a'
              <> "\n"
              <> BC.replicate 40 'b'
              <> "\n"
              <> BC.replicate 90 'c'
              <> "\n"
              <> BC.replicate 70 'd'
          rchars = toRCharList ostr
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str `shouldBe` ostr
    it "Evaluate chunks (empty replacement; short line)" $ do
      let range  = SourceRange (SourceLocation 0 5) (SourceLocation 0 5)
          repl   = Replacement range ""
          rchars = setReplacement (toRCharList "A short one") repl
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str `shouldBe` "A short one"
    it "Evaluate chunks (short replacement; short line)" $ do
      let range  = SourceRange (SourceLocation 0 5) (SourceLocation 0 7)
          repl   = Replacement range "repl"
          rchars = setReplacement (toRCharList "A short line") repl
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str `shouldBe` "A shorepl line"
    it "Evaluate chunks (long replacement; short line)" $ do
      let
        range  = SourceRange (SourceLocation 0 34) (SourceLocation 0 35)
        replS  = replicate 60 'a'
        repl   = Replacement range replS
        rchars = setReplacement
          (toRCharList "A very long line with many words in it")
          repl
        chunks = allChunks rchars
        str    = evaluateChunks chunks
      str
        `shouldBe` "A very long line with many words i"
        <>         "\n     +"
        <>         BC.replicate 60 'a'
        <>         " it"
    it "Evaluate chunks (long replacement; long line)" $ do
      let range  = SourceRange (SourceLocation 0 3) (SourceLocation 0 4)
          replS  = replicate 70 'a'
          repl   = Replacement range replS
          rchars = setReplacement (toRCharList $ BC.replicate 70 'b') repl
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str
        `shouldBe` BC.replicate 3 'b'
        <>         "\n     +"
        <>         BC.replicate 70 'a'
        <>         "\n     +"
        <>         BC.replicate 66 'b'
    it "Evaluate chunks (multiline replacement; short line)" $ do
      let range  = SourceRange (SourceLocation 0 9) (SourceLocation 0 10)
          replS  = replicate 30 'a' <> "\n" <> replicate 30 'b'
          repl   = Replacement range replS
          rchars = setReplacement (toRCharList $ BC.replicate 20 'c') repl
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str
        `shouldBe` BC.replicate 9 'c'
        <>         BC.replicate 30 'a'
        <>         "\n"
        <>         BC.replicate 30 'b'
        <>         BC.replicate 10 'c'
    it "Evaluate chunks (multiline replacement; long line)" $ do
      let range  = SourceRange (SourceLocation 0 8) (SourceLocation 0 9)
          replS  = replicate 30 'a' <> "\n" <> replicate 30 'b'
          repl   = Replacement range replS
          rchars = setReplacement (toRCharList $ BC.replicate 60 'c') repl
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str
        `shouldBe` BC.replicate 8 'c'
        <>         BC.replicate 30 'a'
        <>         "\n"
        <>         BC.replicate 30 'b'
        <>         "\n     +"
        <>         BC.replicate 51 'c'
    it "Evaluate chunks (multiline replacement; multiline)" $ do
      let range  = SourceRange (SourceLocation 0 10) (SourceLocation 0 15)
          replS  = replicate 30 'a' <> "\n" <> replicate 30 'd'
          repl   = Replacement range replS
          rchars = setReplacement
            (toRCharList $ BC.replicate 30 'c' <> "\n" <> BC.replicate 30 'c')
            repl
          chunks = allChunks rchars
          str    = evaluateChunks chunks
      str
        `shouldBe` BC.replicate 10 'c'
        <>         BC.replicate 30 'a'
        <>         "\n"
        <>         BC.replicate 30 'd'
        <>         BC.replicate 15 'c'
        <>         "\n"
        <>         BC.replicate 30 'c'
  describe "Replacement" $ do
    it "Initialize Replacement" $ do
      let
        r =
          Replacement (SourceRange (SourceLocation 0 0) (SourceLocation 0 5)) ""
      r `shouldBe` Replacement
        (SourceRange (SourceLocation 0 0) (SourceLocation 0 5))
        ""
    it "Set replacement (regular)" $ do
      let line  = toRCharList "LineOf"
          range = SourceRange (SourceLocation 0 1) (SourceLocation 0 5)
          replS = "repl"
          r     = Replacement range replS
          res   = setReplacement line r
      res
        `shouldBe` [ RChar (Just 'L') False (SourceLocation 0 0) ""
                   , RChar (Just 'i') True  (SourceLocation 0 1) "repl"
                   , RChar (Just 'n') True  (SourceLocation 0 2) ""
                   , RChar (Just 'e') True  (SourceLocation 0 3) ""
                   , RChar (Just 'O') True  (SourceLocation 0 4) ""
                   , RChar (Just 'f') False (SourceLocation 0 5) ""
                   , RChar Nothing    False (SourceLocation 0 6) ""
                   ]
    it "Set replacement (insertion in the middle)" $ do
      let line  = toRCharList "shorty"
          range = SourceRange (SourceLocation 0 1) (SourceLocation 0 1)
          replS = "Va"
          r     = Replacement range replS
          res   = setReplacement line r
      res
        `shouldBe` [ RChar (Just 's') False (SourceLocation 0 0) ""
                   , RChar (Just 'h') True  (SourceLocation 0 1) "Vah"
                   , RChar (Just 'o') False (SourceLocation 0 2) ""
                   , RChar (Just 'r') False (SourceLocation 0 3) ""
                   , RChar (Just 't') False (SourceLocation 0 4) ""
                   , RChar (Just 'y') False (SourceLocation 0 5) ""
                   , RChar Nothing    False (SourceLocation 0 6) ""
                   ]
    it "Set replacement (insertion at the end)" $ do
      let line  = toRCharList "shorty"
          range = SourceRange (SourceLocation 0 6) (SourceLocation 0 6)
          replS = "Vb"
          r     = Replacement range replS
          res   = setReplacement line r
      res
        `shouldBe` [ RChar (Just 's') False (SourceLocation 0 0) ""
                   , RChar (Just 'h') False (SourceLocation 0 1) ""
                   , RChar (Just 'o') False (SourceLocation 0 2) ""
                   , RChar (Just 'r') False (SourceLocation 0 3) ""
                   , RChar (Just 't') False (SourceLocation 0 4) ""
                   , RChar (Just 'y') False (SourceLocation 0 5) ""
                   , RChar Nothing    True  (SourceLocation 0 6) "Vb"
                   ]
    it "Set replacements" $ do
      let code   = toRCharList $ BC.replicate 6 'a'
          range1 = SourceRange (SourceLocation 0 1) (SourceLocation 0 3)
          range2 = SourceRange (SourceLocation 0 3) (SourceLocation 0 5)
          replS1 = "repl1"
          replS2 = "repl2"
          r1     = Replacement range1 replS1
          r2     = Replacement range2 replS2
          res    = setReplacements code [r1, r2]
      res
        `shouldBe` [ RChar (Just 'a') False (SourceLocation 0 0) ""
                   , RChar (Just 'a') True (SourceLocation 0 1) (BC.pack replS1)
                   , RChar (Just 'a') True  (SourceLocation 0 2) ""
                   , RChar (Just 'a') True (SourceLocation 0 3) (BC.pack replS2)
                   , RChar (Just 'a') True  (SourceLocation 0 4) ""
                   , RChar (Just 'a') False (SourceLocation 0 5) ""
                   , RChar Nothing    False (SourceLocation 0 6) ""
                   ]
    it "Disjoint replacements (disjoint, different lines)" $ do
      let range1 = SourceRange (SourceLocation 0 2) (SourceLocation 0 14)
          range2 = SourceRange (SourceLocation 1 2) (SourceLocation 1 20)
          r1     = Replacement range1 "zz"
          r2     = Replacement range2 "xx"
      areDisjoint r1 r2 `shouldBe` True
      areDisjoint r2 r1 `shouldBe` True
    it "Disjoint replacements (disjoint, same line)" $ do
      let range1 = SourceRange (SourceLocation 0 2) (SourceLocation 0 4)
          range2 = SourceRange (SourceLocation 0 4) (SourceLocation 0 6)
          r1     = Replacement range1 "ee"
          r2     = Replacement range2 "ff"
      areDisjoint r1 r2 `shouldBe` True
      areDisjoint r2 r1 `shouldBe` True
    it "Disjoint replacements (overlapping, same line)" $ do
      let range1 = SourceRange (SourceLocation 0 2) (SourceLocation 0 14)
          range2 = SourceRange (SourceLocation 0 7) (SourceLocation 0 20)
          r1     = Replacement range1 "aa"
          r2     = Replacement range2 "bb"
      areDisjoint r1 r2 `shouldBe` False
      areDisjoint r2 r1 `shouldBe` False
    it "Disjoint replacements (overlapping, across lines)" $ do
      let range1 = SourceRange (SourceLocation 0 2) (SourceLocation 2 14)
          range2 = SourceRange (SourceLocation 1 7) (SourceLocation 5 20)
          r1     = Replacement range1 "cc"
          r2     = Replacement range2 "dd"
      areDisjoint r1 r2 `shouldBe` False
      areDisjoint r2 r1 `shouldBe` False
    it "Apply replacement (continuation line)" $ do
      let source = BC.replicate 30 'a' <> "\n" <> BC.replicate 30 'a'
          range  = SourceRange (SourceLocation 0 2) (SourceLocation 1 2)
          replS  = replicate 71 'b' <> "\n" <> replicate 71 'c'
          r      = Replacement range replS
          res    = applyReplacements source [r]
      res
        `shouldBe` (  BC.replicate 2 'a'
                   <> "\n     +"
                   <> BC.pack replS
                   <> "\n     +"
                   <> BC.replicate 28 'a'
                   )
    it "Apply replacement (insertion in the middle)" $ do
      let source = "abc"
          range  = SourceRange (SourceLocation 0 0) (SourceLocation 0 0)
          replS  = "DDD"
          r      = Replacement range replS
          res    = applyReplacements source [r]
      res `shouldBe` "DDDabc"
    it "Apply replacement (insertion at the end)" $ do
      let source = "abz"
          range  = SourceRange (SourceLocation 0 3) (SourceLocation 0 3)
          replS  = "DFG"
          r      = Replacement range replS
          res    = applyReplacements source [r]
      res `shouldBe` "abzDFG"
    it "Apply replacements (disjoint)" $ do
      let source = BC.replicate 30 'a'
          range1 = SourceRange (SourceLocation 0 2) (SourceLocation 0 4)
          range2 = SourceRange (SourceLocation 0 4) (SourceLocation 0 6)
          replS1 = "repl1"
          replS2 = "2lper"
          r1     = Replacement range1 replS1
          r2     = Replacement range2 replS2
          res    = applyReplacements source [r1, r2]
      res
        `shouldBe` BC.replicate 2 'a'
        <>         BC.pack replS1
        <>         BC.pack replS2
        <>         BC.replicate 24 'a'
    it "Apply replacement ('!' in a string literal)" $ do
      let
        source =
          "      write(8, *) 'hi! this string is really long, overflowing even'"
                                                                           -- ^ Column 68
        range = SourceRange (SourceLocation 0 68) (SourceLocation 0 68)
        replS = ", variableHello"
        r     = Replacement range replS
        res   = applyReplacements source [r]
      res
        `shouldBe`
          "      write(8, *) 'hi! this string is really long, overflowing even'\n     +, variableHello"
    it "Apply replacements (overlapping)" $ do
      let source = BC.replicate 30 'a'
          range1 = SourceRange (SourceLocation 0 2) (SourceLocation 0 4)
          range2 = SourceRange (SourceLocation 0 3) (SourceLocation 0 6)
          replS  = "repl"
          r1     = Replacement range1 replS
          r2     = Replacement range2 replS
      evaluate (applyReplacements source [r1, r2])
        `shouldThrow` (== OverlappingError [(r1, r2)])
    it "Apply replacements (multiple overlapping)" $ do
      let source = BC.replicate 30 'a'
          range1 = SourceRange (SourceLocation 0 2) (SourceLocation 0 4)
          range2 = SourceRange (SourceLocation 0 3) (SourceLocation 0 6)
          range3 = SourceRange (SourceLocation 0 5) (SourceLocation 0 7)
          replS  = "repl"
          r1     = Replacement range1 replS
          r2     = Replacement range2 replS
          r3     = Replacement range3 replS
      evaluate (applyReplacements source [r1, r2, r3])
        `shouldThrow` (== OverlappingError [(r1, r2), (r2, r3)])
    it "Apply replacements (overlapping and out-of-order)" $ do
      let source = BC.replicate 30 'a'
          range1 = SourceRange (SourceLocation 0 2) (SourceLocation 0 4)
          range2 = SourceRange (SourceLocation 0 3) (SourceLocation 0 6)
          range3 = SourceRange (SourceLocation 0 5) (SourceLocation 0 7)
          replS  = "repl"
          r1     = Replacement range1 replS
          r2     = Replacement range2 replS
          r3     = Replacement range3 replS
      evaluate (applyReplacements source [r3, r2, r1])
        `shouldThrow` (== OverlappingError [(r1, r2), (r2, r3)])
    it "Apply replacements (invalid ranges; out of code)" $ do
      let source = BC.replicate 30 'a'
          range  = SourceRange (SourceLocation 0 2) (SourceLocation 0 31)
          repl   = Replacement range ""
      evaluate (applyReplacements source [repl])
        `shouldThrow` (== InvalidRangeError)
    it "Apply replacements (invalid ranges; invalid ordering of the parameters)"
      $ do
          let source = BC.replicate 30 'a'
              range  = SourceRange (SourceLocation 0 20) (SourceLocation 0 5)
              repl   = Replacement range ""
          evaluate (applyReplacements source [repl])
            `shouldThrow` (== InvalidRangeError)