github.com/golang/dep@v0.5.4/gps/verify/locksat_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 verify
     6  
     7  import (
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/golang/dep/gps"
    12  	"github.com/golang/dep/gps/pkgtree"
    13  )
    14  
    15  type lockUnsatisfactionDimension uint8
    16  
    17  const (
    18  	noLock lockUnsatisfactionDimension = 1 << iota
    19  	missingImports
    20  	excessImports
    21  	unmatchedOverrides
    22  	unmatchedConstraints
    23  )
    24  
    25  func (lsd lockUnsatisfactionDimension) String() string {
    26  	var parts []string
    27  	for i := uint(0); i < 5; i++ {
    28  		if lsd&(1<<i) != 0 {
    29  			switch lsd {
    30  			case noLock:
    31  				parts = append(parts, "no lock")
    32  			case missingImports:
    33  				parts = append(parts, "missing imports")
    34  			case excessImports:
    35  				parts = append(parts, "excess imports")
    36  			case unmatchedOverrides:
    37  				parts = append(parts, "unmatched overrides")
    38  			case unmatchedConstraints:
    39  				parts = append(parts, "unmatched constraints")
    40  			}
    41  		}
    42  	}
    43  
    44  	return strings.Join(parts, ", ")
    45  }
    46  
    47  func TestLockSatisfaction(t *testing.T) {
    48  	fooversion := gps.NewVersion("v1.0.0").Pair("foorev1")
    49  	bazversion := gps.NewVersion("v2.0.0").Pair("bazrev1")
    50  	transver := gps.NewVersion("v0.5.0").Pair("transrev1")
    51  	l := safeLock{
    52  		i: []string{"foo.com/bar", "baz.com/qux"},
    53  		p: []gps.LockedProject{
    54  			newVerifiableProject(mkPI("foo.com/bar"), fooversion, []string{".", "subpkg"}),
    55  			newVerifiableProject(mkPI("baz.com/qux"), bazversion, []string{".", "other"}),
    56  			newVerifiableProject(mkPI("transitive.com/dependency"), transver, []string{"."}),
    57  		},
    58  	}
    59  
    60  	ptree := pkgtree.PackageTree{
    61  		ImportRoot: "current",
    62  		Packages: map[string]pkgtree.PackageOrErr{
    63  			"current": {
    64  				P: pkgtree.Package{
    65  					Name:       "current",
    66  					ImportPath: "current",
    67  					Imports:    []string{"foo.com/bar"},
    68  				},
    69  			},
    70  		},
    71  	}
    72  	rm := simpleRootManifest{
    73  		req: map[string]bool{
    74  			"baz.com/qux": true,
    75  		},
    76  	}
    77  
    78  	var dup rootManifestTransformer = func(simpleRootManifest) simpleRootManifest {
    79  		return rm.dup()
    80  	}
    81  
    82  	tt := map[string]struct {
    83  		rmt     rootManifestTransformer
    84  		sat     lockUnsatisfactionDimension
    85  		checkfn func(*testing.T, LockSatisfaction)
    86  	}{
    87  		"ident": {
    88  			rmt: dup,
    89  		},
    90  		"added import": {
    91  			rmt: dup.addReq("fiz.com/wow"),
    92  			sat: missingImports,
    93  		},
    94  		"removed import": {
    95  			rmt: dup.rmReq("baz.com/qux"),
    96  			sat: excessImports,
    97  		},
    98  		"added and removed import": {
    99  			rmt: dup.rmReq("baz.com/qux").addReq("fiz.com/wow"),
   100  			sat: excessImports | missingImports,
   101  			checkfn: func(t *testing.T, lsat LockSatisfaction) {
   102  				if lsat.MissingImports[0] != "fiz.com/wow" {
   103  					t.Errorf("expected 'fiz.com/wow' as sole missing import, got %s", lsat.MissingImports)
   104  				}
   105  				if lsat.ExcessImports[0] != "baz.com/qux" {
   106  					t.Errorf("expected 'baz.com/qux' as sole excess import, got %s", lsat.ExcessImports)
   107  				}
   108  			},
   109  		},
   110  		"acceptable constraint": {
   111  			rmt: dup.setConstraint("baz.com/qux", bazversion.Unpair(), ""),
   112  		},
   113  		"unacceptable constraint": {
   114  			rmt: dup.setConstraint("baz.com/qux", fooversion.Unpair(), ""),
   115  			sat: unmatchedConstraints,
   116  			checkfn: func(t *testing.T, lsat LockSatisfaction) {
   117  				pr := gps.ProjectRoot("baz.com/qux")
   118  				unmet, has := lsat.UnmetConstraints[pr]
   119  				if !has {
   120  					t.Errorf("did not have constraint on expected project %q; map contents: %s", pr, lsat.UnmetConstraints)
   121  				}
   122  
   123  				if unmet.C != fooversion.Unpair() {
   124  					t.Errorf("wanted %s for unmet constraint, got %s", fooversion.Unpair(), unmet.C)
   125  				}
   126  
   127  				if unmet.V != bazversion {
   128  					t.Errorf("wanted %s for version that did not meet constraint, got %s", bazversion, unmet.V)
   129  				}
   130  			},
   131  		},
   132  		"acceptable override": {
   133  			rmt: dup.setOverride("baz.com/qux", bazversion.Unpair(), ""),
   134  		},
   135  		"unacceptable override": {
   136  			rmt: dup.setOverride("baz.com/qux", fooversion.Unpair(), ""),
   137  			sat: unmatchedOverrides,
   138  		},
   139  		"ineffectual constraint": {
   140  			rmt: dup.setConstraint("transitive.com/dependency", bazversion.Unpair(), ""),
   141  		},
   142  		"transitive override": {
   143  			rmt: dup.setOverride("transitive.com/dependency", bazversion.Unpair(), ""),
   144  			sat: unmatchedOverrides,
   145  		},
   146  		"ignores respected": {
   147  			rmt: dup.addIgnore("foo.com/bar"),
   148  			sat: excessImports,
   149  		},
   150  	}
   151  
   152  	for name, fix := range tt {
   153  		fix := fix
   154  		t.Run(name, func(t *testing.T) {
   155  			fixrm := fix.rmt(rm)
   156  			lsat := LockSatisfiesInputs(l, fixrm, ptree)
   157  
   158  			gotsat := lsat.unsatTypes()
   159  			if fix.sat & ^gotsat != 0 {
   160  				t.Errorf("wanted unsat in some dimensions that were satisfied: %s", fix.sat & ^gotsat)
   161  			}
   162  			if gotsat & ^fix.sat != 0 {
   163  				t.Errorf("wanted sat in some dimensions that were unsatisfied: %s", gotsat & ^fix.sat)
   164  			}
   165  
   166  			if lsat.Satisfied() && fix.sat != 0 {
   167  				t.Errorf("Satisfied() incorrectly reporting true when expecting some dimensions to be unsatisfied: %s", fix.sat)
   168  			} else if !lsat.Satisfied() && fix.sat == 0 {
   169  				t.Error("Satisfied() incorrectly reporting false when expecting all dimensions to be satisfied")
   170  			}
   171  
   172  			if fix.checkfn != nil {
   173  				fix.checkfn(t, lsat)
   174  			}
   175  		})
   176  	}
   177  
   178  	var lsat LockSatisfaction
   179  	if lsat.Satisfied() {
   180  		t.Error("zero value of LockSatisfaction should fail")
   181  	}
   182  	if LockSatisfiesInputs(nil, nil, ptree).Satisfied() {
   183  		t.Error("nil lock to LockSatisfiesInputs should produce failing result")
   184  	}
   185  }
   186  
   187  func (ls LockSatisfaction) unsatTypes() lockUnsatisfactionDimension {
   188  	var dims lockUnsatisfactionDimension
   189  
   190  	if !ls.LockExisted {
   191  		dims |= noLock
   192  	}
   193  	if len(ls.MissingImports) != 0 {
   194  		dims |= missingImports
   195  	}
   196  	if len(ls.ExcessImports) != 0 {
   197  		dims |= excessImports
   198  	}
   199  	if len(ls.UnmetOverrides) != 0 {
   200  		dims |= unmatchedOverrides
   201  	}
   202  	if len(ls.UnmetConstraints) != 0 {
   203  		dims |= unmatchedConstraints
   204  	}
   205  
   206  	return dims
   207  }
   208  
   209  type rootManifestTransformer func(simpleRootManifest) simpleRootManifest
   210  
   211  func (rmt rootManifestTransformer) compose(rmt2 rootManifestTransformer) rootManifestTransformer {
   212  	if rmt == nil {
   213  		return rmt2
   214  	}
   215  	return func(rm simpleRootManifest) simpleRootManifest {
   216  		return rmt2(rmt(rm))
   217  	}
   218  }
   219  
   220  func (rmt rootManifestTransformer) addReq(path string) rootManifestTransformer {
   221  	return rmt.compose(func(rm simpleRootManifest) simpleRootManifest {
   222  		rm.req[path] = true
   223  		return rm
   224  	})
   225  }
   226  
   227  func (rmt rootManifestTransformer) rmReq(path string) rootManifestTransformer {
   228  	return rmt.compose(func(rm simpleRootManifest) simpleRootManifest {
   229  		delete(rm.req, path)
   230  		return rm
   231  	})
   232  }
   233  
   234  func (rmt rootManifestTransformer) setConstraint(pr string, c gps.Constraint, source string) rootManifestTransformer {
   235  	return rmt.compose(func(rm simpleRootManifest) simpleRootManifest {
   236  		rm.c[gps.ProjectRoot(pr)] = gps.ProjectProperties{
   237  			Constraint: c,
   238  			Source:     source,
   239  		}
   240  		return rm
   241  	})
   242  }
   243  
   244  func (rmt rootManifestTransformer) setOverride(pr string, c gps.Constraint, source string) rootManifestTransformer {
   245  	return rmt.compose(func(rm simpleRootManifest) simpleRootManifest {
   246  		rm.ovr[gps.ProjectRoot(pr)] = gps.ProjectProperties{
   247  			Constraint: c,
   248  			Source:     source,
   249  		}
   250  		return rm
   251  	})
   252  }
   253  
   254  func (rmt rootManifestTransformer) addIgnore(path string) rootManifestTransformer {
   255  	return rmt.compose(func(rm simpleRootManifest) simpleRootManifest {
   256  		rm.ig = pkgtree.NewIgnoredRuleset(append(rm.ig.ToSlice(), path))
   257  		return rm
   258  	})
   259  }