...
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 --- x
10# |
11# x_test --- y
12#
13# The module dependency graph of m is initially empty.
14# Modules x and y look like:
15#
16# x.1 (provides package x that imports y, but does not depend on module y)
17#
18# x.2-pre (no dependencies, but does not provide package x)
19#
20# y.1 (no dependencies, but provides package y)
21#
22# y.2 --- x.2-pre (provides package y)
23#
24#
25# When we resolve the missing import of y in x_test, we add y@latest — which is
26# y.2, not y.1 — as a new dependency. That upgrades to x to x.2-pre, which
27# removes package x (and also the need for module y). We can then safely remove
28# the dependency on module y, because nothing imports package y any more!
29#
30# We might be tempted to remove the dependency on module x for the same reason:
31# it no longer provides any imported package. However, that would cause 'go mod
32# tidy -e' to become unstable: with x.2-pre out of the way, we could once again
33# resolve the missing import of package x by re-adding x.1.
34
35cp go.mod go.mod.orig
36
37# 'go mod tidy' without -e should fail without modifying go.mod,
38# because it cannot resolve x and y simultaneously.
39! go mod tidy
40
41cmp go.mod go.mod.orig
42
43stderr '^go: found example\.net/y in example\.net/y v0.2.0$'
44stderr '^go: finding module for package example\.net/x$'
45
46 # TODO: This error message should be clearer — it doesn't indicate why v0.2.0-pre is required.
47stderr '^go: example\.net/m imports\n\texample\.net/x: package example\.net/x provided by example\.net/x at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
48
49
50# 'go mod tidy -e' should follow upgrades to try to resolve the modules that it
51# can, and then stop. When we resolve example.net/y, we upgrade to example.net/x
52# to v0.2.0-pre. At that version, package x no longer exists and no longer
53# imports package y, so the import of x should be left unsatisfied and the
54# existing dependency on example.net/x removed.
55#
56# TODO(bcmills): It would be ever better if we could keep the original
57# dependency on example.net/x v0.1.0, but I don't see a way to do that without
58# making the algorithm way too complicated. (We would have to detect that the
59# new dependency on example.net/y interferes with the package that caused us to
60# to add that dependency in the first place, and back out that part of the change
61# without also backing out any other needed changes.)
62
63go mod tidy -e
64cmp go.mod go.mod.tidye
65stderr '^go: found example\.net/y in example\.net/y v0.2.0$'
66
67 # TODO: This error message should be clearer — it doesn't indicate why v0.2.0-pre is required.
68stderr '^go: example\.net/m imports\n\texample\.net/x: package example\.net/x provided by example\.net/x at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
69
70
71# Since we attempt to resolve the dependencies of package x whenever we add x itself,
72# this end state is stable.
73
74go mod tidy -e
75cmp go.mod go.mod.tidye
76
77
78# An explicit 'go get' with the correct versions should allow 'go mod tidy' to
79# succeed and remain stable. y.1 does not upgrade x, and can therefore be used
80# with it.
81
82go get example.net/x@v0.1.0 example.net/y@v0.1.0
83go mod tidy
84cmp go.mod go.mod.postget
85
86
87# The 'tidy' logic for a lazy main module is somewhat different from that for an
88# eager main module, but the overall behavior is the same.
89
90cp go.mod.orig go.mod
91go mod edit -go=1.17 go.mod
92go mod edit -go=1.17 go.mod.tidye
93
94go mod tidy -e
95cmp go.mod go.mod.tidye
96stderr '^go: found example\.net/y in example\.net/y v0.2.0$'
97stderr '^go: example\.net/m imports\n\texample\.net/x: package example\.net/x provided by example\.net/x at latest version v0\.1\.0 but not at required version v0\.2\.0-pre$'
98
99go get example.net/x@v0.1.0 example.net/y@v0.1.0
100go mod tidy
101cmp go.mod go.mod.postget-117
102
103
104-- go.mod --
105module example.net/m
106
107go 1.16
108
109replace (
110 example.net/x v0.1.0 => ./x1
111 example.net/x v0.2.0-pre => ./x2-pre
112 example.net/y v0.1.0 => ./y1
113 example.net/y v0.2.0 => ./y2
114)
115
116require (
117 example.net/x v0.1.0
118)
119-- go.mod.tidye --
120module example.net/m
121
122go 1.16
123
124replace (
125 example.net/x v0.1.0 => ./x1
126 example.net/x v0.2.0-pre => ./x2-pre
127 example.net/y v0.1.0 => ./y1
128 example.net/y v0.2.0 => ./y2
129)
130-- go.mod.postget --
131module example.net/m
132
133go 1.16
134
135replace (
136 example.net/x v0.1.0 => ./x1
137 example.net/x v0.2.0-pre => ./x2-pre
138 example.net/y v0.1.0 => ./y1
139 example.net/y v0.2.0 => ./y2
140)
141
142require (
143 example.net/x v0.1.0
144 example.net/y v0.1.0 // indirect
145)
146-- go.mod.postget-117 --
147module example.net/m
148
149go 1.17
150
151replace (
152 example.net/x v0.1.0 => ./x1
153 example.net/x v0.2.0-pre => ./x2-pre
154 example.net/y v0.1.0 => ./y1
155 example.net/y v0.2.0 => ./y2
156)
157
158require example.net/x v0.1.0
159
160require example.net/y v0.1.0 // indirect
161-- m.go --
162package m
163
164import _ "example.net/x"
165
166-- x1/go.mod --
167module example.net/x
168
169go 1.16
170-- x1/x.go --
171package x
172-- x1/x_test.go --
173package x
174
175import _ "example.net/y"
176
177-- x2-pre/go.mod --
178module example.net/x
179
180go 1.16
181-- x2-pre/README.txt --
182There is no package x here. Use example.com/x/subpkg instead.
183-- x2-pre/subpkg/subpkg.go --
184package subpkg // import "example.net/x/subpkg"
185
186-- y1/go.mod --
187module example.net/y
188
189go 1.16
190-- y1/y.go --
191package y
192
193-- y2/go.mod --
194module example.net/y
195
196go 1.16
197
198require example.net/x v0.2.0-pre
199-- y2/y.go --
200package y
201
202import _ "example.net/x/subpkg"
View as plain text