Text file src/cmd/go/testdata/script/mod_tidy_convergence_loop.txt

     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 '^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$'
    55  stderr '^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$'
    56  stderr '^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  
    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 '^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$'
    78  stderr '^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$'
    79  stderr '^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  
    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