packages feed

tricorder-0.5.0.0: test/Unit/Tricorder/Session/TargetSpec.hs

module Unit.Tricorder.Session.TargetSpec (test_Target) where

import Distribution.PackageDescription.Parsec (parseGenericPackageDescriptionMaybe)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertBool, testCase, (@?=))
import Prelude hiding (compare)

import Tricorder.Session.CabalFile (CabalFile (..))
import Tricorder.Session.Target
    ( ComponentKind (..)
    , Target (..)
    , allComponentTargets
    , compare
    , definesCustomPrelude
    , parse
    , resolve
    )
import Unit.Tricorder.Session.Helpers
    ( gpd
    , libTestCabal
    , libWithPreludeCabal
    , multiCabalFiles
    , singleCabalFile
    )


test_Target :: TestTree
test_Target =
    testGroup
        "Target"
        [ testGroup "resolve" testResolveTargets
        , testGroup "parse" testParseTarget
        , testGroup "compare" testCompareTargets
        , testGroup "allComponentTargets" testAllComponentTargets
        , testGroup "definesCustomPrelude" testDefinesCustomPrelude
        ]


testParseTarget :: [TestTree]
testParseTarget =
    [ testGroup
        "qualified targets"
        [ testCase "parses lib: as the main library (empty name)" do
            parse "lib:" @?= Qualified Lib ""
        , testCase "parses a named lib: target" do
            parse "lib:myapp-utils" @?= Qualified Lib "myapp-utils"
        , testCase "parses an flib: target" do
            parse "flib:myapp-flib" @?= Qualified FLib "myapp-flib"
        , testCase "parses an exe: target" do
            parse "exe:myapp-exe" @?= Qualified Exe "myapp-exe"
        , testCase "parses a test: target" do
            parse "test:myapp-test" @?= Qualified Test "myapp-test"
        , testCase "parses a bench: target" do
            parse "bench:myapp-bench" @?= Qualified Bench "myapp-bench"
        ]
    , testGroup
        "a name with no kind prefix"
        [ testCase "parses as bare" do
            parse "myapp" @?= Bare "myapp"
        ]
    , testGroup
        "unrecognized targets"
        [ testCase "rejects an unknown kind" do
            parse "bogus:myapp" @?= Unrecognized "bogus:myapp"
        , testCase "rejects a form with extra colons" do
            parse "lib:a:b" @?= Unrecognized "lib:a:b"
        ]
    ]


testResolveTargets :: [TestTree]
testResolveTargets =
    [ testGroup
        "when targets are configured"
        [ testCase "parses and sorts configured targets" do
            let actual = resolve [] ["lib:foo", "test:foo-test"]
            actual @?= [Qualified Lib "foo", Qualified Test "foo-test"]
        ]
    , testGroup
        "when no targets are configured"
        [ testCase "auto-detects all components from the cabal file" do
            -- cabalFixture exposes no Prelude module, so all components sort
            -- alphabetically by their rendered form.
            let actual = resolve singleCabalFile []
            actual
                @?= [ PackageQualified "myapp" Bench "myapp-bench"
                    , PackageQualified "myapp" Exe "myapp-exe"
                    , PackageQualified "myapp" FLib "myapp-flib"
                    , PackageQualified "myapp" Lib "myapp"
                    , PackageQualified "myapp" Lib "myapp-utils"
                    , PackageQualified "myapp" Test "myapp-test"
                    ]
        , testCase "surfaces test-suite components so they can be run after a build" do
            let actual = resolve singleCabalFile []
            assertBool "expected myapp-test among the targets"
                $ PackageQualified "myapp" Test "myapp-test" `elem` actual
        , testCase "returns no targets when there are no cabal files" do
            let actual = resolve [] []
            actual @?= []
        , -- [tag: test_resolve_targest_aggregate]
          testCase "aggregates components across every package (regression: was 0)" do
            let actual = resolve multiCabalFiles []
            actual
                @?= [ PackageQualified "pkg-a" Lib "pkg-a"
                    , PackageQualified "pkg-a" Test "pkg-a-test"
                    , PackageQualified "pkg-b" Lib "pkg-b"
                    , PackageQualified "pkg-b" Test "pkg-b-test"
                    ]
        , testCase "sorts a library exposing a custom Prelude last" do
            let cabalFile =
                    CabalFile "/myprelude.cabal"
                        $ fromMaybe (error "libWithPreludeCabal failed to parse")
                        $ parseGenericPackageDescriptionMaybe (libWithPreludeCabal "myprelude")
            let actual = resolve [cabalFile] []
            actual
                @?= [ PackageQualified "myprelude" Exe "myprelude-exe"
                    , PackageQualified "myprelude" Lib "myprelude"
                    ]
        ]
    ]


testCompareTargets :: [TestTree]
testCompareTargets =
    -- A predicate that stands in for 'definesCustomPrelude': marks lib: targets
    -- as "defines custom Prelude" so the comparison contract is exercised
    -- independently of cabal-file parsing.
    [ testGroup
        "Ord"
        [ testGroup
            "only first target matches the predicate"
            [ testGroup
                "first target's render normally sorts as LT"
                [ testCase "should return GT" do
                    compare defPred (Qualified Lib "a") (Qualified Exe "b") @?= GT
                ]
            , testGroup
                "both targets have the same render"
                [ testCase "should return GT" do
                    compare defPred (Qualified Lib "a") (Qualified Exe "a") @?= GT
                ]
            , testGroup
                "first target's render normally sorts as GT"
                [ testCase "should return GT" do
                    compare defPred (Qualified Lib "b") (Qualified Exe "a") @?= GT
                ]
            ]
        , testGroup
            "only second target matches the predicate"
            [ testGroup
                "first target's render normally sorts as LT"
                [ testCase "should return LT" do
                    compare defPred (Qualified Exe "a") (Qualified Lib "b") @?= LT
                ]
            , testGroup
                "both targets have the same render"
                [ testCase "should return LT" do
                    compare defPred (Qualified Exe "a") (Qualified Lib "a") @?= LT
                ]
            , testGroup
                "first target's render normally sorts as GT"
                [ testCase "should return LT" do
                    compare defPred (Qualified Exe "b") (Qualified Lib "a") @?= LT
                ]
            ]
        , testGroup
            "both targets match the predicate"
            [ testGroup
                "first target's render normally sorts as LT"
                [ testCase "should sort normally" do
                    compare defPred (Qualified Lib "a") (Qualified Lib "b") @?= LT
                ]
            , testGroup
                "both targets have the same render"
                [ testCase "should sort normally" do
                    compare defPred (Qualified Lib "a") (Qualified Lib "a") @?= EQ
                ]
            , testGroup
                "first target's render normally sorts as GT"
                [ testCase "should sort normally" do
                    compare defPred (Qualified Lib "b") (Qualified Lib "a") @?= GT
                ]
            ]
        , testGroup
            "neither target matches the predicate"
            [ testGroup
                "first target's render normally sorts as LT"
                [ testCase "should sort normally" do
                    compare defPred (Qualified Exe "a") (Qualified Exe "b") @?= LT
                ]
            , testGroup
                "both targets have the same render"
                [ testCase "should sort normally" do
                    compare defPred (Qualified Exe "a") (Qualified Exe "a") @?= EQ
                ]
            , testGroup
                "first target's render normally sorts as GT"
                [ testCase "should sort normally" do
                    compare defPred (Qualified Exe "b") (Qualified Exe "a") @?= GT
                ]
            ]
        ]
    ]
  where
    defPred (Qualified Lib _) = True
    defPred _ = False


testAllComponentTargets :: [TestTree]
testAllComponentTargets =
    [ testCase "returns every component for the fixture" do
        allComponentTargets gpd
            @?= [ PackageQualified "myapp" Lib "myapp"
                , PackageQualified "myapp" Lib "myapp-utils"
                , PackageQualified "myapp" FLib "myapp-flib"
                , PackageQualified "myapp" Exe "myapp-exe"
                , PackageQualified "myapp" Test "myapp-test"
                , PackageQualified "myapp" Bench "myapp-bench"
                ]
    , -- This test ensures `allComponentTargets`' part of the aggregate test.
      -- [ref:test_resolve_targest_aggregate]
      testCase "returns every component for test fixures" do
        let actual =
                allComponentTargets
                    $ fromMaybe (error "failed to parse cabal")
                    $ parseGenericPackageDescriptionMaybe
                    $ libTestCabal "pkg-a"
        actual
            @?= [ PackageQualified "pkg-a" Lib "pkg-a"
                , PackageQualified "pkg-a" Test "pkg-a-test"
                ]
    ]


testDefinesCustomPrelude :: [TestTree]
testDefinesCustomPrelude =
    [ testGroup
        "when the main library exposes Prelude"
        [ testCase "returns True for Qualified Lib \"\" (unnamed main lib)" do
            definesCustomPrelude [preludeCF] (Qualified Lib "") @?= True
        , testCase "returns True for Qualified Lib matching the package name" do
            definesCustomPrelude [preludeCF] (Qualified Lib "myprelude") @?= True
        , testCase "returns True for Bare matching the package name" do
            definesCustomPrelude [preludeCF] (Bare "myprelude") @?= True
        ]
    , testGroup
        "when no library exposes Prelude"
        [ testCase "returns False for a lib target in a normal package" do
            definesCustomPrelude singleCabalFile (Qualified Lib "myapp") @?= False
        , testCase "returns False for Bare matching the package name" do
            definesCustomPrelude singleCabalFile (Bare "myapp") @?= False
        ]
    , testGroup
        "for non-library targets"
        [ testCase "returns False for Qualified Exe" do
            definesCustomPrelude [preludeCF] (Qualified Exe "myprelude-exe") @?= False
        , testCase "returns False for Qualified Test" do
            definesCustomPrelude singleCabalFile (Qualified Test "myapp-test") @?= False
        , testCase "returns False for Unrecognized" do
            definesCustomPrelude [preludeCF] (Unrecognized "library:myprelude") @?= False
        ]
    , testCase "returns False when the cabal file list is empty" do
        definesCustomPrelude [] (Qualified Lib "anything") @?= False
    ]
  where
    preludeCF =
        CabalFile "/myprelude.cabal"
            $ fromMaybe (error "libWithPreludeCabal failed to parse")
            $ parseGenericPackageDescriptionMaybe (libWithPreludeCabal "myprelude")