1 | // Copyright 2022 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | // This file contains tests that the loopclosure analyzer detects leaked |
6 | // references via parallel subtests. |
7 | |
8 | package subtests |
9 | |
10 | import ( |
11 | "testing" |
12 | ) |
13 | |
14 | // T is used to test that loopclosure only matches T.Run when T is from the |
15 | // testing package. |
16 | type T struct{} |
17 | |
18 | // Run should not match testing.T.Run. Note that the second argument is |
19 | // intentionally a *testing.T, not a *T, so that we can check both |
20 | // testing.T.Parallel inside a T.Run, and a T.Parallel inside a testing.T.Run. |
21 | func (t *T) Run(string, func(*testing.T)) { |
22 | } |
23 | |
24 | func (t *T) Parallel() {} |
25 | |
26 | func _(t *testing.T) { |
27 | for i, test := range []int{1, 2, 3} { |
28 | // Check that parallel subtests are identified. |
29 | t.Run("", func(t *testing.T) { |
30 | t.Parallel() |
31 | println(i) // want "loop variable i captured by func literal" |
32 | println(test) // want "loop variable test captured by func literal" |
33 | }) |
34 | |
35 | // Check that serial tests are OK. |
36 | t.Run("", func(t *testing.T) { |
37 | println(i) |
38 | println(test) |
39 | }) |
40 | |
41 | // Check that the location of t.Parallel matters. |
42 | t.Run("", func(t *testing.T) { |
43 | println(i) |
44 | println(test) |
45 | t.Parallel() |
46 | println(i) // want "loop variable i captured by func literal" |
47 | println(test) // want "loop variable test captured by func literal" |
48 | }) |
49 | |
50 | // Check that *testing.T value matters. |
51 | t.Run("", func(t *testing.T) { |
52 | var x testing.T |
53 | x.Parallel() |
54 | println(i) |
55 | println(test) |
56 | }) |
57 | |
58 | // Check that shadowing the loop variables within the test literal is OK if |
59 | // it occurs before t.Parallel(). |
60 | t.Run("", func(t *testing.T) { |
61 | i := i |
62 | test := test |
63 | t.Parallel() |
64 | println(i) |
65 | println(test) |
66 | }) |
67 | |
68 | // Check that shadowing the loop variables within the test literal is Not |
69 | // OK if it occurs after t.Parallel(). |
70 | t.Run("", func(t *testing.T) { |
71 | t.Parallel() |
72 | i := i // want "loop variable i captured by func literal" |
73 | test := test // want "loop variable test captured by func literal" |
74 | println(i) // OK |
75 | println(test) // OK |
76 | }) |
77 | |
78 | // Check uses in nested blocks. |
79 | t.Run("", func(t *testing.T) { |
80 | t.Parallel() |
81 | { |
82 | println(i) // want "loop variable i captured by func literal" |
83 | println(test) // want "loop variable test captured by func literal" |
84 | } |
85 | }) |
86 | |
87 | // Check that we catch uses in nested subtests. |
88 | t.Run("", func(t *testing.T) { |
89 | t.Parallel() |
90 | t.Run("", func(t *testing.T) { |
91 | println(i) // want "loop variable i captured by func literal" |
92 | println(test) // want "loop variable test captured by func literal" |
93 | }) |
94 | }) |
95 | |
96 | // Check that there is no diagnostic if t is not a *testing.T. |
97 | t.Run("", func(_ *testing.T) { |
98 | t := &T{} |
99 | t.Parallel() |
100 | println(i) |
101 | println(test) |
102 | }) |
103 | |
104 | // Check that there is no diagnostic when a jump to a label may have caused |
105 | // the call to t.Parallel to have been skipped. |
106 | t.Run("", func(t *testing.T) { |
107 | if true { |
108 | goto Test |
109 | } |
110 | t.Parallel() |
111 | Test: |
112 | println(i) |
113 | println(test) |
114 | }) |
115 | |
116 | // Check that there is no diagnostic when a jump to a label may have caused |
117 | // the loop variable reference to be skipped, but there is a diagnostic |
118 | // when both the call to t.Parallel and the loop variable reference occur |
119 | // after the final label in the block. |
120 | t.Run("", func(t *testing.T) { |
121 | if true { |
122 | goto Test |
123 | } |
124 | t.Parallel() |
125 | println(i) // maybe OK |
126 | Test: |
127 | t.Parallel() |
128 | println(test) // want "loop variable test captured by func literal" |
129 | }) |
130 | |
131 | // Check that multiple labels are handled. |
132 | t.Run("", func(t *testing.T) { |
133 | if true { |
134 | goto Test1 |
135 | } else { |
136 | goto Test2 |
137 | } |
138 | Test1: |
139 | Test2: |
140 | t.Parallel() |
141 | println(test) // want "loop variable test captured by func literal" |
142 | }) |
143 | } |
144 | } |
145 | |
146 | // Check that there is no diagnostic when loop variables are shadowed within |
147 | // the loop body. |
148 | func _(t *testing.T) { |
149 | for i, test := range []int{1, 2, 3} { |
150 | i := i |
151 | test := test |
152 | t.Run("", func(t *testing.T) { |
153 | t.Parallel() |
154 | println(i) |
155 | println(test) |
156 | }) |
157 | } |
158 | } |
159 | |
160 | // Check that t.Run must be *testing.T.Run. |
161 | func _(t *T) { |
162 | for i, test := range []int{1, 2, 3} { |
163 | t.Run("", func(t *testing.T) { |
164 | t.Parallel() |
165 | println(i) |
166 | println(test) |
167 | }) |
168 | } |
169 | } |
170 | |
171 | // Check that the top-level must be parallel in order to cause a diagnostic. |
172 | // |
173 | // From https://pkg.go.dev/testing: |
174 | // |
175 | // "Run does not return until parallel subtests have completed, providing a |
176 | // way to clean up after a group of parallel tests" |
177 | func _(t *testing.T) { |
178 | for _, test := range []int{1, 2, 3} { |
179 | // In this subtest, a/b must complete before the synchronous subtest "a" |
180 | // completes, so the reference to test does not escape the current loop |
181 | // iteration. |
182 | t.Run("a", func(s *testing.T) { |
183 | s.Run("b", func(u *testing.T) { |
184 | u.Parallel() |
185 | println(test) |
186 | }) |
187 | }) |
188 | |
189 | // In this subtest, c executes concurrently, so the reference to test may |
190 | // escape the current loop iteration. |
191 | t.Run("c", func(s *testing.T) { |
192 | s.Parallel() |
193 | s.Run("d", func(u *testing.T) { |
194 | println(test) // want "loop variable test captured by func literal" |
195 | }) |
196 | }) |
197 | } |
198 | } |
199 |
Members