tasty-sugar-0.2.0.0: src/internal/Test/Tasty/Sugar/ExpectCheck.hs
-- | Function to find expected results files for a specific root file,
-- along with any parameter values identified by the root file.
module Test.Tasty.Sugar.ExpectCheck
(
findExpectation
)
where
import Control.Monad.Logic
import System.FilePath ( (</>) )
import qualified Data.List as L
import Test.Tasty.Sugar.AssocCheck
import Test.Tasty.Sugar.ParamCheck
import Test.Tasty.Sugar.Types
-- | Finds the possible expected files matching the selected
-- source. There will be either one or none.
findExpectation :: CUBE
-> FilePath -- original name of source
-> [FilePath] -- all of the names to choose from
-> ([NamedParamMatch], FilePath, FilePath) -- param constraints from the root name
-> Maybe ( Sweets, SweetExplanation )
findExpectation pat rootN allNames (rootPMatches, matchPrefix, _) =
let r = mkSweet <$>
trimExpectations $
observeAll $
expectedSearch d matchPrefix rootPMatches seps params expSuffix o
candidates
d = inputDir pat
o = associatedNames pat
seps = separators pat
params = validParams pat
expSuffix = expectedSuffix pat
candidates = filter possible allNames
possible f = and [ matchPrefix `L.isPrefixOf` f
, rootN /= f
]
mkSweet e = Just $ Sweets { rootMatchName = rootN
, rootBaseName = matchPrefix
, rootFile = inputDir pat </> rootN
, cubeParams = validParams pat
, expected = e
}
-- The expectedSearch tries various combinations and ordering of
-- parameter values, separators, and such to find all valid
-- expected file matches. However, the result is an
-- over-sampling, so this function trims the excess and unwanted
-- expectations.
trimExpectations :: [Expectation] -> [Expectation]
trimExpectations =
-- If a parameter is Explicitly matched, discard any
-- Expectation with Assumed matches.
(\l -> let removeNonExplicits lst entry =
let explParams = filter (isExplicit . snd)
(expParamsMatch entry)
removeNonExpl es explParam =
filter (noNonExplicit explParam) es
noNonExplicit (pn, Explicit pv) expl=
let chkPV (pn', pv') =
pn /= pn' || case pv' of
Explicit _ -> True
Assumed v -> v /= pv
NotSpecified -> False
in all chkPV $ expParamsMatch expl
noNonExplicit _ _ = True
in foldl removeNonExpl lst explParams
in foldl removeNonExplicits l l)
-- remove duplicates (uses the Eq instance for Expectation
-- that ignores the order of the expParamsMatch and associated
-- to ensure that different ordering with the same values
-- doesn't cause multiple Expectation.
. L.nub
in case r of
Nothing -> Nothing
Just r' | [] <- expected r' -> Nothing
Just r' -> Just ( r'
, SweetExpl { rootPath = rootN
, base = matchPrefix
, expectedNames =
filter
(if null expSuffix then const True
else (expSuffix `L.isSuffixOf`))
candidates
, results = [ r' ]
})
-- Find all Expectations matching this rootMatch
expectedSearch :: FilePath
-> FilePath
-> [NamedParamMatch]
-> Separators
-> [ParameterPattern]
-> FileSuffix
-> [ (String, FileSuffix) ]
-> [FilePath]
-> Logic Expectation
expectedSearch inpDir rootPrefix rootPVMatches seps params expSuffix assocNames allNames =
do (expFile, pmatch) <-
let bestRanked :: [(FilePath, Int, [NamedParamMatch])]
-> Logic (FilePath, [NamedParamMatch])
bestRanked l =
if null l then mzero
else let m = maximum $ fmap rankValue l
rankValue (_,r,_) = r
rankMatching v (_,r,_) = v == r
dropRank (a,_,b) = (a,b)
in eachFrom $ fmap dropRank $ filter (rankMatching m) l
in bestRanked $
observeAll $
do pseq <- eachFrom $
([] :) $
filter (not . null) $
concatMap L.inits $
L.permutations params
pvals <- getPVals pseq
getExp rootPrefix rootPVMatches seps pvals expSuffix allNames
assocFiles <- getAssoc rootPrefix seps pmatch assocNames allNames
return $ Expectation { expectedFile = inpDir </> expFile
, associated = fmap (inpDir </>) <$> assocFiles
, expParamsMatch = pmatch
}
-- Get all expected files for a particular sequence of param+value.
-- Returns the expected file, the sequence of parameter values that
-- match that expect file, and a ranking (the number of those paramter
-- values that actually appear in the expect file.
getExp :: FilePath
-> [NamedParamMatch]
-> Separators
-> [(String, Maybe String)]
-> FileSuffix
-> [FilePath]
-> Logic (FilePath, Int, [NamedParamMatch])
getExp rootPrefix rootPMatches seps pvals expSuffix allNames =
do (pm, pmcnt, pmstr) <- pvalMatch seps rootPMatches pvals
-- If the expSuffix starts with a separator then *only that*
-- separator is allowed for the suffix (other seps are still
-- allowed for parameter value separation).
let suffixSpecifiesSep = and [ not (null expSuffix)
, head expSuffix `elem` seps
]
let suffixSepMatch = not suffixSpecifiesSep
|| and [ not (null pmstr)
, last pmstr == head expSuffix
]
guard suffixSepMatch
let expFile = if suffixSpecifiesSep
then rootPrefix <> pmstr <> tail expSuffix
else rootPrefix <> pmstr <> expSuffix
guard (expFile `elem` allNames)
return (expFile, pmcnt, pm)