tasty-sugar-2.2.3.3: src/internal/Test/Tasty/Sugar/ParamCheck.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
-- | Functions for checking different parameter/value combinations.
module Test.Tasty.Sugar.ParamCheck
(
getSinglePVals
, namedPMatches
, pmatchCmp
, pmatchMax
, isCompatible
)
where
import Control.Monad
import Data.Function ( on )
import qualified Data.List as DL
import Test.Tasty.Sugar.Types
import Test.Tasty.Sugar.Iterations ( LogicI, eachFrom )
-- | Return a value to use for each parameter in the pattern, retricting those
-- values to the name parameter matches already established. This is a little
-- more complicated because there could be parameter name duplicates in the
-- already established matches (e.g. a matched file contains multiple values for
-- a parameter), so the actual subset of the named parameter matches associated
-- with this pattern selection is also returned.
getSinglePVals :: [NamedParamMatch] -> [ParameterPattern]
-> LogicI ([NamedParamMatch], [(String, Maybe String)])
getSinglePVals sel = fmap (fmap DL.sort) . foldM eachVal (mempty, mempty)
where eachVal (an,av) (pn, Nothing) =
case filter ((pn ==) . fst) sel of
[] -> return (an, (pn, Nothing) : av)
pvsets -> do npv <- snd <$> eachFrom "assigned param value" pvsets
return ((pn, npv) : an, (pn, getParamVal npv) : av)
eachVal (an,av) (pn, Just pvs) =
case filter ((pn ==) . fst) sel of
[] -> do pv <- eachFrom "assumed (non-root) param value" $ DL.sort pvs
return (an, (pn, Just pv) : av)
pvsets -> do npv <- eachFrom "matched param value" (snd <$> pvsets)
return ((pn, npv) : an, (pn, getParamVal npv) : av)
-- | namedPMatches supplements the core set of named matches with the extended
-- set of parameter values, marking all parameters not in the core set as Assumed
-- or NotSpecified.
namedPMatches :: [NamedParamMatch] -> [(String, Maybe String)]
-> [NamedParamMatch]
namedPMatches pmatch =
let addIfMissing (n,mbv) = maybe ((n, maybe NotSpecified Assumed mbv):)
(flip const)
$ lookup n pmatch
in foldr addIfMissing pmatch
-- | This provides an Ordering result of comparing two sets of NamedParamMatch.
-- This can be used for sorting or other prioritization of named matches.
pmatchCmp :: [ NamedParamMatch ] -> [ NamedParamMatch ] -> Ordering
pmatchCmp p1 p2 =
let comparisons =
[
-- the one with more Explicit matches is better
compare `on` (length . filter (isExplicit . snd))
-- the one with more parameters (usually the same)
, compare `on` length
-- comparing keys
, compare `on` (DL.sort . fmap fst)
]
-- comparing the correlated ParamMatch values
<> map (\k -> compare `on` (lookup k)) (fst <$> p1)
in cascadeCompare comparisons p1 p2
-- Runs multiple comparisons on two elements until the first comparison
-- thatreturns a non-EQ result.
cascadeCompare :: [ a -> a -> Ordering ] -> a -> a -> Ordering
cascadeCompare fs x y = mconcat [ f x y | f <- fs ]
-- | Returns the maximum of two arguments based on comparing the
-- [NamedParamMatch] extracted from each argument (via the passed function).
pmatchMax :: (a -> [NamedParamMatch]) -> a -> a -> a
pmatchMax f a b = case pmatchCmp (f a) (f b) of
LT -> b
_ -> a
-- | isCompatible can be used as a filter predicate to determine if the specified
-- file is compatible with the provided parameters and chosen parameter values.
-- One principle compatibility check is ensuring that there is no *other*
-- parameter value in the filename that conflicts with a chosen parameter value.
--
-- Note that a particular candidate file may have multiple matching values for a
-- parameter.
isCompatible :: [(String, Maybe String)]
-> CandidateFile
-> Bool
isCompatible pvals candidatFile =
let isCompatParamV v = \case
Nothing -> True
(Just cv) -> paramMatchVal cv v
isCompatParam (n,mbv) =
let nps = filter ((n ==) . fst) $ candidatePMatch candidatFile
in null nps || any ((`isCompatParamV` mbv) . snd) nps
in all isCompatParam pvals