github.com/sdboyer/gps@v0.16.3/hash_test.go (about) 1 package gps 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "fmt" 7 "strings" 8 "testing" 9 "text/tabwriter" 10 ) 11 12 func TestHashInputs(t *testing.T) { 13 fix := basicFixtures["shared dependency with overlapping constraints"] 14 15 params := SolveParameters{ 16 RootDir: string(fix.ds[0].n), 17 RootPackageTree: fix.rootTree(), 18 Manifest: fix.rootmanifest(), 19 ProjectAnalyzer: naiveAnalyzer{}, 20 } 21 22 s, err := Prepare(params, newdepspecSM(fix.ds, nil)) 23 if err != nil { 24 t.Fatalf("Unexpected error while prepping solver: %s", err) 25 } 26 27 dig := s.HashInputs() 28 h := sha256.New() 29 30 elems := []string{ 31 hhConstraints, 32 "a", 33 "sv-1.0.0", 34 "b", 35 "sv-1.0.0", 36 hhImportsReqs, 37 "a", 38 "b", 39 hhIgnores, 40 hhOverrides, 41 hhAnalyzer, 42 "naive-analyzer", 43 "1", 44 } 45 for _, v := range elems { 46 h.Write([]byte(v)) 47 } 48 correct := h.Sum(nil) 49 50 if !bytes.Equal(dig, correct) { 51 t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems)) 52 } else if strings.Join(elems, "\n")+"\n" != HashingInputsAsString(s) { 53 t.Errorf("Hashes are equal, but hashing input strings are not:\n%s", diffHashingInputs(s, elems)) 54 } 55 } 56 57 func TestHashInputsReqsIgs(t *testing.T) { 58 fix := basicFixtures["shared dependency with overlapping constraints"] 59 60 rm := fix.rootmanifest().(simpleRootManifest).dup() 61 rm.ig = map[string]bool{ 62 "foo": true, 63 "bar": true, 64 } 65 66 params := SolveParameters{ 67 RootDir: string(fix.ds[0].n), 68 RootPackageTree: fix.rootTree(), 69 Manifest: rm, 70 ProjectAnalyzer: naiveAnalyzer{}, 71 } 72 73 s, err := Prepare(params, newdepspecSM(fix.ds, nil)) 74 if err != nil { 75 t.Fatalf("Unexpected error while prepping solver: %s", err) 76 } 77 78 dig := s.HashInputs() 79 h := sha256.New() 80 81 elems := []string{ 82 hhConstraints, 83 "a", 84 "sv-1.0.0", 85 "b", 86 "sv-1.0.0", 87 hhImportsReqs, 88 "a", 89 "b", 90 hhIgnores, 91 "bar", 92 "foo", 93 hhOverrides, 94 hhAnalyzer, 95 "naive-analyzer", 96 "1", 97 } 98 for _, v := range elems { 99 h.Write([]byte(v)) 100 } 101 correct := h.Sum(nil) 102 103 if !bytes.Equal(dig, correct) { 104 t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems)) 105 } 106 107 // Add requires 108 rm.req = map[string]bool{ 109 "baz": true, 110 "qux": true, 111 } 112 113 params.Manifest = rm 114 115 s, err = Prepare(params, newdepspecSM(fix.ds, nil)) 116 if err != nil { 117 t.Fatalf("Unexpected error while prepping solver: %s", err) 118 } 119 120 dig = s.HashInputs() 121 h = sha256.New() 122 123 elems = []string{ 124 hhConstraints, 125 "a", 126 "sv-1.0.0", 127 "b", 128 "sv-1.0.0", 129 hhImportsReqs, 130 "a", 131 "b", 132 "baz", 133 "qux", 134 hhIgnores, 135 "bar", 136 "foo", 137 hhOverrides, 138 hhAnalyzer, 139 "naive-analyzer", 140 "1", 141 } 142 for _, v := range elems { 143 h.Write([]byte(v)) 144 } 145 correct = h.Sum(nil) 146 147 if !bytes.Equal(dig, correct) { 148 t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems)) 149 } 150 151 // remove ignores, just test requires alone 152 rm.ig = nil 153 params.Manifest = rm 154 155 s, err = Prepare(params, newdepspecSM(fix.ds, nil)) 156 if err != nil { 157 t.Fatalf("Unexpected error while prepping solver: %s", err) 158 } 159 160 dig = s.HashInputs() 161 h = sha256.New() 162 163 elems = []string{ 164 hhConstraints, 165 "a", 166 "sv-1.0.0", 167 "b", 168 "sv-1.0.0", 169 hhImportsReqs, 170 "a", 171 "b", 172 "baz", 173 "qux", 174 hhIgnores, 175 hhOverrides, 176 hhAnalyzer, 177 "naive-analyzer", 178 "1", 179 } 180 for _, v := range elems { 181 h.Write([]byte(v)) 182 } 183 correct = h.Sum(nil) 184 185 if !bytes.Equal(dig, correct) { 186 t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems)) 187 } 188 } 189 190 func TestHashInputsOverrides(t *testing.T) { 191 basefix := basicFixtures["shared dependency with overlapping constraints"] 192 193 // Set up base state that we'll mutate over the course of each test 194 rm := basefix.rootmanifest().(simpleRootManifest).dup() 195 params := SolveParameters{ 196 RootDir: string(basefix.ds[0].n), 197 RootPackageTree: basefix.rootTree(), 198 Manifest: rm, 199 ProjectAnalyzer: naiveAnalyzer{}, 200 } 201 202 table := []struct { 203 name string 204 mut func() 205 elems []string 206 }{ 207 { 208 name: "override source; not imported, no deps pp", 209 mut: func() { 210 // First case - override just source, on something without 211 // corresponding project properties in the dependencies from 212 // root 213 rm.ovr = map[ProjectRoot]ProjectProperties{ 214 "c": ProjectProperties{ 215 Source: "car", 216 }, 217 } 218 }, 219 elems: []string{ 220 hhConstraints, 221 "a", 222 "sv-1.0.0", 223 "b", 224 "sv-1.0.0", 225 hhImportsReqs, 226 "a", 227 "b", 228 hhIgnores, 229 hhOverrides, 230 "c", 231 "car", 232 hhAnalyzer, 233 "naive-analyzer", 234 "1", 235 }, 236 }, 237 { 238 name: "override source; required, no deps pp", 239 mut: func() { 240 // Put c into the requires list, which should make it show up under 241 // constraints 242 rm.req = map[string]bool{ 243 "c": true, 244 } 245 }, 246 elems: []string{ 247 hhConstraints, 248 "a", 249 "sv-1.0.0", 250 "b", 251 "sv-1.0.0", 252 "c", 253 "car", 254 "any-*", // Any isn't included under the override, but IS for the constraint b/c it's equivalent 255 hhImportsReqs, 256 "a", 257 "b", 258 "c", 259 hhIgnores, 260 hhOverrides, 261 "c", 262 "car", 263 hhAnalyzer, 264 "naive-analyzer", 265 "1", 266 }, 267 }, 268 { 269 name: "override source; required & imported, no deps pp", 270 mut: func() { 271 // Put c in the root's imports 272 poe := params.RootPackageTree.Packages["root"] 273 poe.P.Imports = []string{"a", "b", "c"} 274 params.RootPackageTree.Packages["root"] = poe 275 }, 276 elems: []string{ 277 hhConstraints, 278 "a", 279 "sv-1.0.0", 280 "b", 281 "sv-1.0.0", 282 "c", 283 "car", 284 "any-*", // Any isn't included under the override, but IS for the constraint b/c it's equivalent 285 hhImportsReqs, 286 "a", 287 "b", 288 "c", 289 hhIgnores, 290 hhOverrides, 291 "c", 292 "car", 293 hhAnalyzer, 294 "naive-analyzer", 295 "1", 296 }, 297 }, 298 { 299 name: "override source; imported, no deps pp", 300 mut: func() { 301 // Take c out of requires list - now it's only imported 302 rm.req = nil 303 }, 304 elems: []string{ 305 hhConstraints, 306 "a", 307 "sv-1.0.0", 308 "b", 309 "sv-1.0.0", 310 "c", 311 "car", 312 "any-*", 313 hhImportsReqs, 314 "a", 315 "b", 316 "c", 317 hhIgnores, 318 hhOverrides, 319 "c", 320 "car", 321 hhAnalyzer, 322 "naive-analyzer", 323 "1", 324 }, 325 }, 326 { 327 name: "other override constraint; not imported, no deps pp", 328 mut: func() { 329 // Override not in root, just with constraint 330 rm.ovr["d"] = ProjectProperties{ 331 Constraint: NewBranch("foobranch"), 332 } 333 }, 334 elems: []string{ 335 hhConstraints, 336 "a", 337 "sv-1.0.0", 338 "b", 339 "sv-1.0.0", 340 "c", 341 "car", 342 "any-*", 343 hhImportsReqs, 344 "a", 345 "b", 346 "c", 347 hhIgnores, 348 hhOverrides, 349 "c", 350 "car", 351 "d", 352 "b-foobranch", 353 hhAnalyzer, 354 "naive-analyzer", 355 "1", 356 }, 357 }, 358 { 359 name: "override constraint; not imported, no deps pp", 360 mut: func() { 361 // Remove the "c" pkg from imports for remainder of tests 362 poe := params.RootPackageTree.Packages["root"] 363 poe.P.Imports = []string{"a", "b"} 364 params.RootPackageTree.Packages["root"] = poe 365 }, 366 elems: []string{ 367 hhConstraints, 368 "a", 369 "sv-1.0.0", 370 "b", 371 "sv-1.0.0", 372 hhImportsReqs, 373 "a", 374 "b", 375 hhIgnores, 376 hhOverrides, 377 "c", 378 "car", 379 "d", 380 "b-foobranch", 381 hhAnalyzer, 382 "naive-analyzer", 383 "1", 384 }, 385 }, 386 { 387 name: "override both; not imported, no deps pp", 388 mut: func() { 389 // Override not in root, both constraint and network name 390 rm.ovr["c"] = ProjectProperties{ 391 Source: "groucho", 392 Constraint: NewBranch("plexiglass"), 393 } 394 }, 395 elems: []string{ 396 hhConstraints, 397 "a", 398 "sv-1.0.0", 399 "b", 400 "sv-1.0.0", 401 hhImportsReqs, 402 "a", 403 "b", 404 hhIgnores, 405 hhOverrides, 406 "c", 407 "groucho", 408 "b-plexiglass", 409 "d", 410 "b-foobranch", 411 hhAnalyzer, 412 "naive-analyzer", 413 "1", 414 }, 415 }, 416 { 417 name: "override constraint; imported, with constraint", 418 mut: func() { 419 // Override dep present in root, just constraint 420 rm.ovr["a"] = ProjectProperties{ 421 Constraint: NewVersion("fluglehorn"), 422 } 423 }, 424 elems: []string{ 425 hhConstraints, 426 "a", 427 "pv-fluglehorn", 428 "b", 429 "sv-1.0.0", 430 hhImportsReqs, 431 "a", 432 "b", 433 hhIgnores, 434 hhOverrides, 435 "a", 436 "pv-fluglehorn", 437 "c", 438 "groucho", 439 "b-plexiglass", 440 "d", 441 "b-foobranch", 442 hhAnalyzer, 443 "naive-analyzer", 444 "1", 445 }, 446 }, 447 { 448 name: "override source; imported, with constraint", 449 mut: func() { 450 // Override in root, only network name 451 rm.ovr["a"] = ProjectProperties{ 452 Source: "nota", 453 } 454 }, 455 elems: []string{ 456 hhConstraints, 457 "a", 458 "nota", 459 "sv-1.0.0", 460 "b", 461 "sv-1.0.0", 462 hhImportsReqs, 463 "a", 464 "b", 465 hhIgnores, 466 hhOverrides, 467 "a", 468 "nota", 469 "c", 470 "groucho", 471 "b-plexiglass", 472 "d", 473 "b-foobranch", 474 hhAnalyzer, 475 "naive-analyzer", 476 "1", 477 }, 478 }, 479 { 480 name: "override both; imported, with constraint", 481 mut: func() { 482 // Override in root, network name and constraint 483 rm.ovr["a"] = ProjectProperties{ 484 Source: "nota", 485 Constraint: NewVersion("fluglehorn"), 486 } 487 }, 488 elems: []string{ 489 hhConstraints, 490 "a", 491 "nota", 492 "pv-fluglehorn", 493 "b", 494 "sv-1.0.0", 495 hhImportsReqs, 496 "a", 497 "b", 498 hhIgnores, 499 hhOverrides, 500 "a", 501 "nota", 502 "pv-fluglehorn", 503 "c", 504 "groucho", 505 "b-plexiglass", 506 "d", 507 "b-foobranch", 508 hhAnalyzer, 509 "naive-analyzer", 510 "1", 511 }, 512 }, 513 } 514 515 for _, fix := range table { 516 fix.mut() 517 params.Manifest = rm 518 519 s, err := Prepare(params, newdepspecSM(basefix.ds, nil)) 520 if err != nil { 521 t.Fatalf("(fix: %q) Unexpected error while prepping solver: %s", fix.name, err) 522 } 523 524 h := sha256.New() 525 for _, v := range fix.elems { 526 h.Write([]byte(v)) 527 } 528 529 if !bytes.Equal(s.HashInputs(), h.Sum(nil)) { 530 t.Errorf("(fix: %q) Hashes are not equal. Inputs:\n%s", fix.name, diffHashingInputs(s, fix.elems)) 531 } 532 } 533 } 534 535 func diffHashingInputs(s Solver, wnt []string) string { 536 actual := HashingInputsAsString(s) 537 got := strings.Split(actual, "\n") 538 // got has a trailing empty, add that to wnt 539 wnt = append(wnt, "") 540 541 lg, lw := len(got), len(wnt) 542 543 var buf bytes.Buffer 544 tw := tabwriter.NewWriter(&buf, 4, 4, 2, ' ', 0) 545 fmt.Fprintln(tw, " (GOT) \t (WANT) \t") 546 547 lmiss, rmiss := ">>>>>>>>>>", "<<<<<<<<<<" 548 if lg == lw { 549 // same length makes the loop pretty straightforward 550 for i := 0; i < lg; i++ { 551 fmt.Fprintf(tw, "%s\t%s\t\n", got[i], wnt[i]) 552 } 553 } else if lg > lw { 554 offset := 0 555 for i := 0; i < lg; i++ { 556 if lw <= i-offset { 557 fmt.Fprintf(tw, "%s\t%s\t\n", got[i], rmiss) 558 } else if got[i] != wnt[i-offset] && i+1 < lg && got[i+1] == wnt[i-offset] { 559 // if the next slot is a match, realign by skipping this one and 560 // bumping the offset 561 fmt.Fprintf(tw, "%s\t%s\t\n", got[i], rmiss) 562 offset++ 563 } else { 564 fmt.Fprintf(tw, "%s\t%s\t\n", got[i], wnt[i-offset]) 565 } 566 } 567 } else { 568 offset := 0 569 for i := 0; i < lw; i++ { 570 if lg <= i-offset { 571 fmt.Fprintf(tw, "%s\t%s\t\n", lmiss, wnt[i]) 572 } else if got[i-offset] != wnt[i] && i+1 < lw && got[i-offset] == wnt[i+1] { 573 // if the next slot is a match, realign by skipping this one and 574 // bumping the offset 575 fmt.Fprintf(tw, "%s\t%s\t\n", lmiss, wnt[i]) 576 offset++ 577 } else { 578 fmt.Fprintf(tw, "%s\t%s\t\n", got[i-offset], wnt[i]) 579 } 580 } 581 } 582 583 tw.Flush() 584 return buf.String() 585 }