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 }