github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/mvs/mvs_test.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mvs
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  	"testing"
    12  
    13  	"golang.org/x/mod/module"
    14  )
    15  
    16  var tests = `
    17  # Scenario from blog.
    18  name: blog
    19  A: B1 C2
    20  B1: D3
    21  C1: D2
    22  C2: D4
    23  C3: D5
    24  C4: G1
    25  D2: E1
    26  D3: E2
    27  D4: E2 F1
    28  D5: E2
    29  G1: C4
    30  A2: B1 C4 D4
    31  build A: A B1 C2 D4 E2 F1
    32  upgrade* A: A B1 C4 D5 E2 F1 G1
    33  upgrade A C4: A B1 C4 D4 E2 F1 G1
    34  downgrade A2 D2: A2 C4 D2
    35  
    36  name: trim
    37  A: B1 C2
    38  B1: D3
    39  C2: B2
    40  B2:
    41  build A: A B2 C2 D3
    42  
    43  # Cross-dependency between D and E.
    44  # No matter how it arises, should get result of merging all build lists via max,
    45  # which leads to including both D2 and E2.
    46  
    47  name: cross1
    48  A: B C
    49  B: D1
    50  C: D2
    51  D1: E2
    52  D2: E1
    53  build A: A B C D2 E2
    54  
    55  name: cross1V
    56  A: B2 C D2 E1
    57  B1: 
    58  B2: D1
    59  C: D2
    60  D1: E2
    61  D2: E1
    62  build A: A B2 C D2 E2
    63  
    64  name: cross1U
    65  A: B1 C
    66  B1: 
    67  B2: D1
    68  C: D2
    69  D1: E2
    70  D2: E1
    71  build A: A B1 C D2 E1
    72  upgrade A B2: A B2 C D2 E2
    73  
    74  name: cross1R
    75  A: B C 
    76  B: D2
    77  C: D1
    78  D1: E2
    79  D2: E1
    80  build A: A B C D2 E2
    81  
    82  name: cross1X
    83  A: B C
    84  B: D1 E2
    85  C: D2
    86  D1: E2
    87  D2: E1
    88  build A: A B C D2 E2
    89  
    90  name: cross2
    91  A: B D2
    92  B: D1
    93  D1: E2
    94  D2: E1
    95  build A: A B D2 E2
    96  
    97  name: cross2X
    98  A: B D2
    99  B: D1 E2
   100  C: D2
   101  D1: E2
   102  D2: E1
   103  build A: A B D2 E2
   104  
   105  name: cross3
   106  A: B D2 E1
   107  B: D1
   108  D1: E2
   109  D2: E1
   110  build A: A B D2 E2
   111  
   112  name: cross3X
   113  A: B D2 E1
   114  B: D1 E2
   115  D1: E2
   116  D2: E1
   117  build A: A B D2 E2
   118  
   119  # Should not get E2 here, because B has been updated
   120  # not to depend on D1 anymore.
   121  name: cross4
   122  A1: B1 D2
   123  A2: B2 D2
   124  B1: D1
   125  B2: D2
   126  D1: E2
   127  D2: E1
   128  build A1: A1 B1 D2 E2
   129  build A2: A2 B2 D2 E1
   130  
   131  # But the upgrade from A1 preserves the E2 dep explicitly.
   132  upgrade A1 B2: A1 B2 D2 E2
   133  upgradereq A1 B2: B2 E2
   134  
   135  name: cross5
   136  A: D1
   137  D1: E2
   138  D2: E1
   139  build A: A D1 E2
   140  upgrade* A: A D2 E2
   141  upgrade A D2: A D2 E2
   142  upgradereq A D2: D2 E2
   143  
   144  name: cross6
   145  A: D2
   146  D1: E2
   147  D2: E1
   148  build A: A D2 E1
   149  upgrade* A: A D2 E2
   150  upgrade A E2: A D2 E2
   151  
   152  name: cross7
   153  A: B C
   154  B: D1
   155  C: E1
   156  D1: E2
   157  E1: D2
   158  build A: A B C D2 E2
   159  
   160  # golang.org/issue/31248:
   161  # Even though we select X2, the requirement on I1
   162  # via X1 should be preserved.
   163  name: cross8
   164  M: A1 B1
   165  A1: X1
   166  B1: X2
   167  X1: I1
   168  X2: 
   169  build M: M A1 B1 I1 X2
   170  
   171  # Upgrade from B1 to B2 should not drop the transitive dep on D.
   172  name: drop
   173  A: B1 C1
   174  B1: D1
   175  B2:
   176  C2:
   177  D2:
   178  build A: A B1 C1 D1
   179  upgrade* A: A B2 C2 D2
   180  
   181  name: simplify
   182  A: B1 C1
   183  B1: C2
   184  C1: D1
   185  C2:
   186  build A: A B1 C2 D1
   187  
   188  name: up1
   189  A: B1 C1
   190  B1:
   191  B2:
   192  B3:
   193  B4:
   194  B5.hidden:
   195  C2:
   196  C3:
   197  build A: A B1 C1
   198  upgrade* A: A B4 C3
   199  
   200  name: up2
   201  A: B5.hidden C1
   202  B1:
   203  B2:
   204  B3:
   205  B4:
   206  B5.hidden:
   207  C2:
   208  C3:
   209  build A: A B5.hidden C1
   210  upgrade* A: A B5.hidden C3
   211  
   212  name: down1
   213  A: B2
   214  B1: C1
   215  B2: C2
   216  build A: A B2 C2
   217  downgrade A C1: A B1
   218  
   219  name: down2
   220  A: B2 E2
   221  B1:
   222  B2: C2 F2
   223  C1:
   224  D1:
   225  C2: D2 E2
   226  D2: B2
   227  E2: D2
   228  E1:
   229  F1:
   230  downgrade A F1: A B1 E1
   231  
   232  name: down3
   233  A: 
   234  
   235  # golang.org/issue/25542.
   236  name: noprev1
   237  A: B4 C2
   238  B2.hidden: 
   239  C2: 
   240  downgrade A B2.hidden: A B2.hidden C2
   241  
   242  name: noprev2
   243  A: B4 C2
   244  B2.hidden: 
   245  B1: 
   246  C2: 
   247  downgrade A B2.hidden: A B2.hidden C2
   248  
   249  name: noprev3
   250  A: B4 C2
   251  B3: 
   252  B2.hidden: 
   253  C2: 
   254  downgrade A B2.hidden: A B2.hidden C2
   255  
   256  # Cycles involving the target.
   257  
   258  # The target must be the newest version of itself.
   259  name: cycle1
   260  A: B1
   261  B1: A1
   262  B2: A2
   263  B3: A3
   264  build A: A B1
   265  upgrade A B2: A B2
   266  upgrade* A: A B3
   267  
   268  # golang.org/issue/29773:
   269  # Requirements of older versions of the target
   270  # must be carried over.
   271  name: cycle2
   272  A: B1
   273  A1: C1
   274  A2: D1
   275  B1: A1
   276  B2: A2
   277  C1: A2
   278  C2:
   279  D2:
   280  build A: A B1 C1 D1
   281  upgrade* A: A B2 C2 D2
   282  
   283  # Cycles with multiple possible solutions.
   284  # (golang.org/issue/34086)
   285  name: cycle3
   286  M: A1 C2
   287  A1: B1
   288  B1: C1
   289  B2: C2
   290  C1:
   291  C2: B2
   292  build M: M A1 B2 C2
   293  req M: A1 B2
   294  req M A: A1 B2
   295  req M C: A1 C2
   296  
   297  # Requirement minimization.
   298  
   299  name: req1
   300  A: B1 C1 D1 E1 F1
   301  B1: C1 E1 F1
   302  req A: B1 D1
   303  req A C: B1 C1 D1
   304  
   305  name: req2
   306  A: G1 H1
   307  G1: H1
   308  H1: G1
   309  req A: G1
   310  req A G: G1
   311  req A H: H1
   312  
   313  name: req3
   314  M: A1 B1
   315  A1: X1
   316  B1: X2
   317  X1: I1
   318  X2: 
   319  req M: A1 B1
   320  `
   321  
   322  func Test(t *testing.T) {
   323  	var (
   324  		name string
   325  		reqs reqsMap
   326  		fns  []func(*testing.T)
   327  	)
   328  	flush := func() {
   329  		if name != "" {
   330  			t.Run(name, func(t *testing.T) {
   331  				for _, fn := range fns {
   332  					fn(t)
   333  				}
   334  			})
   335  		}
   336  	}
   337  	m := func(s string) module.Version {
   338  		return module.Version{Path: s[:1], Version: s[1:]}
   339  	}
   340  	ms := func(list []string) []module.Version {
   341  		var mlist []module.Version
   342  		for _, s := range list {
   343  			mlist = append(mlist, m(s))
   344  		}
   345  		return mlist
   346  	}
   347  	checkList := func(t *testing.T, desc string, list []module.Version, err error, val string) {
   348  		if err != nil {
   349  			t.Fatalf("%s: %v", desc, err)
   350  		}
   351  		vs := ms(strings.Fields(val))
   352  		if !reflect.DeepEqual(list, vs) {
   353  			t.Errorf("%s = %v, want %v", desc, list, vs)
   354  		}
   355  	}
   356  
   357  	for _, line := range strings.Split(tests, "\n") {
   358  		line = strings.TrimSpace(line)
   359  		if strings.HasPrefix(line, "#") || line == "" {
   360  			continue
   361  		}
   362  		i := strings.Index(line, ":")
   363  		if i < 0 {
   364  			t.Fatalf("missing colon: %q", line)
   365  		}
   366  		key := strings.TrimSpace(line[:i])
   367  		val := strings.TrimSpace(line[i+1:])
   368  		if key == "" {
   369  			t.Fatalf("missing key: %q", line)
   370  		}
   371  		kf := strings.Fields(key)
   372  		switch kf[0] {
   373  		case "name":
   374  			if len(kf) != 1 {
   375  				t.Fatalf("name takes no arguments: %q", line)
   376  			}
   377  			flush()
   378  			reqs = make(reqsMap)
   379  			fns = nil
   380  			name = val
   381  			continue
   382  		case "build":
   383  			if len(kf) != 2 {
   384  				t.Fatalf("build takes one argument: %q", line)
   385  			}
   386  			fns = append(fns, func(t *testing.T) {
   387  				list, err := BuildList(m(kf[1]), reqs)
   388  				checkList(t, key, list, err, val)
   389  			})
   390  			continue
   391  		case "upgrade*":
   392  			if len(kf) != 2 {
   393  				t.Fatalf("upgrade* takes one argument: %q", line)
   394  			}
   395  			fns = append(fns, func(t *testing.T) {
   396  				list, err := UpgradeAll(m(kf[1]), reqs)
   397  				checkList(t, key, list, err, val)
   398  			})
   399  			continue
   400  		case "upgradereq":
   401  			if len(kf) < 2 {
   402  				t.Fatalf("upgrade takes at least one argument: %q", line)
   403  			}
   404  			fns = append(fns, func(t *testing.T) {
   405  				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
   406  				if err == nil {
   407  					// Copy the reqs map, but substitute the upgraded requirements in
   408  					// place of the target's original requirements.
   409  					upReqs := make(reqsMap, len(reqs))
   410  					for m, r := range reqs {
   411  						upReqs[m] = r
   412  					}
   413  					upReqs[m(kf[1])] = list
   414  
   415  					list, err = Req(m(kf[1]), nil, upReqs)
   416  				}
   417  				checkList(t, key, list, err, val)
   418  			})
   419  			continue
   420  		case "upgrade":
   421  			if len(kf) < 2 {
   422  				t.Fatalf("upgrade takes at least one argument: %q", line)
   423  			}
   424  			fns = append(fns, func(t *testing.T) {
   425  				list, err := Upgrade(m(kf[1]), reqs, ms(kf[2:])...)
   426  				checkList(t, key, list, err, val)
   427  			})
   428  			continue
   429  		case "downgrade":
   430  			if len(kf) < 2 {
   431  				t.Fatalf("downgrade takes at least one argument: %q", line)
   432  			}
   433  			fns = append(fns, func(t *testing.T) {
   434  				list, err := Downgrade(m(kf[1]), reqs, ms(kf[1:])...)
   435  				checkList(t, key, list, err, val)
   436  			})
   437  			continue
   438  		case "req":
   439  			if len(kf) < 2 {
   440  				t.Fatalf("req takes at least one argument: %q", line)
   441  			}
   442  			fns = append(fns, func(t *testing.T) {
   443  				list, err := Req(m(kf[1]), kf[2:], reqs)
   444  				checkList(t, key, list, err, val)
   445  			})
   446  			continue
   447  		}
   448  		if len(kf) == 1 && 'A' <= key[0] && key[0] <= 'Z' {
   449  			var rs []module.Version
   450  			for _, f := range strings.Fields(val) {
   451  				r := m(f)
   452  				if reqs[r] == nil {
   453  					reqs[r] = []module.Version{}
   454  				}
   455  				rs = append(rs, r)
   456  			}
   457  			reqs[m(key)] = rs
   458  			continue
   459  		}
   460  		t.Fatalf("bad line: %q", line)
   461  	}
   462  	flush()
   463  }
   464  
   465  type reqsMap map[module.Version][]module.Version
   466  
   467  func (r reqsMap) Max(v1, v2 string) string {
   468  	if v1 == "none" || v2 == "" {
   469  		return v2
   470  	}
   471  	if v2 == "none" || v1 == "" {
   472  		return v1
   473  	}
   474  	if v1 < v2 {
   475  		return v2
   476  	}
   477  	return v1
   478  }
   479  
   480  func (r reqsMap) Upgrade(m module.Version) (module.Version, error) {
   481  	var u module.Version
   482  	for k := range r {
   483  		if k.Path == m.Path && u.Version < k.Version && !strings.HasSuffix(k.Version, ".hidden") {
   484  			u = k
   485  		}
   486  	}
   487  	if u.Path == "" {
   488  		return module.Version{}, fmt.Errorf("missing module: %v", module.Version{Path: m.Path})
   489  	}
   490  	return u, nil
   491  }
   492  
   493  func (r reqsMap) Previous(m module.Version) (module.Version, error) {
   494  	var p module.Version
   495  	for k := range r {
   496  		if k.Path == m.Path && p.Version < k.Version && k.Version < m.Version && !strings.HasSuffix(k.Version, ".hidden") {
   497  			p = k
   498  		}
   499  	}
   500  	if p.Path == "" {
   501  		return module.Version{Path: m.Path, Version: "none"}, nil
   502  	}
   503  	return p, nil
   504  }
   505  
   506  func (r reqsMap) Required(m module.Version) ([]module.Version, error) {
   507  	rr, ok := r[m]
   508  	if !ok {
   509  		return nil, fmt.Errorf("missing module: %v", m)
   510  	}
   511  	return rr, nil
   512  }