diff --git a/luaunit.lua b/luaunit.lua index 0f320f6f..5493a201 100755 --- a/luaunit.lua +++ b/luaunit.lua @@ -265,18 +265,49 @@ end M.private.strMatch = strMatch local function patternFilter(patterns, expr, nil_result) - -- Check if any of `patterns` is contained in `expr`. If so, return `true`. - -- Return `false` if none of the patterns are contained in expr. If patterns - -- is `nil` (= unset), return default value passed in `nil_result`. - if patterns ~= nil then + -- Run `expr` through the inclusion and exclusion rules defined in patterns + -- and return true if expr shall be included, false for excluded. + -- Inclusion pattern are defined as normal patterns, exclusions + -- patterns start with `!` and are followed by a normal pattern + -- If patterns is `nil` (= unset) or empty, return default value passed in `nil_result`. + + if patterns ~= nil and #patterns > 0 then + local DONT_KNOW, ACCEPT, REJECT = nil, true, false + local result = DONT_KNOW for _, pattern in ipairs(patterns) do - if string.find(expr, pattern) then - return true + local exclude = false + if (pattern:sub(1,1) == '!') then + exclude = true + pattern = pattern:sub(2) + end + -- print('pattern: ',pattern) + -- print('exclude: ',exclude) + + -- when result is not set or already accepted, match pattern + -- only for rejecting + if exclude and (result == DONT_KNOW or result == ACCEPT) then + if string.find(expr, pattern) then + result = REJECT + end + end + + -- when result is not set or already rejected, match pattern + -- only for accepting + if (not exclude) and (result == DONT_KNOW or result == REJECT) then + if string.find(expr, pattern) then + result = ACCEPT + end end end - return false -- no match from patterns + if result == DONT_KNOW then + -- when no match is found, for inclusive patterns, it means refuse expr + -- for negative pattern, it means accept expr + return (patterns[1]:sub(1,1) == '!') + end + + return result end return nil_result @@ -2104,10 +2135,11 @@ end end return elseif state == SET_EXCLUDE then - if result['exclude'] then - table.insert( result['exclude'], cmdArg ) + local notArg = '!'..cmdArg + if result['pattern'] then + table.insert( result['pattern'], notArg ) else - result['exclude'] = { cmdArg } + result['pattern'] = { notArg } end return end @@ -2276,7 +2308,6 @@ end startDate = os.date(os.getenv('LUAUNIT_DATEFMT')), startIsodate = os.date('%Y-%m-%dT%H:%M:%S'), patternIncludeFilter = self.patternIncludeFilter, - patternExcludeFilter = self.patternExcludeFilter, tests = {}, failures = {}, errors = {}, @@ -2580,12 +2611,11 @@ end return result end - function M.LuaUnit.applyPatternFilter( patternIncFilter, patternExcFilter, listOfNameAndInst ) + function M.LuaUnit.applyPatternFilter( patternIncFilter, listOfNameAndInst ) local included, excluded = {}, {} for i, v in ipairs( listOfNameAndInst ) do -- local name, instance = v[1], v[2] - if patternFilter( patternIncFilter, v[1], true ) and - not patternFilter( patternExcFilter, v[1], false ) then + if patternFilter( patternIncFilter, v[1], true ) then table.insert( included, v ) else table.insert( excluded, v ) @@ -2607,7 +2637,7 @@ end randomizeTable( expandedList ) end local filteredList, filteredOutList = self.applyPatternFilter( - self.patternIncludeFilter, self.patternExcludeFilter, expandedList ) + self.patternIncludeFilter, expandedList ) self:startSuite( #filteredList, #filteredOutList ) @@ -2727,7 +2757,6 @@ end self.exeCount = options.exeCount self.patternIncludeFilter = options.pattern - self.patternExcludeFilter = options.exclude self.randomize = options.randomize if options.output then diff --git a/test/test_luaunit.lua b/test/test_luaunit.lua index e3ed155e..13609da1 100644 --- a/test/test_luaunit.lua +++ b/test/test_luaunit.lua @@ -566,9 +566,10 @@ TestLuaUnitUtilities = { __class__ = 'TestLuaUnitUtilities' } lu.assertEquals( lu.LuaUnit.parseCmdLine( { '-p', 'toto' } ), { pattern={'toto'} } ) lu.assertEquals( lu.LuaUnit.parseCmdLine( { '-p', 'titi', '-p', 'toto' } ), { pattern={'titi', 'toto'} } ) lu.assertErrorMsgContains( 'Missing argument after -p', lu.LuaUnit.parseCmdLine, { '-p', } ) - lu.assertEquals( lu.LuaUnit.parseCmdLine( { '--exclude', 'toto' } ), { exclude={'toto'} } ) - lu.assertEquals( lu.LuaUnit.parseCmdLine( { '-x', 'toto' } ), { exclude={'toto'} } ) - lu.assertEquals( lu.LuaUnit.parseCmdLine( { '-x', 'titi', '-x', 'toto' } ), { exclude={'titi', 'toto'} } ) + lu.assertEquals( lu.LuaUnit.parseCmdLine( { '--exclude', 'toto' } ), { pattern={'!toto'} } ) + lu.assertEquals( lu.LuaUnit.parseCmdLine( { '-x', 'toto' } ), { pattern={'!toto'} } ) + lu.assertEquals( lu.LuaUnit.parseCmdLine( { '-x', 'titi', '-x', 'toto' } ), { pattern={'!titi', '!toto'} } ) + lu.assertEquals( lu.LuaUnit.parseCmdLine( { '-x', 'titi', '-p', 'foo', '-x', 'toto' } ), { pattern={'!titi', 'foo', '!toto'} } ) lu.assertErrorMsgContains( 'Missing argument after -x', lu.LuaUnit.parseCmdLine, { '-x', } ) -- count @@ -586,12 +587,46 @@ TestLuaUnitUtilities = { __class__ = 'TestLuaUnitUtilities' } function TestLuaUnitUtilities:test_patternFilter() lu.assertEquals( lu.private.patternFilter( nil, 'toto', true), true ) - lu.assertEquals( lu.private.patternFilter( {}, 'toto', true), false ) lu.assertEquals( lu.private.patternFilter( nil, 'titi', false), false ) + lu.assertEquals( lu.private.patternFilter( {}, 'toto', false), false ) + lu.assertEquals( lu.private.patternFilter( {}, 'toto', true), true ) + + -- positive pattern lu.assertEquals( lu.private.patternFilter( {'toto'}, 'toto'), true ) lu.assertEquals( lu.private.patternFilter( {'toto'}, 'yyytotoxxx'), true ) lu.assertEquals( lu.private.patternFilter( {'titi', 'toto'}, 'yyytotoxxx'), true ) + lu.assertEquals( lu.private.patternFilter( {'titi', 'toto'}, 'tutu'), false ) lu.assertEquals( lu.private.patternFilter( {'titi', 'to..'}, 'yyytoxxx'), true ) + + -- negative pattern + lu.assertEquals( lu.private.patternFilter( {'!toto'}, 'toto'), false ) + lu.assertEquals( lu.private.patternFilter( {'!t.t.'}, 'titi'), false ) + lu.assertEquals( lu.private.patternFilter( {'!toto'}, 'titi'), true ) + lu.assertEquals( lu.private.patternFilter( {'!toto'}, 'yyytotoxxx'), false ) + lu.assertEquals( lu.private.patternFilter( {'!titi', '!toto'}, 'yyytotoxxx'), false ) + lu.assertEquals( lu.private.patternFilter( {'!titi', '!toto'}, 'tutu'), true ) + lu.assertEquals( lu.private.patternFilter( {'!titi', '!to..'}, 'yyytoxxx'), false ) + + -- combine patterns + lu.assertEquals( lu.private.patternFilter( { 'foo' }, 'foo'), true ) + lu.assertEquals( lu.private.patternFilter( { 'foo', '!foo' }, 'foo'), false ) + lu.assertEquals( lu.private.patternFilter( { 'foo', '!foo', 'foo' }, 'foo'), true ) + lu.assertEquals( lu.private.patternFilter( { 'foo', '!foo', 'foo', '!foo' }, 'foo'), false ) + + lu.assertEquals( lu.private.patternFilter( { '!foo' }, 'foo'), false ) + lu.assertEquals( lu.private.patternFilter( { '!foo', 'foo' }, 'foo'), true ) + lu.assertEquals( lu.private.patternFilter( { '!foo', 'foo', '!foo' }, 'foo'), false ) + lu.assertEquals( lu.private.patternFilter( { '!foo', 'foo', '!foo', 'foo' }, 'foo'), true ) + + lu.assertEquals( lu.private.patternFilter( { 'f..', '!foo', '__foo__' }, 'toto'), false ) + lu.assertEquals( lu.private.patternFilter( { 'f..', '!foo', '__foo__' }, 'fii'), true ) + lu.assertEquals( lu.private.patternFilter( { 'f..', '!foo', '__foo__' }, 'foo'), false ) + lu.assertEquals( lu.private.patternFilter( { 'f..', '!foo', '__foo__' }, '__foo__'), true ) + + lu.assertEquals( lu.private.patternFilter( { '!f..', 'foo', '!__foo__' }, 'toto'), true ) + lu.assertEquals( lu.private.patternFilter( { '!f..', 'foo', '!__foo__' }, 'fii'), false ) + lu.assertEquals( lu.private.patternFilter( { '!f..', 'foo', '!__foo__' }, 'foo'), true ) + lu.assertEquals( lu.private.patternFilter( { '!f..', 'foo', '!__foo__' }, '__foo__'), false ) end function TestLuaUnitUtilities:test_applyPatternFilter() @@ -604,39 +639,44 @@ TestLuaUnitUtilities = { __class__ = 'TestLuaUnitUtilities' } } -- default action: include everything - local included, excluded = lu.LuaUnit.applyPatternFilter( nil, nil, testset ) + local included, excluded = lu.LuaUnit.applyPatternFilter( nil, testset ) lu.assertEquals( #included, 8 ) lu.assertEquals( excluded, {} ) -- single exclude pattern (= select anything not matching "bar") - included, excluded = lu.LuaUnit.applyPatternFilter( nil, {'bar'}, testset ) + included, excluded = lu.LuaUnit.applyPatternFilter( {'!bar'}, testset ) lu.assertEquals( included, {testset[1], testset[3], testset[5]} ) lu.assertEquals( #excluded, 5 ) -- single include pattern - included, excluded = lu.LuaUnit.applyPatternFilter( {'t.t.'}, nil, testset ) + included, excluded = lu.LuaUnit.applyPatternFilter( {'t.t.'}, testset ) lu.assertEquals( #included, 6 ) lu.assertEquals( excluded, {testset[7], testset[8]} ) -- single include and exclude patterns - included, excluded = lu.LuaUnit.applyPatternFilter( {'foo'}, {'test'}, testset ) + included, excluded = lu.LuaUnit.applyPatternFilter( {'foo', '!test'}, testset ) lu.assertEquals( included, {testset[1], testset[3], testset[5], testset[7]} ) lu.assertEquals( #excluded, 4 ) -- multiple (specific) includes - included, excluded = lu.LuaUnit.applyPatternFilter( {'toto', 'titi'}, nil, testset ) + included, excluded = lu.LuaUnit.applyPatternFilter( {'toto', 'titi'}, testset ) lu.assertEquals( included, {testset[1], testset[2], testset[3], testset[4]} ) lu.assertEquals( #excluded, 4 ) -- multiple excludes - included, excluded = lu.LuaUnit.applyPatternFilter( nil, {'tata', '%.bar'}, testset ) + included, excluded = lu.LuaUnit.applyPatternFilter( {'!tata', '!%.bar'}, testset ) lu.assertEquals( included, {testset[1], testset[3], testset[8]} ) lu.assertEquals( #excluded, 5 ) -- combined test - included, excluded = lu.LuaUnit.applyPatternFilter( {'t[oai]', 'bar$', 'test'}, {'%.b', 'titi'}, testset ) + included, excluded = lu.LuaUnit.applyPatternFilter( {'t[oai]', 'bar$', 'test', '!%.b', '!titi'}, testset ) lu.assertEquals( included, {testset[1], testset[5], testset[8]} ) lu.assertEquals( #excluded, 5 ) + + --[[ Combining positive and negative filters ]]-- + included, excluded = lu.LuaUnit.applyPatternFilter( {'foo', 'bar', '!t.t.', '%.bar'}, testset ) + lu.assertEquals( included, {testset[2], testset[4], testset[6], testset[7], testset[8]} ) + lu.assertEquals( #excluded, 3 ) end function TestLuaUnitUtilities:test_strMatch() @@ -2638,7 +2678,7 @@ TestLuaUnitExecution = { __class__ = 'TestLuaUnitExecution' } lu.assertEquals( executedTests[7], "MyTestToto2:test1" ) lu.assertEquals( #executedTests, 7) - runner:runSuite('-x', 'Toto2', '-p', 'Toto.' ) + runner:runSuite('-p', 'Toto.', '-x', 'Toto2' ) lu.assertEquals( runner.result.testCount, 5) -- MyTestToto2 excluded end