packages feed

tasty-sugar-1.3.0.0: src/internal/Test/Tasty/Sugar/Analysis.hs

{-# OPTIONS_GHC -fno-warn-deprecations #-}

-- | Main internal entry point for determining the various test
-- configurations specified by a CUBE input.

module Test.Tasty.Sugar.Analysis
  (
    checkRoots
  )
where

import           Control.Monad.Logic
import           Data.Bifunctor ( bimap )
import           Data.Function ( on )
import qualified Data.List as L
import           Data.Maybe ( catMaybes )
import           Data.Ord ( comparing )
import qualified System.FilePath.GlobPattern as FPGP

import           Test.Tasty.Sugar.ExpectCheck
import           Test.Tasty.Sugar.RootCheck
import           Test.Tasty.Sugar.ParamCheck ( pmatchCmp )
import           Test.Tasty.Sugar.Types


-- | Given a 'CUBE' and a list of candidate files in the target directories,
-- return all 'Sweets' matches along with an explanation of the search process.
-- This is the core implementation for the 'Test.Tasty.Sugar.findSugar' API
-- interface.
checkRoots :: CUBE -> [CandidateFile]
           -> (Int, [([Sweets], [SweetExplanation])])
checkRoots pat allFiles =
  let isRootMatch n = candidateFile n FPGP.~~ (rootName pat)
      roots = filter isRootMatch allFiles
      checked = filter (not . null . fst)
                ((checkRoot pat allFiles) <$> roots)
  in (length checked, checked)


-- checkRoot will attempt to split the identified root file into three
-- parts:
--
--     basename + [param-values] + [suffix/extension]
--
-- Once it has performed this split, the calls findExpectation to
-- check if there are any expected file that matches the basename,
-- expSuffix, and any param-values provided.  A 'Sweets' will be
-- returned for each expected file matching this root configuration
checkRoot :: CUBE
          -> [CandidateFile] --  all possible expect candidates
          -> CandidateFile  --  root path
          -> ([Sweets], [SweetExplanation])
checkRoot pat allFiles rootF =
  let seps = separators pat
      params = validParams pat
      combineExpRes (swts, expl) = bimap (swts :) (expl :)

      mergeSweets swl =
        -- If multiple Sweets have the same rootMatchName this likely means that
        -- there were multiple expected files that could have matched.  Merge the
        -- Expectations: for each of the second Sweet's expectations:
        --
        --   - If one has a longer rootBaseName, that one represents the more
        --     explicit match and should be used.  Otherwise,
        --
        --   - If no explicit (expParamsMatch) elements match the first, this
        --     is a unique Expectation, add it to the first Sweet
        --
        --   - Find the Expectation in the first Sweet with the most number of
        --     Explicit matches, then select the one that has the most number of
        --     remaining Explicit that don't match the other (one should be a
        --     strict subset of the other!)
        let combineIfRootsMatch s sl =
              -- Add s to sl, or if s matches a root in sl, merge s with that sl
              uncurry (flip (:))
              ( combineSweets s <$> L.partition (not . isRootMatch s) sl)
            isRootMatch = (==) `on` (rootMatchName . fst)
            combineSweets s slm =
              -- Merge all the expectations from each of slm sweets into the main
              -- sweet s.
              foldr chooseOrCombineExpectations s slm
            chooseOrCombineExpectations (s,e) (sm,sme) =
              case comparing rootBaseName s sm of
                GT -> (s,e)
                LT -> (sm, sme)
                EQ -> bestExpectations (s,e) (sm,sme)
            bestExpectations (s,e) (sm,_sme) =
              -- combine the expectations in s with the expectations in each of
              -- sm, where expectations overlap based on explicit expParamsMatch
              -- matchups.
              let swts = s { expected =
                               foldr mergeExp (expected s) (expected sm)
                           }
                  mergeExp oneExp exps =
                    concat
                    $ fmap (take 1)
                    $ L.groupBy ((==) `on`
                                  (fmap (fmap getParamVal) . expParamsMatch))
                    $ L.sortBy (pmatchCmp `on` expParamsMatch)
                    $ oneExp : exps
              in ( swts, e { results = swts } )
        in foldr combineIfRootsMatch [] swl

  in foldr combineExpRes ([], []) $
     mergeSweets $
     catMaybes $
     fmap (findExpectation pat rootF allFiles) $
     observeAll $
     rootMatch rootF seps params (rootName pat)