From 4f22124fbb6e8417ab3ca9a65960e02030f1de13 Mon Sep 17 00:00:00 2001 From: Spencer Nelson Date: Fri, 11 Aug 2017 14:30:05 -0400 Subject: [PATCH] Add importer for github.com/LK4D4/vndr (#978) --- cmd/dep/glide_importer_test.go | 28 +- cmd/dep/godep_importer_test.go | 14 +- cmd/dep/root_analyzer.go | 1 + .../init/vndr/case1/final/Gopkg.lock | 21 ++ .../init/vndr/case1/final/Gopkg.toml | 4 + .../init/vndr/case1/initial/main.go | 16 + .../init/vndr/case1/initial/vendor.conf | 3 + .../init/vndr/case1/testcase.json | 13 + cmd/dep/testdata/vndr/golden.txt | 6 + cmd/dep/testdata/vndr/vendor.conf | 4 + cmd/dep/vndr_importer.go | 161 ++++++++++ cmd/dep/vndr_importer_test.go | 296 ++++++++++++++++++ 12 files changed, 545 insertions(+), 22 deletions(-) create mode 100644 cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.lock create mode 100644 cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.toml create mode 100644 cmd/dep/testdata/harness_tests/init/vndr/case1/initial/main.go create mode 100644 cmd/dep/testdata/harness_tests/init/vndr/case1/initial/vendor.conf create mode 100644 cmd/dep/testdata/harness_tests/init/vndr/case1/testcase.json create mode 100644 cmd/dep/testdata/vndr/golden.txt create mode 100644 cmd/dep/testdata/vndr/vendor.conf create mode 100644 cmd/dep/vndr_importer.go create mode 100644 cmd/dep/vndr_importer_test.go diff --git a/cmd/dep/glide_importer_test.go b/cmd/dep/glide_importer_test.go index ce624234bf..2478184492 100644 --- a/cmd/dep/glide_importer_test.go +++ b/cmd/dep/glide_importer_test.go @@ -18,8 +18,6 @@ import ( "github.com/pkg/errors" ) -const testGlideProjectRoot = "github.com/golang/notexist" - var ( discardLogger = log.New(ioutil.Discard, "", 0) ) @@ -110,7 +108,7 @@ func TestGlideConfig_Convert(t *testing.T) { yaml: glideYaml{ ExcludeDirs: []string{"samples"}, }, - projectRoot: testGlideProjectRoot, + projectRoot: testProjectRoot, wantIgnoreCount: 1, wantIgnoredPackages: []string{"github.com/golang/notexist/samples"}, }, @@ -119,7 +117,7 @@ func TestGlideConfig_Convert(t *testing.T) { Name: "github.com/golang/mismatched-package-name", ExcludeDirs: []string{"samples"}, }, - projectRoot: testGlideProjectRoot, + projectRoot: testProjectRoot, wantIgnoreCount: 1, wantIgnoredPackages: []string{"github.com/golang/notexist/samples"}, }, @@ -127,7 +125,7 @@ func TestGlideConfig_Convert(t *testing.T) { yaml: glideYaml{ Imports: []glidePackage{{Name: ""}}, }, - projectRoot: testGlideProjectRoot, + projectRoot: testProjectRoot, wantConvertErr: true, }, } @@ -243,10 +241,10 @@ func TestGlideConfig_Import(t *testing.T) { h.Must(err) defer sm.Release() - h.TempDir(filepath.Join("src", testGlideProjectRoot)) - h.TempCopy(filepath.Join(testGlideProjectRoot, glideYamlName), "glide/glide.yaml") - h.TempCopy(filepath.Join(testGlideProjectRoot, glideLockName), "glide/glide.lock") - projectRoot := h.Path(testGlideProjectRoot) + h.TempDir(filepath.Join("src", testProjectRoot)) + h.TempCopy(filepath.Join(testProjectRoot, glideYamlName), "glide/glide.yaml") + h.TempCopy(filepath.Join(testProjectRoot, glideLockName), "glide/glide.lock") + projectRoot := h.Path(testProjectRoot) // Capture stderr so we can verify output verboseOutput := &bytes.Buffer{} @@ -257,7 +255,7 @@ func TestGlideConfig_Import(t *testing.T) { t.Fatal("Expected the importer to detect the glide configuration files") } - m, l, err := g.Import(projectRoot, testGlideProjectRoot) + m, l, err := g.Import(projectRoot, testProjectRoot) h.Must(err) if m == nil { @@ -291,16 +289,16 @@ func TestGlideConfig_Import_MissingLockFile(t *testing.T) { h.Must(err) defer sm.Release() - h.TempDir(filepath.Join("src", testGlideProjectRoot)) - h.TempCopy(filepath.Join(testGlideProjectRoot, glideYamlName), "glide/glide.yaml") - projectRoot := h.Path(testGlideProjectRoot) + h.TempDir(filepath.Join("src", testProjectRoot)) + h.TempCopy(filepath.Join(testProjectRoot, glideYamlName), "glide/glide.yaml") + projectRoot := h.Path(testProjectRoot) g := newGlideImporter(ctx.Err, true, sm) if !g.HasDepMetadata(projectRoot) { t.Fatal("The glide importer should gracefully handle when only glide.yaml is present") } - m, l, err := g.Import(projectRoot, testGlideProjectRoot) + m, l, err := g.Import(projectRoot, testProjectRoot) h.Must(err) if m == nil { @@ -340,7 +338,7 @@ func TestGlideConfig_Convert_WarnsForUnusedFields(t *testing.T) { Imports: []glidePackage{pkg}, } - _, _, err = g.convert(testGlideProjectRoot) + _, _, err = g.convert(testProjectRoot) if err != nil { t.Fatal(err) } diff --git a/cmd/dep/godep_importer_test.go b/cmd/dep/godep_importer_test.go index 874455f22f..0dbe192132 100644 --- a/cmd/dep/godep_importer_test.go +++ b/cmd/dep/godep_importer_test.go @@ -15,7 +15,7 @@ import ( "github.com/pkg/errors" ) -const testGodepProjectRoot = "github.com/golang/notexist" +const testProjectRoot = "github.com/golang/notexist" func TestGodepConfig_Convert(t *testing.T) { testCases := map[string]struct { @@ -198,10 +198,10 @@ func TestGodepConfig_Import(t *testing.T) { cacheDir := "gps-repocache" h.TempDir(cacheDir) h.TempDir("src") - h.TempDir(filepath.Join("src", testGodepProjectRoot)) - h.TempCopy(filepath.Join(testGodepProjectRoot, godepPath), "godep/Godeps.json") + h.TempDir(filepath.Join("src", testProjectRoot)) + h.TempCopy(filepath.Join(testProjectRoot, godepPath), "godep/Godeps.json") - projectRoot := h.Path(testGodepProjectRoot) + projectRoot := h.Path(testProjectRoot) sm, err := gps.NewSourceManager(h.Path(cacheDir)) h.Must(err) defer sm.Release() @@ -215,7 +215,7 @@ func TestGodepConfig_Import(t *testing.T) { t.Fatal("Expected the importer to detect godep configuration file") } - m, l, err := g.Import(projectRoot, testGodepProjectRoot) + m, l, err := g.Import(projectRoot, testProjectRoot) h.Must(err) if m == nil { @@ -261,9 +261,9 @@ func TestGodepConfig_JsonLoad(t *testing.T) { ctx := newTestContext(h) - h.TempCopy(filepath.Join(testGodepProjectRoot, godepPath), "godep/Godeps.json") + h.TempCopy(filepath.Join(testProjectRoot, godepPath), "godep/Godeps.json") - projectRoot := h.Path(testGodepProjectRoot) + projectRoot := h.Path(testProjectRoot) g := newGodepImporter(ctx.Err, true, nil) err := g.load(projectRoot) diff --git a/cmd/dep/root_analyzer.go b/cmd/dep/root_analyzer.go index 2ef6f94cb5..76a4d6d817 100644 --- a/cmd/dep/root_analyzer.go +++ b/cmd/dep/root_analyzer.go @@ -73,6 +73,7 @@ func (a *rootAnalyzer) importManifestAndLock(dir string, pr gps.ProjectRoot, sup importers := []importer{ newGlideImporter(logger, a.ctx.Verbose, a.sm), newGodepImporter(logger, a.ctx.Verbose, a.sm), + newVndrImporter(logger, a.ctx.Verbose, a.sm), } for _, i := range importers { diff --git a/cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.lock b/cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.lock new file mode 100644 index 0000000000..ac445c05d2 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.lock @@ -0,0 +1,21 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/sdboyer/deptest" + packages = ["."] + revision = "3f4c3bea144e112a69bbe5d8d01c1b09a544253f" + version = "v0.8.1" + +[[projects]] + name = "github.com/sdboyer/deptestdos" + packages = ["."] + revision = "5c607206be5decd28e6263ffffdcee067266015e" + version = "v2.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "1ed417a0bec57ffe988fae1cba8f3d49994fb893394d61844e0b3c96d69573fe" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.toml b/cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.toml new file mode 100644 index 0000000000..aaf78303fa --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/vndr/case1/final/Gopkg.toml @@ -0,0 +1,4 @@ + +[[constraint]] + name = "github.com/sdboyer/deptestdos" + version = "2.0.0" diff --git a/cmd/dep/testdata/harness_tests/init/vndr/case1/initial/main.go b/cmd/dep/testdata/harness_tests/init/vndr/case1/initial/main.go new file mode 100644 index 0000000000..2b2c7c396e --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/vndr/case1/initial/main.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "github.com/sdboyer/deptestdos" +) + +func main() { + var x deptestdos.Bar + fmt.Println(x) +} diff --git a/cmd/dep/testdata/harness_tests/init/vndr/case1/initial/vendor.conf b/cmd/dep/testdata/harness_tests/init/vndr/case1/initial/vendor.conf new file mode 100644 index 0000000000..d91f18b3e2 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/vndr/case1/initial/vendor.conf @@ -0,0 +1,3 @@ +# Comment on its own line +github.com/sdboyer/deptest 3f4c3bea144e112a69bbe5d8d01c1b09a544253f https://github.com/sdboyer/deptest.git +github.com/sdboyer/deptestdos v2.0.0 diff --git a/cmd/dep/testdata/harness_tests/init/vndr/case1/testcase.json b/cmd/dep/testdata/harness_tests/init/vndr/case1/testcase.json new file mode 100644 index 0000000000..017dc4cd55 --- /dev/null +++ b/cmd/dep/testdata/harness_tests/init/vndr/case1/testcase.json @@ -0,0 +1,13 @@ +{ + "commands": [ + ["init", "-no-examples"] + ], + "error-expected": "", + "gopath-initial": { + "github.com/sdboyer/deptest": "3f4c3bea144e112a69bbe5d8d01c1b09a544253f" + }, + "vendor-final": [ + "github.com/sdboyer/deptest", + "github.com/sdboyer/deptestdos" + ] +} diff --git a/cmd/dep/testdata/vndr/golden.txt b/cmd/dep/testdata/vndr/golden.txt new file mode 100644 index 0000000000..a35fdc8669 --- /dev/null +++ b/cmd/dep/testdata/vndr/golden.txt @@ -0,0 +1,6 @@ +Detected vndr configuration file... +Converting from vendor.conf... + Using 3f4c3bea144e112a69bbe5d8d01c1b09a544253f as initial hint for imported dep github.com/sdboyer/deptest + Trying v0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest + Using ^2.0.0 as initial constraint for imported dep github.com/sdboyer/deptestdos + Trying * (v2.0.0) as initial lock for imported dep github.com/sdboyer/deptestdos diff --git a/cmd/dep/testdata/vndr/vendor.conf b/cmd/dep/testdata/vndr/vendor.conf new file mode 100644 index 0000000000..072166aa57 --- /dev/null +++ b/cmd/dep/testdata/vndr/vendor.conf @@ -0,0 +1,4 @@ +github.com/sdboyer/deptest 3f4c3bea144e112a69bbe5d8d01c1b09a544253f https://github.com/sdboyer/deptest.git # trailing comment +# line comment + +github.com/sdboyer/deptestdos v2.0.0 # trailing comment diff --git a/cmd/dep/vndr_importer.go b/cmd/dep/vndr_importer.go new file mode 100644 index 0000000000..db936401c1 --- /dev/null +++ b/cmd/dep/vndr_importer.go @@ -0,0 +1,161 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bufio" + "log" + "os" + "path/filepath" + "strings" + + "github.com/golang/dep" + fb "github.com/golang/dep/internal/feedback" + "github.com/golang/dep/internal/gps" + "github.com/pkg/errors" +) + +func vndrFile(dir string) string { + return filepath.Join(dir, "vendor.conf") +} + +type vndrImporter struct { + packages []vndrPackage + + logger *log.Logger + verbose bool + sm gps.SourceManager +} + +func newVndrImporter(log *log.Logger, verbose bool, sm gps.SourceManager) *vndrImporter { + return &vndrImporter{ + logger: log, + verbose: verbose, + sm: sm, + } +} + +func (v *vndrImporter) Name() string { return "vndr" } + +func (v *vndrImporter) HasDepMetadata(dir string) bool { + _, err := os.Stat(vndrFile(dir)) + return err == nil +} + +func (v *vndrImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + v.logger.Println("Detected vndr configuration file...") + + err := v.loadVndrFile(dir) + if err != nil { + return nil, nil, errors.Wrapf(err, "unable to load vndr file") + } + + return v.convert(pr) +} + +func (v *vndrImporter) loadVndrFile(dir string) error { + v.logger.Printf("Converting from vendor.conf...") + + f, err := os.Open(vndrFile(dir)) + if err != nil { + return errors.Wrapf(err, "Unable to open %s", vndrFile(dir)) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + pkg, err := parseVndrLine(scanner.Text()) + if err != nil { + return errors.Wrapf(err, "unable to parse line") + } + if pkg == nil { + // Could be an empty line or one which is just a comment + continue + } + v.packages = append(v.packages, *pkg) + } + + if scanner.Err() != nil { + return errors.Wrapf(err, "unable to read %s", vndrFile(dir)) + } + + return nil +} + +func (v *vndrImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + var ( + manifest = &dep.Manifest{ + Constraints: make(gps.ProjectConstraints), + } + lock = &dep.Lock{} + err error + ) + + for _, pkg := range v.packages { + pc := gps.ProjectConstraint{ + Ident: gps.ProjectIdentifier{ + ProjectRoot: gps.ProjectRoot(pkg.importPath), + Source: pkg.repository, + }, + } + pc.Constraint, err = v.sm.InferConstraint(pkg.revision, pc.Ident) + if err != nil { + return nil, nil, errors.Wrapf(err, "Unable to interpret revision specifier '%s' for package %s", pkg.importPath, pkg.revision) + } + + manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{ + Source: pc.Ident.Source, + Constraint: pc.Constraint, + } + fb.NewConstraintFeedback(pc, fb.DepTypeImported).LogFeedback(v.logger) + + revision := gps.Revision(pkg.revision) + version, err := lookupVersionForLockedProject(pc.Ident, pc.Constraint, revision, v.sm) + if err != nil { + v.logger.Println(err.Error()) + } + + lp := gps.NewLockedProject(pc.Ident, version, nil) + + lock.P = append(lock.P, lp) + fb.NewLockedProjectFeedback(lp, fb.DepTypeImported).LogFeedback(v.logger) + } + + return manifest, lock, nil +} + +type vndrPackage struct { + importPath string + revision string + repository string +} + +func parseVndrLine(line string) (*vndrPackage, error) { + commentIdx := strings.Index(line, "#") + if commentIdx >= 0 { + line = line[:commentIdx] + } + line = strings.TrimSpace(line) + + if line == "" { + return nil, nil + } + + parts := strings.Fields(line) + + if !(len(parts) == 2 || len(parts) == 3) { + return nil, errors.Errorf("invalid config format: %q", line) + } + + pkg := &vndrPackage{ + importPath: parts[0], + revision: parts[1], + } + if len(parts) == 3 { + pkg.repository = parts[2] + } + + return pkg, nil +} diff --git a/cmd/dep/vndr_importer_test.go b/cmd/dep/vndr_importer_test.go new file mode 100644 index 0000000000..2d7fb1a045 --- /dev/null +++ b/cmd/dep/vndr_importer_test.go @@ -0,0 +1,296 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "log" + "path/filepath" + "reflect" + "testing" + + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/test" + "github.com/pkg/errors" +) + +func TestVndrConfig_Convert(t *testing.T) { + testCases := map[string]struct { + vndr []vndrPackage + wantConvertErr bool + matchPairedVersion bool + projectRoot gps.ProjectRoot + wantConstraint string + wantRevision gps.Revision + wantVersion string + wantLockCount int + }{ + "project": { + vndr: []vndrPackage{{ + importPath: "github.com/sdboyer/deptest", + revision: "v0.8.0", + repository: "https://github.com/sdboyer/deptest.git", + }}, + matchPairedVersion: false, + projectRoot: gps.ProjectRoot("github.com/sdboyer/deptest"), + wantConstraint: "^0.8.0", + wantRevision: gps.Revision("ff2948a2ac8f538c4ecd55962e919d1e13e74baf"), + wantVersion: "v0.8.0", + wantLockCount: 1, + }, + "with semver suffix": { + vndr: []vndrPackage{{ + importPath: "github.com/sdboyer/deptest", + revision: "v1.12.0-12-g2fd980e", + }}, + matchPairedVersion: false, + projectRoot: gps.ProjectRoot("github.com/sdboyer/deptest"), + wantConstraint: "^1.12.0-12-g2fd980e", + wantVersion: "v1.0.0", + wantLockCount: 1, + }, + "hash revision": { + vndr: []vndrPackage{{ + importPath: "github.com/sdboyer/deptest", + revision: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf", + }}, + matchPairedVersion: false, + projectRoot: gps.ProjectRoot("github.com/sdboyer/deptest"), + wantConstraint: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf", + wantVersion: "v1.0.0", + wantLockCount: 1, + }, + "missing importPath": { + vndr: []vndrPackage{{ + revision: "v1.0.0", + }}, + wantConvertErr: true, + }, + } + + h := test.NewHelper(t) + defer h.Cleanup() + + ctx := newTestContext(h) + sm, err := ctx.SourceManager() + h.Must(err) + defer sm.Release() + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + v := newVndrImporter(discardLogger, true, sm) + v.packages = testCase.vndr + + manifest, lock, err := v.convert(testCase.projectRoot) + if err != nil { + if testCase.wantConvertErr { + return + } + t.Fatal(err) + } else { + if testCase.wantConvertErr { + t.Fatal("expected err, have nil") + } + } + + if len(lock.P) != testCase.wantLockCount { + t.Fatalf("Expected lock to have %d project(s), got %d", + testCase.wantLockCount, + len(lock.P)) + } + + d, ok := manifest.Constraints[testCase.projectRoot] + if !ok { + t.Fatalf("Expected the manifest to have a dependency for '%s' but got none", + testCase.projectRoot) + } + + c := d.Constraint.String() + if c != testCase.wantConstraint { + t.Fatalf("Expected manifest constraint to be %s, got %s", testCase.wantConstraint, c) + } + + p := lock.P[0] + if p.Ident().ProjectRoot != testCase.projectRoot { + t.Fatalf("Expected the lock to have a project for '%s' but got '%s'", + testCase.projectRoot, + p.Ident().ProjectRoot) + } + + lv := p.Version() + lpv, ok := lv.(gps.PairedVersion) + + if !ok { + if testCase.matchPairedVersion { + t.Fatalf("Expected locked version to be PairedVersion but got %T", lv) + } + + return + } + + ver := lpv.String() + if ver != testCase.wantVersion { + t.Fatalf("Expected locked version to be '%s', got %s", testCase.wantVersion, ver) + } + + if testCase.wantRevision != "" { + rev := lpv.Revision() + if rev != testCase.wantRevision { + t.Fatalf("Expected locked revision to be '%s', got %s", + testCase.wantRevision, + rev) + } + } + }) + } + +} + +func TestVndrConfig_Import(t *testing.T) { + h := test.NewHelper(t) + defer h.Cleanup() + + ctx := newTestContext(h) + sm, err := ctx.SourceManager() + h.Must(err) + defer sm.Release() + + h.TempDir(filepath.Join("src", testProjectRoot)) + h.TempCopy(vndrFile(testProjectRoot), "vndr/vendor.conf") + projectRoot := h.Path(testProjectRoot) + + logOutput := bytes.NewBuffer(nil) + ctx.Err = log.New(logOutput, "", 0) + + v := newVndrImporter(ctx.Err, false, sm) + if !v.HasDepMetadata(projectRoot) { + t.Fatal("Expected the importer to detect vndr configuration file") + } + + m, l, err := v.Import(projectRoot, testProjectRoot) + h.Must(err) + + constraint, err := gps.NewSemverConstraint("^2.0.0") + h.Must(err) + wantM := &dep.Manifest{ + Constraints: gps.ProjectConstraints{ + "github.com/sdboyer/deptest": gps.ProjectProperties{ + Source: "https://github.com/sdboyer/deptest.git", + Constraint: gps.Revision("3f4c3bea144e112a69bbe5d8d01c1b09a544253f"), + }, + "github.com/sdboyer/deptestdos": gps.ProjectProperties{ + Constraint: constraint, + }, + }, + } + if !reflect.DeepEqual(wantM, m) { + t.Errorf("unexpected manifest\nhave=%+v\nwant=%+v", m, wantM) + } + + wantL := &dep.Lock{ + P: []gps.LockedProject{ + gps.NewLockedProject( + gps.ProjectIdentifier{ + ProjectRoot: "github.com/sdboyer/deptest", + Source: "https://github.com/sdboyer/deptest.git", + }, + gps.NewVersion("v0.8.1").Pair(gps.Revision("3f4c3bea144e112a69bbe5d8d01c1b09a544253f")), + nil, + ), + gps.NewLockedProject( + gps.ProjectIdentifier{ + ProjectRoot: "github.com/sdboyer/deptestdos", + }, + gps.Revision("v2.0.0"), + nil, + ), + }, + } + if !reflect.DeepEqual(wantL, l) { + t.Errorf("unexpected lock\nhave=%+v\nwant=%+v", l, wantL) + } + + goldenFile := "vndr/golden.txt" + got := logOutput.String() + want := h.GetTestFileString(goldenFile) + if want != got { + if *test.UpdateGolden { + if err := h.WriteTestFile(goldenFile, got); err != nil { + t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", goldenFile)) + } + } else { + t.Fatalf("expected %s, got %s", want, got) + } + } +} + +func TestParseVndrLine(t *testing.T) { + testcase := func(in string, wantPkg *vndrPackage, wantErr error) func(*testing.T) { + return func(t *testing.T) { + havePkg, haveErr := parseVndrLine(in) + switch { + case wantPkg == nil: + if havePkg != nil { + t.Errorf("expected nil package, have %v", havePkg) + } + case havePkg == nil: + if wantPkg != nil { + t.Errorf("expected non-nil package %v, have nil", wantPkg) + } + default: + if !reflect.DeepEqual(havePkg, wantPkg) { + t.Errorf("unexpected package, have=%v, want=%v", *havePkg, *wantPkg) + } + } + + switch { + case wantErr == nil: + if haveErr != nil { + t.Errorf("expected nil err, have %v", haveErr) + } + case haveErr == nil: + if wantErr != nil { + t.Errorf("expected non-nil err %v, have nil", wantErr) + } + default: + if haveErr.Error() != wantErr.Error() { + t.Errorf("expected err=%q, have err=%q", wantErr.Error(), haveErr.Error()) + } + } + } + } + t.Run("normal line", + testcase("github.com/golang/notreal v1.0.0", + &vndrPackage{ + importPath: "github.com/golang/notreal", + revision: "v1.0.0", + }, nil)) + + t.Run("with repo", + testcase("github.com/golang/notreal v1.0.0 https://github.com/golang/notreal", + &vndrPackage{ + importPath: "github.com/golang/notreal", + revision: "v1.0.0", + repository: "https://github.com/golang/notreal", + }, nil)) + + t.Run("trailing comment", + testcase("github.com/golang/notreal v1.0.0 https://github.com/golang/notreal # cool comment", + &vndrPackage{ + importPath: "github.com/golang/notreal", + revision: "v1.0.0", + repository: "https://github.com/golang/notreal", + }, nil)) + + t.Run("empty line", testcase("", nil, nil)) + t.Run("comment line", testcase("# comment", nil, nil)) + t.Run("comment line with leading whitespace", testcase(" # comment", nil, nil)) + + t.Run("missing revision", + testcase("github.com/golang/notreal", nil, + errors.New("invalid config format: \"github.com/golang/notreal\""), + )) +}