diff --git a/go.mod b/go.mod index d3a5af6..9b59657 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.30.0 github.com/spf13/cobra v1.8.1 + github.com/spf13/pflag v1.0.5 + k8s.io/apimachinery v0.31.1 ) require ( @@ -14,8 +16,6 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.8.4 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect diff --git a/go.sum b/go.sum index 8f3a615..cbcfa07 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= @@ -18,8 +19,9 @@ github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20240314173009-2cd07f4ca53d h1:GDn4rF5hmB+d0tnFcPQhPy1YTooJH6U+HeYYdyjktmI= github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20240314173009-2cd07f4ca53d/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -27,8 +29,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= @@ -37,10 +39,12 @@ golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= +k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= diff --git a/pkg/cmd/cmdlist/list.go b/pkg/cmd/cmdlist/list.go index b2ade26..bb5a918 100644 --- a/pkg/cmd/cmdlist/list.go +++ b/pkg/cmd/cmdlist/list.go @@ -7,10 +7,13 @@ import ( "github.com/spf13/cobra" + "github.com/openshift-eng/openshift-tests-extension/pkg/flags" g "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo" ) func NewCommand() *cobra.Command { + envFlags := flags.NewEnvironmentFlags() + cmd := &cobra.Command{ Use: "list", Short: "List available tests", @@ -26,6 +29,7 @@ func NewCommand() *cobra.Command { return nil }, } + envFlags.BindFlags(cmd.Flags()) return cmd } diff --git a/pkg/flags/environment.go b/pkg/flags/environment.go new file mode 100644 index 0000000..41eef56 --- /dev/null +++ b/pkg/flags/environment.go @@ -0,0 +1,23 @@ +package flags + +import ( + "github.com/spf13/pflag" +) + +// EnvironmentFlags contains information for specifying multiple environments. +type EnvironmentFlags struct { + Environments []string +} + +func NewEnvironmentFlags() *EnvironmentFlags { + return &EnvironmentFlags{ + Environments: []string{}, + } +} + +func (f *EnvironmentFlags) BindFlags(fs *pflag.FlagSet) { + fs.StringSliceVar(&f.Environments, + "env", + f.Environments, + "specify one or more environments (use --env multiple times for more than one)") +} diff --git a/pkg/ginkgo/ginkgofilter/filter.go b/pkg/ginkgo/ginkgofilter/filter.go new file mode 100644 index 0000000..8b116a3 --- /dev/null +++ b/pkg/ginkgo/ginkgofilter/filter.go @@ -0,0 +1,60 @@ +package ginkgofilter + +import ( + "regexp" + "strings" + + "k8s.io/apimachinery/pkg/util/sets" + + "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo" +) + +// FilterTestCases filters test cases based on the environment flags +func FilterTestCases(testCases []*ginkgo.TestCase, envFlags sets.Set[string]) []*ginkgo.TestCase { + var filtered []*ginkgo.TestCase + +testCaseLoop: + for _, testCase := range testCases { + testHasInclude := false + annotations := extractAnnotations(testCase.Name) + + for _, a := range annotations { + la := strings.ToLower(a) + + if strings.HasPrefix(strings.ToLower(a), "skipped:") { + condition := strings.TrimPrefix(la, "skipped:") + if envFlags.Has(condition) { + continue testCaseLoop // skip this test + } + } + + if strings.HasPrefix(strings.ToLower(a), "include:") { + testHasInclude = true + condition := strings.TrimPrefix(la, "include:") + if envFlags.Has(condition) { + filtered = append(filtered, testCase) // include this test for sure + break + } + } + } + if !testHasInclude { + filtered = append(filtered, testCase) + } + } + + return filtered +} + +func extractAnnotations(testName string) []string { + // Define a regex to match all text within square brackets but exclude the brackets themselves + re := regexp.MustCompile(`\[(.*?)\]`) + matches := re.FindAllStringSubmatch(testName, -1) + + var annotations []string + for _, match := range matches { + if len(match) > 1 { + annotations = append(annotations, match[1]) + } + } + return annotations +} diff --git a/pkg/ginkgo/ginkgofilter/filter_test.go b/pkg/ginkgo/ginkgofilter/filter_test.go new file mode 100644 index 0000000..513fa5d --- /dev/null +++ b/pkg/ginkgo/ginkgofilter/filter_test.go @@ -0,0 +1,158 @@ +package ginkgofilter + +import ( + "reflect" + "strings" + "testing" + + "k8s.io/apimachinery/pkg/util/sets" + + "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo" +) + +func TestFilterTestCases(t *testing.T) { + tests := []struct { + name string + testCases []*ginkgo.TestCase + envFlags sets.Set[string] + want []*ginkgo.TestCase + }{ + { + name: "Skip AWS platform test", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:Platform:AWS]"}, + {Name: "Test2 [Include:Platform:AWS]"}, + {Name: "Test3"}, + }, + envFlags: sets.New[string]("platform:aws"), + want: []*ginkgo.TestCase{ + {Name: "Test2 [Include:Platform:AWS]"}, + {Name: "Test3"}, + }, + }, + { + name: "No match for skipped conditions", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:Platform:GCP]"}, + {Name: "Test2"}, + }, + envFlags: sets.New[string]("platform:aws"), + want: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:Platform:GCP]"}, + {Name: "Test2"}, + }, + }, + { + name: "Run only specific environment", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Include:platform:gcp]"}, + {Name: "Test2 [Include:platform:aws]"}, + {Name: "Test3"}, + }, + envFlags: sets.New[string]("platform:aws"), + want: []*ginkgo.TestCase{ + {Name: "Test2 [Include:platform:aws]"}, + {Name: "Test3"}, + }, + }, + { + name: "Skip test with no matching environment", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:topology:ha]"}, + {Name: "Test2 [platform:aws]"}, + }, + envFlags: sets.New[string]("platform:aws", "network:v6"), + want: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:topology:ha]"}, + {Name: "Test2 [platform:aws]"}, + }, + }, + { + name: "Empty test cases list", + testCases: []*ginkgo.TestCase{}, + envFlags: sets.New[string]("platform:aws"), + want: []*ginkgo.TestCase{}, + }, + { + name: "No env flags provided", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [platform:aws]"}, + {Name: "Test2 [Skipped:platform:gcp]"}, + {Name: "Test3"}, + }, + envFlags: sets.New[string](), + want: []*ginkgo.TestCase{ + {Name: "Test1 [platform:aws]"}, + {Name: "Test2 [Skipped:platform:gcp]"}, + {Name: "Test3"}, + }, + }, + { + name: "Skip condition in middle of test name", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:platform:aws] Test with AWS"}, + {Name: "Test2 [Skipped:platform:gcp]"}, + {Name: "Test3"}, + }, + envFlags: sets.New[string]("platform:aws"), + want: []*ginkgo.TestCase{ + {Name: "Test2 [Skipped:platform:gcp]"}, + {Name: "Test3"}, + }, + }, + { + name: "Case insensitive skip", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:Platform:AWS]"}, + {Name: "Test2 [platform:aws]"}, + {Name: "Test3"}, + }, + envFlags: sets.New[string]("platform:aws"), + want: []*ginkgo.TestCase{ + {Name: "Test2 [platform:aws]"}, + {Name: "Test3"}, + }, + }, + { + name: "Multiple environment conditions", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Skipped:Platform:AWS]"}, + {Name: "Test2 [Include:NetworkStack:v6]"}, + {Name: "Test3 [Skipped:topology:ha]"}, + }, + envFlags: sets.New("platform:aws", "networkstack:v6"), + want: []*ginkgo.TestCase{ + {Name: "Test2 [Include:NetworkStack:v6]"}, + {Name: "Test3 [Skipped:topology:ha]"}, + }, + }, + { + name: "Multiple run-only conditions, no matches", + testCases: []*ginkgo.TestCase{ + {Name: "Test1 [Include:platform:gcp]"}, + {Name: "Test2 [Include:networkstack:v4]"}, + }, + envFlags: sets.New[string]("platform:aws", "networkstack:v6"), + want: []*ginkgo.TestCase{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := FilterTestCases(tt.testCases, tt.envFlags); !reflect.DeepEqual(got, tt.want) { + if len(tt.want) == 0 && len(got) == 0 { + return + } + + var gotSlice, wantSlice []string + for _, g := range got { + gotSlice = append(gotSlice, g.Name) + } + for _, w := range tt.want { + wantSlice = append(wantSlice, w.Name) + } + + t.Errorf("FilterTestCases() = %v, wanted %v", strings.Join(gotSlice, ", "), strings.Join(wantSlice, ", ")) + } + }) + } +}