packages feed

fix-imports-2.1.0: src/Config_test.hs

{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
module Config_test where
import qualified Data.Map as Map
import qualified Data.Text as Text
import EL.Test.Global
import qualified EL.Test.Testing as Testing
import qualified Language.Haskell.Exts as Haskell

import qualified Config
import qualified FixImports
import qualified Types


test_parse = do
    let f = check . Config.parse . Text.unlines
        check (config, errs)
            | null errs = Right config
            | otherwise = Left $ Text.unlines errs
    equal (f []) $ Right Config.empty
    leftLike (f ["include: a", "include: b"]) "duplicate fields"
    equal (Config._order <$>
            f ["import-order-first: A", "import-order-last: B"]) $
        Right $ Config.Order
            { _importOrder = Config.Priority ["A"] ["B"]
            , _sortUnqualifiedLast = False
            }

test_parseUnqualified = do
    let f = Config._unqualified . parseConfig
    equal (f ["unqualified: A.B (c); D.E ((+))"]) $ Map.fromList
        [ (Haskell.Ident () "c", "A.B")
        , (Haskell.Symbol () "+", "D.E")
        ]
    equal (f ["unqualified: A.B(c, d)"]) $ Map.fromList
        [ (Haskell.Ident () "c", "A.B")
        , (Haskell.Ident () "d", "A.B")
        ]
    equal (f ["unqualified: A.B (c,(+))"]) $ Map.fromList
        [ (Haskell.Ident () "c", "A.B")
        , (Haskell.Symbol () "+", "A.B")
        ]

test_parseQualifyAs = do
    let f = Config._qualifyAs . parseConfig
    equal (f ["qualify-as: A.B as AB, E as F"]) $ Map.fromList
        [ ("AB", "A.B")
        , ("F", "E")
        ]
    stringsLike (snd $ Config.parse "qualify-as: gibble gabble")
        ["stanza should look like"]

test_pickModule = do
    let f config modulePath candidates = Config.pickModule
            (Config._modulePriority (parseConfig config))
            modulePath candidates
    equal (f [] "X.hs" []) Nothing
    let localAB = [(Nothing, "A.M"), (Nothing, "B.M")]
    equal (f [] "X.hs" localAB) $
        Just (Nothing, "A.M")
    equal (f ["prio-module-high: B.M"] "X.hs" localAB) $
        Just (Nothing, "B.M")
    -- Has to be an exact match.
    equal (f ["prio-module-high: B"] "X.hs" localAB) $
        Just (Nothing, "A.M")

    -- Local modules take precedence.
    equal (f [] "A/B.hs" [(Nothing, "B.M"), (Just "pkg", "B.M")]) $
        Just (Nothing, "B.M")
    equal (f [] "A/B/C.hs" [(Nothing, "A.B.M"), (Just "pkg", "B.M")]) $
        Just (Nothing, "A.B.M")
    -- Closer local modules precede further ones.
    equal (f [] "A/B/C.hs" [(Nothing, "A.B.M"), (Nothing, "A.M")]) $
        Just (Nothing, "A.B.M")
    -- Prefer fewer dots.
    equal (f [] "X.hs" [(Just "p1", "A.B.M"), (Just "p2", "Z.M")]) $
        Just (Just "p2", "Z.M")

test_formatGroups = do
    let f config imports = lines $ Config.formatGroups Config.defaultFormat
            (Config._order (parseConfig config))
            (Testing.expectRight (parse (unlines imports)))
    equal (f [] []) []
    -- Unqualified import-all goes last.
    equal (f ["sort-unqualified-last: t"]
            [ "import Z", "import A"
            , "import qualified C", "import qualified B"
            , "import C (a)"
            ])
        [ "import qualified B"
        , "import qualified C"
        , "import C (a)"
        , ""
        , "import A"
        , "import Z"
        ]

    equal (f [] ["import qualified Z", "import qualified A"])
        [ "import qualified A"
        , "import qualified Z"
        ]
    equal (f ["import-order-first: Z"]
            ["import qualified Z", "import qualified A"])
        [ "import qualified Z"
        , "import qualified A"
        ]
    equal (f ["import-order-last: A"]
            ["import qualified Z", "import qualified A"])
        [ "import qualified Z"
        , "import qualified A"
        ]

    -- Exact match.
    equal (f ["import-order-first: Z"]
            ["import qualified Z.A", "import qualified A"])
        [ "import qualified A"
        , "import qualified Z.A"
        ]
    -- Unless it's a prefix match.
    equal (f ["import-order-first: Z."]
            ["import qualified Z.A", "import qualified A"])
        [ "import qualified Z.A"
        , "import qualified A"
        ]

test_showImportLine = do
    let f = fmap (Config.showImportDecl style . Types.importDecl . head)
            . parse
        style = Config.defaultFormat
            { Config._ppConfig = Just $ Config.PPConfig
                { _leaveSpaceForQualified = True }
            }
    -- pprint $ fmap (Types.importDecl . head) . parse $ "import A.B as C (x)"
    equal (f "import A.B as C (x)") $ Right "import           A.B as C (x)"

-- * util

importLine :: Types.ImportDecl -> Types.ImportLine
importLine decl = Types.ImportLine
    { importDecl = decl
    , importComments = []
    , importSource = Types.Local
    }

parse :: String -> Either String [Types.ImportLine]
parse source = case FixImports.parse [] "" source of
    Haskell.ParseFailed srcloc err ->
        Left $ Haskell.prettyPrint srcloc ++ ": " ++ err
    Haskell.ParseOk (mod, _cmts) ->
        Right $ map importLine $ FixImports.moduleImportDecls mod

parseConfig :: [Text.Text] -> Config.Config
parseConfig lines
    | null errs = config
    | otherwise = error $ "parsing " <> show lines  <> ": "
        <> Text.unpack (Text.unlines errs)
    where (config, errs) = Config.parse (Text.unlines lines)