1 # This test demonstrates a simple case in which 'go mod tidy' may resolve a
2 # missing package, only to remove that package when resolving its dependencies.
3 #
4 # If we naively iterate 'go mod tidy' until the dependency graph converges, this
5 # scenario may fail to converge.
6
7 # The import graph used in this test looks like:
8 #
9 # m --- w
10 # |
11 # + --- x
12 # |
13 # + --- y
14 # |
15 # + --- z
16 #
17 # The module dependency graph of m initially contains w.1 (and, by extension,
18 # y.2-pre and z.2-pre). This is an arbitrary point in the cycle of possible
19 # configurations.
20 #
21 # w.1 requires y.2-pre and z.2-pre
22 # x.1 requires z.2-pre and w.2-pre
23 # y.1 requires w.2-pre and x.2-pre
24 # z.1 requires x.2-pre and y.2-pre
25 #
26 # At each point, exactly one missing package can be resolved by adding a
27 # dependency on the .1 release of the module that provides that package.
28 # However, adding that dependency causes the module providing another package to
29 # roll over from its .1 release to its .2-pre release, which removes the
30 # package. Once the package is removed, 'go mod tidy -e' no longer sees the
31 # module as relevant to the main module, and will happily remove the existing
32 # dependency on it.
33 #
34 # The cycle is of length 4 so that at every step only one package can be
35 # resolved. This is important because it prevents the iteration from ever
36 # reaching a state in which every package is simultaneously over-upgraded — such
37 # a state is stable and does not exhibit failure to converge.
38
39 cp go.mod go.mod.orig
40
41 # 'go mod tidy' without -e should fail without modifying go.mod,
42 # because it cannot resolve x, y, and z simultaneously.
43 ! go mod tidy
44
45 cmp go.mod go.mod.orig
46
47 stderr '^go: finding module for package example\.net/w$'
48 stderr '^go: finding module for package example\.net/x$'
49 stderr -count=2 '^go: finding module for package example\.net/y$'
50 stderr -count=2 '^go: finding module for package example\.net/z$'
51 stderr '^go: found example\.net/x in example\.net/x v0.1.0$'
52
53 # TODO: These error messages should be clearer — it doesn't indicate why v0.2.0-pre is required.
54 stderr '^example\.net/m imports\n\texample\.net/w: package example\.net/w provided by example\.net/w at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
55 stderr '^example\.net/m imports\n\texample\.net/y: package example\.net/y provided by example\.net/y at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
56 stderr '^example\.net/m imports\n\texample\.net/z: package example\.net/z provided by example\.net/z at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
57
58
59 # 'go mod tidy -e' should preserve all of the upgrades to modules that could
60 # provide the missing packages but don't. That would at least explain why they
61 # are missing, and why no individual module can be upgraded in order to satisfy
62 # a missing import.
63 #
64 # TODO(bcmills): Today, it doesn't preserve those upgrades, and instead advances
65 # the state by one through the cycle of semi-tidy states.
66
67 go mod tidy -e
68
69 cmp go.mod go.mod.tidye1
70
71 stderr '^go: finding module for package example\.net/w$'
72 stderr '^go: finding module for package example\.net/x$'
73 stderr -count=2 '^go: finding module for package example\.net/y$'
74 stderr -count=2 '^go: finding module for package example\.net/z$'
75 stderr '^go: found example\.net/x in example\.net/x v0.1.0$'
76
77 stderr '^example\.net/m imports\n\texample\.net/w: package example\.net/w provided by example\.net/w at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
78 stderr '^example\.net/m imports\n\texample\.net/y: package example\.net/y provided by example\.net/y at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
79 stderr '^example\.net/m imports\n\texample\.net/z: package example\.net/z provided by example\.net/z at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
80
81
82 go mod tidy -e
83 cmp go.mod go.mod.tidye2
84
85 go mod tidy -e
86 cmp go.mod go.mod.tidye3
87
88 go mod tidy -e
89 cmp go.mod go.mod.orig
90
91
92 # If we upgrade away all of the packages simultaneously, the resulting tidy
93 # state converges at "no dependencies", because simultaneously adding all of the
94 # packages simultaneously over-upgrades all of the dependencies, and 'go mod
95 # tidy' treats "no package can be added" as a terminal state.
96
97 go get example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre
98 go mod tidy -e
99 cmp go.mod go.mod.postget
100 go mod tidy -e
101 cmp go.mod go.mod.postget
102
103
104 # The 'tidy' logic for a lazy main module requires more iterations to converge,
105 # because it is willing to drop dependencies on non-root modules that do not
106 # otherwise provide imported packages.
107 #
108 # On the first iteration, it adds x.1 as a root, which upgrades z and w,
109 # dropping w.1's requirement on y. w.1 was initially a root, so the upgraded
110 # w.2-pre is retained as a root.
111 #
112 # On the second iteration, it adds y.1 as a root, which upgrades w and x,
113 # dropping x.1's requirement on z. x.1 was added as a root in the previous step,
114 # so the upgraded x.2-pre is retained as a root.
115 #
116 # On the third iteration, it adds z.1 as a root, which upgrades x and y.
117 # x and y were already roots (from the previous steps), so their upgraded versions
118 # are retained (not dropped) and the iteration stops.
119 #
120 # At that point, we have z.1 as a root providing package z,
121 # and w, x, and y have all been upgraded to no longer provide any packages.
122 # So only z is retained as a new root.
123 #
124 # (From the above, we can see that in a lazy module we still cycle through the
125 # same possible root states, but in a different order from the eager case.)
126 #
127 # TODO(bcmills): if we retained the upgrades on w, x, and y (since they are
128 # lexical prefixes for unresolved packages w, x, and y, respectively), then 'go
129 # mod tidy -e' itself would become stable and no longer cycle through states.
130
131 cp go.mod.orig go.mod
132 go mod edit -go=1.17 go.mod
133 cp go.mod go.mod.117
134 go mod edit -go=1.17 go.mod.tidye1
135 go mod edit -go=1.17 go.mod.tidye2
136 go mod edit -go=1.17 go.mod.tidye3
137 go mod edit -go=1.17 go.mod.postget
138
139 go list -m all
140
141 go mod tidy -e
142 cmp go.mod go.mod.tidye3
143
144 go mod tidy -e
145 cmp go.mod go.mod.tidye2
146
147 go mod tidy -e
148 cmp go.mod go.mod.tidye1
149
150 go mod tidy -e
151 cmp go.mod go.mod.117
152
153
154 # As in the eager case, for the lazy module the fully-upgraded dependency graph
155 # becomes empty, and the empty graph is stable.
156
157 go get example.net/w@v0.2.0-pre example.net/x@v0.2.0-pre example.net/y@v0.2.0-pre example.net/z@v0.2.0-pre
158 go mod tidy -e
159 cmp go.mod go.mod.postget
160 go mod tidy -e
161 cmp go.mod go.mod.postget
162
163
164 -- m.go --
165 package m
166
167 import (
168 _ "example.net/w"
169 _ "example.net/x"
170 _ "example.net/y"
171 _ "example.net/z"
172 )
173
174 -- go.mod --
175 module example.net/m
176
177 go 1.16
178
179 replace (
180 example.net/w v0.1.0 => ./w1
181 example.net/w v0.2.0-pre => ./w2-pre
182 example.net/x v0.1.0 => ./x1
183 example.net/x v0.2.0-pre => ./x2-pre
184 example.net/y v0.1.0 => ./y1
185 example.net/y v0.2.0-pre => ./y2-pre
186 example.net/z v0.1.0 => ./z1
187 example.net/z v0.2.0-pre => ./z2-pre
188 )
189
190 require example.net/w v0.1.0
191 -- go.mod.tidye1 --
192 module example.net/m
193
194 go 1.16
195
196 replace (
197 example.net/w v0.1.0 => ./w1
198 example.net/w v0.2.0-pre => ./w2-pre
199 example.net/x v0.1.0 => ./x1
200 example.net/x v0.2.0-pre => ./x2-pre
201 example.net/y v0.1.0 => ./y1
202 example.net/y v0.2.0-pre => ./y2-pre
203 example.net/z v0.1.0 => ./z1
204 example.net/z v0.2.0-pre => ./z2-pre
205 )
206
207 require example.net/x v0.1.0
208 -- go.mod.tidye2 --
209 module example.net/m
210
211 go 1.16
212
213 replace (
214 example.net/w v0.1.0 => ./w1
215 example.net/w v0.2.0-pre => ./w2-pre
216 example.net/x v0.1.0 => ./x1
217 example.net/x v0.2.0-pre => ./x2-pre
218 example.net/y v0.1.0 => ./y1
219 example.net/y v0.2.0-pre => ./y2-pre
220 example.net/z v0.1.0 => ./z1
221 example.net/z v0.2.0-pre => ./z2-pre
222 )
223
224 require example.net/y v0.1.0
225 -- go.mod.tidye3 --
226 module example.net/m
227
228 go 1.16
229
230 replace (
231 example.net/w v0.1.0 => ./w1
232 example.net/w v0.2.0-pre => ./w2-pre
233 example.net/x v0.1.0 => ./x1
234 example.net/x v0.2.0-pre => ./x2-pre
235 example.net/y v0.1.0 => ./y1
236 example.net/y v0.2.0-pre => ./y2-pre
237 example.net/z v0.1.0 => ./z1
238 example.net/z v0.2.0-pre => ./z2-pre
239 )
240
241 require example.net/z v0.1.0
242 -- go.mod.postget --
243 module example.net/m
244
245 go 1.16
246
247 replace (
248 example.net/w v0.1.0 => ./w1
249 example.net/w v0.2.0-pre => ./w2-pre
250 example.net/x v0.1.0 => ./x1
251 example.net/x v0.2.0-pre => ./x2-pre
252 example.net/y v0.1.0 => ./y1
253 example.net/y v0.2.0-pre => ./y2-pre
254 example.net/z v0.1.0 => ./z1
255 example.net/z v0.2.0-pre => ./z2-pre
256 )
257 -- w1/go.mod --
258 module example.net/w
259
260 go 1.16
261
262 require (
263 example.net/y v0.2.0-pre
264 example.net/z v0.2.0-pre
265 )
266 -- w1/w.go --
267 package w
268 -- w2-pre/go.mod --
269 module example.net/w
270
271 go 1.16
272 -- w2-pre/README.txt --
273 Package w has been removed.
274
275 -- x1/go.mod --
276 module example.net/x
277
278 go 1.16
279
280 require (
281 example.net/z v0.2.0-pre
282 example.net/w v0.2.0-pre
283 )
284 -- x1/x.go --
285 package x
286 -- x2-pre/go.mod --
287 module example.net/x
288
289 go 1.16
290 -- x2-pre/README.txt --
291 Package x has been removed.
292
293 -- y1/go.mod --
294 module example.net/y
295
296 go 1.16
297
298 require (
299 example.net/w v0.2.0-pre
300 example.net/x v0.2.0-pre
301 )
302 -- y1/y.go --
303 package y
304
305 -- y2-pre/go.mod --
306 module example.net/y
307
308 go 1.16
309 -- y2-pre/README.txt --
310 Package y has been removed.
311
312 -- z1/go.mod --
313 module example.net/z
314
315 go 1.16
316
317 require (
318 example.net/x v0.2.0-pre
319 example.net/y v0.2.0-pre
320 )
321 -- z1/z.go --
322 package z
323
324 -- z2-pre/go.mod --
325 module example.net/z
326
327 go 1.16
328 -- z2-pre/README.txt --
329 Package z has been removed.
330
View as plain text