packages feed

b9-0.5.67: src/tests/B9/Content/ErlangPropListSpec.hs

{-# LANGUAGE OverloadedStrings #-}
module B9.Content.ErlangPropListSpec
   ( spec
   )
where

import           Data.List
import           Test.Hspec
import           Test.QuickCheck
import           Data.Text                      ( )
import           B9.Text
import           B9.Artifact.Content.ErlTerms
import           B9.Artifact.Content.ErlangPropList

spec :: Spec
spec = describe "ErlangPropList" $ do

   it "parseFromText"
      $ let v = parseFromText "ok."
        in  v `shouldBe` Right (ErlangPropList (ErlAtom "ok"))

   it "renterToText"
      $ let v = renderToText (ErlangPropList (ErlAtom "ok"))
        in  v `shouldBe` Right "ok."

   it "combines primitives by putting them in a list"
      $ let p1       = ErlangPropList (ErlList [ErlAtom "a"])
            p2       = ErlangPropList (ErlList [ErlNatural 123])
            combined = ErlangPropList (ErlList [ErlAtom "a", ErlNatural 123])
        in  (p1 <> p2) `shouldBe` combined

   it "combines a list and a primitve by extending the list"
      $ let (Right l) =
               parseFromText "[a,b,c]." :: Either String ErlangPropList
            (Right p       ) = parseFromText "{ok,value}."
            (Right combined) = parseFromText "[a,b,c,{ok,value}]."
        in  l <> p `shouldBe` combined

   it "combines a primitve and a list by extending the list"
      $ let (Right l) =
               parseFromText "[a,b,c]." :: Either String ErlangPropList
            (Right p       ) = parseFromText "{ok,value}."
            (Right combined) = parseFromText "[{ok,value},a,b,c]."
        in  p <> l `shouldBe` combined

   it
         "merges lists with distinct elements to lists containing the elements of both lists"
      $ let p1 = ErlangPropList
               (ErlList [ErlTuple [ErlAtom "k_p1", ErlList [ErlNatural 1]]])
            p2 = ErlangPropList
               (ErlList [ErlTuple [ErlAtom "k_p2", ErlList [ErlNatural 1]]])
            expected = ErlangPropList
               (ErlList
                  [ ErlTuple [ErlAtom "k_p1", ErlList [ErlNatural 1]]
                  , ErlTuple [ErlAtom "k_p2", ErlList [ErlNatural 1]]
                  ]
               )
        in  p1 <> p2 `shouldBe` expected

   it
      "merges two property lists into a prop list that has the lenght of the left + the right proplist - the number of entries sharing the same key"
      (property mergedPropListsHaveCorrectLength)

data ErlPropListTestData =
  ErlPropListTestData { plistLeft :: [SimpleErlangTerm]
                      , plistRight :: [SimpleErlangTerm]
                      , commonKeys :: [SimpleErlangTerm]
                      } deriving (Eq,Ord,Show)

mergedPropListsHaveCorrectLength :: ErlPropListTestData -> Bool
mergedPropListsHaveCorrectLength (ErlPropListTestData l r common) =
   let (ErlangPropList (ErlList merged)) =
             ErlangPropList (ErlList l) <> ErlangPropList (ErlList r)
       expectedLen = length l + length r - length common
   in  length merged == expectedLen

instance Arbitrary ErlPropListTestData where
   arbitrary = do
      someKeys    <- nub <$> listOf arbitraryPlistKey
      numLeftOnly <- choose (0, length someKeys - 1)
      let keysLeftOnly = take numLeftOnly someKeys
      numCommon <- choose (0, length someKeys - numLeftOnly - 1)
      let keysCommon    = take numCommon (drop numLeftOnly someKeys)
      let keysRightOnly = drop (numLeftOnly + numCommon) someKeys
      let numRightOnly  = length someKeys - numLeftOnly - numCommon
      valuesLeft  <- vectorOf (numLeftOnly + numCommon) arbitraryPlistValue
      valuesRight <- vectorOf (numRightOnly + numCommon) arbitraryPlistValue
      return ErlPropListTestData
         { plistLeft  = zipWith toPair (keysLeftOnly <> keysCommon) valuesLeft
         , plistRight = zipWith toPair (keysRightOnly <> keysCommon) valuesRight
         , commonKeys = keysCommon
         }
    where
     toPair a b = ErlTuple [a, b]
     arbitraryPlist      = ErlList <$> listOf arbitraryPlistEntry
     arbitraryPlistEntry = toPair <$> arbitraryPlistKey <*> arbitraryPlistValue
     arbitraryPlistKey   = arbitraryErlSimpleAtom
     arbitraryPlistValue =
        oneof [arbitraryLiteral, arbitraryList, arbitraryPlist, arbitraryTuple]
     arbitraryTuple = ErlTuple <$> listOf arbitraryPlistValue
     arbitraryList  = ErlList <$> listOf arbitraryPlistValue
     arbitraryLiteral =
        oneof [arbitraryPlistKey, arbitraryErlString, arbitraryErlNumber]