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
35 cp 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
41 cmp go.mod go.mod.orig
42
43 stderr '^go: found example\.net/y in example\.net/y v0.2.0$'
44 stderr '^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.
47 stderr '^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
63 go mod tidy -e
64 cmp go.mod go.mod.tidye
65 stderr '^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.
68 stderr '^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
74 go mod tidy -e
75 cmp 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
82 go get example.net/x@v0.1.0 example.net/y@v0.1.0
83 go mod tidy
84 cmp 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
90 cp go.mod.orig go.mod
91 go mod edit -go=1.17 go.mod
92 go mod edit -go=1.17 go.mod.tidye
93
94 go mod tidy -e
95 cmp go.mod go.mod.tidye
96 stderr '^go: found example\.net/y in example\.net/y v0.2.0$'
97 stderr '^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
99 go get example.net/x@v0.1.0 example.net/y@v0.1.0
100 go mod tidy
101 cmp go.mod go.mod.postget-117
102
103
104 -- go.mod --
105 module example.net/m
106
107 go 1.16
108
109 replace (
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
116 require (
117 example.net/x v0.1.0
118 )
119 -- go.mod.tidye --
120 module example.net/m
121
122 go 1.16
123
124 replace (
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 --
131 module example.net/m
132
133 go 1.16
134
135 replace (
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
142 require (
143 example.net/x v0.1.0
144 example.net/y v0.1.0 // indirect
145 )
146 -- go.mod.postget-117 --
147 module example.net/m
148
149 go 1.17
150
151 replace (
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
158 require example.net/x v0.1.0
159
160 require example.net/y v0.1.0 // indirect
161 -- m.go --
162 package m
163
164 import _ "example.net/x"
165
166 -- x1/go.mod --
167 module example.net/x
168
169 go 1.16
170 -- x1/x.go --
171 package x
172 -- x1/x_test.go --
173 package x
174
175 import _ "example.net/y"
176
177 -- x2-pre/go.mod --
178 module example.net/x
179
180 go 1.16
181 -- x2-pre/README.txt --
182 There is no package x here. Use example.com/x/subpkg instead.
183 -- x2-pre/subpkg/subpkg.go --
184 package subpkg // import "example.net/x/subpkg"
185
186 -- y1/go.mod --
187 module example.net/y
188
189 go 1.16
190 -- y1/y.go --
191 package y
192
193 -- y2/go.mod --
194 module example.net/y
195
196 go 1.16
197
198 require example.net/x v0.2.0-pre
199 -- y2/y.go --
200 package y
201
202 import _ "example.net/x/subpkg"
203
View as plain text