...
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
39cp 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
45cmp go.mod go.mod.orig
46
47stderr '^go: finding module for package example\.net/w$'
48stderr '^go: finding module for package example\.net/x$'
49stderr -count=2 '^go: finding module for package example\.net/y$'
50stderr -count=2 '^go: finding module for package example\.net/z$'
51stderr '^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.
54stderr '^go: 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$'
55stderr '^go: 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$'
56stderr '^go: 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
67go mod tidy -e
68
69cmp go.mod go.mod.tidye1
70
71stderr '^go: finding module for package example\.net/w$'
72stderr '^go: finding module for package example\.net/x$'
73stderr -count=2 '^go: finding module for package example\.net/y$'
74stderr -count=2 '^go: finding module for package example\.net/z$'
75stderr '^go: found example\.net/x in example\.net/x v0.1.0$'
76
77stderr '^go: 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$'
78stderr '^go: 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$'
79stderr '^go: 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
82go mod tidy -e
83cmp go.mod go.mod.tidye2
84
85go mod tidy -e
86cmp go.mod go.mod.tidye3
87
88go mod tidy -e
89cmp 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
97go 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
98go mod tidy -e
99cmp go.mod go.mod.postget
100go mod tidy -e
101cmp 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
131cp go.mod.orig go.mod
132go mod edit -go=1.17 go.mod
133cp go.mod go.mod.117
134go mod edit -go=1.17 go.mod.tidye1
135go mod edit -go=1.17 go.mod.tidye2
136go mod edit -go=1.17 go.mod.tidye3
137go mod edit -go=1.17 go.mod.postget
138
139go list -m all
140
141go mod tidy -e
142cmp go.mod go.mod.tidye3
143
144go mod tidy -e
145cmp go.mod go.mod.tidye2
146
147go mod tidy -e
148cmp go.mod go.mod.tidye1
149
150go mod tidy -e
151cmp 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
157go 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
158go mod tidy -e
159cmp go.mod go.mod.postget
160go mod tidy -e
161cmp go.mod go.mod.postget
162
163
164-- m.go --
165package m
166
167import (
168 _ "example.net/w"
169 _ "example.net/x"
170 _ "example.net/y"
171 _ "example.net/z"
172)
173
174-- go.mod --
175module example.net/m
176
177go 1.16
178
179replace (
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
190require example.net/w v0.1.0
191-- go.mod.tidye1 --
192module example.net/m
193
194go 1.16
195
196replace (
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
207require example.net/x v0.1.0
208-- go.mod.tidye2 --
209module example.net/m
210
211go 1.16
212
213replace (
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
224require example.net/y v0.1.0
225-- go.mod.tidye3 --
226module example.net/m
227
228go 1.16
229
230replace (
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
241require example.net/z v0.1.0
242-- go.mod.postget --
243module example.net/m
244
245go 1.16
246
247replace (
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 --
258module example.net/w
259
260go 1.16
261
262require (
263 example.net/y v0.2.0-pre
264 example.net/z v0.2.0-pre
265)
266-- w1/w.go --
267package w
268-- w2-pre/go.mod --
269module example.net/w
270
271go 1.16
272-- w2-pre/README.txt --
273Package w has been removed.
274
275-- x1/go.mod --
276module example.net/x
277
278go 1.16
279
280require (
281 example.net/z v0.2.0-pre
282 example.net/w v0.2.0-pre
283)
284-- x1/x.go --
285package x
286-- x2-pre/go.mod --
287module example.net/x
288
289go 1.16
290-- x2-pre/README.txt --
291Package x has been removed.
292
293-- y1/go.mod --
294module example.net/y
295
296go 1.16
297
298require (
299 example.net/w v0.2.0-pre
300 example.net/x v0.2.0-pre
301)
302-- y1/y.go --
303package y
304
305-- y2-pre/go.mod --
306module example.net/y
307
308go 1.16
309-- y2-pre/README.txt --
310Package y has been removed.
311
312-- z1/go.mod --
313module example.net/z
314
315go 1.16
316
317require (
318 example.net/x v0.2.0-pre
319 example.net/y v0.2.0-pre
320)
321-- z1/z.go --
322package z
323
324-- z2-pre/go.mod --
325module example.net/z
326
327go 1.16
328-- z2-pre/README.txt --
329Package z has been removed.
View as plain text