extensions-0.0.0.0: test/Test/Extensions/Cabal.hs
module Test.Extensions.Cabal
( cabalSpec
, defaultExtensions
) where
import Data.ByteString (ByteString)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Map.Strict (Map)
import GHC.LanguageExtensions.Type (Extension (..))
import Test.Hspec (Spec, describe, it, shouldBe, shouldThrow)
import Extensions.Cabal (parseCabalExtensions, parseCabalFileExtensions)
import Extensions.Types (CabalException (..), OnOffExtension (..), ParsedExtensions (..),
SafeHaskellExtension (..), emptyParsedExtensions)
import qualified Data.Map.Strict as Map
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
cabalSpec :: Spec
cabalSpec = describe "Cabal file Extensions Parser" $ do
it "should throw the 'CabalFileNotFound' exception on non-existing file" $
parseCabalFileExtensions "xno-extensions.cabal" `shouldThrow`
(== CabalFileNotFound "xno-extensions.cabal")
it "should throw 'CabalParseError'" $
parseCabalExtensions exampleCabal "stack.yaml" `shouldThrow`
(== CabalParseError "example.cabal:0:0: \"name\" field missing")
it "should throw 'CabalSafeExtensionsConflict'" $
parseCabalExtensions exampleCabal conflictSafeCabal `shouldThrow`
(== CabalSafeExtensionsConflict (Safe :| [Unsafe]))
it "should parse minimal Cabal file" $
parseCabalExtensions exampleCabal minimalCabal >>= \extMap ->
extMap `shouldBe` mempty
it "should parse Cabal file with a single module in a library" $
parseCabalExtensions exampleCabal singleModuleCabal >>= \extMap ->
extMap `shouldBe` singleModuleMap
it "should parse Cabal file with multiple directories" $
parseCabalExtensions exampleCabal multipleDirsCabal >>= \extMap ->
extMap `shouldBe` singleModuleMap
it "should parse Cabal file with default-extensions" $
parseCabalExtensions exampleCabal singleExtensionCabal >>= \extMap ->
extMap `shouldBe` singleExtensionMap
it "should parse Cabal file with default-extensions inside common stanza" $
parseCabalExtensions exampleCabal commonStanzaCabal >>= \extMap ->
extMap `shouldBe` singleExtensionMap
it "should parse Cabal file with Safe in default-extensions" $
parseCabalExtensions exampleCabal safeHaskellCabal >>= \extMap ->
extMap `shouldBe` safeExtensionMap
it "should parse Cabal file with different safe extensions in different stanzas" $
parseCabalExtensions exampleCabal manySafeCabal >>= \extMap ->
extMap `shouldBe` manySafeMap
it "should parse extensions.cabal" $
parseCabalFileExtensions "extensions.cabal" >>= \extMap ->
extMap `shouldBe` expectedMap
where
to :: FilePath -> ParsedExtensions -> (FilePath, ParsedExtensions)
to = (,)
exampleCabal :: FilePath
exampleCabal = "example.cabal"
singleModuleMap :: Map FilePath ParsedExtensions
singleModuleMap = Map.singleton "src/Extensions.hs" emptyParsedExtensions
singleExtensionMap :: Map FilePath ParsedExtensions
singleExtensionMap = Map.singleton "src/Extensions.hs" emptyParsedExtensions
{ parsedExtensionsAll = [On TypeApplications]
}
safeExtensionMap :: Map FilePath ParsedExtensions
safeExtensionMap = Map.singleton "src/Extensions.hs" emptyParsedExtensions
{ parsedExtensionsSafe = Just Safe
}
manySafeMap :: Map FilePath ParsedExtensions
manySafeMap = Map.fromList
[ "src/Extensions.hs" `to` emptyParsedExtensions
{ parsedExtensionsSafe = Just Safe
}
, "test/Spec.hs" `to` emptyParsedExtensions
{ parsedExtensionsSafe = Just Trustworthy
}
]
-- Map for the project itself
expectedMap :: Map FilePath ParsedExtensions
expectedMap = Map.fromList
[ "app/Cli.hs" `to` defaultExtensions
, "app/Main.hs" `to` defaultExtensions
, "src/Extensions.hs" `to` defaultExtensions
, "src/Extensions/Cabal.hs" `to` defaultExtensions
, "src/Extensions/Module.hs" `to` defaultExtensions
, "src/Extensions/Package.hs" `to` defaultExtensions
, "src/Extensions/Types.hs" `to` defaultExtensions
, "test/Test/Extensions.hs" `to` defaultExtensions
, "test/Test/Extensions/Cabal.hs" `to` defaultExtensions
, "test/Test/Extensions/Module.hs" `to` defaultExtensions
, "test/Test/Extensions/Types.hs" `to` defaultExtensions
, "test/Spec.hs" `to` defaultExtensions
]
-- Minimal cabal file with no modules and no extensions
minimalCabal :: ByteString
minimalCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "library"
]
-- Cabal file with a single directory and single module
singleModuleCabal :: ByteString
singleModuleCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "library"
, " hs-source-dirs: src"
, " exposed-modules: Extensions"
]
-- Cabal file with multiple directories
multipleDirsCabal :: ByteString
multipleDirsCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "library"
, " hs-source-dirs: foo, bar, baz, quux, src"
, " exposed-modules: Extensions"
]
-- Cabal file with a single directory, single module and single extension
singleExtensionCabal :: ByteString
singleExtensionCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "library"
, " hs-source-dirs: src"
, " exposed-modules: Extensions"
, " default-extensions: TypeApplications"
]
-- Cabal file with a common stanza
commonStanzaCabal :: ByteString
commonStanzaCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "common common-extensions"
, " default-extensions: TypeApplications"
, ""
, "library"
, " import: common-extensions"
, " hs-source-dirs: src"
, " exposed-modules: Extensions"
]
-- Cabal file with safe haskell extension
safeHaskellCabal :: ByteString
safeHaskellCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "library"
, " hs-source-dirs: src"
, " exposed-modules: Extensions"
, " default-extensions: Safe"
]
-- Cabal file with conflicting safe haskell extensions
conflictSafeCabal :: ByteString
conflictSafeCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "library"
, " hs-source-dirs: src"
, " exposed-modules: Extensions"
, " default-extensions: Safe, Unsafe"
]
-- Cabal file with different Safe extensions
manySafeCabal :: ByteString
manySafeCabal = Text.encodeUtf8 $ Text.unlines
[ "cabal-version: 2.4"
, "name: example"
, "version: 0.0.0.0"
, ""
, "library"
, " hs-source-dirs: src"
, " exposed-modules: Extensions"
, " default-extensions: Safe"
, ""
, "test-suite example-test"
, " type: exitcode-stdio-1.0"
, " hs-source-dirs: test"
, " main-is: Spec.hs"
, " default-extensions: Trustworthy"
]
defaultExtensions :: ParsedExtensions
defaultExtensions = emptyParsedExtensions
{ parsedExtensionsAll = map On
[ ConstraintKinds
, DeriveGeneric
, DerivingStrategies
, GeneralizedNewtypeDeriving
, InstanceSigs
, KindSignatures
, LambdaCase
, OverloadedStrings
, RecordWildCards
, ScopedTypeVariables
, StandaloneDeriving
, TupleSections
, TypeApplications
, ViewPatterns
]
}