github.com/golang/dep@v0.5.4/gps/solve_bimodal_test.go (about) 1 // Copyright 2017 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 gps 6 7 import ( 8 "fmt" 9 "path/filepath" 10 "strings" 11 12 "github.com/golang/dep/gps/pkgtree" 13 ) 14 15 // dsp - "depspec with packages" 16 // 17 // Wraps a set of tpkgs onto a depspec, and returns it. 18 func dsp(ds depspec, pkgs ...tpkg) depspec { 19 ds.pkgs = pkgs 20 return ds 21 } 22 23 // pkg makes a tpkg appropriate for use in bimodal testing 24 func pkg(path string, imports ...string) tpkg { 25 return tpkg{ 26 path: path, 27 imports: imports, 28 } 29 } 30 31 func init() { 32 for k, fix := range bimodalFixtures { 33 // Assign the name into the fixture itself 34 fix.n = k 35 bimodalFixtures[k] = fix 36 } 37 } 38 39 // Fixtures that rely on simulated bimodal (project and package-level) 40 // analysis for correct operation. The name given in the map gets assigned into 41 // the fixture itself in init(). 42 var bimodalFixtures = map[string]bimodalFixture{ 43 // Simple case, ensures that we do the very basics of picking up and 44 // including a single, simple import that is not expressed as a constraint 45 "simple bm-add": { 46 ds: []depspec{ 47 dsp(mkDepspec("root 0.0.0"), 48 pkg("root", "a")), 49 dsp(mkDepspec("a 1.0.0"), 50 pkg("a")), 51 }, 52 r: mksolution( 53 "a 1.0.0", 54 ), 55 }, 56 // Ensure it works when the import jump is not from the package with the 57 // same path as root, but from a subpkg 58 "subpkg bm-add": { 59 ds: []depspec{ 60 dsp(mkDepspec("root 0.0.0"), 61 pkg("root", "root/foo"), 62 pkg("root/foo", "a"), 63 ), 64 dsp(mkDepspec("a 1.0.0"), 65 pkg("a"), 66 ), 67 }, 68 r: mksolution( 69 "a 1.0.0", 70 ), 71 }, 72 // The same, but with a jump through two subpkgs 73 "double-subpkg bm-add": { 74 ds: []depspec{ 75 dsp(mkDepspec("root 0.0.0"), 76 pkg("root", "root/foo"), 77 pkg("root/foo", "root/bar"), 78 pkg("root/bar", "a"), 79 ), 80 dsp(mkDepspec("a 1.0.0"), 81 pkg("a"), 82 ), 83 }, 84 r: mksolution( 85 "a 1.0.0", 86 ), 87 }, 88 // Same again, but now nest the subpkgs 89 "double nested subpkg bm-add": { 90 ds: []depspec{ 91 dsp(mkDepspec("root 0.0.0"), 92 pkg("root", "root/foo"), 93 pkg("root/foo", "root/foo/bar"), 94 pkg("root/foo/bar", "a"), 95 ), 96 dsp(mkDepspec("a 1.0.0"), 97 pkg("a"), 98 ), 99 }, 100 r: mksolution( 101 "a 1.0.0", 102 ), 103 }, 104 // Importing package from project with no root package 105 "bm-add on project with no pkg in root dir": { 106 ds: []depspec{ 107 dsp(mkDepspec("root 0.0.0"), 108 pkg("root", "a/foo")), 109 dsp(mkDepspec("a 1.0.0"), 110 pkg("a/foo")), 111 }, 112 r: mksolution( 113 mklp("a 1.0.0", "foo"), 114 ), 115 }, 116 // Import jump is in a dep, and points to a transitive dep 117 "transitive bm-add": { 118 ds: []depspec{ 119 dsp(mkDepspec("root 0.0.0"), 120 pkg("root", "root/foo"), 121 pkg("root/foo", "a"), 122 ), 123 dsp(mkDepspec("a 1.0.0"), 124 pkg("a", "b"), 125 ), 126 dsp(mkDepspec("b 1.0.0"), 127 pkg("b"), 128 ), 129 }, 130 r: mksolution( 131 "a 1.0.0", 132 "b 1.0.0", 133 ), 134 }, 135 // Constraints apply only if the project that declares them has a 136 // reachable import 137 "constraints activated by import": { 138 ds: []depspec{ 139 dsp(mkDepspec("root 0.0.0", "b 1.0.0"), 140 pkg("root", "root/foo"), 141 pkg("root/foo", "a"), 142 ), 143 dsp(mkDepspec("a 1.0.0"), 144 pkg("a", "b"), 145 ), 146 dsp(mkDepspec("b 1.0.0"), 147 pkg("b"), 148 ), 149 dsp(mkDepspec("b 1.1.0"), 150 pkg("b"), 151 ), 152 }, 153 r: mksolution( 154 "a 1.0.0", 155 "b 1.1.0", 156 ), 157 }, 158 // Constraints apply only if the project that declares them has a 159 // reachable import - non-root 160 "constraints activated by import, transitive": { 161 ds: []depspec{ 162 dsp(mkDepspec("root 0.0.0"), 163 pkg("root", "root/foo", "b"), 164 pkg("root/foo", "a"), 165 ), 166 dsp(mkDepspec("a 1.0.0", "b 1.0.0"), 167 pkg("a"), 168 ), 169 dsp(mkDepspec("b 1.0.0"), 170 pkg("b"), 171 ), 172 dsp(mkDepspec("b 1.1.0"), 173 pkg("b"), 174 ), 175 }, 176 r: mksolution( 177 "a 1.0.0", 178 "b 1.1.0", 179 ), 180 }, 181 // Import jump is in a dep, and points to a transitive dep - but only in not 182 // the first version we try 183 "transitive bm-add on older version": { 184 ds: []depspec{ 185 dsp(mkDepspec("root 0.0.0", "a ~1.0.0"), 186 pkg("root", "root/foo"), 187 pkg("root/foo", "a"), 188 ), 189 dsp(mkDepspec("a 1.0.0"), 190 pkg("a", "b"), 191 ), 192 dsp(mkDepspec("a 1.1.0"), 193 pkg("a"), 194 ), 195 dsp(mkDepspec("b 1.0.0"), 196 pkg("b"), 197 ), 198 }, 199 r: mksolution( 200 "a 1.0.0", 201 "b 1.0.0", 202 ), 203 }, 204 // Import jump is in a dep, and points to a transitive dep - but will only 205 // get there via backtracking 206 "backtrack to dep on bm-add": { 207 ds: []depspec{ 208 dsp(mkDepspec("root 0.0.0"), 209 pkg("root", "root/foo"), 210 pkg("root/foo", "a", "b"), 211 ), 212 dsp(mkDepspec("a 1.0.0"), 213 pkg("a", "c"), 214 ), 215 dsp(mkDepspec("a 1.1.0"), 216 pkg("a"), 217 ), 218 // Include two versions of b, otherwise it'll be selected first 219 dsp(mkDepspec("b 0.9.0"), 220 pkg("b", "c"), 221 ), 222 dsp(mkDepspec("b 1.0.0"), 223 pkg("b", "c"), 224 ), 225 dsp(mkDepspec("c 1.0.0", "a 1.0.0"), 226 pkg("c", "a"), 227 ), 228 }, 229 r: mksolution( 230 "a 1.0.0", 231 "b 1.0.0", 232 "c 1.0.0", 233 ), 234 }, 235 "backjump through pkg-only selection": { 236 ds: []depspec{ 237 dsp(mkDepspec("root 0.0.0"), 238 pkg("root", "root/foo"), 239 pkg("root/foo", "a", "b"), 240 ), 241 dsp(mkDepspec("a 1.0.0"), 242 pkg("a", "c"), 243 ), 244 // Include two versions of b to ensure that a is visited first 245 dsp(mkDepspec("b 0.9.0", "d ^1.0.0"), 246 pkg("b", "c/other", "d"), 247 ), 248 dsp(mkDepspec("b 1.0.0", "d ^1.2.0"), 249 pkg("b", "c/other", "d"), 250 ), 251 // Three versions of c so it's last 252 dsp(mkDepspec("c 1.0.0", "d ^1.0.0"), 253 pkg("c", "d"), 254 pkg("c/other"), 255 ), 256 dsp(mkDepspec("d 1.0.0"), 257 pkg("d"), 258 ), 259 dsp(mkDepspec("d 1.1.0"), 260 pkg("d"), 261 ), 262 }, 263 r: mksolution( 264 "a 1.0.0", 265 "b 0.9.0", 266 mklp("c 1.0.0", ".", "other"), 267 "d 1.1.0", 268 ), 269 }, 270 // Import jump is in a dep subpkg, and points to a transitive dep 271 "transitive subpkg bm-add": { 272 ds: []depspec{ 273 dsp(mkDepspec("root 0.0.0"), 274 pkg("root", "root/foo"), 275 pkg("root/foo", "a"), 276 ), 277 dsp(mkDepspec("a 1.0.0"), 278 pkg("a", "a/bar"), 279 pkg("a/bar", "b"), 280 ), 281 dsp(mkDepspec("b 1.0.0"), 282 pkg("b"), 283 ), 284 }, 285 r: mksolution( 286 mklp("a 1.0.0", ".", "bar"), 287 "b 1.0.0", 288 ), 289 }, 290 // Import jump is in a dep subpkg, pointing to a transitive dep, but only in 291 // not the first version we try 292 "transitive subpkg bm-add on older version": { 293 ds: []depspec{ 294 dsp(mkDepspec("root 0.0.0", "a ~1.0.0"), 295 pkg("root", "root/foo"), 296 pkg("root/foo", "a"), 297 ), 298 dsp(mkDepspec("a 1.0.0"), 299 pkg("a", "a/bar"), 300 pkg("a/bar", "b"), 301 ), 302 dsp(mkDepspec("a 1.1.0"), 303 pkg("a", "a/bar"), 304 pkg("a/bar"), 305 ), 306 dsp(mkDepspec("b 1.0.0"), 307 pkg("b"), 308 ), 309 }, 310 r: mksolution( 311 mklp("a 1.0.0", ".", "bar"), 312 "b 1.0.0", 313 ), 314 }, 315 "project cycle involving root": { 316 ds: []depspec{ 317 dsp(mkDepspec("root 0.0.0", "a ~1.0.0"), 318 pkg("root", "a"), 319 pkg("root/foo"), 320 ), 321 dsp(mkDepspec("a 1.0.0"), 322 pkg("a", "root/foo"), 323 ), 324 }, 325 r: mksolution( 326 "a 1.0.0", 327 ), 328 }, 329 "project cycle involving root with backtracking": { 330 ds: []depspec{ 331 dsp(mkDepspec("root 0.0.0", "a ~1.0.0"), 332 pkg("root", "a", "b"), 333 pkg("root/foo"), 334 ), 335 dsp(mkDepspec("a 1.0.0"), 336 pkg("a", "root/foo"), 337 ), 338 dsp(mkDepspec("a 1.0.1"), 339 pkg("a", "root/foo"), 340 ), 341 dsp(mkDepspec("b 1.0.0", "a 1.0.0"), 342 pkg("b", "a"), 343 ), 344 dsp(mkDepspec("b 1.0.1", "a 1.0.0"), 345 pkg("b", "a"), 346 ), 347 dsp(mkDepspec("b 1.0.2", "a 1.0.0"), 348 pkg("b", "a"), 349 ), 350 }, 351 r: mksolution( 352 "a 1.0.0", 353 "b 1.0.2", 354 ), 355 }, 356 "unify project on disjoint package imports + source switching": { 357 ds: []depspec{ 358 dsp(mkDepspec("root 0.0.0", "b from baz 1.0.0"), 359 pkg("root", "a", "b"), 360 ), 361 dsp(mkDepspec("a 1.0.0"), 362 pkg("a", "b/foo"), 363 ), 364 dsp(mkDepspec("b 1.0.0"), 365 pkg("b"), 366 pkg("b/foo"), 367 ), 368 dsp(mkDepspec("baz 1.0.0"), 369 pkg("b"), 370 pkg("b/foo"), 371 ), 372 }, 373 r: mksolution( 374 "a 1.0.0", 375 mklp("b from baz 1.0.0", ".", "foo"), 376 ), 377 }, 378 "project cycle not involving root": { 379 ds: []depspec{ 380 dsp(mkDepspec("root 0.0.0", "a ~1.0.0"), 381 pkg("root", "a"), 382 ), 383 dsp(mkDepspec("a 1.0.0"), 384 pkg("a", "b"), 385 pkg("a/foo"), 386 ), 387 dsp(mkDepspec("b 1.0.0"), 388 pkg("b", "a/foo"), 389 ), 390 }, 391 r: mksolution( 392 mklp("a 1.0.0", ".", "foo"), 393 "b 1.0.0", 394 ), 395 }, 396 "project cycle not involving root with internal paths": { 397 ds: []depspec{ 398 dsp(mkDepspec("root 0.0.0", "a ~1.0.0"), 399 pkg("root", "a"), 400 ), 401 dsp(mkDepspec("a 1.0.0"), 402 pkg("a", "b/baz"), 403 pkg("a/foo", "a/quux", "a/quark"), 404 pkg("a/quux"), 405 pkg("a/quark"), 406 ), 407 dsp(mkDepspec("b 1.0.0"), 408 pkg("b", "a/foo"), 409 pkg("b/baz", "b"), 410 ), 411 }, 412 r: mksolution( 413 mklp("a 1.0.0", ".", "foo", "quark", "quux"), 414 mklp("b 1.0.0", ".", "baz"), 415 ), 416 }, 417 // Ensure that if a constraint is expressed, but no actual import exists, 418 // then the constraint is disregarded - the project named in the constraint 419 // is not part of the solution. 420 "ignore constraint without import": { 421 ds: []depspec{ 422 dsp(mkDepspec("root 0.0.0", "a 1.0.0"), 423 pkg("root", "root/foo"), 424 pkg("root/foo"), 425 ), 426 dsp(mkDepspec("a 1.0.0"), 427 pkg("a"), 428 ), 429 }, 430 r: mksolution(), 431 }, 432 // Transitive deps from one project (a) get incrementally included as other 433 // deps incorporate its various packages. 434 "multi-stage pkg incorporation": { 435 ds: []depspec{ 436 dsp(mkDepspec("root 0.0.0"), 437 pkg("root", "a", "d"), 438 ), 439 dsp(mkDepspec("a 1.0.0"), 440 pkg("a", "b"), 441 pkg("a/second", "c"), 442 ), 443 dsp(mkDepspec("b 2.0.0"), 444 pkg("b"), 445 ), 446 dsp(mkDepspec("c 1.2.0"), 447 pkg("c"), 448 ), 449 dsp(mkDepspec("d 1.0.0"), 450 pkg("d", "a/second"), 451 ), 452 }, 453 r: mksolution( 454 mklp("a 1.0.0", ".", "second"), 455 "b 2.0.0", 456 "c 1.2.0", 457 "d 1.0.0", 458 ), 459 }, 460 // Regression - make sure that the the constraint/import intersector only 461 // accepts a project 'match' if exactly equal, or a separating slash is 462 // present. 463 "radix path separator post-check": { 464 ds: []depspec{ 465 dsp(mkDepspec("root 0.0.0"), 466 pkg("root", "foo", "foobar"), 467 ), 468 dsp(mkDepspec("foo 1.0.0"), 469 pkg("foo"), 470 ), 471 dsp(mkDepspec("foobar 1.0.0"), 472 pkg("foobar"), 473 ), 474 }, 475 r: mksolution( 476 "foo 1.0.0", 477 "foobar 1.0.0", 478 ), 479 }, 480 // Well-formed failure when there's a dependency on a pkg that doesn't exist 481 "fail when imports nonexistent package": { 482 ds: []depspec{ 483 dsp(mkDepspec("root 0.0.0", "a 1.0.0"), 484 pkg("root", "a/foo"), 485 ), 486 dsp(mkDepspec("a 1.0.0"), 487 pkg("a"), 488 ), 489 }, 490 fail: &noVersionError{ 491 pn: mkPI("a"), 492 fails: []failedVersion{ 493 { 494 v: NewVersion("1.0.0"), 495 f: &checkeeHasProblemPackagesFailure{ 496 goal: mkAtom("a 1.0.0"), 497 failpkg: map[string]errDeppers{ 498 "a/foo": { 499 err: nil, // nil indicates package is missing 500 deppers: []atom{ 501 mkAtom("root"), 502 }, 503 }, 504 }, 505 }, 506 }, 507 }, 508 }, 509 }, 510 // Transitive deps from one project (a) get incrementally included as other 511 // deps incorporate its various packages, and fail with proper error when we 512 // discover one incrementally that isn't present 513 "fail multi-stage missing pkg": { 514 ds: []depspec{ 515 dsp(mkDepspec("root 0.0.0"), 516 pkg("root", "a", "d"), 517 ), 518 dsp(mkDepspec("a 1.0.0"), 519 pkg("a", "b"), 520 pkg("a/second", "c"), 521 ), 522 dsp(mkDepspec("b 2.0.0"), 523 pkg("b"), 524 ), 525 dsp(mkDepspec("c 1.2.0"), 526 pkg("c"), 527 ), 528 dsp(mkDepspec("d 1.0.0"), 529 pkg("d", "a/second"), 530 pkg("d", "a/nonexistent"), 531 ), 532 }, 533 fail: &noVersionError{ 534 pn: mkPI("d"), 535 fails: []failedVersion{ 536 { 537 v: NewVersion("1.0.0"), 538 f: &depHasProblemPackagesFailure{ 539 goal: mkADep("d 1.0.0", "a", Any(), "a/nonexistent"), 540 v: NewVersion("1.0.0"), 541 prob: map[string]error{ 542 "a/nonexistent": nil, 543 }, 544 }, 545 }, 546 }, 547 }, 548 }, 549 // Check ignores on the root project 550 "ignore in double-subpkg": { 551 ds: []depspec{ 552 dsp(mkDepspec("root 0.0.0"), 553 pkg("root", "root/foo"), 554 pkg("root/foo", "root/bar", "b"), 555 pkg("root/bar", "a"), 556 ), 557 dsp(mkDepspec("a 1.0.0"), 558 pkg("a"), 559 ), 560 dsp(mkDepspec("b 1.0.0"), 561 pkg("b"), 562 ), 563 }, 564 ignore: []string{"root/bar"}, 565 r: mksolution( 566 "b 1.0.0", 567 ), 568 }, 569 // Ignores on a dep pkg 570 "ignore through dep pkg": { 571 ds: []depspec{ 572 dsp(mkDepspec("root 0.0.0"), 573 pkg("root", "root/foo"), 574 pkg("root/foo", "a"), 575 ), 576 dsp(mkDepspec("a 1.0.0"), 577 pkg("a", "a/bar"), 578 pkg("a/bar", "b"), 579 ), 580 dsp(mkDepspec("b 1.0.0"), 581 pkg("b"), 582 ), 583 }, 584 ignore: []string{"a/bar"}, 585 r: mksolution( 586 "a 1.0.0", 587 ), 588 }, 589 // Preferred version, as derived from a dep's lock, is attempted first 590 "respect prefv, simple case": { 591 ds: []depspec{ 592 dsp(mkDepspec("root 0.0.0"), 593 pkg("root", "a")), 594 dsp(mkDepspec("a 1.0.0"), 595 pkg("a", "b")), 596 dsp(mkDepspec("b 1.0.0 foorev"), 597 pkg("b")), 598 dsp(mkDepspec("b 2.0.0 barrev"), 599 pkg("b")), 600 }, 601 lm: map[string]fixLock{ 602 "a 1.0.0": mklock( 603 "b 1.0.0 foorev", 604 ), 605 }, 606 r: mksolution( 607 "a 1.0.0", 608 "b 1.0.0 foorev", 609 ), 610 }, 611 // Preferred version, as derived from a dep's lock, is attempted first, even 612 // if the root also has a direct dep on it (root doesn't need to use 613 // preferreds, because it has direct control AND because the root lock 614 // already supersedes dep lock "preferences") 615 "respect dep prefv with root import": { 616 ds: []depspec{ 617 dsp(mkDepspec("root 0.0.0"), 618 pkg("root", "a", "b")), 619 dsp(mkDepspec("a 1.0.0"), 620 pkg("a", "b")), 621 //dsp(newDepspec("a 1.0.1"), 622 //pkg("a", "b")), 623 //dsp(newDepspec("a 1.1.0"), 624 //pkg("a", "b")), 625 dsp(mkDepspec("b 1.0.0 foorev"), 626 pkg("b")), 627 dsp(mkDepspec("b 2.0.0 barrev"), 628 pkg("b")), 629 }, 630 lm: map[string]fixLock{ 631 "a 1.0.0": mklock( 632 "b 1.0.0 foorev", 633 ), 634 }, 635 r: mksolution( 636 "a 1.0.0", 637 "b 1.0.0 foorev", 638 ), 639 }, 640 // Preferred versions can only work if the thing offering it has been 641 // selected, or at least marked in the unselected queue 642 "prefv only works if depper is selected": { 643 ds: []depspec{ 644 dsp(mkDepspec("root 0.0.0"), 645 pkg("root", "a", "b")), 646 // Three atoms for a, which will mean it gets visited after b 647 dsp(mkDepspec("a 1.0.0"), 648 pkg("a", "b")), 649 dsp(mkDepspec("a 1.0.1"), 650 pkg("a", "b")), 651 dsp(mkDepspec("a 1.1.0"), 652 pkg("a", "b")), 653 dsp(mkDepspec("b 1.0.0 foorev"), 654 pkg("b")), 655 dsp(mkDepspec("b 2.0.0 barrev"), 656 pkg("b")), 657 }, 658 lm: map[string]fixLock{ 659 "a 1.0.0": mklock( 660 "b 1.0.0 foorev", 661 ), 662 }, 663 r: mksolution( 664 "a 1.1.0", 665 "b 2.0.0 barrev", 666 ), 667 }, 668 "override unconstrained root import": { 669 ds: []depspec{ 670 dsp(mkDepspec("root 0.0.0"), 671 pkg("root", "a")), 672 dsp(mkDepspec("a 1.0.0"), 673 pkg("a")), 674 dsp(mkDepspec("a 2.0.0"), 675 pkg("a")), 676 }, 677 ovr: ProjectConstraints{ 678 ProjectRoot("a"): ProjectProperties{ 679 Constraint: NewVersion("1.0.0"), 680 }, 681 }, 682 r: mksolution( 683 "a 1.0.0", 684 ), 685 }, 686 "simple case-only differences": { 687 ds: []depspec{ 688 dsp(mkDepspec("root 0.0.0"), 689 pkg("root", "foo", "bar")), 690 dsp(mkDepspec("foo 1.0.0"), 691 pkg("foo", "Bar")), 692 dsp(mkDepspec("bar 1.0.0"), 693 pkg("bar")), 694 }, 695 fail: &noVersionError{ 696 pn: mkPI("foo"), 697 fails: []failedVersion{ 698 { 699 v: NewVersion("1.0.0"), 700 f: &caseMismatchFailure{ 701 goal: mkDep("foo 1.0.0", "Bar 1.0.0", "Bar"), 702 current: ProjectRoot("bar"), 703 failsib: []dependency{mkDep("root", "bar 1.0.0", "bar")}, 704 }, 705 }, 706 }, 707 }, 708 }, 709 "case variations acceptable with agreement": { 710 ds: []depspec{ 711 dsp(mkDepspec("root 0.0.0"), 712 pkg("root", "foo")), 713 dsp(mkDepspec("foo 1.0.0"), 714 pkg("foo", "Bar", "baz")), 715 dsp(mkDepspec("baz 1.0.0"), 716 pkg("baz", "Bar")), 717 dsp(mkDepspec("bar 1.0.0"), 718 pkg("bar")), 719 }, 720 r: mksolution( 721 "foo 1.0.0", 722 "Bar 1.0.0", 723 "baz 1.0.0", 724 ), 725 }, 726 "case variations within root": { 727 ds: []depspec{ 728 dsp(mkDepspec("root 0.0.0"), 729 pkg("root", "foo", "bar", "Bar")), 730 dsp(mkDepspec("foo 1.0.0"), 731 pkg("foo")), 732 dsp(mkDepspec("bar 1.0.0"), 733 pkg("bar")), 734 }, 735 fail: &noVersionError{ 736 pn: mkPI("foo"), 737 fails: []failedVersion{ 738 { 739 v: NewVersion("1.0.0"), 740 f: &caseMismatchFailure{ 741 goal: mkDep("foo 1.0.0", "Bar 1.0.0", "Bar"), 742 current: ProjectRoot("bar"), 743 failsib: []dependency{mkDep("root", "foo 1.0.0", "foo")}, 744 }, 745 }, 746 }, 747 }, 748 broken: "need to implement checking for import case variations *within* the root", 749 }, 750 "case variations within single dep": { 751 ds: []depspec{ 752 dsp(mkDepspec("root 0.0.0"), 753 pkg("root", "foo")), 754 dsp(mkDepspec("foo 1.0.0"), 755 pkg("foo", "bar", "Bar")), 756 dsp(mkDepspec("bar 1.0.0"), 757 pkg("bar")), 758 }, 759 fail: &noVersionError{ 760 pn: mkPI("foo"), 761 fails: []failedVersion{ 762 { 763 v: NewVersion("1.0.0"), 764 f: &caseMismatchFailure{ 765 goal: mkDep("foo 1.0.0", "Bar 1.0.0", "Bar"), 766 current: ProjectRoot("bar"), 767 failsib: []dependency{mkDep("root", "foo 1.0.0", "foo")}, 768 }, 769 }, 770 }, 771 }, 772 broken: "need to implement checking for import case variations *within* a single project", 773 }, 774 "case variations across multiple deps": { 775 ds: []depspec{ 776 dsp(mkDepspec("root 0.0.0"), 777 pkg("root", "foo", "bar")), 778 dsp(mkDepspec("foo 1.0.0"), 779 pkg("foo", "bar", "baz")), 780 dsp(mkDepspec("baz 1.0.0"), 781 pkg("baz", "Bar")), 782 dsp(mkDepspec("bar 1.0.0"), 783 pkg("bar")), 784 }, 785 fail: &noVersionError{ 786 pn: mkPI("baz"), 787 fails: []failedVersion{ 788 { 789 v: NewVersion("1.0.0"), 790 f: &caseMismatchFailure{ 791 goal: mkDep("baz 1.0.0", "Bar 1.0.0", "Bar"), 792 current: ProjectRoot("bar"), 793 failsib: []dependency{ 794 mkDep("root", "bar 1.0.0", "bar"), 795 mkDep("foo 1.0.0", "bar 1.0.0", "bar"), 796 }, 797 }, 798 }, 799 }, 800 }, 801 }, 802 // This isn't actually as crazy as it might seem, as the root is defined by 803 // the addresser, not the addressee. It would occur (to provide a 804 // real-as-of-this-writing example) if something imports 805 // github.com/Sirupsen/logrus, as the contained subpackage at 806 // github.com/Sirupsen/logrus/hooks/syslog imports 807 // github.com/sirupsen/logrus. The only reason that doesn't blow up all the 808 // time is that most people only import the root package, not the syslog 809 // subpackage. 810 "canonical case is established by mutual self-imports": { 811 ds: []depspec{ 812 dsp(mkDepspec("root 0.0.0"), 813 pkg("root", "foo")), 814 dsp(mkDepspec("foo 1.0.0"), 815 pkg("foo", "Bar")), 816 dsp(mkDepspec("bar 1.0.0"), 817 pkg("bar", "bar/subpkg"), 818 pkg("bar/subpkg")), 819 }, 820 fail: &noVersionError{ 821 pn: mkPI("Bar"), 822 fails: []failedVersion{ 823 { 824 v: NewVersion("1.0.0"), 825 f: &wrongCaseFailure{ 826 correct: ProjectRoot("bar"), 827 goal: mkDep("Bar 1.0.0", "bar 1.0.0", "bar"), 828 badcase: []dependency{mkDep("foo 1.0.0", "Bar 1.0.0", "Bar/subpkg")}, 829 }, 830 }, 831 }, 832 }, 833 }, 834 "canonical case only applies if relevant imports are activated": { 835 ds: []depspec{ 836 dsp(mkDepspec("root 0.0.0"), 837 pkg("root", "foo")), 838 dsp(mkDepspec("foo 1.0.0"), 839 pkg("foo", "Bar/subpkg")), 840 dsp(mkDepspec("bar 1.0.0"), 841 pkg("bar", "bar/subpkg"), 842 pkg("bar/subpkg")), 843 }, 844 r: mksolution( 845 "foo 1.0.0", 846 mklp("Bar 1.0.0", "subpkg"), 847 ), 848 }, 849 "simple case-only variations plus source variance": { 850 ds: []depspec{ 851 dsp(mkDepspec("root 0.0.0"), 852 pkg("root", "foo", "bar")), 853 dsp(mkDepspec("foo 1.0.0", "Bar from quux 1.0.0"), 854 pkg("foo", "Bar")), 855 dsp(mkDepspec("bar 1.0.0"), 856 pkg("bar")), 857 dsp(mkDepspec("quux 1.0.0"), 858 pkg("bar")), 859 }, 860 fail: &noVersionError{ 861 pn: mkPI("foo"), 862 fails: []failedVersion{ 863 { 864 v: NewVersion("1.0.0"), 865 f: &caseMismatchFailure{ 866 goal: mkDep("foo 1.0.0", "Bar from quux 1.0.0", "Bar"), 867 current: ProjectRoot("bar"), 868 failsib: []dependency{mkDep("root", "bar 1.0.0", "bar")}, 869 }, 870 }, 871 }, 872 }, 873 }, 874 "case-only variations plus source variance with internal canonicality": { 875 ds: []depspec{ 876 dsp(mkDepspec("root 0.0.0", "Bar from quux 1.0.0"), 877 pkg("root", "foo", "Bar")), 878 dsp(mkDepspec("foo 1.0.0", "Bar from quux 1.0.0"), 879 pkg("foo", "Bar")), 880 dsp(mkDepspec("bar 1.0.0"), 881 pkg("bar", "bar/subpkg"), 882 pkg("bar/subpkg")), 883 dsp(mkDepspec("quux 1.0.0"), 884 pkg("bar", "bar/subpkg"), 885 pkg("bar/subpkg")), 886 }, 887 fail: &noVersionError{ 888 pn: mkPI("Bar"), 889 fails: []failedVersion{ 890 { 891 v: NewVersion("1.0.0"), 892 f: &wrongCaseFailure{ 893 correct: ProjectRoot("bar"), 894 goal: mkDep("Bar from quux 1.0.0", "bar 1.0.0", "bar"), 895 badcase: []dependency{mkDep("root", "Bar 1.0.0", "Bar/subpkg")}, 896 }, 897 }, 898 }, 899 }, 900 }, 901 "alternate net address": { 902 ds: []depspec{ 903 dsp(mkDepspec("root 1.0.0", "foo from bar 2.0.0"), 904 pkg("root", "foo")), 905 dsp(mkDepspec("foo 1.0.0"), 906 pkg("foo")), 907 dsp(mkDepspec("foo 2.0.0"), 908 pkg("foo")), 909 dsp(mkDepspec("bar 1.0.0"), 910 pkg("foo")), 911 dsp(mkDepspec("bar 2.0.0"), 912 pkg("foo")), 913 }, 914 r: mksolution( 915 "foo from bar 2.0.0", 916 ), 917 }, 918 "alternate net address, version only in alt": { 919 ds: []depspec{ 920 dsp(mkDepspec("root 1.0.0", "foo from bar 2.0.0"), 921 pkg("root", "foo")), 922 dsp(mkDepspec("foo 1.0.0"), 923 pkg("foo")), 924 dsp(mkDepspec("bar 1.0.0"), 925 pkg("foo")), 926 dsp(mkDepspec("bar 2.0.0"), 927 pkg("foo")), 928 }, 929 r: mksolution( 930 "foo from bar 2.0.0", 931 ), 932 }, 933 "alternate net address in dep": { 934 ds: []depspec{ 935 dsp(mkDepspec("root 1.0.0", "foo 1.0.0"), 936 pkg("root", "foo")), 937 dsp(mkDepspec("foo 1.0.0", "bar from baz 2.0.0"), 938 pkg("foo", "bar")), 939 dsp(mkDepspec("bar 1.0.0"), 940 pkg("bar")), 941 dsp(mkDepspec("baz 1.0.0"), 942 pkg("bar")), 943 dsp(mkDepspec("baz 2.0.0"), 944 pkg("bar")), 945 }, 946 r: mksolution( 947 "foo 1.0.0", 948 "bar from baz 2.0.0", 949 ), 950 }, 951 // Because NOT specifying an alternate net address for a given import path 952 // is taken as an "eh, whatever", if we see an empty net addr after 953 // something else has already set an alternate one, then the second should 954 // just "go along" with whatever's already been specified. 955 "alternate net address with second depper": { 956 ds: []depspec{ 957 dsp(mkDepspec("root 1.0.0", "foo from bar 2.0.0"), 958 pkg("root", "foo", "baz")), 959 dsp(mkDepspec("foo 1.0.0"), 960 pkg("foo")), 961 dsp(mkDepspec("foo 2.0.0"), 962 pkg("foo")), 963 dsp(mkDepspec("bar 1.0.0"), 964 pkg("foo")), 965 dsp(mkDepspec("bar 2.0.0"), 966 pkg("foo")), 967 dsp(mkDepspec("baz 1.0.0"), 968 pkg("baz", "foo")), 969 }, 970 r: mksolution( 971 "foo from bar 2.0.0", 972 "baz 1.0.0", 973 ), 974 }, 975 // Same as the previous, except the alternate declaration originates in a 976 // dep, not the root. 977 "alternate net addr from dep, with second default depper": { 978 ds: []depspec{ 979 dsp(mkDepspec("root 1.0.0", "foo 1.0.0"), 980 pkg("root", "foo", "bar")), 981 dsp(mkDepspec("foo 1.0.0", "bar 2.0.0"), 982 pkg("foo", "baz")), 983 dsp(mkDepspec("foo 2.0.0", "bar 2.0.0"), 984 pkg("foo", "baz")), 985 dsp(mkDepspec("bar 2.0.0", "baz from quux 1.0.0"), 986 pkg("bar", "baz")), 987 dsp(mkDepspec("baz 1.0.0"), 988 pkg("baz")), 989 dsp(mkDepspec("baz 2.0.0"), 990 pkg("baz")), 991 dsp(mkDepspec("quux 1.0.0"), 992 pkg("baz")), 993 }, 994 r: mksolution( 995 "foo 1.0.0", 996 "bar 2.0.0", 997 "baz from quux 1.0.0", 998 ), 999 }, 1000 // When a given project is initially brought in using the default (i.e., 1001 // empty) ProjectIdentifier.Source, and a later, presumably 1002 // as-yet-undiscovered dependency specifies an alternate net addr for it, we 1003 // have to fail - even though, if the deps were visited in the opposite 1004 // order (deeper dep w/the alternate location first, default location 1005 // second), it would be fine. 1006 // 1007 // TODO A better solution here would involve restarting the solver w/a 1008 // marker to use that alternate, or (ugh) introducing a new failure 1009 // path/marker type that changes how backtracking works. (In fact, these 1010 // approaches are probably demonstrably equivalent.) 1011 "fails with net mismatch when deeper dep specs it": { 1012 ds: []depspec{ 1013 dsp(mkDepspec("root 1.0.0", "foo 1.0.0"), 1014 pkg("root", "foo", "baz")), 1015 dsp(mkDepspec("foo 1.0.0", "bar 2.0.0"), 1016 pkg("foo", "bar")), 1017 dsp(mkDepspec("bar 2.0.0", "baz from quux 1.0.0"), 1018 pkg("bar", "baz")), 1019 dsp(mkDepspec("baz 1.0.0"), 1020 pkg("baz")), 1021 dsp(mkDepspec("quux 1.0.0"), 1022 pkg("baz")), 1023 }, 1024 fail: &noVersionError{ 1025 pn: mkPI("bar"), 1026 fails: []failedVersion{ 1027 { 1028 v: NewVersion("2.0.0"), 1029 f: &sourceMismatchFailure{ 1030 shared: ProjectRoot("baz"), 1031 current: "baz", 1032 mismatch: "quux", 1033 prob: mkAtom("bar 2.0.0"), 1034 sel: []dependency{mkDep("foo 1.0.0", "bar 2.0.0", "bar")}, 1035 }, 1036 }, 1037 }, 1038 }, 1039 }, 1040 "with mismatched net addrs": { 1041 ds: []depspec{ 1042 dsp(mkDepspec("root 1.0.0", "foo 1.0.0", "bar 1.0.0"), 1043 pkg("root", "foo", "bar")), 1044 dsp(mkDepspec("foo 1.0.0", "bar from baz 1.0.0"), 1045 pkg("foo", "bar")), 1046 dsp(mkDepspec("bar 1.0.0"), 1047 pkg("bar")), 1048 dsp(mkDepspec("baz 1.0.0"), 1049 pkg("bar")), 1050 }, 1051 fail: &noVersionError{ 1052 pn: mkPI("foo"), 1053 fails: []failedVersion{ 1054 { 1055 v: NewVersion("1.0.0"), 1056 f: &sourceMismatchFailure{ 1057 shared: ProjectRoot("bar"), 1058 current: "bar", 1059 mismatch: "baz", 1060 prob: mkAtom("foo 1.0.0"), 1061 sel: []dependency{mkDep("root", "foo 1.0.0", "foo")}, 1062 }, 1063 }, 1064 }, 1065 }, 1066 }, 1067 "overridden mismatched net addrs, alt in dep": { 1068 ds: []depspec{ 1069 dsp(mkDepspec("root 0.0.0"), 1070 pkg("root", "foo")), 1071 dsp(mkDepspec("foo 1.0.0", "bar from baz 1.0.0"), 1072 pkg("foo", "bar")), 1073 dsp(mkDepspec("bar 1.0.0"), 1074 pkg("bar")), 1075 dsp(mkDepspec("baz 1.0.0"), 1076 pkg("bar")), 1077 }, 1078 ovr: ProjectConstraints{ 1079 ProjectRoot("bar"): ProjectProperties{ 1080 Source: "baz", 1081 }, 1082 }, 1083 r: mksolution( 1084 "foo 1.0.0", 1085 "bar from baz 1.0.0", 1086 ), 1087 }, 1088 "overridden mismatched net addrs, alt in root": { 1089 ds: []depspec{ 1090 dsp(mkDepspec("root 0.0.0", "bar from baz 1.0.0"), 1091 pkg("root", "foo")), 1092 dsp(mkDepspec("foo 1.0.0"), 1093 pkg("foo", "bar")), 1094 dsp(mkDepspec("bar 1.0.0"), 1095 pkg("bar")), 1096 dsp(mkDepspec("baz 1.0.0"), 1097 pkg("bar")), 1098 }, 1099 ovr: ProjectConstraints{ 1100 ProjectRoot("bar"): ProjectProperties{ 1101 Source: "baz", 1102 }, 1103 }, 1104 r: mksolution( 1105 "foo 1.0.0", 1106 "bar from baz 1.0.0", 1107 ), 1108 }, 1109 "require package": { 1110 ds: []depspec{ 1111 dsp(mkDepspec("root 0.0.0", "bar 1.0.0"), 1112 pkg("root", "foo")), 1113 dsp(mkDepspec("foo 1.0.0"), 1114 pkg("foo", "bar")), 1115 dsp(mkDepspec("bar 1.0.0"), 1116 pkg("bar")), 1117 dsp(mkDepspec("baz 1.0.0"), 1118 pkg("baz")), 1119 }, 1120 require: []string{"baz"}, 1121 r: mksolution( 1122 "foo 1.0.0", 1123 "bar 1.0.0", 1124 "baz 1.0.0", 1125 ), 1126 }, 1127 "require activates constraints": { 1128 ds: []depspec{ 1129 dsp(mkDepspec("root 0.0.0", "foo 1.0.0", "bar 1.0.0"), 1130 pkg("root", "foo")), 1131 dsp(mkDepspec("foo 1.0.0"), 1132 pkg("foo", "bar")), 1133 dsp(mkDepspec("bar 1.0.0"), 1134 pkg("bar")), 1135 dsp(mkDepspec("bar 1.1.0"), 1136 pkg("bar")), 1137 }, 1138 require: []string{"bar"}, 1139 r: mksolution( 1140 "foo 1.0.0", 1141 "bar 1.0.0", 1142 ), 1143 }, 1144 "require subpackage": { 1145 ds: []depspec{ 1146 dsp(mkDepspec("root 0.0.0", "bar 1.0.0"), 1147 pkg("root", "foo")), 1148 dsp(mkDepspec("foo 1.0.0"), 1149 pkg("foo", "bar")), 1150 dsp(mkDepspec("bar 1.0.0"), 1151 pkg("bar")), 1152 dsp(mkDepspec("baz 1.0.0"), 1153 pkg("baz", "baz/qux"), 1154 pkg("baz/qux")), 1155 }, 1156 require: []string{"baz/qux"}, 1157 r: mksolution( 1158 "foo 1.0.0", 1159 "bar 1.0.0", 1160 mklp("baz 1.0.0", "qux"), 1161 ), 1162 }, 1163 "require impossible subpackage": { 1164 ds: []depspec{ 1165 dsp(mkDepspec("root 0.0.0", "baz 1.0.0"), 1166 pkg("root", "foo")), 1167 dsp(mkDepspec("foo 1.0.0"), 1168 pkg("foo")), 1169 dsp(mkDepspec("baz 1.0.0"), 1170 pkg("baz")), 1171 dsp(mkDepspec("baz 2.0.0"), 1172 pkg("baz", "baz/qux"), 1173 pkg("baz/qux")), 1174 }, 1175 require: []string{"baz/qux"}, 1176 fail: &noVersionError{ 1177 pn: mkPI("baz"), 1178 fails: []failedVersion{ 1179 { 1180 v: NewVersion("2.0.0"), 1181 f: &versionNotAllowedFailure{ 1182 goal: mkAtom("baz 2.0.0"), 1183 failparent: []dependency{mkDep("root", "baz 1.0.0", "baz/qux")}, 1184 c: NewVersion("1.0.0"), 1185 }, 1186 }, 1187 { 1188 v: NewVersion("1.0.0"), 1189 f: &checkeeHasProblemPackagesFailure{ 1190 goal: mkAtom("baz 1.0.0"), 1191 failpkg: map[string]errDeppers{ 1192 "baz/qux": { 1193 err: nil, // nil indicates package is missing 1194 deppers: []atom{ 1195 mkAtom("root"), 1196 }, 1197 }, 1198 }, 1199 }, 1200 }, 1201 }, 1202 }, 1203 }, 1204 "require subpkg conflicts with other dep constraint": { 1205 ds: []depspec{ 1206 dsp(mkDepspec("root 0.0.0"), 1207 pkg("root", "foo")), 1208 dsp(mkDepspec("foo 1.0.0", "baz 1.0.0"), 1209 pkg("foo", "baz")), 1210 dsp(mkDepspec("baz 1.0.0"), 1211 pkg("baz")), 1212 dsp(mkDepspec("baz 2.0.0"), 1213 pkg("baz", "baz/qux"), 1214 pkg("baz/qux")), 1215 }, 1216 require: []string{"baz/qux"}, 1217 fail: &noVersionError{ 1218 pn: mkPI("baz"), 1219 fails: []failedVersion{ 1220 { 1221 v: NewVersion("2.0.0"), 1222 f: &versionNotAllowedFailure{ 1223 goal: mkAtom("baz 2.0.0"), 1224 failparent: []dependency{mkDep("foo 1.0.0", "baz 1.0.0", "baz")}, 1225 c: NewVersion("1.0.0"), 1226 }, 1227 }, 1228 { 1229 v: NewVersion("1.0.0"), 1230 f: &checkeeHasProblemPackagesFailure{ 1231 goal: mkAtom("baz 1.0.0"), 1232 failpkg: map[string]errDeppers{ 1233 "baz/qux": { 1234 err: nil, // nil indicates package is missing 1235 deppers: []atom{ 1236 mkAtom("root"), 1237 }, 1238 }, 1239 }, 1240 }, 1241 }, 1242 }, 1243 }, 1244 }, 1245 "require independent subpkg conflicts with other dep constraint": { 1246 ds: []depspec{ 1247 dsp(mkDepspec("root 0.0.0"), 1248 pkg("root", "foo")), 1249 dsp(mkDepspec("foo 1.0.0", "baz 1.0.0"), 1250 pkg("foo", "baz")), 1251 dsp(mkDepspec("baz 1.0.0"), 1252 pkg("baz")), 1253 dsp(mkDepspec("baz 2.0.0"), 1254 pkg("baz"), 1255 pkg("baz/qux")), 1256 }, 1257 require: []string{"baz/qux"}, 1258 fail: &noVersionError{ 1259 pn: mkPI("baz"), 1260 fails: []failedVersion{ 1261 { 1262 v: NewVersion("2.0.0"), 1263 f: &versionNotAllowedFailure{ 1264 goal: mkAtom("baz 2.0.0"), 1265 failparent: []dependency{mkDep("foo 1.0.0", "baz 1.0.0", "baz")}, 1266 c: NewVersion("1.0.0"), 1267 }, 1268 }, 1269 { 1270 v: NewVersion("1.0.0"), 1271 f: &checkeeHasProblemPackagesFailure{ 1272 goal: mkAtom("baz 1.0.0"), 1273 failpkg: map[string]errDeppers{ 1274 "baz/qux": { 1275 err: nil, // nil indicates package is missing 1276 deppers: []atom{ 1277 mkAtom("root"), 1278 }, 1279 }, 1280 }, 1281 }, 1282 }, 1283 }, 1284 }, 1285 }, 1286 } 1287 1288 // tpkg is a representation of a single package. It has its own import path, as 1289 // well as a list of paths it itself "imports". 1290 type tpkg struct { 1291 // Full import path of this package 1292 path string 1293 // Slice of full paths to its virtual imports 1294 imports []string 1295 } 1296 1297 type bimodalFixture struct { 1298 // name of this fixture datum 1299 n string 1300 // bimodal project; first is always treated as root project 1301 ds []depspec 1302 // results; map of name/version pairs 1303 r map[ProjectIdentifier]LockedProject 1304 // max attempts the solver should need to find solution. 0 means no limit 1305 maxAttempts int 1306 // Use downgrade instead of default upgrade sorter 1307 downgrade bool 1308 // lock file simulator, if one's to be used at all 1309 l fixLock 1310 // map of locks for deps, if any. keys should be of the form: 1311 // "<project> <version>" 1312 lm map[string]fixLock 1313 // solve failure expected, if any 1314 fail error 1315 // overrides, if any 1316 ovr ProjectConstraints 1317 // request up/downgrade to all projects 1318 changeall bool 1319 // pkgs to ignore 1320 ignore []string 1321 // pkgs to require 1322 require []string 1323 // if the fixture is currently broken/expected to fail, this has a message 1324 // recording why 1325 broken string 1326 } 1327 1328 func (f bimodalFixture) name() string { 1329 return f.n 1330 } 1331 1332 func (f bimodalFixture) specs() []depspec { 1333 return f.ds 1334 } 1335 1336 func (f bimodalFixture) maxTries() int { 1337 return f.maxAttempts 1338 } 1339 1340 func (f bimodalFixture) solution() map[ProjectIdentifier]LockedProject { 1341 return f.r 1342 } 1343 1344 func (f bimodalFixture) rootmanifest() RootManifest { 1345 m := simpleRootManifest{ 1346 c: pcSliceToMap(f.ds[0].deps), 1347 ovr: f.ovr, 1348 ig: pkgtree.NewIgnoredRuleset(f.ignore), 1349 req: make(map[string]bool), 1350 } 1351 for _, req := range f.require { 1352 m.req[req] = true 1353 } 1354 1355 return m 1356 } 1357 1358 func (f bimodalFixture) rootTree() pkgtree.PackageTree { 1359 pt := pkgtree.PackageTree{ 1360 ImportRoot: string(f.ds[0].n), 1361 Packages: map[string]pkgtree.PackageOrErr{}, 1362 } 1363 1364 for _, pkg := range f.ds[0].pkgs { 1365 elems := strings.Split(pkg.path, "/") 1366 pt.Packages[pkg.path] = pkgtree.PackageOrErr{ 1367 P: pkgtree.Package{ 1368 ImportPath: pkg.path, 1369 Name: elems[len(elems)-1], 1370 // TODO(sdboyer) ugh, tpkg type has no space for supporting test 1371 // imports... 1372 Imports: pkg.imports, 1373 }, 1374 } 1375 } 1376 1377 return pt 1378 } 1379 1380 func (f bimodalFixture) failure() error { 1381 return f.fail 1382 } 1383 1384 // bmSourceManager is an SM specifically for the bimodal fixtures. It composes 1385 // the general depspec SM, and differs from it in how it answers static analysis 1386 // calls, and its support for package ignores and dep lock data. 1387 type bmSourceManager struct { 1388 depspecSourceManager 1389 lm map[string]fixLock 1390 } 1391 1392 var _ SourceManager = &bmSourceManager{} 1393 1394 func newbmSM(bmf bimodalFixture) *bmSourceManager { 1395 sm := &bmSourceManager{ 1396 depspecSourceManager: *newdepspecSM(bmf.ds, bmf.ignore), 1397 } 1398 sm.rm = computeBimodalExternalMap(bmf.ds) 1399 sm.lm = bmf.lm 1400 1401 return sm 1402 } 1403 1404 func (sm *bmSourceManager) ListPackages(id ProjectIdentifier, v Version) (pkgtree.PackageTree, error) { 1405 // Deal with address-based root-switching with both case folding and 1406 // alternate sources. 1407 var src, fsrc, root, froot string 1408 src, fsrc = id.normalizedSource(), toFold(id.normalizedSource()) 1409 if id.Source != "" { 1410 root = string(id.ProjectRoot) 1411 froot = toFold(root) 1412 } else { 1413 root, froot = src, fsrc 1414 } 1415 1416 for k, ds := range sm.specs { 1417 // Cheat for root, otherwise we blow up b/c version is empty 1418 if fsrc == string(ds.n) && (k == 0 || ds.v.Matches(v)) { 1419 var replace bool 1420 if root != string(ds.n) { 1421 // We're in a case-varying lookup; ensure we replace the actual 1422 // leading ProjectRoot portion of import paths with the literal 1423 // string from the input. 1424 replace = true 1425 } 1426 1427 ptree := pkgtree.PackageTree{ 1428 ImportRoot: src, 1429 Packages: make(map[string]pkgtree.PackageOrErr), 1430 } 1431 for _, pkg := range ds.pkgs { 1432 if replace { 1433 pkg.path = strings.Replace(pkg.path, froot, root, 1) 1434 } 1435 ptree.Packages[pkg.path] = pkgtree.PackageOrErr{ 1436 P: pkgtree.Package{ 1437 ImportPath: pkg.path, 1438 Name: filepath.Base(pkg.path), 1439 Imports: pkg.imports, 1440 }, 1441 } 1442 } 1443 1444 return ptree, nil 1445 } 1446 } 1447 1448 return pkgtree.PackageTree{}, fmt.Errorf("project %s at version %s could not be found", id, v) 1449 } 1450 1451 func (sm *bmSourceManager) GetManifestAndLock(id ProjectIdentifier, v Version, an ProjectAnalyzer) (Manifest, Lock, error) { 1452 src := toFold(id.normalizedSource()) 1453 for _, ds := range sm.specs { 1454 if src == string(ds.n) && v.Matches(ds.v) { 1455 if l, exists := sm.lm[src+" "+v.String()]; exists { 1456 return ds, l, nil 1457 } 1458 return ds, dummyLock{}, nil 1459 } 1460 } 1461 1462 // TODO(sdboyer) proper solver-type errors 1463 return nil, nil, fmt.Errorf("project %s at version %s could not be found", id, v) 1464 } 1465 1466 // computeBimodalExternalMap takes a set of depspecs and computes an 1467 // internally-versioned ReachMap that is useful for quickly answering 1468 // ReachMap.Flatten()-type calls. 1469 // 1470 // Note that it does not do things like stripping out stdlib packages - these 1471 // maps are intended for use in SM fixtures, and that's a higher-level 1472 // responsibility within the system. 1473 func computeBimodalExternalMap(specs []depspec) map[pident]map[string][]string { 1474 // map of project name+version -> map of subpkg name -> external pkg list 1475 rm := make(map[pident]map[string][]string) 1476 1477 for _, ds := range specs { 1478 ptree := pkgtree.PackageTree{ 1479 ImportRoot: string(ds.n), 1480 Packages: make(map[string]pkgtree.PackageOrErr), 1481 } 1482 for _, pkg := range ds.pkgs { 1483 ptree.Packages[pkg.path] = pkgtree.PackageOrErr{ 1484 P: pkgtree.Package{ 1485 ImportPath: pkg.path, 1486 Name: filepath.Base(pkg.path), 1487 Imports: pkg.imports, 1488 }, 1489 } 1490 } 1491 reachmap, em := ptree.ToReachMap(false, true, true, nil) 1492 if len(em) > 0 { 1493 panic(fmt.Sprintf("pkgs with errors in reachmap processing: %s", em)) 1494 } 1495 1496 drm := make(map[string][]string) 1497 for ip, ie := range reachmap { 1498 drm[ip] = ie.External 1499 } 1500 rm[pident{n: ds.n, v: ds.v}] = drm 1501 } 1502 1503 return rm 1504 }