{-# 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