Skip to content

Commit c3ca966

Browse files
Merge pull request #31270 from openshift-cherrypick-robot/cherry-pick-31261-to-release-4.22
[release-4.22] OCPBUGS-87838: support Parents field on origin test suites
2 parents 2a925e8 + 7da0220 commit c3ca966

3 files changed

Lines changed: 220 additions & 16 deletions

File tree

pkg/test/ginkgo/test_suite.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ type TestSuite struct {
133133
TestTimeout time.Duration `json:"testTimeout,omitempty"`
134134

135135
// OTE
136+
Parents []string `json:"parents,omitempty"`
136137
Qualifiers []string `json:"qualifiers,omitempty"`
137138
Extension *extensions.Extension `json:"-"`
138139
}

pkg/testsuites/parents_test.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package testsuites
2+
3+
import (
4+
"testing"
5+
6+
"github.com/openshift/origin/pkg/test/ginkgo"
7+
)
8+
9+
func TestMergeParentQualifiers(t *testing.T) {
10+
t.Run("child qualifiers merge into parent", func(t *testing.T) {
11+
suites := []*ginkgo.TestSuite{
12+
{Name: "parent"},
13+
{Name: "child", Parents: []string{"parent"}, Qualifiers: []string{"q1", "q2"}},
14+
}
15+
mergeParentQualifiers(suites)
16+
assertQualifiers(t, suites, "parent", []string{"q1", "q2"})
17+
})
18+
19+
t.Run("multiple children merge into same parent", func(t *testing.T) {
20+
suites := []*ginkgo.TestSuite{
21+
{Name: "parent"},
22+
{Name: "child-a", Parents: []string{"parent"}, Qualifiers: []string{"qa"}},
23+
{Name: "child-b", Parents: []string{"parent"}, Qualifiers: []string{"qb"}},
24+
}
25+
mergeParentQualifiers(suites)
26+
assertQualifiers(t, suites, "parent", []string{"qa", "qb"})
27+
})
28+
29+
t.Run("duplicate qualifiers are not added twice", func(t *testing.T) {
30+
suites := []*ginkgo.TestSuite{
31+
{Name: "parent", Qualifiers: []string{"shared"}},
32+
{Name: "child", Parents: []string{"parent"}, Qualifiers: []string{"shared", "unique"}},
33+
}
34+
mergeParentQualifiers(suites)
35+
assertQualifiers(t, suites, "parent", []string{"shared", "unique"})
36+
})
37+
38+
t.Run("transitive parents propagate grandchild to grandparent", func(t *testing.T) {
39+
suites := []*ginkgo.TestSuite{
40+
{Name: "grandparent"},
41+
{Name: "parent", Parents: []string{"grandparent"}, Qualifiers: []string{"qp"}},
42+
{Name: "child", Parents: []string{"parent"}, Qualifiers: []string{"qc"}},
43+
}
44+
mergeParentQualifiers(suites)
45+
assertQualifiers(t, suites, "parent", []string{"qp", "qc"})
46+
assertQualifiers(t, suites, "grandparent", []string{"qp", "qc"})
47+
})
48+
49+
t.Run("transitive parents work regardless of slice order", func(t *testing.T) {
50+
suites := []*ginkgo.TestSuite{
51+
{Name: "child", Parents: []string{"parent"}, Qualifiers: []string{"qc"}},
52+
{Name: "grandparent"},
53+
{Name: "parent", Parents: []string{"grandparent"}, Qualifiers: []string{"qp"}},
54+
}
55+
mergeParentQualifiers(suites)
56+
assertQualifiers(t, suites, "parent", []string{"qp", "qc"})
57+
assertQualifiers(t, suites, "grandparent", []string{"qp", "qc"})
58+
})
59+
60+
t.Run("four levels deep propagates to root", func(t *testing.T) {
61+
suites := []*ginkgo.TestSuite{
62+
{Name: "root"},
63+
{Name: "l1", Parents: []string{"root"}, Qualifiers: []string{"q1"}},
64+
{Name: "l2", Parents: []string{"l1"}, Qualifiers: []string{"q2"}},
65+
{Name: "l3", Parents: []string{"l2"}, Qualifiers: []string{"q3"}},
66+
}
67+
mergeParentQualifiers(suites)
68+
assertQualifiers(t, suites, "l2", []string{"q2", "q3"})
69+
assertQualifiers(t, suites, "l1", []string{"q1", "q2", "q3"})
70+
assertQualifiers(t, suites, "root", []string{"q1", "q2", "q3"})
71+
})
72+
73+
t.Run("child with multiple parents merges into all", func(t *testing.T) {
74+
suites := []*ginkgo.TestSuite{
75+
{Name: "parent-a"},
76+
{Name: "parent-b"},
77+
{Name: "child", Parents: []string{"parent-a", "parent-b"}, Qualifiers: []string{"qc"}},
78+
}
79+
mergeParentQualifiers(suites)
80+
assertQualifiers(t, suites, "parent-a", []string{"qc"})
81+
assertQualifiers(t, suites, "parent-b", []string{"qc"})
82+
})
83+
84+
t.Run("child qualifiers are unchanged", func(t *testing.T) {
85+
suites := []*ginkgo.TestSuite{
86+
{Name: "parent"},
87+
{Name: "child", Parents: []string{"parent"}, Qualifiers: []string{"qc"}},
88+
}
89+
mergeParentQualifiers(suites)
90+
assertQualifiers(t, suites, "child", []string{"qc"})
91+
})
92+
93+
t.Run("suite with no parents is unaffected", func(t *testing.T) {
94+
suites := []*ginkgo.TestSuite{
95+
{Name: "standalone", Qualifiers: []string{"qs"}},
96+
{Name: "other", Qualifiers: []string{"qo"}},
97+
}
98+
mergeParentQualifiers(suites)
99+
assertQualifiers(t, suites, "standalone", []string{"qs"})
100+
assertQualifiers(t, suites, "other", []string{"qo"})
101+
})
102+
103+
t.Run("parent with no children keeps its own qualifiers", func(t *testing.T) {
104+
suites := []*ginkgo.TestSuite{
105+
{Name: "parent", Qualifiers: []string{"qp"}},
106+
}
107+
mergeParentQualifiers(suites)
108+
assertQualifiers(t, suites, "parent", []string{"qp"})
109+
})
110+
}
111+
112+
func TestConformanceInheritsFromChildren(t *testing.T) {
113+
suites := InternalTestSuites()
114+
115+
var conformance, parallel, serial *ginkgo.TestSuite
116+
for _, s := range suites {
117+
switch s.Name {
118+
case "openshift/conformance":
119+
conformance = s
120+
case "openshift/conformance/parallel":
121+
parallel = s
122+
case "openshift/conformance/serial":
123+
serial = s
124+
}
125+
}
126+
127+
if conformance == nil || parallel == nil || serial == nil {
128+
t.Fatal("expected to find conformance, parallel, and serial suites")
129+
}
130+
131+
if len(parallel.Qualifiers) == 0 {
132+
t.Fatal("parallel suite has no qualifiers")
133+
}
134+
if len(serial.Qualifiers) == 0 {
135+
t.Fatal("serial suite has no qualifiers")
136+
}
137+
138+
qualifierSet := make(map[string]bool, len(conformance.Qualifiers))
139+
for _, q := range conformance.Qualifiers {
140+
qualifierSet[q] = true
141+
}
142+
143+
for _, q := range parallel.Qualifiers {
144+
if !qualifierSet[q] {
145+
t.Errorf("conformance suite missing qualifier from parallel: %s", q)
146+
}
147+
}
148+
for _, q := range serial.Qualifiers {
149+
if !qualifierSet[q] {
150+
t.Errorf("conformance suite missing qualifier from serial: %s", q)
151+
}
152+
}
153+
}
154+
155+
func findSuite(suites []*ginkgo.TestSuite, name string) *ginkgo.TestSuite {
156+
for _, s := range suites {
157+
if s.Name == name {
158+
return s
159+
}
160+
}
161+
return nil
162+
}
163+
164+
func assertQualifiers(t *testing.T, suites []*ginkgo.TestSuite, suiteName string, expected []string) {
165+
t.Helper()
166+
suite := findSuite(suites, suiteName)
167+
if suite == nil {
168+
t.Fatalf("suite %q not found", suiteName)
169+
}
170+
if len(suite.Qualifiers) != len(expected) {
171+
t.Errorf("suite %q: expected %d qualifiers %v, got %d: %v",
172+
suiteName, len(expected), expected, len(suite.Qualifiers), suite.Qualifiers)
173+
return
174+
}
175+
for i, q := range expected {
176+
if suite.Qualifiers[i] != q {
177+
t.Errorf("suite %q qualifier[%d]: expected %q, got %q",
178+
suiteName, i, q, suite.Qualifiers[i])
179+
}
180+
}
181+
}

pkg/testsuites/standard_suites.go

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func InternalTestSuites() []*ginkgo.TestSuite {
2828
curr := staticSuites[i]
2929
copied = append(copied, &curr)
3030
}
31+
mergeParentQualifiers(copied)
3132
return copied
3233
}
3334

@@ -88,6 +89,7 @@ func AllTestSuites(ctx context.Context) ([]*ginkgo.TestSuite, error) {
8889
Kind: ginkgo.KindExternal,
8990
Count: s.Count,
9091
Extension: e,
92+
Parents: s.Parents,
9193
Parallelism: s.Parallelism,
9294
Qualifiers: s.Qualifiers,
9395
TestTimeout: timeout,
@@ -96,19 +98,9 @@ func AllTestSuites(ctx context.Context) ([]*ginkgo.TestSuite, error) {
9698
}
9799
}
98100

99-
// Now handle setting qualifiers for parent suites once we've assembled the complete
100-
// list of suites.
101-
for _, e := range extensionInfos {
102-
for _, s := range e.Suites {
103-
for _, p := range s.Parents {
104-
for _, parent := range suites {
105-
if parent.Name == p {
106-
parent.Qualifiers = append(parent.Qualifiers, s.Qualifiers...)
107-
}
108-
}
109-
}
110-
}
111-
}
101+
// Merge qualifiers from child suites into their declared parents. The fixed-point
102+
// loop handles arbitrary depth (e.g. extension → parallel → conformance).
103+
mergeParentQualifiers(suites)
112104

113105
return suites, nil
114106
}
@@ -120,16 +112,14 @@ var staticSuites = []ginkgo.TestSuite{
120112
Description: templates.LongDesc(`
121113
Tests that ensure an OpenShift cluster and components are working properly.
122114
`),
123-
Qualifiers: []string{
124-
withExcludedTestsFilter("name.contains('[Suite:openshift/conformance/')"),
125-
},
126115
Parallelism: 30,
127116
},
128117
{
129118
Name: "openshift/conformance/parallel",
130119
Description: templates.LongDesc(`
131120
Only the portion of the openshift/conformance test suite that run in parallel.
132121
`),
122+
Parents: []string{"openshift/conformance"},
133123
Qualifiers: []string{
134124
withExcludedTestsFilter("name.contains('[Suite:openshift/conformance/parallel')"),
135125
},
@@ -141,6 +131,7 @@ var staticSuites = []ginkgo.TestSuite{
141131
Description: templates.LongDesc(`
142132
Only the portion of the openshift/conformance test suite that run serially.
143133
`),
134+
Parents: []string{"openshift/conformance"},
144135
Qualifiers: []string{
145136
// Standard early and late tests are included in the serial suite
146137
withExcludedTestsFilter(withStandardEarlyOrLateTests("name.contains('[Suite:openshift/conformance/serial')")),
@@ -511,6 +502,37 @@ var staticSuites = []ginkgo.TestSuite{
511502
},
512503
}
513504

505+
// mergeParentQualifiers appends each suite's qualifiers to its declared parent
506+
// suites, deduplicating so the same qualifier isn't added twice. It repeats
507+
// until no new qualifiers are added, so transitive parent chains (grandchildren)
508+
// propagate fully regardless of slice order.
509+
func mergeParentQualifiers(suites []*ginkgo.TestSuite) {
510+
for {
511+
changed := false
512+
for _, child := range suites {
513+
for _, parentName := range child.Parents {
514+
for _, parent := range suites {
515+
if parent.Name == parentName {
516+
existing := make(map[string]bool, len(parent.Qualifiers))
517+
for _, q := range parent.Qualifiers {
518+
existing[q] = true
519+
}
520+
for _, q := range child.Qualifiers {
521+
if !existing[q] {
522+
parent.Qualifiers = append(parent.Qualifiers, q)
523+
changed = true
524+
}
525+
}
526+
}
527+
}
528+
}
529+
}
530+
if !changed {
531+
break
532+
}
533+
}
534+
}
535+
514536
func withExcludedTestsFilter(baseExpr string) string {
515537
filter := ""
516538
for i, s := range extensions.ExcludedTests {

0 commit comments

Comments
 (0)