Skip to content

Commit f19698d

Browse files
authored
feat: make inclusion to the test case list based on matching tags (#317)
Use tags to narrow down test cases to those labeled with strings matching the regex passed as a config parameter.
1 parent c33cb00 commit f19698d

File tree

11 files changed

+114
-9
lines changed

11 files changed

+114
-9
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ Flags:
133133
-r, --rate-limit duration Limit the request rate to the server to 1 request per specified duration. 0 is the default, and disables rate limiting.
134134
--read-timeout duration timeout for receiving responses during test execution (default 10s)
135135
--show-failures-only shows only the results of failed tests
136+
-T, --include-tags string include tests tagged with labels matching this Go regular expression (e.g. to include all tests being tagged with "cookie", use "^cookie$").
136137
-t, --time show time spent per test
137138
--wait-delay duration Time to wait between retries for all wait operations. (default 1s)
138139
--wait-for-connection-timeout duration Http connection timeout, The timeout includes connection time, any redirects, and reading the response body. (default 3s)

cmd/run.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func NewRunCommand() *cobra.Command {
3333

3434
runCmd.Flags().StringP("exclude", "e", "", "exclude tests matching this Go regular expression (e.g. to exclude all tests beginning with \"91\", use \"^91.*\"). \nIf you want more permanent exclusion, check the 'exclude' option in the config file.")
3535
runCmd.Flags().StringP("include", "i", "", "include only tests matching this Go regular expression (e.g. to include only tests beginning with \"91\", use \"^91.*\"). \\nIf you want more permanent inclusion, check the 'include' option in the config file.\"")
36+
runCmd.Flags().StringP("include-tags", "T", "", "include tests tagged with labels matching this Go regular expression (e.g. to include all tests being tagged with \"cookie\", use \"^cookie$\").")
3637
runCmd.Flags().StringP("dir", "d", ".", "recursively find yaml tests in this directory")
3738
runCmd.Flags().StringP("output", "o", "normal", "output type for ftw tests. \"normal\" is the default.")
3839
runCmd.Flags().StringP("file", "f", "", "output file path for ftw tests. Prints to standard output by default.")
@@ -65,6 +66,7 @@ func runE(cmd *cobra.Command, _ []string) error {
6566
cmd.SilenceUsage = true
6667
exclude, _ := cmd.Flags().GetString("exclude")
6768
include, _ := cmd.Flags().GetString("include")
69+
includeTags, _ := cmd.Flags().GetString("include-tags")
6870
dir, _ := cmd.Flags().GetString("dir")
6971
outputFilename, _ := cmd.Flags().GetString("file")
7072
logFilePath, _ := cmd.Flags().GetString("log-file")
@@ -113,11 +115,21 @@ func runE(cmd *cobra.Command, _ []string) error {
113115

114116
var includeRE *regexp.Regexp
115117
if include != "" {
116-
includeRE = regexp.MustCompile(include)
118+
if includeRE, err = regexp.Compile(include); err != nil {
119+
return fmt.Errorf("invalid --include regular expression: %w", err)
120+
}
117121
}
118122
var excludeRE *regexp.Regexp
119123
if exclude != "" {
120-
excludeRE = regexp.MustCompile(exclude)
124+
if excludeRE, err = regexp.Compile(exclude); err != nil {
125+
return fmt.Errorf("invalid --exclude regular expression: %w", err)
126+
}
127+
}
128+
var includeTagsRE *regexp.Regexp
129+
if includeTags != "" {
130+
if includeTagsRE, err = regexp.Compile(includeTags); err != nil {
131+
return fmt.Errorf("invalid --include-tags regular expression: %w", err)
132+
}
121133
}
122134

123135
// Add wait4x checkers
@@ -167,6 +179,7 @@ func runE(cmd *cobra.Command, _ []string) error {
167179
currentRun, err := runner.Run(cfg, tests, runner.RunnerConfig{
168180
Include: includeRE,
169181
Exclude: excludeRE,
182+
IncludeTags: includeTagsRE,
170183
ShowTime: showTime,
171184
ShowOnlyFailed: showOnlyFailed,
172185
ConnectTimeout: connectTimeout,

config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func NewDefaultConfig() *FTWConfiguration {
2828
MaxMarkerLogLines: DefaultMaxMarkerLogLines,
2929
IncludeTests: make(map[*FTWRegexp]string),
3030
ExcludeTests: make(map[*FTWRegexp]string),
31+
IncludeTags: make(map[*FTWRegexp]string),
3132
}
3233
return cfg
3334
}

config/config_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ include:
2020
'^9.*': 'Include all tests starting with 9'
2121
exclude:
2222
'^920400-2$': 'Exclude this test'
23+
include_tags:
24+
'^cookie$': 'Run test tagged with this label'
2325
testoverride:
2426
input:
2527
dest_addr: 'httpbingo.org'

config/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ type FTWConfiguration struct {
4848
IncludeTests map[*FTWRegexp]string `koanf:"include"`
4949
// ExcludeTests is a list of tests to exclude (same as --exclude)
5050
ExcludeTests map[*FTWRegexp]string `koanf:"exclude"`
51+
// IncludeTags is a list of tags matching tests to run (same as --tag)
52+
IncludeTags map[*FTWRegexp]string `koanf:"include_tags"`
5153
}
5254

5355
type PlatformOverrides struct {

runner/run.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package runner
66
import (
77
"errors"
88
"fmt"
9-
"regexp"
109
"time"
1110

1211
schema "github.com/coreruleset/ftw-tests-schema/v2/types"
@@ -52,6 +51,7 @@ func Run(cfg *config.FTWConfiguration, tests []*test.FTWTest, c RunnerConfig, ou
5251
RunnerConfig: &c,
5352
Include: c.Include,
5453
Exclude: c.Exclude,
54+
IncludeTags: c.IncludeTags,
5555
ShowTime: c.ShowTime,
5656
Output: out,
5757
ShowOnlyFailed: c.ShowOnlyFailed,
@@ -84,7 +84,7 @@ func RunTest(runContext *TestRunContext, ftwTest *test.FTWTest) error {
8484

8585
for _, testCase := range ftwTest.Tests {
8686
// if we received a particular test ID, skip until we find it
87-
if needToSkipTest(runContext.Include, runContext.Exclude, &testCase) {
87+
if needToSkipTest(runContext, &testCase) {
8888
runContext.Stats.addResultToStats(Skipped, &testCase)
8989
continue
9090
}
@@ -257,7 +257,11 @@ func markAndFlush(runContext *TestRunContext, dest *ftwhttp.Destination, stageID
257257
return nil, fmt.Errorf("can't find log marker. Am I reading the correct log? Log file: %s", runContext.Config.LogFile)
258258
}
259259

260-
func needToSkipTest(include *regexp.Regexp, exclude *regexp.Regexp, testCase *schema.Test) bool {
260+
func needToSkipTest(runContext *TestRunContext, testCase *schema.Test) bool {
261+
include := runContext.Include
262+
exclude := runContext.Exclude
263+
includeTags := runContext.IncludeTags
264+
261265
// never skip enabled explicit inclusions
262266
if include != nil {
263267
if include.MatchString(testCase.IdString()) {
@@ -266,24 +270,31 @@ func needToSkipTest(include *regexp.Regexp, exclude *regexp.Regexp, testCase *sc
266270
}
267271
}
268272

269-
result := false
273+
// if the test's tags do not match the passed ones
274+
// it needs to be skipped
275+
if includeTags != nil {
276+
if !utils.MatchSlice(includeTags, testCase.Tags) {
277+
return true
278+
}
279+
}
280+
270281
// if we need to exclude tests, and the ID matches,
271282
// it needs to be skipped
272283
if exclude != nil {
273284
if exclude.MatchString(testCase.IdString()) {
274-
result = true
285+
return true
275286
}
276287
}
277288

278289
// if we need to include tests, but the ID does not match
279290
// it needs to be skipped
280291
if include != nil {
281292
if !include.MatchString(testCase.IdString()) {
282-
result = true
293+
return true
283294
}
284295
}
285296

286-
return result
297+
return false
287298
}
288299

289300
func checkTestSanity(stage *schema.Stage) error {

runner/run_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,24 @@ func (s *runTestSuite) TestRunTests_Run() {
313313
s.Equal(res.Stats.TotalFailed(), 0, "failed to exclude test")
314314
})
315315

316+
s.Run("count tests tagged with `tag-10`", func() {
317+
res, err := Run(s.cfg, s.ftwTests, RunnerConfig{
318+
IncludeTags: regexp.MustCompile("^tag-10$"),
319+
}, s.out)
320+
s.Require().NoError(err)
321+
s.Len(res.Stats.Success, 1, "failed to incorporate tagged test")
322+
s.Equal(res.Stats.TotalFailed(), 0, "failed to incorporate tagged test")
323+
})
324+
325+
s.Run("count tests tagged with `tag-8` and `tag-10`", func() {
326+
res, err := Run(s.cfg, s.ftwTests, RunnerConfig{
327+
IncludeTags: regexp.MustCompile("^tag-8$|^tag-10$"),
328+
}, s.out)
329+
s.Require().NoError(err)
330+
s.Len(res.Stats.Success, 2, "failed to incorporate tagged test")
331+
s.Equal(res.Stats.TotalFailed(), 0, "failed to incorporate tagged test")
332+
})
333+
316334
s.Run("test exceptions 1", func() {
317335
res, err := Run(s.cfg, s.ftwTests, RunnerConfig{
318336
Include: regexp.MustCompile("-1.*"),

runner/testdata/TestRunTests_Run.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ tests:
1717
expect_error: False
1818
status: 200
1919
- test_id: 8
20+
tags:
21+
- tag-8
22+
- local
2023
description: "this test is number 8"
2124
stages:
2225
- input:
@@ -29,6 +32,9 @@ tests:
2932
output:
3033
status: 200
3134
- test_id: 10
35+
tags:
36+
- tag-10
37+
- other
3238
stages:
3339
- input:
3440
dest_addr: "{{ .TestAddr }}"

runner/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ type RunnerConfig struct {
1919
Include *regexp.Regexp
2020
// Exclude is a regular expression to filter tests to exclude. If nil, no tests are excluded.
2121
Exclude *regexp.Regexp
22+
// IncludeTags is a regular expression to filter tests to count the ones tagged with the mathing label. If nil, no impact on test runner.
23+
IncludeTags *regexp.Regexp
2224
// ShowTime determines whether to show the time taken to run each test.
2325
ShowTime bool
2426
// ShowOnlyFailed will only output information related to failed tests
@@ -43,6 +45,7 @@ type TestRunContext struct {
4345
RunnerConfig *RunnerConfig
4446
Include *regexp.Regexp
4547
Exclude *regexp.Regexp
48+
IncludeTags *regexp.Regexp
4649
ShowTime bool
4750
ShowOnlyFailed bool
4851
Output *output.Output

utils/slice.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2023 OWASP ModSecurity Core Rule Set Project
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package utils
5+
6+
import (
7+
"regexp"
8+
)
9+
10+
func MatchSlice(regex *regexp.Regexp, hayStack []string) bool {
11+
for _, str := range hayStack {
12+
if regex.MatchString(str) {
13+
return true
14+
}
15+
}
16+
17+
return false
18+
}

0 commit comments

Comments
 (0)