module Test.Framework.Runners.TestPattern (
TestPattern, parseTestPattern, testPatternMatches
) where
import Test.Framework.Utilities
import Text.Regex.Posix.Wrap
import Text.Regex.Posix.String()
import Data.List
data Token = SlashToken
| WildcardToken
| DoubleWildcardToken
| LiteralToken Char
deriving (Eq)
tokenize :: String -> [Token]
tokenize ('/':rest) = SlashToken : tokenize rest
tokenize ('*':'*':rest) = DoubleWildcardToken : tokenize rest
tokenize ('*':rest) = WildcardToken : tokenize rest
tokenize (c:rest) = LiteralToken c : tokenize rest
tokenize [] = []
data TestPatternMatchMode = TestMatchMode
| PathMatchMode
data TestPattern = TestPattern {
tp_categories_only :: Bool,
tp_negated :: Bool,
tp_match_mode :: TestPatternMatchMode,
tp_tokens :: [Token]
}
instance Read TestPattern where
readsPrec _ string = [(parseTestPattern string, "")]
parseTestPattern :: String -> TestPattern
parseTestPattern string = TestPattern {
tp_categories_only = categories_only,
tp_negated = negated,
tp_match_mode = match_mode,
tp_tokens = tokens''
}
where
tokens = tokenize string
(negated, tokens')
| (LiteralToken '!'):rest <- tokens = (True, rest)
| otherwise = (False, tokens)
(categories_only, tokens'')
| (prefix, [SlashToken]) <- splitAt (length tokens' 1) tokens' = (True, prefix)
| otherwise = (False, tokens')
match_mode
| SlashToken `elem` tokens = PathMatchMode
| otherwise = TestMatchMode
testPatternMatches :: TestPattern -> [String] -> Bool
testPatternMatches test_pattern path = not_maybe $ any (=~ tokens_regex) things_to_match
where
not_maybe | tp_negated test_pattern = not
| otherwise = id
path_to_consider | tp_categories_only test_pattern = dropLast 1 path
| otherwise = path
tokens_regex = buildTokenRegex (tp_tokens test_pattern)
things_to_match = case tp_match_mode test_pattern of
TestMatchMode -> path_to_consider
PathMatchMode -> map pathToString $ inits path_to_consider
buildTokenRegex :: [Token] -> String
buildTokenRegex [] = []
buildTokenRegex (token:tokens) = concat (firstTokenToRegex token : map tokenToRegex tokens)
where
firstTokenToRegex SlashToken = "^"
firstTokenToRegex other = tokenToRegex other
tokenToRegex SlashToken = "/"
tokenToRegex WildcardToken = "[^/]*"
tokenToRegex DoubleWildcardToken = "*"
tokenToRegex (LiteralToken lit) = regexEscapeChar lit
regexEscapeChar :: Char -> String
regexEscapeChar c | c `elem` "\\*+?|{}[]()^$." = '\\' : [c]
| otherwise = [c]
pathToString :: [String] -> String
pathToString path = "/" ++ concat (intersperse "/" path)