packages feed

dump-0.2.7: spec/Spec.hs

import Test.Hspec
import Test.QuickCheck

import Text.InterpolatedString.Perl6
import Data.List (isPrefixOf)

import Debug.Dump
import Internal.Parser
import Internal.Utils

main = spec

spec = hspec $ do
  describe "wrapInParens" $ do
    it "should work" $ do
      wrapInParens "a" `shouldBe` "(a)"
      wrapInParens "" `shouldBe` "()"

  describe "parseLeaf" $ do
    let p = parseLeaf (\xs->("",xs)) '"'
    it "knows to stop on regular quote" $ do
      p [q|a"   |] `shouldBe` ([q|a"|], "   ")

    it "knows not to terminate after escaped quote" $ do
      p [q|a\""   |] `shouldBe` ([q|a\""|], "   ")

    it "does not trip up on escape characters" $ do
      p [q|a\\  |] `shouldBe` ([q|a\\  |], "")

    it "does not trip up on escape character at the end" $ do
      p [q|a\\|] `shouldBe` ([q|a\\|], "")

  describe "parseExpr" $ do
    let p = parseExpr .> fst
    it "handles flat" $ do
      p "asd" `shouldBe` "asd"
      p "asd,asd" `shouldBe` "asd"
      p "asd,a,sd" `shouldBe` "asd"

    it "handles parens" $ do
      p "(a,b)" `shouldBe` "(a,b)"
      p "(a,b) (a,b)" `shouldBe` "(a,b) (a,b)"
      p "(a,b), c" `shouldBe` "(a,b)"
      p "((a), b), c" `shouldBe` "((a), b)"
      p "((a,b),(a,b)), a" `shouldBe` "((a,b),(a,b))"

    it "handles char literals" $ do
      p "','" `shouldBe` "','"
      p "',', a" `shouldBe` "','"

    it "handles string literals" $ do
      p [q|","|] `shouldBe` [q|","|]
      p [q|",", a|] `shouldBe` [q|","|]

    it "handles list literals" $ do
      p "[a,b]" `shouldBe` "[a,b]"

    it "treats string literals as leaves" $ do
      p [q|"(", a|] `shouldBe` [q|"("|]

    it "support escaping in leaves" $ do
      p [q|"\"", a|] `shouldBe` [q|"\""|]
      p [q|"a" ++ "b", a|] `shouldBe` [q|"a" ++ "b"|]

    it "misc" $ do
      p [q|"((", a|] `shouldBe` [q|"(("|]
      p [q|"()(", a|] `shouldBe` [q|"()("|]
      p "(a, )" `shouldBe` "(a, )"
      p "(, b)" `shouldBe` "(, b)"

    it "shouldn't modify the list" $ do
      p "[" `shouldBe` "["
      p "([)]" `shouldBe` "([)]"
      -- p "([)], a" `shouldBe` "([)]"

    it "shouldn't modify the list (QuickCheck)" $ property $ let
      prop :: String -> Property
      prop s = (parseExpr s $> fst) `isPrefixOf` s
        $> counterexample [d|show s, parseExpr s|]
      in prop

  describe "parseExp'" $ do
    it "should work" $ do
      let p = parseExp'
      p []     "asd" `shouldBe` ("asd", "")
      p []     "a,b" `shouldBe` ("a", "b")
      p ")"    "a,b" `shouldBe` ("a,b", "")
      p []     "(a,b)" `shouldBe` ("(a,b)", "")
      p []     "(a,b),c" `shouldBe` ("(a,b)", "c")
      p []     "[a,b],c" `shouldBe` ("[a,b]", "c")
      p "]]"   "],b],c" `shouldBe` ("],b]", "c")
      p []     [q|"a,b",c|] `shouldBe` ([q|"a,b"|], "c")
      p []     [q|',',c|] `shouldBe` ([q|','|], "c")
      p []     [q|'"',c|] `shouldBe` ([q|'"'|], "c")
      -- p []     [q|'\',',|] `shouldBe` ([q|'\','|], "c")
      p []     [q|"\",",c|] `shouldBe` ([q|"\","|], "c")
      p []     [q|'\'',c|] `shouldBe` ([q|'\''|], "c")
      p []     [q|(\),c|] `shouldBe` ([q|(\)|], "c")

    -- TODO decide
    -- it "should? handle quotes int its own stack" $ do
    --   p ['"']  [q|a,b",c|] `shouldBe` ([q|a,b"|], "c")
    --   p ['"']  [q|(a",c|] `shouldBe` ([q|(a"|], "c")
    --   p ['\''] [q|,',c|] `shouldBe` ([q|,'|], "c")

  describe "splitOnCommas" $ do
    it "should work" $ do
      let s = splitOnCommas
      s "a" `shouldBe` ["a"]
      s "a,b" `shouldBe` ["a", "b"]
      s "a, b" `shouldBe` ["a", " b"]
      s "(a)" `shouldBe` ["(a)"]
      s "(a, b)" `shouldBe` ["(a, b)"]
      s "(a, b), b" `shouldBe` ["(a, b)", " b"]
      s "','" `shouldBe` ["','"]
      s "a ',' b" `shouldBe` ["a ',' b"]
      s "(a, b \")\"), b" `shouldBe` ["(a, b \")\")", " b"]
      s "(a, b \")\\\",\"), b" `shouldBe` ["(a, b \")\\\",\")", " b"]

  describe "Debug.Dump" $ do
    -- TODO decide if this is useful enough to warrant support
    -- it "should execute even if empty" $ do
    --   [d||] `shouldBe` ""

    it "should work with single literal" $ do
      [d|1|] `shouldBe` "(1) = 1"

    it "should work with simple expression" $ do
      [d|1 + 1|] `shouldBe` "(1 + 1) = 2"

    it "should work with bindings" $ do
      let a = 1
      [d|a|] `shouldBe` "(a) = 1"
      [d|a + 1|] `shouldBe` "(a + 1) = 2"

    it "should work with comma separated expressions" $ do
      [d|1, 2|] `shouldBe` "(1) = 1\t  (2) = 2"
      let i = 0.25
      let f = (1 -)
      [d|i, 1/i, f i, f (1/i)|] `shouldBe`
        "(i) = 0.25\t  (1/i) = 4.0\t  (f i) = 0.75\t  (f (1/i)) = -3.0"

    it "should step over commas in sub-expressions" $ do
      [d|(1, 2), 1|] `shouldBe` "((1, 2)) = (1,2)\t  (1) = 1"