packages feed

nirum-0.4.0: test/Nirum/Package/ModuleSetSpec.hs

{-# LANGUAGE OverloadedLists, TypeFamilies #-}
module Nirum.Package.ModuleSetSpec where

import Data.List (sort, sortOn)
import Data.Maybe (isNothing)
import Prelude hiding (length, lookup, null)

import qualified Data.Map.Strict as M
import qualified Data.Set as S
import Test.Hspec.Meta

import Nirum.Constructs.Annotation (empty)
import Nirum.Constructs.ModulePath (ModulePath)
import Nirum.Constructs.Module (Module (Module))
import Nirum.Constructs.TypeDeclaration ( Type (Alias)
                                        , TypeDeclaration ( Import
                                                          , TypeDeclaration
                                                          )
                                        )
import Nirum.Package.ModuleSet ( ImportError ( CircularImportError
                                             , MissingImportError
                                             , MissingModulePathError
                                             )
                               , fromList
                               , fromMap
                               , keys
                               , keysSet
                               , length
                               , lookup
                               , null
                               , toAscList
                               , toList
                               , toMap
                               )

fooBarModule :: Module
fooBarModule = Module [] $ Just "foo.bar"

validModules :: [(ModulePath, Module)]
validModules =
    [ ( ["abc"]
      , Module [TypeDeclaration "a" (Alias "text") empty]
               Nothing
      )
    , (["foo"], Module [] $ Just "foo")
    , (["foo", "bar"], fooBarModule)
    , (["foo", "baz"], Module [] $ Just "foo.baz")
    , (["qux"], Module [] $ Just "qux")
    , ( ["xyz"]
      , Module [ Import ["abc"] "a" "a" empty
               , TypeDeclaration "x" (Alias "text") empty
               ]
               Nothing
      )
    , ( ["zar"]
      , Module [ Import ["abc"] "aliased" "a" empty
               , TypeDeclaration "quuz" (Alias "text") empty
               ]
               Nothing
      )
    ]

missingImportsModules :: [(ModulePath, Module)]
missingImportsModules =
    [ ( ["foo"]
      , Module
            [ Import ["foo", "bar"] "xyz" "xyz" empty -- MissingModulePathError
            , Import ["foo", "bar"] "zzz" "zzz" empty -- MissingModulePathError
            , Import ["baz"] "qux" "qux" empty
            ]
            Nothing
      )
    , ( ["baz"]
      , Module [ TypeDeclaration "qux" (Alias "text") empty ] Nothing
      )
    , ( ["qux"]
      , Module [ Import ["foo"] "abc" "abc" empty -- MissingImportError
               , Import ["foo"] "def" "def" empty -- MissingImportError
               ]
               Nothing
      )
    ]

circularImportsModules :: [(ModulePath, Module)]
circularImportsModules =
    [ ( ["asdf"]
      , Module [ Import ["asdf"] "foo" "foo" empty
               , TypeDeclaration "bar" (Alias "text") empty
               ]
               Nothing
      )
    , ( ["abc", "def"]
      , Module [ Import ["abc", "ghi"] "bar" "bar" empty
               , TypeDeclaration
                     "foo" (Alias "text") empty
               ]
               Nothing
      )
    , ( ["abc", "ghi"]
      , Module [ Import ["abc", "xyz"] "baz" "baz" empty
               , TypeDeclaration "bar" (Alias "text") empty
               ]
               Nothing
      )
    , ( ["abc", "xyz"]
      , Module [ Import ["abc", "def"] "foo" "foo" empty
               , TypeDeclaration "baz" (Alias "text") empty
               ]
               Nothing
      )
    ]

spec :: Spec
spec =
    describe "ModuleSet" $ do
        context "(Map ModulePath Module)" $
            testImportErrors (fromMap . M.fromList)
        context "(Map ModulePath Module)" $
            testImportErrors fromList
        specify "fromMap" $ do
            let Right moduleSet = fromMap $ M.fromList validModules
            toAscList moduleSet `shouldBe` validModules
        specify "fromList" $ do
            let Right moduleSet = fromList validModules
            toAscList moduleSet `shouldBe` validModules
        let Right validModuleSet = fromList validModules
        specify "toMap" $
            toMap validModuleSet `shouldBe` M.fromList validModules
        specify "toList" $
            sortOn fst (toList validModuleSet) `shouldBe` validModules
        specify "toAscList" $
            toAscList validModuleSet `shouldBe` validModules
        specify "keys" $
            sort (keys validModuleSet) `shouldBe`
                sort [path | (path, _) <- validModules]
        specify "keysSet" $
            keysSet validModuleSet `shouldBe`
                S.fromList [p | (p, _) <- validModules]
        specify "lookup" $ do
            let Just mod' = lookup ["foo", "bar"] validModuleSet
            mod' `shouldBe` fooBarModule
            lookup ["wrong", "path"] validModuleSet `shouldSatisfy` isNothing
        specify "length" $
            length validModuleSet `shouldBe` 7
        specify "null" $
            validModuleSet `shouldNotSatisfy` null
  where
    testImportErrors makeModuleSet = do
        specify "detectMissingImports" $
            makeModuleSet missingImportsModules `shouldBe`
                Left [ MissingModulePathError ["foo"] ["foo", "bar"]
                     , MissingImportError ["qux"] ["foo"] "abc"
                     , MissingImportError ["qux"] ["foo"] "def"
                     ]
        specify "detectCircularImports" $
            makeModuleSet circularImportsModules `shouldBe`
                Left [ CircularImportError [["asdf"], ["asdf"]]
                     , MissingImportError ["asdf"] ["asdf"] "foo"
                     , CircularImportError [ ["abc", "def"]
                                           , ["abc", "ghi"]
                                           , ["abc", "xyz"]
                                           , ["abc", "def"]
                                           ]
                     , CircularImportError [ ["abc", "ghi"]
                                           , ["abc", "xyz"]
                                           , ["abc", "def"]
                                           , ["abc", "ghi"]
                                           ]
                     , CircularImportError [ ["abc", "xyz"]
                                           , ["abc", "def"]
                                           , ["abc", "ghi"]
                                           , ["abc", "xyz"]
                                           ]
                     ]