packages feed

nirum-0.4.0: test/Nirum/Constructs/DeclarationSetSpec.hs

{-# LANGUAGE OverloadedLists #-}
module Nirum.Constructs.DeclarationSetSpec (SampleDecl (..), spec) where

import Control.Exception.Base (evaluate)
import Data.List (sort)
import Data.String (IsString (..))

import qualified Data.Set as S
import Test.Hspec.Meta

import Nirum.Constructs (Construct (..))
import qualified Nirum.Constructs.Annotation as A
import Nirum.Constructs.Annotation (AnnotationSet)
import Nirum.Constructs.Declaration (Declaration (..), Documented)
import Nirum.Constructs.DeclarationSet ( DeclarationSet
                                       , NameDuplication (..)
                                       , delete
                                       , empty
                                       , fromList
                                       , lookup'
                                       , null'
                                       , size
                                       , toList
                                       , union
                                       , (!)
                                       )
import Nirum.Constructs.Identifier (Identifier)
import Nirum.Constructs.Name (Name (Name))

data SampleDecl = SampleDecl Name AnnotationSet deriving (Eq, Ord, Show)

instance Construct SampleDecl where
    toCode _ = "(do not impl)"

instance Documented SampleDecl

instance Declaration SampleDecl where
    name (SampleDecl name' _) = name'
    annotations (SampleDecl _ anno') = anno'

instance IsString SampleDecl where
    fromString s = SampleDecl (fromString s) A.empty

type SampleDeclSet = DeclarationSet SampleDecl

data SampleDecl2 =
    SampleDecl2 Name (S.Set Identifier) AnnotationSet deriving (Eq, Ord, Show)

instance Construct SampleDecl2 where
    toCode _ = "(do not impl)"

instance Documented SampleDecl2

instance Declaration SampleDecl2 where
    name (SampleDecl2 name' _ _) = name'
    extraPublicNames (SampleDecl2 _ otherNames _) = otherNames
    annotations (SampleDecl2 _ _ annotations') = annotations'

spec :: Spec
spec =
    describe "DeclarationSet" $ do
        let sd fname bname = SampleDecl (Name fname bname) A.empty
        context "fromList" $ do
            let fl = fromList :: [SampleDecl]
                              -> Either NameDuplication SampleDeclSet
            it ("returns Left FacialNameDuplication "
                ++ "if duplicated facial name exists") $ do
                fl ["foo", "bar", "foo"] `shouldBe`
                    Left (FacialNameDuplication "foo")
                fl ["foo", "bar", "bar"] `shouldBe`
                    Left (FacialNameDuplication "bar")
                fl ["foo", "bar", sd "foo" "baz"] `shouldBe`
                    Left (FacialNameDuplication $ Name "foo" "baz")
            it ("returns Left BehindNameDuplication "
                ++ "if duplicated behind name exists") $ do
                fl ["foo", "bar", sd "baz" "foo"] `shouldBe`
                    Left (BehindNameDuplication $ Name "baz" "foo")
                fl [sd "foo" "bar", "bar", "baz"] `shouldBe`
                    Left (BehindNameDuplication "bar")
            it "returns Right DeclarationSet if there are no duplications" $ do
                let Right dset = fl ["foo", "bar", "baz"]
                toList dset `shouldBe` ["foo", "bar", "baz"]
            it "checks extraPublicNames as well" $ do
                let sampleDecl n on = SampleDecl2 n on A.empty
                let decls =
                        [ sampleDecl "foo" ["foo-a", "foo-b"]
                        , sampleDecl (Name "bar" "bar2") ["bar-a", "bar-b"]
                        ]
                let Right a = fromList decls
                sort (toList a) `shouldBe` sort decls
                fromList (sampleDecl "baz" ["foo"] : decls) `shouldBe`
                    Left (FacialNameDuplication "foo")
                fromList (sampleDecl "baz" ["bar"] : decls) `shouldBe`
                    Left (FacialNameDuplication $ Name "bar" "bar2")
                fromList (sampleDecl "baz" ["foo-a"] : decls) `shouldBe`
                    Left (FacialNameDuplication "foo-a")
                fromList [sampleDecl "foo" ["foo"]] `shouldBe`
                    Left (FacialNameDuplication "foo")
        let dset = ["foo", "bar", sd "baz" "asdf"] :: SampleDeclSet
        context "toList" $
            it "returns [Declaration]" $ do
                toList dset `shouldBe` ["foo", "bar", sd "baz" "asdf"]
                toList (empty :: SampleDeclSet) `shouldBe` []
        context "size" $
            it "returns its cardinality" $ do
                size dset `shouldBe` 3
                size (empty :: SampleDeclSet) `shouldBe` 0
        context "null" $ do
            it "returns True if the set is empty" $ do
                ([] :: SampleDeclSet) `shouldSatisfy` null'
                (empty :: SampleDeclSet) `shouldSatisfy` null'
            it "returns False if the set is not empty" $
                dset `shouldNotSatisfy` null'
        context "lookup" $ do
            it "returns Nothing if there is no such facial name" $
                lookup' "not-exists" dset `shouldBe` Nothing
            it "returns Just Declaration if the name exists" $ do
                lookup' "foo" dset `shouldBe` Just "foo"
                lookup' "bar" dset `shouldBe` Just "bar"
                lookup' "baz" dset `shouldBe` Just (sd "baz" "asdf")
        context "DeclarationSet ! Identifier" $ do
            it "returns Declaration if the name exists" $ do
                dset ! "foo" `shouldBe` "foo"
                dset ! "bar" `shouldBe` "bar"
                dset ! "baz" `shouldBe` sd "baz" "asdf"
            it "fails if there is no such facial name" $
                evaluate (dset ! "not-exists") `shouldThrow` anyException
        context "union" $ do
            it "returns Right DeclarationSet if there aren't no duplications" $
                union dset ["apple", "banana"] `shouldBe`
                    Right ["foo" , "bar" , sd "baz" "asdf", "apple", "banana"]
            it "returns Left FacialNameDuplication if facial names are dup" $
                union dset [sd "foo" "xyz"] `shouldBe`
                    Left (FacialNameDuplication $ Name "foo" "xyz")
            it "returns Left BehindNameDuplication if behind names are dup" $
                union dset [sd "xyz" "foo"] `shouldBe`
                    Left (BehindNameDuplication $ Name "xyz" "foo")
        specify "delete" $
            delete "bar" dset `shouldBe` ["foo", sd "baz" "asdf"]