b9-0.2.0: src/tests/B9/Content/ErlTermsSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module B9.Content.ErlTermsSpec (spec) where
import Control.Applicative
import Data.List
import Test.Hspec
import Test.QuickCheck
import Data.Maybe
import B9.Content.ErlTerms
import qualified Data.ByteString.Char8 as B
spec :: Spec
spec = do
describe "parseErlTerm" $ do
it "parses a non-empty string"
(parseErlTerm "test" "\"hello world\"."
`shouldBe` Right (ErlString "hello world"))
it "parses a string with escaped characters"
(parseErlTerm "test" "\"\\b\\^A\"."
`shouldBe` Right (ErlString "\b\^A"))
it "parses a string with escaped octals: \\X"
(parseErlTerm "test" "\"\\7\"."
`shouldBe` Right (ErlString "\o7"))
it "parses a string with escaped octals: \\XY"
(parseErlTerm "test" "\"\\73\"."
`shouldBe` Right (ErlString "\o73"))
it "parses a string with escaped octals: \\XYZ"
(parseErlTerm "test" "\"\\431\"."
`shouldBe` Right (ErlString "\o431"))
it "parses a string with escaped hex: \\xNN"
(parseErlTerm "test" "\"\\xbE\"."
`shouldBe` Right (ErlString "\xbe"))
it "parses a string with escaped hex: \\x{N} (1)"
(parseErlTerm "test" "\"\\x{a}\"."
`shouldBe` Right (ErlString "\xa"))
it "parses a string with escaped hex: \\x{N} (2)"
(parseErlTerm "test" "\"\\x{2}\"."
`shouldBe` Right (ErlString "\x2"))
it "parses a two digit octal followed by a non-octal digit"
(parseErlTerm "test" "\"\\779\"."
`shouldBe` Right (ErlString "\o77\&9"))
it "parses a string with escaped hex: \\x{NNNNNN...}"
(parseErlTerm "test" "\"\\x{000000Fa}\"."
`shouldBe` Right (ErlString "\xfa"))
it "parses decimal literals"
(property
(do decimal <- arbitrary `suchThat` (>= 0)
let decimalStr = B.pack (show (decimal :: Integer) ++ ".")
parsedTerm <- case parseErlTerm "test" decimalStr of
(Left e) -> fail e
(Right parsedTerm) -> return parsedTerm
return (ErlNatural decimal == parsedTerm)))
it "parses a negative signed decimal"
(parseErlTerm "test" "-1." `shouldBe` Right (ErlNatural (-1)))
it "parses a positive signed decimal"
(parseErlTerm "test" "+1." `shouldBe` Right (ErlNatural 1))
it "parses decimal literals with radix notation"
(property
(do radix <- choose (2, 36)
digitsInRadix <- listOf1 (choose (0, radix - 1))
let (Right parsedTerm) = parseErlTerm "test" erlNumber
erlNumber = B.pack (show radix ++ "#" ++ digitChars ++ ".")
expected = convertStrToDecimal radix digitChars
digitChars = (naturals !!) <$> digitsInRadix
return (ErlNatural expected == parsedTerm)))
it "parses a floating point literal with exponent and sign"
(parseErlTerm "test" "-10.40E02." `shouldBe` Right (ErlFloat (-10.4e2)))
it "parses a simple erlang character literal"
(parseErlTerm "test" "$ ." `shouldBe` Right (ErlChar (toEnum 32)))
it "parses an erlang character literal with escape sequence"
(parseErlTerm "test" "$\\x{Fe}." `shouldBe` Right (ErlChar (toEnum 254)))
it "parses an unquoted atom with @ and _"
(parseErlTerm "test" "a@0_T." `shouldBe` Right (ErlAtom "a@0_T"))
it "parses a quoted atom with letters, spaces and special characters"
(parseErlTerm "test" "' $s<\\\\.0_=@\\e\\''."
`shouldBe` Right (ErlAtom " $s<\\.0_=@\ESC'"))
it "parses a binary literal containing a string"
(parseErlTerm "test" "<<\"1 ok!\">>." `shouldBe` Right (ErlBinary "1 ok!"))
it "parses an empty binary"
(parseErlTerm "test" "<<>>." `shouldBe` Right (ErlBinary ""))
it "parses an empty list"
(parseErlTerm "test" "[]." `shouldBe` Right (ErlList []))
it "parses a list of atoms"
(parseErlTerm "test" " [ hello, 'world' ] ." `shouldBe` Right (ErlList [ErlAtom "hello", ErlAtom "world"]))
it "parses an empty tuple"
(parseErlTerm "test" " { } ." `shouldBe` Right (ErlTuple []))
it "parses a tuple of atoms"
(parseErlTerm "test" " { hello, 'world' } ." `shouldBe` Right (ErlTuple [ErlAtom "hello", ErlAtom "world"]))
describe "renderErlTerm" $ do
it "renders an empty binary as \"<<>>\"."
(renderErlTerm (ErlBinary "") `shouldBe` "<<>>.")
it "renders an erlang character"
(renderErlTerm (ErlChar 'a') `shouldBe` "$a.")
it "renders a quoted atom and escapes special characters"
(renderErlTerm (ErlAtom " $s\"<\\.0_=@\ESC'")
`shouldBe` "' $s\"<\\\\.0_=@\\x{1b}\\''.")
it "renders a string and escapes special characters"
(renderErlTerm (ErlString "' $s\"<\\.0_=@\ESC''")
`shouldBe` "\"' $s\\\"<\\\\.0_=@\\x{1b}''\".")
it "renders an empty list"
(renderErlTerm (ErlList []) `shouldBe` "[].")
it "renders an empty tuple"
(renderErlTerm (ErlTuple []) `shouldBe` "{}.")
describe "renderErlTerm and parseErlTerm" $
it "parseErlTerm parses all terms rendered by renderErlTerm"
(property parsesRenderedTerms)
parsesRenderedTerms :: SimpleErlangTerm -> Bool
parsesRenderedTerms term =
either error (term ==) (parseErlTerm "test" (renderErlTerm term))
naturals :: String
naturals = ['0'..'9'] ++ ['a' .. 'z']
convertStrToDecimal :: Int -> String -> Integer
convertStrToDecimal radix digitChars =
let hornersMethod acc d = acc * radixHighPrecision + digitCharToInteger d
digitCharToInteger d = toInteger $ fromJust $ elemIndex d naturals
radixHighPrecision = toInteger radix
in foldl hornersMethod 0 digitChars