github.com/golang/dep@v0.5.4/cmd/dep/status_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 main 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "log" 13 "path/filepath" 14 "reflect" 15 "strings" 16 "testing" 17 "text/tabwriter" 18 "text/template" 19 20 "github.com/golang/dep" 21 "github.com/golang/dep/gps" 22 "github.com/golang/dep/internal/test" 23 "github.com/pkg/errors" 24 ) 25 26 func TestStatusFormatVersion(t *testing.T) { 27 t.Parallel() 28 29 tests := map[gps.Version]string{ 30 nil: "", 31 gps.NewBranch("master"): "branch master", 32 gps.NewVersion("1.0.0"): "1.0.0", 33 gps.Revision("flooboofoobooo"): "flooboo", 34 } 35 for version, expected := range tests { 36 str := formatVersion(version) 37 if str != expected { 38 t.Fatalf("expected '%v', got '%v'", expected, str) 39 } 40 } 41 } 42 43 func TestBasicLine(t *testing.T) { 44 project := dep.Project{} 45 aSemverConstraint, _ := gps.NewSemverConstraint("1.2.3") 46 47 templateString := "PR:{{.ProjectRoot}}, Const:{{.Constraint}}, Ver:{{.Version}}, Rev:{{.Revision}}, Lat:{{.Latest}}, PkgCt:{{.PackageCount}}" 48 equalityTestTemplate := `{{if eq .Constraint "1.2.3"}}Constraint is 1.2.3{{end}}|{{if eq .Version "flooboo"}}Version is flooboo{{end}}|{{if eq .Latest "unknown"}}Latest is unknown{{end}}` 49 50 tests := []struct { 51 name string 52 status BasicStatus 53 wantDotStatus []string 54 wantJSONStatus []string 55 wantTableStatus []string 56 wantTemplateStatus []string 57 wantEqTemplateStatus []string 58 }{ 59 { 60 name: "BasicStatus with ProjectRoot only", 61 status: BasicStatus{ 62 ProjectRoot: "github.com/foo/bar", 63 }, 64 wantDotStatus: []string{`[label="github.com/foo/bar"];`}, 65 wantJSONStatus: []string{`"Version":""`, `"Revision":""`}, 66 wantTableStatus: []string{`github.com/foo/bar 0`}, 67 wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:, Rev:, Lat:, PkgCt:0`}, 68 wantEqTemplateStatus: []string{`||`}, 69 }, 70 { 71 name: "BasicStatus with Revision", 72 status: BasicStatus{ 73 ProjectRoot: "github.com/foo/bar", 74 Revision: gps.Revision("flooboofoobooo"), 75 }, 76 wantDotStatus: []string{`[label="github.com/foo/bar\nflooboo"];`}, 77 wantJSONStatus: []string{`"Version":""`, `"Revision":"flooboofoobooo"`, `"Constraint":""`}, 78 wantTableStatus: []string{`github.com/foo/bar flooboo 0`}, 79 wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:flooboo, Rev:flooboofoobooo, Lat:, PkgCt:0`}, 80 wantEqTemplateStatus: []string{`|Version is flooboo|`}, 81 }, 82 { 83 name: "BasicStatus with Version and Revision", 84 status: BasicStatus{ 85 ProjectRoot: "github.com/foo/bar", 86 Version: gps.NewVersion("1.0.0"), 87 Revision: gps.Revision("flooboofoobooo"), 88 }, 89 wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`}, 90 wantJSONStatus: []string{`"Version":"1.0.0"`, `"Revision":"flooboofoobooo"`, `"Constraint":""`}, 91 wantTableStatus: []string{`github.com/foo/bar 1.0.0 flooboo 0`}, 92 wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:1.0.0, Rev:flooboofoobooo, Lat:, PkgCt:0`}, 93 wantEqTemplateStatus: []string{`||`}, 94 }, 95 { 96 name: "BasicStatus with Constraint, Version and Revision", 97 status: BasicStatus{ 98 ProjectRoot: "github.com/foo/bar", 99 Constraint: aSemverConstraint, 100 Version: gps.NewVersion("1.0.0"), 101 Revision: gps.Revision("revxyz"), 102 }, 103 wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`}, 104 wantJSONStatus: []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`}, 105 wantTableStatus: []string{`github.com/foo/bar 1.2.3 1.0.0 revxyz 0`}, 106 wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:1.2.3, Ver:1.0.0, Rev:revxyz, Lat:, PkgCt:0`}, 107 wantEqTemplateStatus: []string{`Constraint is 1.2.3||`}, 108 }, 109 { 110 name: "BasicStatus with update error", 111 status: BasicStatus{ 112 ProjectRoot: "github.com/foo/bar", 113 hasError: true, 114 }, 115 wantDotStatus: []string{`[label="github.com/foo/bar"];`}, 116 wantJSONStatus: []string{`"Version":""`, `"Revision":""`, `"Latest":"unknown"`}, 117 wantTableStatus: []string{`github.com/foo/bar unknown 0`}, 118 wantTemplateStatus: []string{`PR:github.com/foo/bar, Const:, Ver:, Rev:, Lat:unknown, PkgCt:0`}, 119 wantEqTemplateStatus: []string{`||Latest is unknown`}, 120 }, 121 } 122 123 for _, test := range tests { 124 t.Run(test.name, func(t *testing.T) { 125 var buf bytes.Buffer 126 127 dotout := &dotOutput{ 128 p: &project, 129 w: &buf, 130 } 131 dotout.BasicHeader() 132 dotout.BasicLine(&test.status) 133 dotout.BasicFooter() 134 135 for _, wantStatus := range test.wantDotStatus { 136 if ok := strings.Contains(buf.String(), wantStatus); !ok { 137 t.Errorf("Did not find expected node status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 138 } 139 } 140 141 buf.Reset() 142 143 jsonout := &jsonOutput{w: &buf} 144 145 jsonout.BasicHeader() 146 jsonout.BasicLine(&test.status) 147 jsonout.BasicFooter() 148 149 for _, wantStatus := range test.wantJSONStatus { 150 if ok := strings.Contains(buf.String(), wantStatus); !ok { 151 t.Errorf("Did not find expected JSON status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 152 } 153 } 154 155 buf.Reset() 156 157 tabw := tabwriter.NewWriter(&buf, 0, 4, 2, ' ', 0) 158 159 tableout := &tableOutput{w: tabw} 160 161 tableout.BasicHeader() 162 tableout.BasicLine(&test.status) 163 tableout.BasicFooter() 164 165 for _, wantStatus := range test.wantTableStatus { 166 if ok := strings.Contains(buf.String(), wantStatus); !ok { 167 t.Errorf("Did not find expected Table status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 168 } 169 } 170 171 buf.Reset() 172 template, _ := template.New("status").Parse(templateString) 173 templateout := &templateOutput{w: &buf, tmpl: template} 174 templateout.BasicHeader() 175 templateout.BasicLine(&test.status) 176 templateout.BasicFooter() 177 178 for _, wantStatus := range test.wantTemplateStatus { 179 if ok := strings.Contains(buf.String(), wantStatus); !ok { 180 t.Errorf("Did not find expected template status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 181 } 182 } 183 184 // The following test is to ensure that certain fields usable with string operations such as .eq 185 buf.Reset() 186 template, _ = template.New("status").Parse(equalityTestTemplate) 187 templateout = &templateOutput{w: &buf, tmpl: template} 188 templateout.BasicHeader() 189 templateout.BasicLine(&test.status) 190 templateout.BasicFooter() 191 192 for _, wantStatus := range test.wantEqTemplateStatus { 193 if ok := strings.Contains(buf.String(), wantStatus); !ok { 194 t.Errorf("Did not find expected template status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 195 } 196 } 197 }) 198 } 199 } 200 201 func TestDetailLine(t *testing.T) { 202 project := dep.Project{} 203 aSemverConstraint, _ := gps.NewSemverConstraint("1.2.3") 204 205 templateString := "{{range $p := .Projects}}PR:{{$p.ProjectRoot}}, Src:{{$p.Source}}, Const:{{$p.Constraint}}, Ver:{{$p.Locked.Version}}, Rev:{{$p.Locked.Revision}}, Lat:{{$p.Latest.Revision}}, PkgCt:{{$p.PackageCount}}, Pkgs:{{$p.Packages}}{{end}}" 206 equalityTestTemplate := `{{range $p := .Projects}}{{if eq $p.Constraint "1.2.3"}}Constraint is 1.2.3{{end}}|{{if eq $p.Locked.Version "flooboo"}}Version is flooboo{{end}}|{{if eq $p.Locked.Revision "flooboofoobooo"}}Revision is flooboofoobooo{{end}}|{{if eq $p.Latest.Revision "unknown"}}Latest is unknown{{end}}{{end}}` 207 208 tests := []struct { 209 name string 210 status DetailStatus 211 wantDotStatus []string 212 wantJSONStatus []string 213 wantTableStatus []string 214 wantTemplateStatus []string 215 wantEqTemplateStatus []string 216 }{ 217 { 218 name: "DetailStatus with ProjectRoot only", 219 status: DetailStatus{ 220 BasicStatus: BasicStatus{ 221 ProjectRoot: "github.com/foo/bar", 222 }, 223 Packages: []string{}, 224 }, 225 wantDotStatus: []string{`[label="github.com/foo/bar"];`}, 226 wantJSONStatus: []string{`"Locked":{}`}, 227 wantTableStatus: []string{`github.com/foo/bar []`}, 228 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:, Const:, Ver:, Rev:, Lat:, PkgCt:0, Pkgs:[]`}, 229 wantEqTemplateStatus: []string{`||`}, 230 }, 231 { 232 name: "DetailStatus with Revision", 233 status: DetailStatus{ 234 BasicStatus: BasicStatus{ 235 ProjectRoot: "github.com/foo/bar", 236 Revision: gps.Revision("flooboofoobooo"), 237 }, 238 Packages: []string{}, 239 }, 240 wantDotStatus: []string{`[label="github.com/foo/bar\nflooboo"];`}, 241 wantJSONStatus: []string{`"Locked":{"Revision":"flooboofoobooo"}`, `"Constraint":""`}, 242 wantTableStatus: []string{`github.com/foo/bar flooboo []`}, 243 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:, Const:, Ver:, Rev:flooboofoobooo, Lat:, PkgCt:0, Pkgs:[]`}, 244 wantEqTemplateStatus: []string{`|Revision is flooboofoobooo|`}, 245 }, 246 { 247 name: "DetailStatus with Source", 248 status: DetailStatus{ 249 BasicStatus: BasicStatus{ 250 ProjectRoot: "github.com/foo/bar", 251 }, 252 Packages: []string{}, 253 Source: "github.com/baz/bar", 254 }, 255 wantDotStatus: []string{`[label="github.com/foo/bar"];`}, 256 wantJSONStatus: []string{`"Locked":{}`, `"Source":"github.com/baz/bar"`, `"Constraint":""`}, 257 wantTableStatus: []string{`github.com/foo/bar github.com/baz/bar []`}, 258 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:github.com/baz/bar, Const:, Ver:, Rev:, Lat:, PkgCt:0, Pkgs:[]`}, 259 wantEqTemplateStatus: []string{`||`}, 260 }, 261 { 262 name: "DetailStatus with Version and Revision", 263 status: DetailStatus{ 264 BasicStatus: BasicStatus{ 265 ProjectRoot: "github.com/foo/bar", 266 Version: gps.NewVersion("1.0.0"), 267 Revision: gps.Revision("flooboofoobooo"), 268 }, 269 Packages: []string{}, 270 }, 271 wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`}, 272 wantJSONStatus: []string{`"Version":"1.0.0"`, `"Revision":"flooboofoobooo"`, `"Constraint":""`}, 273 wantTableStatus: []string{`github.com/foo/bar 1.0.0 flooboo []`}, 274 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:, Const:, Ver:1.0.0, Rev:flooboofoobooo, Lat:, PkgCt:0, Pkgs:[]`}, 275 wantEqTemplateStatus: []string{`||`}, 276 }, 277 { 278 name: "DetailStatus with Constraint, Version and Revision", 279 status: DetailStatus{ 280 BasicStatus: BasicStatus{ 281 ProjectRoot: "github.com/foo/bar", 282 Constraint: aSemverConstraint, 283 Version: gps.NewVersion("1.0.0"), 284 Revision: gps.Revision("revxyz"), 285 }, 286 Packages: []string{}, 287 }, 288 wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`}, 289 wantJSONStatus: []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`}, 290 wantTableStatus: []string{`github.com/foo/bar 1.2.3 1.0.0 revxyz []`}, 291 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:, Const:1.2.3, Ver:1.0.0, Rev:revxyz, Lat:, PkgCt:0, Pkgs:[]`}, 292 wantEqTemplateStatus: []string{`Constraint is 1.2.3||`}, 293 }, 294 { 295 name: "DetailStatus with Constraint, Version, Revision, and Package", 296 status: DetailStatus{ 297 BasicStatus: BasicStatus{ 298 ProjectRoot: "github.com/foo/bar", 299 Constraint: aSemverConstraint, 300 Version: gps.NewVersion("1.0.0"), 301 Revision: gps.Revision("revxyz"), 302 PackageCount: 1, 303 }, 304 Packages: []string{"."}, 305 }, 306 wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`}, 307 wantJSONStatus: []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`, `"PackageCount":1`, `"Packages":["."]`}, 308 wantTableStatus: []string{`github.com/foo/bar 1.2.3 1.0.0 revxyz [.]`}, 309 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:, Const:1.2.3, Ver:1.0.0, Rev:revxyz, Lat:, PkgCt:1, Pkgs:[.]`}, 310 wantEqTemplateStatus: []string{`Constraint is 1.2.3||`}, 311 }, 312 { 313 name: "DetailStatus with Constraint, Version, Revision, and Packages", 314 status: DetailStatus{ 315 BasicStatus: BasicStatus{ 316 ProjectRoot: "github.com/foo/bar", 317 Constraint: aSemverConstraint, 318 Version: gps.NewVersion("1.0.0"), 319 Revision: gps.Revision("revxyz"), 320 PackageCount: 3, 321 }, 322 Packages: []string{".", "foo", "bar"}, 323 }, 324 wantDotStatus: []string{`[label="github.com/foo/bar\n1.0.0"];`}, 325 wantJSONStatus: []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`, `"PackageCount":3`, `"Packages":[".","foo","bar"]`}, 326 wantTableStatus: []string{`github.com/foo/bar 1.2.3 1.0.0 revxyz [., foo, bar]`}, 327 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:, Const:1.2.3, Ver:1.0.0, Rev:revxyz, Lat:, PkgCt:3, Pkgs:[. foo bar]`}, 328 wantEqTemplateStatus: []string{`Constraint is 1.2.3||`}, 329 }, 330 { 331 name: "DetailStatus with update error", 332 status: DetailStatus{ 333 BasicStatus: BasicStatus{ 334 ProjectRoot: "github.com/foo/bar", 335 hasError: true, 336 }, 337 Packages: []string{}, 338 }, 339 wantDotStatus: []string{`[label="github.com/foo/bar"];`}, 340 wantJSONStatus: []string{`"Locked":{}`, `"Latest":{"Revision":"unknown"}`}, 341 wantTableStatus: []string{`github.com/foo/bar unknown []`}, 342 wantTemplateStatus: []string{`PR:github.com/foo/bar, Src:, Const:, Ver:, Rev:, Lat:unknown, PkgCt:0, Pkgs:[]`}, 343 wantEqTemplateStatus: []string{`||Latest is unknown`}, 344 }, 345 } 346 347 for _, test := range tests { 348 t.Run(test.name, func(t *testing.T) { 349 var buf bytes.Buffer 350 351 dotout := &dotOutput{ 352 p: &project, 353 w: &buf, 354 } 355 dotout.DetailHeader(nil) 356 dotout.DetailLine(&test.status) 357 dotout.DetailFooter(nil) 358 359 for _, wantStatus := range test.wantDotStatus { 360 if ok := strings.Contains(buf.String(), wantStatus); !ok { 361 t.Errorf("Did not find expected node status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 362 } 363 } 364 365 buf.Reset() 366 367 jsonout := &jsonOutput{w: &buf} 368 369 jsonout.DetailHeader(nil) 370 jsonout.DetailLine(&test.status) 371 jsonout.DetailFooter(nil) 372 373 for _, wantStatus := range test.wantJSONStatus { 374 if ok := strings.Contains(buf.String(), wantStatus); !ok { 375 t.Errorf("Did not find expected JSON status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 376 } 377 } 378 379 buf.Reset() 380 381 tabw := tabwriter.NewWriter(&buf, 0, 4, 2, ' ', 0) 382 383 tableout := &tableOutput{w: tabw} 384 385 tableout.DetailHeader(nil) 386 tableout.DetailLine(&test.status) 387 tableout.DetailFooter(nil) 388 389 for _, wantStatus := range test.wantTableStatus { 390 if ok := strings.Contains(buf.String(), wantStatus); !ok { 391 t.Errorf("Did not find expected Table status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 392 } 393 } 394 395 buf.Reset() 396 template, _ := template.New("status").Parse(templateString) 397 templateout := &templateOutput{w: &buf, tmpl: template} 398 templateout.DetailHeader(nil) 399 templateout.DetailLine(&test.status) 400 templateout.DetailFooter(nil) 401 402 for _, wantStatus := range test.wantTemplateStatus { 403 if ok := strings.Contains(buf.String(), wantStatus); !ok { 404 t.Errorf("Did not find expected template status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 405 } 406 } 407 408 // The following test is to ensure that certain fields usable with string operations such as .eq 409 buf.Reset() 410 template, _ = template.New("status").Parse(equalityTestTemplate) 411 templateout = &templateOutput{w: &buf, tmpl: template} 412 templateout.DetailHeader(nil) 413 templateout.DetailLine(&test.status) 414 templateout.DetailFooter(nil) 415 416 for _, wantStatus := range test.wantEqTemplateStatus { 417 if ok := strings.Contains(buf.String(), wantStatus); !ok { 418 t.Errorf("Did not find expected template status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus) 419 } 420 } 421 }) 422 } 423 } 424 425 func TestBasicStatusGetConsolidatedConstraint(t *testing.T) { 426 aSemverConstraint, _ := gps.NewSemverConstraint("1.2.1") 427 428 testCases := []struct { 429 name string 430 basicStatus BasicStatus 431 wantConstraint string 432 }{ 433 { 434 name: "empty BasicStatus", 435 basicStatus: BasicStatus{}, 436 wantConstraint: "", 437 }, 438 { 439 name: "BasicStatus with Any Constraint", 440 basicStatus: BasicStatus{ 441 Constraint: gps.Any(), 442 }, 443 wantConstraint: "*", 444 }, 445 { 446 name: "BasicStatus with Semver Constraint", 447 basicStatus: BasicStatus{ 448 Constraint: aSemverConstraint, 449 }, 450 wantConstraint: "1.2.1", 451 }, 452 { 453 name: "BasicStatus with Override", 454 basicStatus: BasicStatus{ 455 Constraint: aSemverConstraint, 456 hasOverride: true, 457 }, 458 wantConstraint: "1.2.1 (override)", 459 }, 460 { 461 name: "BasicStatus with Revision Constraint", 462 basicStatus: BasicStatus{ 463 Constraint: gps.Revision("ddeb6f5d27091ff291b16232e99076a64fb375b8"), 464 }, 465 wantConstraint: "ddeb6f5", 466 }, 467 } 468 469 for _, tc := range testCases { 470 t.Run(tc.name, func(t *testing.T) { 471 if tc.basicStatus.getConsolidatedConstraint() != tc.wantConstraint { 472 t.Errorf("unexpected consolidated constraint: \n\t(GOT) %v \n\t(WNT) %v", tc.basicStatus.getConsolidatedConstraint(), tc.wantConstraint) 473 } 474 }) 475 } 476 } 477 478 func TestBasicStatusGetConsolidatedVersion(t *testing.T) { 479 testCases := []struct { 480 name string 481 basicStatus BasicStatus 482 wantVersion string 483 }{ 484 { 485 name: "empty BasicStatus", 486 basicStatus: BasicStatus{}, 487 wantVersion: "", 488 }, 489 { 490 name: "BasicStatus with Version and Revision", 491 basicStatus: BasicStatus{ 492 Version: gps.NewVersion("1.0.0"), 493 Revision: gps.Revision("revxyz"), 494 }, 495 wantVersion: "1.0.0", 496 }, 497 { 498 name: "BasicStatus with only Revision", 499 basicStatus: BasicStatus{ 500 Revision: gps.Revision("revxyz"), 501 }, 502 wantVersion: "revxyz", 503 }, 504 } 505 506 for _, tc := range testCases { 507 t.Run(tc.name, func(t *testing.T) { 508 if tc.basicStatus.getConsolidatedVersion() != tc.wantVersion { 509 t.Errorf("unexpected consolidated version: \n\t(GOT) %v \n\t(WNT) %v", tc.basicStatus.getConsolidatedVersion(), tc.wantVersion) 510 } 511 }) 512 } 513 } 514 515 func TestBasicStatusGetConsolidatedLatest(t *testing.T) { 516 testCases := []struct { 517 name string 518 basicStatus BasicStatus 519 revSize uint8 520 wantLatest string 521 }{ 522 { 523 name: "empty BasicStatus", 524 basicStatus: BasicStatus{}, 525 revSize: shortRev, 526 wantLatest: "", 527 }, 528 { 529 name: "nil latest", 530 basicStatus: BasicStatus{ 531 Latest: nil, 532 }, 533 revSize: shortRev, 534 wantLatest: "", 535 }, 536 { 537 name: "with error", 538 basicStatus: BasicStatus{ 539 hasError: true, 540 }, 541 revSize: shortRev, 542 wantLatest: "unknown", 543 }, 544 { 545 name: "short latest", 546 basicStatus: BasicStatus{ 547 Latest: gps.Revision("adummylonglongrevision"), 548 }, 549 revSize: shortRev, 550 wantLatest: "adummyl", 551 }, 552 { 553 name: "long latest", 554 basicStatus: BasicStatus{ 555 Latest: gps.Revision("adummylonglongrevision"), 556 }, 557 revSize: longRev, 558 wantLatest: "adummylonglongrevision", 559 }, 560 } 561 562 for _, tc := range testCases { 563 t.Run(tc.name, func(t *testing.T) { 564 gotRev := tc.basicStatus.getConsolidatedLatest(tc.revSize) 565 if gotRev != tc.wantLatest { 566 t.Errorf("unexpected consolidated latest: \n\t(GOT) %v \n\t(WNT) %v", gotRev, tc.wantLatest) 567 } 568 }) 569 } 570 } 571 572 func TestCollectConstraints(t *testing.T) { 573 ver1, _ := gps.NewSemverConstraintIC("v1.0.0") 574 ver08, _ := gps.NewSemverConstraintIC("v0.8.0") 575 ver2, _ := gps.NewSemverConstraintIC("v2.0.0") 576 577 cases := []struct { 578 name string 579 lock dep.Lock 580 manifest dep.Manifest 581 wantConstraints constraintsCollection 582 wantErr bool 583 }{ 584 { 585 name: "without any constraints", 586 lock: dep.Lock{ 587 P: []gps.LockedProject{ 588 gps.NewLockedProject( 589 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}, 590 gps.NewVersion("v1.0.0"), 591 []string{"."}, 592 ), 593 }, 594 }, 595 wantConstraints: constraintsCollection{}, 596 }, 597 { 598 name: "with multiple constraints from dependencies", 599 lock: dep.Lock{ 600 P: []gps.LockedProject{ 601 gps.NewLockedProject( 602 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}, 603 gps.NewVersion("v1.0.0"), 604 []string{"."}, 605 ), 606 gps.NewLockedProject( 607 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/darkowlzz/deptest-project-1")}, 608 gps.NewVersion("v0.1.0"), 609 []string{"."}, 610 ), 611 gps.NewLockedProject( 612 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/darkowlzz/deptest-project-2")}, 613 gps.NewBranch("master").Pair(gps.Revision("824a8d56a4c6b2f4718824a98cd6d70d3dbd4c3e")), 614 []string{"."}, 615 ), 616 }, 617 }, 618 wantConstraints: constraintsCollection{ 619 "github.com/sdboyer/deptestdos": []projectConstraint{ 620 {"github.com/darkowlzz/deptest-project-2", ver2}, 621 }, 622 "github.com/sdboyer/dep-test": []projectConstraint{ 623 {"github.com/darkowlzz/deptest-project-2", ver1}, 624 }, 625 "github.com/sdboyer/deptest": []projectConstraint{ 626 {"github.com/darkowlzz/deptest-project-1", ver1}, 627 {"github.com/darkowlzz/deptest-project-2", ver08}, 628 }, 629 }, 630 }, 631 { 632 name: "with multiple constraints from dependencies and root project", 633 lock: dep.Lock{ 634 P: []gps.LockedProject{ 635 gps.NewLockedProject( 636 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}, 637 gps.NewVersion("v1.0.0"), 638 []string{"."}, 639 ), 640 gps.NewLockedProject( 641 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/darkowlzz/deptest-project-1")}, 642 gps.NewVersion("v0.1.0"), 643 []string{"."}, 644 ), 645 gps.NewLockedProject( 646 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/darkowlzz/deptest-project-2")}, 647 gps.NewBranch("master").Pair(gps.Revision("824a8d56a4c6b2f4718824a98cd6d70d3dbd4c3e")), 648 []string{"."}, 649 ), 650 }, 651 }, 652 manifest: dep.Manifest{ 653 Constraints: map[gps.ProjectRoot]gps.ProjectProperties{ 654 gps.ProjectRoot("github.com/sdboyer/deptest"): { 655 Constraint: gps.Revision("3f4c3bea144e112a69bbe5d8d01c1b09a544253f"), 656 }, 657 }, 658 Ovr: make(gps.ProjectConstraints), 659 PruneOptions: gps.CascadingPruneOptions{ 660 DefaultOptions: gps.PruneNestedVendorDirs, 661 PerProjectOptions: make(map[gps.ProjectRoot]gps.PruneOptionSet), 662 }, 663 }, 664 wantConstraints: constraintsCollection{ 665 "github.com/sdboyer/deptestdos": []projectConstraint{ 666 {"github.com/darkowlzz/deptest-project-2", ver2}, 667 }, 668 "github.com/sdboyer/dep-test": []projectConstraint{ 669 {"github.com/darkowlzz/deptest-project-2", ver1}, 670 }, 671 "github.com/sdboyer/deptest": []projectConstraint{ 672 {"github.com/darkowlzz/deptest-project-1", ver1}, 673 {"github.com/darkowlzz/deptest-project-2", ver08}, 674 {"root", gps.Revision("3f4c3bea144e112a69bbe5d8d01c1b09a544253f")}, 675 }, 676 }, 677 }, 678 { 679 name: "skip projects with invalid versions", 680 lock: dep.Lock{ 681 P: []gps.LockedProject{ 682 gps.NewLockedProject( 683 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/darkowlzz/deptest-project-1")}, 684 gps.NewVersion("v0.1.0"), 685 []string{"."}, 686 ), 687 gps.NewLockedProject( 688 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/darkowlzz/deptest-project-2")}, 689 gps.NewVersion("v1.0.0"), 690 []string{"."}, 691 ), 692 }, 693 }, 694 wantConstraints: constraintsCollection{ 695 "github.com/sdboyer/deptest": []projectConstraint{ 696 {"github.com/darkowlzz/deptest-project-1", ver1}, 697 }, 698 }, 699 wantErr: true, 700 }, 701 { 702 name: "collect only applicable constraints", 703 lock: dep.Lock{ 704 P: []gps.LockedProject{ 705 gps.NewLockedProject( 706 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/darkowlzz/dep-applicable-constraints")}, 707 gps.NewVersion("v1.0.0"), 708 []string{"."}, 709 ), 710 }, 711 }, 712 wantConstraints: constraintsCollection{ 713 "github.com/boltdb/bolt": []projectConstraint{ 714 {"github.com/darkowlzz/dep-applicable-constraints", gps.NewBranch("master")}, 715 }, 716 "github.com/sdboyer/deptest": []projectConstraint{ 717 {"github.com/darkowlzz/dep-applicable-constraints", ver08}, 718 }, 719 }, 720 }, 721 { 722 name: "skip ineffective constraint from manifest", 723 lock: dep.Lock{ 724 P: []gps.LockedProject{ 725 gps.NewLockedProject( 726 gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}, 727 gps.NewVersion("v1.0.0"), 728 []string{"."}, 729 ), 730 }, 731 }, 732 manifest: dep.Manifest{ 733 Constraints: map[gps.ProjectRoot]gps.ProjectProperties{ 734 gps.ProjectRoot("github.com/darkowlzz/deptest-project-1"): { 735 Constraint: ver1, 736 }, 737 }, 738 Ovr: make(gps.ProjectConstraints), 739 PruneOptions: gps.CascadingPruneOptions{ 740 DefaultOptions: gps.PruneNestedVendorDirs, 741 PerProjectOptions: make(map[gps.ProjectRoot]gps.PruneOptionSet), 742 }, 743 }, 744 wantConstraints: constraintsCollection{}, 745 }, 746 } 747 748 h := test.NewHelper(t) 749 defer h.Cleanup() 750 751 testdir := filepath.Join("src", "collect_constraints_test") 752 h.TempDir(testdir) 753 h.TempCopy(filepath.Join(testdir, "main.go"), filepath.Join("status", "collect_constraints", "main.go")) 754 testProjPath := h.Path(testdir) 755 756 discardLogger := log.New(ioutil.Discard, "", 0) 757 758 ctx := &dep.Ctx{ 759 GOPATH: testProjPath, 760 Out: discardLogger, 761 Err: discardLogger, 762 } 763 764 sm, err := ctx.SourceManager() 765 h.Must(err) 766 defer sm.Release() 767 768 // Create new project and set root. Setting root is required for PackageList 769 // to run properly. 770 p := new(dep.Project) 771 p.SetRoot(testProjPath) 772 773 for _, c := range cases { 774 t.Run(c.name, func(t *testing.T) { 775 p.Lock = &c.lock 776 p.Manifest = &c.manifest 777 gotConstraints, err := collectConstraints(ctx, p, sm) 778 if len(err) > 0 && !c.wantErr { 779 t.Fatalf("unexpected errors while collecting constraints: %v", err) 780 } else if len(err) == 0 && c.wantErr { 781 t.Fatalf("expected errors while collecting constraints, but got none") 782 } 783 784 if !reflect.DeepEqual(gotConstraints, c.wantConstraints) { 785 t.Fatalf("unexpected collected constraints: \n\t(GOT): %v\n\t(WNT): %v", gotConstraints, c.wantConstraints) 786 } 787 }) 788 } 789 } 790 791 func TestValidateFlags(t *testing.T) { 792 testCases := []struct { 793 name string 794 cmd statusCommand 795 wantErr error 796 }{ 797 { 798 name: "no flags", 799 cmd: statusCommand{}, 800 wantErr: nil, 801 }, 802 { 803 name: "-dot only", 804 cmd: statusCommand{dot: true}, 805 wantErr: nil, 806 }, 807 { 808 name: "-dot with template", 809 cmd: statusCommand{dot: true, template: "foo"}, 810 wantErr: errors.New("cannot pass template string with -dot"), 811 }, 812 { 813 name: "-dot with -json", 814 cmd: statusCommand{dot: true, json: true}, 815 wantErr: errors.New("cannot pass multiple output format flags"), 816 }, 817 { 818 name: "-dot with operating mode", 819 cmd: statusCommand{dot: true, old: true}, 820 wantErr: errors.New("-dot generates dependency graph; cannot pass other flags"), 821 }, 822 { 823 name: "single operating mode", 824 cmd: statusCommand{old: true}, 825 wantErr: nil, 826 }, 827 { 828 name: "multiple operating modes", 829 cmd: statusCommand{missing: true, old: true}, 830 wantErr: errors.Wrapf(errors.New("cannot pass multiple operating mode flags"), "[-old -missing]"), 831 }, 832 { 833 name: "old with -dot", 834 cmd: statusCommand{dot: true, old: true}, 835 wantErr: errors.New("-dot generates dependency graph; cannot pass other flags"), 836 }, 837 { 838 name: "old with template", 839 cmd: statusCommand{old: true, template: "foo"}, 840 wantErr: nil, 841 }, 842 } 843 844 for _, tc := range testCases { 845 t.Run(tc.name, func(t *testing.T) { 846 err := tc.cmd.validateFlags() 847 848 if err == nil { 849 if tc.wantErr != nil { 850 t.Errorf("unexpected error: \n\t(GOT): %v\n\t(WNT): %v", err, tc.wantErr) 851 } 852 } else if err.Error() != tc.wantErr.Error() { 853 t.Errorf("unexpected error: \n\t(GOT): %v\n\t(WNT): %v", err, tc.wantErr) 854 } 855 }) 856 } 857 } 858 859 func execStatusTemplate(w io.Writer, format string, data interface{}) error { 860 tpl, err := parseStatusTemplate(format) 861 if err != nil { 862 return err 863 } 864 return tpl.Execute(w, data) 865 } 866 867 const expectedStatusDetail = `# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 868 869 870 %s[solve-meta] 871 analyzer-name = "dep" 872 analyzer-version = 1 873 input-imports = %s 874 solver-name = "gps-cdcl" 875 solver-version = 1 876 ` 877 878 func TestStatusDetailTemplates(t *testing.T) { 879 expectedStatusMetadata := rawDetailMetadata{ 880 AnalyzerName: "dep", 881 AnalyzerVersion: 1, 882 SolverName: "gps-cdcl", 883 SolverVersion: 1, 884 } 885 expectWithInputs := expectedStatusMetadata 886 expectWithInputs.InputImports = []string{"github.com/akutz/one", "github.com/akutz/three/a"} 887 888 testCases := []struct { 889 name string 890 tpl string 891 exp string 892 data rawDetail 893 }{ 894 { 895 name: "Lock Template No Projects", 896 tpl: statusLockTemplate, 897 exp: fmt.Sprintf(expectedStatusDetail, "", tomlStrSplit(nil)), 898 data: rawDetail{ 899 Metadata: expectedStatusMetadata, 900 }, 901 }, 902 { 903 name: "Lock Template", 904 tpl: statusLockTemplate, 905 exp: fmt.Sprintf(expectedStatusDetail, `[[projects]] 906 branch = "master" 907 digest = "1:cbcdef1234" 908 name = "github.com/akutz/one" 909 packages = ["."] 910 pruneopts = "UT" 911 revision = "b78744579491c1ceeaaa3b40205e56b0591b93a3" 912 913 [[projects]] 914 digest = "1:dbcdef1234" 915 name = "github.com/akutz/two" 916 packages = [ 917 ".", 918 "helloworld", 919 ] 920 pruneopts = "NUT" 921 revision = "12bd96e66386c1960ab0f74ced1362f66f552f7b" 922 version = "v1.0.0" 923 924 [[projects]] 925 branch = "feature/morning" 926 digest = "1:abcdef1234" 927 name = "github.com/akutz/three" 928 packages = [ 929 "a", 930 "b", 931 "c", 932 ] 933 pruneopts = "NUT" 934 revision = "890a5c3458b43e6104ff5da8dfa139d013d77544" 935 source = "https://github.com/mandy/three" 936 937 `, tomlStrSplit([]string{"github.com/akutz/one", "github.com/akutz/three/a"})), 938 data: rawDetail{ 939 Projects: []rawDetailProject{ 940 rawDetailProject{ 941 Locked: rawDetailVersion{ 942 Branch: "master", 943 Revision: "b78744579491c1ceeaaa3b40205e56b0591b93a3", 944 }, 945 Packages: []string{"."}, 946 ProjectRoot: "github.com/akutz/one", 947 PruneOpts: "UT", 948 Digest: "1:cbcdef1234", 949 }, 950 rawDetailProject{ 951 Locked: rawDetailVersion{ 952 Revision: "12bd96e66386c1960ab0f74ced1362f66f552f7b", 953 Version: "v1.0.0", 954 }, 955 ProjectRoot: "github.com/akutz/two", 956 Packages: []string{ 957 ".", 958 "helloworld", 959 }, 960 PruneOpts: "NUT", 961 Digest: "1:dbcdef1234", 962 }, 963 rawDetailProject{ 964 Locked: rawDetailVersion{ 965 Branch: "feature/morning", 966 Revision: "890a5c3458b43e6104ff5da8dfa139d013d77544", 967 }, 968 ProjectRoot: "github.com/akutz/three", 969 Packages: []string{ 970 "a", 971 "b", 972 "c", 973 }, 974 Source: "https://github.com/mandy/three", 975 PruneOpts: "NUT", 976 Digest: "1:abcdef1234", 977 }, 978 }, 979 Metadata: expectWithInputs, 980 }, 981 }, 982 } 983 984 for _, tc := range testCases { 985 t.Run(tc.name, func(t *testing.T) { 986 w := &bytes.Buffer{} 987 if err := execStatusTemplate(w, tc.tpl, tc.data); err != nil { 988 t.Error(err) 989 } 990 act := w.String() 991 if act != tc.exp { 992 t.Errorf( 993 "unexpected error: \n"+ 994 "(GOT):\n=== BEGIN ===\n%v\n=== END ===\n"+ 995 "(WNT):\n=== BEGIN ===\n%v\n=== END ===\n", 996 act, 997 tc.exp) 998 } 999 }) 1000 } 1001 }