@@ -19,6 +19,8 @@ package testing
19
19
import (
20
20
gotest "testing"
21
21
22
+ "github.com/stretchr/testify/assert"
23
+ "github.com/stretchr/testify/require"
22
24
"k8s.io/component-base/featuregate"
23
25
)
24
26
@@ -67,8 +69,11 @@ func TestSpecialGates(t *gotest.T) {
67
69
"stable_default_off_set_on" : true ,
68
70
}
69
71
expect (t , gate , before )
72
+ t .Cleanup (func () {
73
+ expect (t , gate , before )
74
+ })
70
75
71
- cleanupAlpha := SetFeatureGateDuringTest (t , gate , "AllAlpha" , true )
76
+ SetFeatureGateDuringTest (t , gate , "AllAlpha" , true )
72
77
expect (t , gate , map [featuregate.Feature ]bool {
73
78
"AllAlpha" : true ,
74
79
"AllBeta" : false ,
@@ -89,7 +94,7 @@ func TestSpecialGates(t *gotest.T) {
89
94
"stable_default_off_set_on" : true ,
90
95
})
91
96
92
- cleanupBeta := SetFeatureGateDuringTest (t , gate , "AllBeta" , true )
97
+ SetFeatureGateDuringTest (t , gate , "AllBeta" , true )
93
98
expect (t , gate , map [featuregate.Feature ]bool {
94
99
"AllAlpha" : true ,
95
100
"AllBeta" : true ,
@@ -109,11 +114,6 @@ func TestSpecialGates(t *gotest.T) {
109
114
"stable_default_off" : false ,
110
115
"stable_default_off_set_on" : true ,
111
116
})
112
-
113
- // run cleanups in reverse order like defer would
114
- cleanupBeta ()
115
- cleanupAlpha ()
116
- expect (t , gate , before )
117
117
}
118
118
119
119
func expect (t * gotest.T , gate featuregate.FeatureGate , expect map [featuregate.Feature ]bool ) {
@@ -124,3 +124,127 @@ func expect(t *gotest.T, gate featuregate.FeatureGate, expect map[featuregate.Fe
124
124
}
125
125
}
126
126
}
127
+
128
+ func TestSetFeatureGateInTest (t * gotest.T ) {
129
+ gate := featuregate .NewFeatureGate ()
130
+ err := gate .Add (map [featuregate.Feature ]featuregate.FeatureSpec {
131
+ "feature" : {PreRelease : featuregate .Alpha , Default : false },
132
+ })
133
+ require .NoError (t , err )
134
+
135
+ assert .False (t , gate .Enabled ("feature" ))
136
+ defer SetFeatureGateDuringTest (t , gate , "feature" , true )()
137
+ defer SetFeatureGateDuringTest (t , gate , "feature" , true )()
138
+
139
+ assert .True (t , gate .Enabled ("feature" ))
140
+ t .Run ("Subtest" , func (t * gotest.T ) {
141
+ assert .True (t , gate .Enabled ("feature" ))
142
+ })
143
+
144
+ t .Run ("ParallelSubtest" , func (t * gotest.T ) {
145
+ assert .True (t , gate .Enabled ("feature" ))
146
+ // Calling t.Parallel in subtest will resume the main test body
147
+ t .Parallel ()
148
+ assert .True (t , gate .Enabled ("feature" ))
149
+ })
150
+ assert .True (t , gate .Enabled ("feature" ))
151
+
152
+ t .Run ("OverwriteInSubtest" , func (t * gotest.T ) {
153
+ defer SetFeatureGateDuringTest (t , gate , "feature" , false )()
154
+ assert .False (t , gate .Enabled ("feature" ))
155
+ })
156
+ assert .True (t , gate .Enabled ("feature" ))
157
+ }
158
+
159
+ func TestDetectLeakToMainTest (t * gotest.T ) {
160
+ t .Cleanup (func () {
161
+ featureFlagOverride = map [featuregate.Feature ]string {}
162
+ })
163
+ gate := featuregate .NewFeatureGate ()
164
+ err := gate .Add (map [featuregate.Feature ]featuregate.FeatureSpec {
165
+ "feature" : {PreRelease : featuregate .Alpha , Default : false },
166
+ })
167
+ require .NoError (t , err )
168
+
169
+ // Subtest setting feature gate and calling parallel will leak it out
170
+ t .Run ("LeakingSubtest" , func (t * gotest.T ) {
171
+ fakeT := & ignoreFatalT {T : t }
172
+ defer SetFeatureGateDuringTest (fakeT , gate , "feature" , true )()
173
+ // Calling t.Parallel in subtest will resume the main test body
174
+ t .Parallel ()
175
+ // Leaked false from main test
176
+ assert .False (t , gate .Enabled ("feature" ))
177
+ })
178
+ // Leaked true from subtest
179
+ assert .True (t , gate .Enabled ("feature" ))
180
+ fakeT := & ignoreFatalT {T : t }
181
+ defer SetFeatureGateDuringTest (fakeT , gate , "feature" , false )()
182
+ assert .True (t , fakeT .fatalRecorded )
183
+ }
184
+
185
+ func TestDetectLeakToOtherSubtest (t * gotest.T ) {
186
+ t .Cleanup (func () {
187
+ featureFlagOverride = map [featuregate.Feature ]string {}
188
+ })
189
+ gate := featuregate .NewFeatureGate ()
190
+ err := gate .Add (map [featuregate.Feature ]featuregate.FeatureSpec {
191
+ "feature" : {PreRelease : featuregate .Alpha , Default : false },
192
+ })
193
+ require .NoError (t , err )
194
+
195
+ subtestName := "Subtest"
196
+ // Subtest setting feature gate and calling parallel will leak it out
197
+ t .Run (subtestName , func (t * gotest.T ) {
198
+ fakeT := & ignoreFatalT {T : t }
199
+ defer SetFeatureGateDuringTest (fakeT , gate , "feature" , true )()
200
+ t .Parallel ()
201
+ })
202
+ // Add suffix to name to prevent tests with the same prefix.
203
+ t .Run (subtestName + "Suffix" , func (t * gotest.T ) {
204
+ // Leaked true
205
+ assert .True (t , gate .Enabled ("feature" ))
206
+
207
+ fakeT := & ignoreFatalT {T : t }
208
+ defer SetFeatureGateDuringTest (fakeT , gate , "feature" , false )()
209
+ assert .True (t , fakeT .fatalRecorded )
210
+ })
211
+ }
212
+
213
+ func TestCannotDetectLeakFromSubtest (t * gotest.T ) {
214
+ t .Cleanup (func () {
215
+ featureFlagOverride = map [featuregate.Feature ]string {}
216
+ })
217
+ gate := featuregate .NewFeatureGate ()
218
+ err := gate .Add (map [featuregate.Feature ]featuregate.FeatureSpec {
219
+ "feature" : {PreRelease : featuregate .Alpha , Default : false },
220
+ })
221
+ require .NoError (t , err )
222
+
223
+ defer SetFeatureGateDuringTest (t , gate , "feature" , false )()
224
+ // Subtest setting feature gate and calling parallel will leak it out
225
+ t .Run ("Subtest" , func (t * gotest.T ) {
226
+ defer SetFeatureGateDuringTest (t , gate , "feature" , true )()
227
+ t .Parallel ()
228
+ })
229
+ // Leaked true
230
+ assert .True (t , gate .Enabled ("feature" ))
231
+ }
232
+
233
+ type ignoreFatalT struct {
234
+ * gotest.T
235
+ fatalRecorded bool
236
+ }
237
+
238
+ func (f * ignoreFatalT ) Fatal (args ... any ) {
239
+ f .T .Helper ()
240
+ f .fatalRecorded = true
241
+ newArgs := []any {"[IGNORED]" }
242
+ newArgs = append (newArgs , args ... )
243
+ f .T .Log (newArgs ... )
244
+ }
245
+
246
+ func (f * ignoreFatalT ) Fatalf (format string , args ... any ) {
247
+ f .T .Helper ()
248
+ f .fatalRecorded = true
249
+ f .T .Logf ("[IGNORED] " + format , args ... )
250
+ }
0 commit comments