From 53960fec7e2abebf3b27cc40001f84a9b1144086 Mon Sep 17 00:00:00 2001 From: Carolyn Van Slyck Date: Sat, 9 Sep 2017 17:11:09 -0500 Subject: [PATCH] cmd/dep: Move importers under internal/importers --- cmd/dep/base_importer_test.go | 571 ------------------ cmd/dep/ensure.go | 2 +- cmd/dep/gopath_scanner.go | 29 +- cmd/dep/gopath_scanner_test.go | 21 +- cmd/dep/init_test.go | 5 +- cmd/dep/prune_test.go | 4 + cmd/dep/root_analyzer.go | 20 +- internal/gps/identifier.go | 27 + .../importers/base}/base_importer.go | 84 +-- internal/importers/base/base_importer_test.go | 387 ++++++++++++ .../importers/glide}/glide_importer.go | 61 +- .../importers/glide}/glide_importer_test.go | 138 ++--- .../importers/glide/testdata}/glide.lock | 0 .../importers/glide/testdata}/glide.yaml | 0 .../importers/glide/testdata}/golden.txt | 0 .../importers/godep}/godep_importer.go | 42 +- .../importers/godep}/godep_importer_test.go | 81 ++- .../importers/godep/testdata}/Godeps.json | 0 .../importers/godep/testdata}/golden.txt | 0 .../importers/govend}/govend_importer.go | 43 +- .../importers/govend}/govend_importer_test.go | 67 +- .../importers/govend/testdata}/golden.txt | 0 .../importers/govend/testdata}/vendor.yml | 0 internal/importers/importers.go | 35 ++ internal/importers/importertest/testcase.go | 193 ++++++ internal/importers/importertest/testdata.go | 64 ++ .../importers/vndr/testdata}/golden.txt | 0 .../importers/vndr/testdata}/vendor.conf | 0 .../importers/vndr}/vndr_importer.go | 38 +- .../importers/vndr}/vndr_importer_test.go | 97 +-- 30 files changed, 1065 insertions(+), 944 deletions(-) delete mode 100644 cmd/dep/base_importer_test.go rename {cmd/dep => internal/importers/base}/base_importer.go (76%) create mode 100644 internal/importers/base/base_importer_test.go rename {cmd/dep => internal/importers/glide}/glide_importer.go (72%) rename {cmd/dep => internal/importers/glide}/glide_importer_test.go (50%) rename {cmd/dep/testdata/init/glide => internal/importers/glide/testdata}/glide.lock (100%) rename {cmd/dep/testdata/init/glide => internal/importers/glide/testdata}/glide.yaml (100%) rename {cmd/dep/testdata/init/glide => internal/importers/glide/testdata}/golden.txt (100%) rename {cmd/dep => internal/importers/godep}/godep_importer.go (57%) rename {cmd/dep => internal/importers/godep}/godep_importer_test.go (62%) rename {cmd/dep/testdata/init/godep => internal/importers/godep/testdata}/Godeps.json (100%) rename {cmd/dep/testdata/init/godep => internal/importers/godep/testdata}/golden.txt (100%) rename {cmd/dep => internal/importers/govend}/govend_importer.go (58%) rename {cmd/dep => internal/importers/govend}/govend_importer_test.go (65%) rename {cmd/dep/testdata/init/govend => internal/importers/govend/testdata}/golden.txt (100%) rename {cmd/dep/testdata/init/govend => internal/importers/govend/testdata}/vendor.yml (100%) create mode 100644 internal/importers/importers.go create mode 100644 internal/importers/importertest/testcase.go create mode 100644 internal/importers/importertest/testdata.go rename {cmd/dep/testdata/init/vndr => internal/importers/vndr/testdata}/golden.txt (100%) rename {cmd/dep/testdata/init/vndr => internal/importers/vndr/testdata}/vendor.conf (100%) rename {cmd/dep => internal/importers/vndr}/vndr_importer.go (67%) rename {cmd/dep => internal/importers/vndr}/vndr_importer_test.go (64%) diff --git a/cmd/dep/base_importer_test.go b/cmd/dep/base_importer_test.go deleted file mode 100644 index 011df83461..0000000000 --- a/cmd/dep/base_importer_test.go +++ /dev/null @@ -1,571 +0,0 @@ -// Copyright 2016 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" - "sort" - "strings" - "testing" - - "github.com/golang/dep" - "github.com/golang/dep/internal/gps" - "github.com/golang/dep/internal/test" - "github.com/pkg/errors" -) - -const ( - importerTestProject = "github.com/carolynvs/deptest-importers" - importerTestProjectSrc = "https://github.com/carolynvs/deptest-importers.git" - importerTestUntaggedRev = "9b670d143bfb4a00f7461451d5c4a62f80e9d11d" - importerTestUntaggedRevAbbrv = "v1.0.0-1-g9b670d1" - importerTestBeta1Tag = "beta1" - importerTestBeta1Rev = "7913ab26988c6fb1e16225f845a178e8849dd254" - importerTestV2Branch = "v2" - importerTestV2Rev = "45dcf5a09c64b48b6e836028a3bc672b19b9d11d" - importerTestV2PatchTag = "v2.0.0-alpha1" - importerTestV2PatchRev = "347760b50204948ea63e531dd6560e56a9adde8f" - importerTestV1Tag = "v1.0.0" - importerTestV1Rev = "d0c29640b17f77426b111f4c1640d716591aa70e" - importerTestV1PatchTag = "v1.0.2" - importerTestV1PatchRev = "788963efe22e3e6e24c776a11a57468bb2fcd780" - importerTestV1Constraint = "^1.0.0" - importerTestMultiTaggedRev = "34cf993cc346f65601fe4356dd68bd54d20a1bfe" - importerTestMultiTaggedSemverTag = "v1.0.4" - importerTestMultiTaggedPlainTag = "stable" -) - -func TestBaseImporter_IsTag(t *testing.T) { - testcases := map[string]struct { - input string - wantIsTag bool - wantTag gps.Version - }{ - "non-semver tag": { - input: importerTestBeta1Tag, - wantIsTag: true, - wantTag: gps.NewVersion(importerTestBeta1Tag).Pair(importerTestBeta1Rev), - }, - "semver-tag": { - input: importerTestV1PatchTag, - wantIsTag: true, - wantTag: gps.NewVersion(importerTestV1PatchTag).Pair(importerTestV1PatchRev)}, - "untagged revision": { - input: importerTestUntaggedRev, - wantIsTag: false, - }, - "branch name": { - input: importerTestV2Branch, - wantIsTag: false, - }, - "empty": { - input: "", - wantIsTag: false, - }, - } - - pi := gps.ProjectIdentifier{ProjectRoot: importerTestProject} - - for name, tc := range testcases { - name := name - tc := tc - t.Run(name, func(t *testing.T) { - h := test.NewHelper(t) - defer h.Cleanup() - h.Parallel() - - ctx := newTestContext(h) - sm, err := ctx.SourceManager() - h.Must(err) - defer sm.Release() - - i := newBaseImporter(discardLogger, false, sm) - gotIsTag, gotTag, err := i.isTag(pi, tc.input) - h.Must(err) - - if tc.wantIsTag != gotIsTag { - t.Fatalf("unexpected isTag result for %v: \n\t(GOT) %v \n\t(WNT) %v", - tc.input, gotIsTag, tc.wantIsTag) - } - - if tc.wantTag != gotTag { - t.Fatalf("unexpected tag for %v: \n\t(GOT) %v \n\t(WNT) %v", - tc.input, gotTag, tc.wantTag) - } - }) - } -} - -func TestBaseImporter_LookupVersionForLockedProject(t *testing.T) { - testcases := map[string]struct { - revision gps.Revision - constraint gps.Constraint - wantVersion string - }{ - "match revision to tag": { - revision: importerTestV1PatchRev, - wantVersion: importerTestV1PatchTag, - }, - "match revision with multiple tags using constraint": { - revision: importerTestMultiTaggedRev, - constraint: gps.NewVersion(importerTestMultiTaggedPlainTag), - wantVersion: importerTestMultiTaggedPlainTag, - }, - "revision with multiple tags with no constraint defaults to best match": { - revision: importerTestMultiTaggedRev, - wantVersion: importerTestMultiTaggedSemverTag, - }, - "revision with multiple tags with nonmatching constraint defaults to best match": { - revision: importerTestMultiTaggedRev, - constraint: gps.NewVersion("thismatchesnothing"), - wantVersion: importerTestMultiTaggedSemverTag, - }, - "untagged revision fallback to branch constraint": { - revision: importerTestUntaggedRev, - constraint: gps.NewBranch("master"), - wantVersion: "master", - }, - "fallback to revision": { - revision: importerTestUntaggedRev, - wantVersion: importerTestUntaggedRev, - }, - } - - pi := gps.ProjectIdentifier{ProjectRoot: importerTestProject} - - for name, tc := range testcases { - name := name - tc := tc - t.Run(name, func(t *testing.T) { - h := test.NewHelper(t) - defer h.Cleanup() - h.Parallel() - - ctx := newTestContext(h) - sm, err := ctx.SourceManager() - h.Must(err) - defer sm.Release() - - i := newBaseImporter(discardLogger, false, sm) - v, err := i.lookupVersionForLockedProject(pi, tc.constraint, tc.revision) - h.Must(err) - - gotVersion := v.String() - if gotVersion != tc.wantVersion { - t.Fatalf("unexpected locked version: \n\t(GOT) %v\n\t(WNT) %v", gotVersion, tc.wantVersion) - } - }) - } -} - -func TestBaseImporter_ImportProjects(t *testing.T) { - testcases := map[string]struct { - convertTestCase - projects []importedPackage - }{ - "tag constraints are ignored": { - convertTestCase{ - wantConstraint: "*", - wantVersion: importerTestBeta1Tag, - wantRevision: importerTestBeta1Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestBeta1Rev, - ConstraintHint: importerTestBeta1Tag, - }, - }, - }, - "tag lock hints lock to tagged revision": { - convertTestCase{ - wantConstraint: "*", - wantVersion: importerTestBeta1Tag, - wantRevision: importerTestBeta1Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestBeta1Tag, - }, - }, - }, - "untagged revision ignores range constraint": { - convertTestCase{ - wantConstraint: "*", - wantRevision: importerTestUntaggedRev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestUntaggedRev, - ConstraintHint: importerTestV1Constraint, - }, - }, - }, - "untagged revision keeps branch constraint": { - convertTestCase{ - wantConstraint: "master", - wantVersion: "master", - wantRevision: importerTestUntaggedRev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestUntaggedRev, - ConstraintHint: "master", - }, - }, - }, - "HEAD revisions default constraint to the matching branch": { - convertTestCase{ - defaultConstraintFromLock: true, - wantConstraint: importerTestV2Branch, - wantVersion: importerTestV2Branch, - wantRevision: importerTestV2Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV2Rev, - }, - }, - }, - "Semver tagged revisions default to ^VERSION": { - convertTestCase{ - defaultConstraintFromLock: true, - wantConstraint: importerTestV1Constraint, - wantVersion: importerTestV1Tag, - wantRevision: importerTestV1Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV1Rev, - }, - }, - }, - "Semver lock hint defaults constraint to ^VERSION": { - convertTestCase{ - defaultConstraintFromLock: true, - wantConstraint: importerTestV1Constraint, - wantVersion: importerTestV1Tag, - wantRevision: importerTestV1Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV1Tag, - }, - }, - }, - "Semver constraint hint": { - convertTestCase{ - wantConstraint: importerTestV1Constraint, - wantVersion: importerTestV1PatchTag, - wantRevision: importerTestV1PatchRev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV1PatchRev, - ConstraintHint: importerTestV1Constraint, - }, - }, - }, - "Semver prerelease lock hint": { - convertTestCase{ - wantConstraint: importerTestV2Branch, - wantVersion: importerTestV2PatchTag, - wantRevision: importerTestV2PatchRev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV2PatchRev, - ConstraintHint: importerTestV2Branch, - }, - }, - }, - "Revision constraints are ignored": { - convertTestCase{ - wantConstraint: "*", - wantVersion: importerTestV1Tag, - wantRevision: importerTestV1Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV1Rev, - ConstraintHint: importerTestV1Rev, - }, - }, - }, - "Branch constraint hint": { - convertTestCase{ - wantConstraint: "master", - wantVersion: importerTestV1Tag, - wantRevision: importerTestV1Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV1Rev, - ConstraintHint: "master", - }, - }, - }, - "Non-matching semver constraint is ignored": { - convertTestCase{ - wantConstraint: "*", - wantVersion: importerTestV1Tag, - wantRevision: importerTestV1Rev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestV1Rev, - ConstraintHint: "^2.0.0", - }, - }, - }, - "git describe constraint is ignored": { - convertTestCase{ - wantConstraint: "*", - wantRevision: importerTestUntaggedRev, - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: importerTestUntaggedRev, - ConstraintHint: importerTestUntaggedRevAbbrv, - }, - }, - }, - "consolidate subpackages under root": { - convertTestCase{ - wantConstraint: "master", - wantVersion: "master", - wantRevision: importerTestUntaggedRev, - }, - []importedPackage{ - { - Name: importerTestProject + "/subpkA", - ConstraintHint: "master", - }, - { - Name: importerTestProject, - LockHint: importerTestUntaggedRev, - }, - }, - }, - "ignore duplicate packages": { - convertTestCase{ - wantConstraint: "*", - wantRevision: importerTestUntaggedRev, - }, - []importedPackage{ - { - Name: importerTestProject + "/subpkgA", - LockHint: importerTestUntaggedRev, // first wins - }, - { - Name: importerTestProject + "/subpkgB", - LockHint: importerTestV1Rev, - }, - }, - }, - "skip empty lock hints": { - convertTestCase{ - wantConstraint: "*", - wantRevision: "", - }, - []importedPackage{ - { - Name: importerTestProject, - LockHint: "", - }, - }, - }, - } - - for name, tc := range testcases { - name := name - tc := tc - t.Run(name, func(t *testing.T) { - t.Parallel() - - err := tc.Exec(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { - i := newBaseImporter(logger, true, sm) - convertErr := i.importPackages(tc.projects, tc.defaultConstraintFromLock) - return i.manifest, i.lock, convertErr - }) - if err != nil { - t.Fatalf("%#v", err) - } - }) - } -} - -// convertTestCase is a common set of validations applied to the result -// of an importer converting from an external config format to dep's. -type convertTestCase struct { - defaultConstraintFromLock bool - wantConvertErr bool - wantSourceRepo string - wantConstraint string - wantRevision gps.Revision - wantVersion string - wantIgnored []string - wantWarning string -} - -func (tc convertTestCase) Exec(t *testing.T, convert func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error)) error { - h := test.NewHelper(t) - defer h.Cleanup() - - ctx := newTestContext(h) - sm, err := ctx.SourceManager() - h.Must(err) - defer sm.Release() - - // Capture stderr so we can verify warnings - output := &bytes.Buffer{} - ctx.Err = log.New(output, "", 0) - - manifest, lock, convertErr := convert(ctx.Err, sm) - return tc.validate(manifest, lock, convertErr, output) -} - -// validate returns an error if any of the testcase validations failed. -func (tc convertTestCase) validate(manifest *dep.Manifest, lock *dep.Lock, convertErr error, output *bytes.Buffer) error { - if tc.wantConvertErr { - if convertErr == nil { - return errors.New("Expected the conversion to fail, but it did not return an error") - } - return nil - } - - if convertErr != nil { - return errors.Wrap(convertErr, "Expected the conversion to pass, but it returned an error") - } - - if !equalSlice(manifest.Ignored, tc.wantIgnored) { - return errors.Errorf("unexpected set of ignored projects: \n\t(GOT) %v \n\t(WNT) %v", - manifest.Ignored, tc.wantIgnored) - } - - wantConstraintCount := 0 - if tc.wantConstraint != "" { - wantConstraintCount = 1 - } - gotConstraintCount := len(manifest.Constraints) - if gotConstraintCount != wantConstraintCount { - return errors.Errorf("unexpected number of constraints: \n\t(GOT) %v \n\t(WNT) %v", - gotConstraintCount, wantConstraintCount) - } - - if tc.wantConstraint != "" { - d, ok := manifest.Constraints[importerTestProject] - if !ok { - return errors.Errorf("Expected the manifest to have a dependency for '%v'", - importerTestProject) - } - - gotConstraint := d.Constraint.String() - if gotConstraint != tc.wantConstraint { - return errors.Errorf("unexpected constraint: \n\t(GOT) %v \n\t(WNT) %v", - gotConstraint, tc.wantConstraint) - } - - } - - // Lock checks. - wantLockCount := 0 - if tc.wantRevision != "" { - wantLockCount = 1 - } - gotLockCount := 0 - if lock != nil { - gotLockCount = len(lock.P) - } - if gotLockCount != wantLockCount { - return errors.Errorf("unexpected number of locked projects: \n\t(GOT) %v \n\t(WNT) %v", - gotLockCount, wantLockCount) - } - - if tc.wantRevision != "" { - lp := lock.P[0] - - gotProjectRoot := lp.Ident().ProjectRoot - if gotProjectRoot != importerTestProject { - return errors.Errorf("unexpected root project in lock: \n\t(GOT) %v \n\t(WNT) %v", - gotProjectRoot, importerTestProject) - } - - gotSource := lp.Ident().Source - if gotSource != tc.wantSourceRepo { - return errors.Errorf("unexpected source repository: \n\t(GOT) %v \n\t(WNT) %v", - gotSource, tc.wantSourceRepo) - } - - // Break down the locked "version" into a version (optional) and revision - var gotVersion string - var gotRevision gps.Revision - if lpv, ok := lp.Version().(gps.PairedVersion); ok { - gotVersion = lpv.String() - gotRevision = lpv.Revision() - } else if lr, ok := lp.Version().(gps.Revision); ok { - gotRevision = lr - } else { - return errors.New("could not determine the type of the locked version") - } - - if gotRevision != tc.wantRevision { - return errors.Errorf("unexpected locked revision: \n\t(GOT) %v \n\t(WNT) %v", - gotRevision, - tc.wantRevision) - } - if gotVersion != tc.wantVersion { - return errors.Errorf("unexpected locked version: \n\t(GOT) %v \n\t(WNT) %v", - gotVersion, - tc.wantVersion) - } - } - - if tc.wantWarning != "" { - if !strings.Contains(output.String(), tc.wantWarning) { - return errors.Errorf("Expected the output to include the warning '%s'", tc.wantWarning) - } - } - - return nil -} - -// equalSlice is comparing two string slices for equality. -func equalSlice(a, b []string) bool { - if a == nil && b == nil { - return true - } - - if a == nil || b == nil { - return false - } - - if len(a) != len(b) { - return false - } - - sort.Strings(a) - sort.Strings(b) - for i := range a { - if a[i] != b[i] { - return false - } - } - - return true -} diff --git a/cmd/dep/ensure.go b/cmd/dep/ensure.go index 583ae33e33..f39cc26963 100644 --- a/cmd/dep/ensure.go +++ b/cmd/dep/ensure.go @@ -619,7 +619,7 @@ func (cmd *ensureCommand) runAdd(ctx *dep.Ctx, args []string, p *dep.Project, sm // could have been converted into a source by the solver. if proj.Ident().ProjectRoot == pr { found = true - pp = getProjectPropertiesFromVersion(proj.Version()) + pp = gps.GetProjectPropertiesFromVersion(proj.Version()) break } } diff --git a/cmd/dep/gopath_scanner.go b/cmd/dep/gopath_scanner.go index 48d3189ac9..20bea3aa59 100644 --- a/cmd/dep/gopath_scanner.go +++ b/cmd/dep/gopath_scanner.go @@ -150,33 +150,6 @@ func contains(a []string, b string) bool { return false } -// getProjectPropertiesFromVersion takes a gps.Version and returns a proper -// gps.ProjectProperties with Constraint value based on the provided version. -func getProjectPropertiesFromVersion(v gps.Version) gps.ProjectProperties { - pp := gps.ProjectProperties{} - - // extract version and ignore if it's revision only - switch tv := v.(type) { - case gps.PairedVersion: - v = tv.Unpair() - case gps.Revision: - return pp - } - - switch v.Type() { - case gps.IsBranch, gps.IsVersion: - pp.Constraint = v - case gps.IsSemver: - c, err := gps.NewSemverConstraintIC(v.String()) - if err != nil { - panic(err) - } - pp.Constraint = c - } - - return pp -} - type projectData struct { constraints gps.ProjectConstraints // constraints that could be found dependencies map[gps.ProjectRoot][]string // all dependencies (imports) found by project root @@ -230,7 +203,7 @@ func (g *gopathScanner) scanGopathForDependencies() (projectData, error) { } ondisk[pr] = v - pp := getProjectPropertiesFromVersion(v) + pp := gps.GetProjectPropertiesFromVersion(v) if pp.Constraint != nil || pp.Source != "" { constraints[pr] = pp } diff --git a/cmd/dep/gopath_scanner_test.go b/cmd/dep/gopath_scanner_test.go index 87591646de..57c5c2823f 100644 --- a/cmd/dep/gopath_scanner_test.go +++ b/cmd/dep/gopath_scanner_test.go @@ -5,6 +5,8 @@ package main import ( + "io/ioutil" + "log" "reflect" "testing" @@ -16,11 +18,24 @@ import ( const testProject1 string = "github.com/sdboyer/deptest" const testProject2 string = "github.com/sdboyer/deptestdos" +// NewTestContext creates a unique context with its own GOPATH for a single test. +func NewTestContext(h *test.Helper) *dep.Ctx { + h.TempDir("src") + pwd := h.Path(".") + discardLogger := log.New(ioutil.Discard, "", 0) + + return &dep.Ctx{ + GOPATH: pwd, + Out: discardLogger, + Err: discardLogger, + } +} + func TestGopathScanner_OverlayManifestConstraints(t *testing.T) { t.Parallel() h := test.NewHelper(t) - ctx := newTestContext(h) + ctx := NewTestContext(h) pi1 := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(testProject1)} pi2 := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(testProject2)} @@ -72,7 +87,7 @@ func TestGopathScanner_OverlayLockProjects(t *testing.T) { t.Parallel() h := test.NewHelper(t) - ctx := newTestContext(h) + ctx := NewTestContext(h) rootM := dep.NewManifest() pi1 := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(testProject1)} @@ -164,7 +179,7 @@ func TestGetProjectPropertiesFromVersion(t *testing.T) { } for _, c := range cases { - actualProp := getProjectPropertiesFromVersion(c.version.(gps.Version)) + actualProp := gps.GetProjectPropertiesFromVersion(c.version.(gps.Version)) if !reflect.DeepEqual(c.want, actualProp.Constraint) { t.Fatalf("Constraints are not as expected: \n\t(GOT) %v\n\t(WNT) %v", actualProp.Constraint, c.want) } diff --git a/cmd/dep/init_test.go b/cmd/dep/init_test.go index f853fe0193..18c2fb540f 100644 --- a/cmd/dep/init_test.go +++ b/cmd/dep/init_test.go @@ -5,9 +5,8 @@ package main import ( - "testing" - "path/filepath" + "testing" "github.com/golang/dep" "github.com/golang/dep/internal/gps" @@ -18,7 +17,7 @@ func TestGetDirectDependencies_ConsolidatesRootProjects(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() - ctx := newTestContext(h) + ctx := NewTestContext(h) sm, err := ctx.SourceManager() h.Must(err) defer sm.Release() diff --git a/cmd/dep/prune_test.go b/cmd/dep/prune_test.go index a1bc2b7ffe..8a9c1d1d96 100644 --- a/cmd/dep/prune_test.go +++ b/cmd/dep/prune_test.go @@ -5,6 +5,8 @@ package main import ( + "io/ioutil" + "log" "path/filepath" "reflect" "sort" @@ -27,6 +29,8 @@ func TestCalculatePrune(t *testing.T) { filepath.FromSlash("github.com/keep/pkg/sub"), } + discardLogger := log.New(ioutil.Discard, "", 0) + got, err := calculatePrune(h.Path(vendorDir), toKeep, discardLogger) if err != nil { t.Fatal(err) diff --git a/cmd/dep/root_analyzer.go b/cmd/dep/root_analyzer.go index 471b673b33..0db40519b2 100644 --- a/cmd/dep/root_analyzer.go +++ b/cmd/dep/root_analyzer.go @@ -11,16 +11,9 @@ import ( "github.com/golang/dep" fb "github.com/golang/dep/internal/feedback" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers" ) -// importer handles importing configuration from other dependency managers into -// the dep configuration format. -type importer interface { - Name() string - Import(path string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) - HasDepMetadata(dir string) bool -} - // rootAnalyzer supplies manifest/lock data from both dep and external tool's // configuration files. // * When used on the root project, it imports only from external tools. @@ -66,14 +59,7 @@ func (a *rootAnalyzer) importManifestAndLock(dir string, pr gps.ProjectRoot, sup logger = log.New(ioutil.Discard, "", 0) } - importers := []importer{ - newGlideImporter(logger, a.ctx.Verbose, a.sm), - newGodepImporter(logger, a.ctx.Verbose, a.sm), - newVndrImporter(logger, a.ctx.Verbose, a.sm), - newGovendImporter(logger, a.ctx.Verbose, a.sm), - } - - for _, i := range importers { + for _, i := range importers.BuildAll(logger, a.ctx.Verbose, a.sm) { if i.HasDepMetadata(dir) { a.ctx.Err.Printf("Importing configuration from %s. These are only initial constraints, and are further refined during the solve process.", i.Name()) m, l, err := i.Import(dir, pr) @@ -134,7 +120,7 @@ func (a *rootAnalyzer) FinalizeRootManifestAndLock(m *dep.Manifest, l *dep.Lock, // New constraints: in new lock and dir dep but not in manifest if _, ok := a.directDeps[string(pr)]; ok { if _, ok := m.Constraints[pr]; !ok { - pp := getProjectPropertiesFromVersion(y.Version()) + pp := gps.GetProjectPropertiesFromVersion(y.Version()) if pp.Constraint != nil { m.Constraints[pr] = pp pc := gps.ProjectConstraint{Ident: y.Ident(), Constraint: pp.Constraint} diff --git a/internal/gps/identifier.go b/internal/gps/identifier.go index 5b2c9d4f68..5ee8dc35d1 100644 --- a/internal/gps/identifier.go +++ b/internal/gps/identifier.go @@ -167,6 +167,33 @@ type ProjectProperties struct { Constraint Constraint } +// GetProjectPropertiesFromVersion takes a Version and returns a proper +// ProjectProperties with Constraint value based on the provided version. +func GetProjectPropertiesFromVersion(v Version) ProjectProperties { + pp := ProjectProperties{} + + // extract version and ignore if it's revision only + switch tv := v.(type) { + case PairedVersion: + v = tv.Unpair() + case Revision: + return pp + } + + switch v.Type() { + case IsBranch, IsVersion: + pp.Constraint = v + case IsSemver: + c, err := NewSemverConstraintIC(v.String()) + if err != nil { + panic(err) + } + pp.Constraint = c + } + + return pp +} + // bimodalIdentifiers are used to track work to be done in the unselected queue. type bimodalIdentifier struct { id ProjectIdentifier diff --git a/cmd/dep/base_importer.go b/internal/importers/base/base_importer.go similarity index 76% rename from cmd/dep/base_importer.go rename to internal/importers/base/base_importer.go index 8e81e8bf42..58b3d8f9ac 100644 --- a/cmd/dep/base_importer.go +++ b/internal/importers/base/base_importer.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package base import ( "log" @@ -13,30 +13,30 @@ import ( "github.com/pkg/errors" ) -// baseImporter provides a common implementation for importing from other +// Importer provides a common implementation for importing from other // dependency managers. -type baseImporter struct { - logger *log.Logger - verbose bool - sm gps.SourceManager - manifest *dep.Manifest - lock *dep.Lock +type Importer struct { + Logger *log.Logger + Verbose bool + Sm gps.SourceManager + Manifest *dep.Manifest + Lock *dep.Lock } -// newBaseImporter creates a new baseImporter for embedding in an importer. -func newBaseImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *baseImporter { - return &baseImporter{ - logger: logger, - verbose: verbose, - manifest: dep.NewManifest(), - lock: &dep.Lock{}, - sm: sm, +// NewImporter creates a new Importer for embedding in an importer. +func NewImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *Importer { + return &Importer{ + Logger: logger, + Verbose: verbose, + Manifest: dep.NewManifest(), + Lock: &dep.Lock{}, + Sm: sm, } } // isTag determines if the specified value is a tag (plain or semver). -func (i *baseImporter) isTag(pi gps.ProjectIdentifier, value string) (bool, gps.Version, error) { - versions, err := i.sm.ListVersions(pi) +func (i *Importer) isTag(pi gps.ProjectIdentifier, value string) (bool, gps.Version, error) { + versions, err := i.Sm.ListVersions(pi) if err != nil { return false, nil, errors.Wrapf(err, "unable to list versions for %s(%s)", pi.ProjectRoot, pi.Source) } @@ -58,9 +58,9 @@ func (i *baseImporter) isTag(pi gps.ProjectIdentifier, value string) (bool, gps. // project based on the locked revision and the constraint from the manifest. // First try matching the revision to a version, then try the constraint from the // manifest, then finally the revision. -func (i *baseImporter) lookupVersionForLockedProject(pi gps.ProjectIdentifier, c gps.Constraint, rev gps.Revision) (gps.Version, error) { +func (i *Importer) lookupVersionForLockedProject(pi gps.ProjectIdentifier, c gps.Constraint, rev gps.Revision) (gps.Version, error) { // Find the version that goes with this revision, if any - versions, err := i.sm.ListVersions(pi) + versions, err := i.Sm.ListVersions(pi) if err != nil { return rev, errors.Wrapf(err, "Unable to lookup the version represented by %s in %s(%s). Falling back to locking the revision only.", rev, pi.ProjectRoot, pi.Source) } @@ -98,9 +98,9 @@ func (i *baseImporter) lookupVersionForLockedProject(pi gps.ProjectIdentifier, c return rev, nil } -// importedPackage is a common intermediate representation of a package imported +// ImportedPackage is a common intermediate representation of a package imported // from an external tool's configuration. -type importedPackage struct { +type ImportedPackage struct { // Required. The package path, not necessarily the project root. Name string @@ -118,18 +118,18 @@ type importedPackage struct { // for the same project root. type importedProject struct { Root gps.ProjectRoot - importedPackage + ImportedPackage } // loadPackages consolidates all package references into a set of project roots. -func (i *baseImporter) loadPackages(packages []importedPackage) ([]importedProject, error) { +func (i *Importer) loadPackages(packages []ImportedPackage) ([]importedProject, error) { // preserve the original order of the packages so that messages that // are printed as they are processed are in a consistent order. orderedProjects := make([]importedProject, 0, len(packages)) projects := make(map[gps.ProjectRoot]*importedProject, len(packages)) for _, pkg := range packages { - pr, err := i.sm.DeduceProjectRoot(pkg.Name) + pr, err := i.Sm.DeduceProjectRoot(pkg.Name) if err != nil { return nil, errors.Wrapf(err, "Cannot determine the project root for %s", pkg.Name) } @@ -161,8 +161,8 @@ func (i *baseImporter) loadPackages(packages []importedPackage) ([]importedProje return orderedProjects, nil } -// importPackages loads imported packages into the manifest and lock. -// - defaultConstraintFromLock specifies if a constraint should be defaulted +// ImportPackages loads imported packages into the manifest and lock. +// - DefaultConstraintFromLock specifies if a constraint should be defaulted // based on the locked version when there wasn't a constraint hint. // // Rules: @@ -172,7 +172,7 @@ func (i *baseImporter) loadPackages(packages []importedPackage) ([]importedProje // * Revision constraints are ignored. // * Versions that don't satisfy the constraint, drop the constraint. // * Untagged revisions ignore non-branch constraint hints. -func (i *baseImporter) importPackages(packages []importedPackage, defaultConstraintFromLock bool) (err error) { +func (i *Importer) ImportPackages(packages []ImportedPackage, DefaultConstraintFromLock bool) (err error) { projects, err := i.loadPackages(packages) if err != nil { return err @@ -186,7 +186,7 @@ func (i *baseImporter) importPackages(packages []importedPackage, defaultConstra }, } - pc.Constraint, err = i.sm.InferConstraint(prj.ConstraintHint, pc.Ident) + pc.Constraint, err = i.Sm.InferConstraint(prj.ConstraintHint, pc.Ident) if err != nil { pc.Constraint = gps.Any() } @@ -206,13 +206,13 @@ func (i *baseImporter) importPackages(packages []importedPackage, defaultConstra version, err = i.lookupVersionForLockedProject(pc.Ident, pc.Constraint, revision) if err != nil { version = nil - i.logger.Println(err) + i.Logger.Println(err) } } // Default the constraint based on the locked version - if defaultConstraintFromLock && prj.ConstraintHint == "" && version != nil { - props := getProjectPropertiesFromVersion(version) + if DefaultConstraintFromLock && prj.ConstraintHint == "" && version != nil { + props := gps.GetProjectPropertiesFromVersion(version) if props.Constraint != nil { pc.Constraint = props.Constraint } @@ -221,8 +221,8 @@ func (i *baseImporter) importPackages(packages []importedPackage, defaultConstra // Ignore pinned constraints if i.isConstraintPinned(pc.Constraint) { - if i.verbose { - i.logger.Printf(" Ignoring pinned constraint %v for %v.\n", pc.Constraint, pc.Ident) + if i.Verbose { + i.Logger.Printf(" Ignoring pinned constraint %v for %v.\n", pc.Constraint, pc.Ident) } pc.Constraint = gps.Any() } @@ -230,22 +230,22 @@ func (i *baseImporter) importPackages(packages []importedPackage, defaultConstra // Ignore constraints which conflict with the locked revision, so that // solve doesn't later change the revision to satisfy the constraint. if !i.testConstraint(pc.Constraint, version) { - if i.verbose { - i.logger.Printf(" Ignoring constraint %v for %v because it would invalidate the locked version %v.\n", pc.Constraint, pc.Ident, version) + if i.Verbose { + i.Logger.Printf(" Ignoring constraint %v for %v because it would invalidate the locked version %v.\n", pc.Constraint, pc.Ident, version) } pc.Constraint = gps.Any() } - i.manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{ + i.Manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{ Source: pc.Ident.Source, Constraint: pc.Constraint, } - fb.NewConstraintFeedback(pc, fb.DepTypeImported).LogFeedback(i.logger) + fb.NewConstraintFeedback(pc, fb.DepTypeImported).LogFeedback(i.Logger) if version != nil { lp := gps.NewLockedProject(pc.Ident, version, nil) - i.lock.P = append(i.lock.P, lp) - fb.NewLockedProjectFeedback(lp, fb.DepTypeImported).LogFeedback(i.logger) + i.Lock.P = append(i.Lock.P, lp) + fb.NewLockedProjectFeedback(lp, fb.DepTypeImported).LogFeedback(i.Logger) } } @@ -253,7 +253,7 @@ func (i *baseImporter) importPackages(packages []importedPackage, defaultConstra } // isConstraintPinned returns if a constraint is pinned to a specific revision. -func (i *baseImporter) isConstraintPinned(c gps.Constraint) bool { +func (i *Importer) isConstraintPinned(c gps.Constraint) bool { if version, isVersion := c.(gps.Version); isVersion { switch version.Type() { case gps.IsRevision, gps.IsVersion: @@ -264,7 +264,7 @@ func (i *baseImporter) isConstraintPinned(c gps.Constraint) bool { } // testConstraint verifies that the constraint won't invalidate the locked version. -func (i *baseImporter) testConstraint(c gps.Constraint, v gps.Version) bool { +func (i *Importer) testConstraint(c gps.Constraint, v gps.Version) bool { // Assume branch constraints are satisfied if version, isVersion := c.(gps.Version); isVersion { if version.Type() == gps.IsBranch { diff --git a/internal/importers/base/base_importer_test.go b/internal/importers/base/base_importer_test.go new file mode 100644 index 0000000000..fcb930d31b --- /dev/null +++ b/internal/importers/base/base_importer_test.go @@ -0,0 +1,387 @@ +// Copyright 2016 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 base + +import ( + "log" + "testing" + + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/importertest" + "github.com/golang/dep/internal/test" +) + +func TestBaseImporter_IsTag(t *testing.T) { + testcases := map[string]struct { + input string + WantIsTag bool + WantTag gps.Version + }{ + "non-semver tag": { + input: importertest.Beta1Tag, + WantIsTag: true, + WantTag: gps.NewVersion(importertest.Beta1Tag).Pair(importertest.Beta1Rev), + }, + "semver-tag": { + input: importertest.V1PatchTag, + WantIsTag: true, + WantTag: gps.NewVersion(importertest.V1PatchTag).Pair(importertest.V1PatchRev)}, + "untagged revision": { + input: importertest.UntaggedRev, + WantIsTag: false, + }, + "branch name": { + input: importertest.V2Branch, + WantIsTag: false, + }, + "empty": { + input: "", + WantIsTag: false, + }, + } + + pi := gps.ProjectIdentifier{ProjectRoot: importertest.Project} + + for name, tc := range testcases { + name := name + tc := tc + t.Run(name, func(t *testing.T) { + h := test.NewHelper(t) + defer h.Cleanup() + h.Parallel() + + ctx := importertest.NewTestContext(h) + sm, err := ctx.SourceManager() + h.Must(err) + defer sm.Release() + + i := NewImporter(ctx.Err, ctx.Verbose, sm) + gotIsTag, gotTag, err := i.isTag(pi, tc.input) + h.Must(err) + + if tc.WantIsTag != gotIsTag { + t.Fatalf("unexpected isTag result for %v: \n\t(GOT) %v \n\t(WNT) %v", + tc.input, gotIsTag, tc.WantIsTag) + } + + if tc.WantTag != gotTag { + t.Fatalf("unexpected tag for %v: \n\t(GOT) %v \n\t(WNT) %v", + tc.input, gotTag, tc.WantTag) + } + }) + } +} + +func TestBaseImporter_LookupVersionForLockedProject(t *testing.T) { + testcases := map[string]struct { + revision gps.Revision + constraint gps.Constraint + WantVersion string + }{ + "match revision to tag": { + revision: importertest.V1PatchRev, + WantVersion: importertest.V1PatchTag, + }, + "match revision with multiple tags using constraint": { + revision: importertest.MultiTaggedRev, + constraint: gps.NewVersion(importertest.MultiTaggedPlainTag), + WantVersion: importertest.MultiTaggedPlainTag, + }, + "revision with multiple tags with no constraint defaults to best match": { + revision: importertest.MultiTaggedRev, + WantVersion: importertest.MultiTaggedSemverTag, + }, + "revision with multiple tags with nonmatching constraint defaults to best match": { + revision: importertest.MultiTaggedRev, + constraint: gps.NewVersion("thismatchesnothing"), + WantVersion: importertest.MultiTaggedSemverTag, + }, + "untagged revision fallback to branch constraint": { + revision: importertest.UntaggedRev, + constraint: gps.NewBranch("master"), + WantVersion: "master", + }, + "fallback to revision": { + revision: importertest.UntaggedRev, + WantVersion: importertest.UntaggedRev, + }, + } + + pi := gps.ProjectIdentifier{ProjectRoot: importertest.Project} + + for name, tc := range testcases { + name := name + tc := tc + t.Run(name, func(t *testing.T) { + h := test.NewHelper(t) + defer h.Cleanup() + h.Parallel() + + ctx := importertest.NewTestContext(h) + sm, err := ctx.SourceManager() + h.Must(err) + defer sm.Release() + + i := NewImporter(ctx.Err, ctx.Verbose, sm) + v, err := i.lookupVersionForLockedProject(pi, tc.constraint, tc.revision) + h.Must(err) + + gotVersion := v.String() + if gotVersion != tc.WantVersion { + t.Fatalf("unexpected locked version: \n\t(GOT) %v\n\t(WNT) %v", gotVersion, tc.WantVersion) + } + }) + } +} + +func TestBaseImporter_ImportProjects(t *testing.T) { + testcases := map[string]struct { + importertest.TestCase + projects []ImportedPackage + }{ + "tag constraints are ignored": { + importertest.TestCase{ + WantConstraint: "*", + WantVersion: importertest.Beta1Tag, + WantRevision: importertest.Beta1Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.Beta1Rev, + ConstraintHint: importertest.Beta1Tag, + }, + }, + }, + "tag lock hints Lock to tagged revision": { + importertest.TestCase{ + WantConstraint: "*", + WantVersion: importertest.Beta1Tag, + WantRevision: importertest.Beta1Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.Beta1Tag, + }, + }, + }, + "untagged revision ignores range constraint": { + importertest.TestCase{ + WantConstraint: "*", + WantRevision: importertest.UntaggedRev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.UntaggedRev, + ConstraintHint: importertest.V1Constraint, + }, + }, + }, + "untagged revision keeps branch constraint": { + importertest.TestCase{ + WantConstraint: "master", + WantVersion: "master", + WantRevision: importertest.UntaggedRev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.UntaggedRev, + ConstraintHint: "master", + }, + }, + }, + "HEAD revisions default constraint to the matching branch": { + importertest.TestCase{ + DefaultConstraintFromLock: true, + WantConstraint: importertest.V2Branch, + WantVersion: importertest.V2Branch, + WantRevision: importertest.V2Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V2Rev, + }, + }, + }, + "Semver tagged revisions default to ^VERSION": { + importertest.TestCase{ + DefaultConstraintFromLock: true, + WantConstraint: importertest.V1Constraint, + WantVersion: importertest.V1Tag, + WantRevision: importertest.V1Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V1Rev, + }, + }, + }, + "Semver lock hint defaults constraint to ^VERSION": { + importertest.TestCase{ + DefaultConstraintFromLock: true, + WantConstraint: importertest.V1Constraint, + WantVersion: importertest.V1Tag, + WantRevision: importertest.V1Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V1Tag, + }, + }, + }, + "Semver constraint hint": { + importertest.TestCase{ + WantConstraint: importertest.V1Constraint, + WantVersion: importertest.V1PatchTag, + WantRevision: importertest.V1PatchRev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V1PatchRev, + ConstraintHint: importertest.V1Constraint, + }, + }, + }, + "Semver prerelease lock hint": { + importertest.TestCase{ + WantConstraint: importertest.V2Branch, + WantVersion: importertest.V2PatchTag, + WantRevision: importertest.V2PatchRev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V2PatchRev, + ConstraintHint: importertest.V2Branch, + }, + }, + }, + "Revision constraints are ignored": { + importertest.TestCase{ + WantConstraint: "*", + WantVersion: importertest.V1Tag, + WantRevision: importertest.V1Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V1Rev, + ConstraintHint: importertest.V1Rev, + }, + }, + }, + "Branch constraint hint": { + importertest.TestCase{ + WantConstraint: "master", + WantVersion: importertest.V1Tag, + WantRevision: importertest.V1Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V1Rev, + ConstraintHint: "master", + }, + }, + }, + "Non-matching semver constraint is ignored": { + importertest.TestCase{ + WantConstraint: "*", + WantVersion: importertest.V1Tag, + WantRevision: importertest.V1Rev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.V1Rev, + ConstraintHint: "^2.0.0", + }, + }, + }, + "git describe constraint is ignored": { + importertest.TestCase{ + WantConstraint: "*", + WantRevision: importertest.UntaggedRev, + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: importertest.UntaggedRev, + ConstraintHint: importertest.UntaggedRevAbbrv, + }, + }, + }, + "consolidate subpackages under root": { + importertest.TestCase{ + WantConstraint: "master", + WantVersion: "master", + WantRevision: importertest.UntaggedRev, + }, + []ImportedPackage{ + { + Name: importertest.Project + "/subpkA", + ConstraintHint: "master", + }, + { + Name: importertest.Project, + LockHint: importertest.UntaggedRev, + }, + }, + }, + "ignore duplicate packages": { + importertest.TestCase{ + WantConstraint: "*", + WantRevision: importertest.UntaggedRev, + }, + []ImportedPackage{ + { + Name: importertest.Project + "/subpkgA", + LockHint: importertest.UntaggedRev, // first wins + }, + { + Name: importertest.Project + "/subpkgB", + LockHint: importertest.V1Rev, + }, + }, + }, + "skip empty lock hints": { + importertest.TestCase{ + WantConstraint: "*", + WantRevision: "", + }, + []ImportedPackage{ + { + Name: importertest.Project, + LockHint: "", + }, + }, + }, + } + + for name, tc := range testcases { + name := name + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + err := tc.Execute(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { + i := NewImporter(logger, true, sm) + convertErr := i.ImportPackages(tc.projects, tc.DefaultConstraintFromLock) + return i.Manifest, i.Lock, convertErr + }) + if err != nil { + t.Fatalf("%#v", err) + } + }) + } +} diff --git a/cmd/dep/glide_importer.go b/internal/importers/glide/glide_importer.go similarity index 72% rename from cmd/dep/glide_importer.go rename to internal/importers/glide/glide_importer.go index e5dae70bff..1745b1375c 100644 --- a/cmd/dep/glide_importer.go +++ b/internal/importers/glide/glide_importer.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package glide import ( "bytes" @@ -16,22 +16,24 @@ import ( "github.com/golang/dep" "github.com/golang/dep/internal/fs" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/base" "github.com/pkg/errors" ) const glideYamlName = "glide.yaml" const glideLockName = "glide.lock" -// glideImporter imports glide configuration into the dep configuration format. -type glideImporter struct { - *baseImporter +// Importer imports glide configuration into the dep configuration format. +type Importer struct { + *base.Importer glideConfig glideYaml glideLock glideLock lockFound bool } -func newGlideImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *glideImporter { - return &glideImporter{baseImporter: newBaseImporter(logger, verbose, sm)} +// NewImporter for glide. +func NewImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *Importer { + return &Importer{Importer: base.NewImporter(logger, verbose, sm)} } type glideYaml struct { @@ -64,11 +66,13 @@ type glideLockedPackage struct { Repository string `yaml:"repo"` } -func (g *glideImporter) Name() string { +// Name of the importer. +func (g *Importer) Name() string { return "glide" } -func (g *glideImporter) HasDepMetadata(dir string) bool { +// HasDepMetadata checks if a directory contains config that the importer can handle. +func (g *Importer) HasDepMetadata(dir string) bool { // Only require glide.yaml, the lock is optional y := filepath.Join(dir, glideYamlName) if _, err := os.Stat(y); err != nil { @@ -78,7 +82,8 @@ func (g *glideImporter) HasDepMetadata(dir string) bool { return true } -func (g *glideImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { +// Import the config found in the directory. +func (g *Importer) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { err := g.load(dir) if err != nil { return nil, nil, err @@ -88,11 +93,11 @@ func (g *glideImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, * } // load the glide configuration files. -func (g *glideImporter) load(projectDir string) error { - g.logger.Println("Detected glide configuration files...") +func (g *Importer) load(projectDir string) error { + g.Logger.Println("Detected glide configuration files...") y := filepath.Join(projectDir, glideYamlName) - if g.verbose { - g.logger.Printf(" Loading %s", y) + if g.Verbose { + g.Logger.Printf(" Loading %s", y) } yb, err := ioutil.ReadFile(y) if err != nil { @@ -105,8 +110,8 @@ func (g *glideImporter) load(projectDir string) error { l := filepath.Join(projectDir, glideLockName) if exists, _ := fs.IsRegular(l); exists { - if g.verbose { - g.logger.Printf(" Loading %s", l) + if g.Verbose { + g.Logger.Printf(" Loading %s", l) } g.lockFound = true lb, err := ioutil.ReadFile(l) @@ -125,7 +130,7 @@ func (g *glideImporter) load(projectDir string) error { } // convert the glide configuration files into dep configuration files. -func (g *glideImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { +func (g *Importer) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { projectName := string(pr) task := bytes.NewBufferString("Converting from glide.yaml") @@ -133,10 +138,10 @@ func (g *glideImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e task.WriteString(" and glide.lock") } task.WriteString("...") - g.logger.Println(task) + g.Logger.Println(task) numPkgs := len(g.glideConfig.Imports) + len(g.glideConfig.TestImports) + len(g.glideLock.Imports) + len(g.glideLock.TestImports) - packages := make([]importedPackage, 0, numPkgs) + packages := make([]base.ImportedPackage, 0, numPkgs) // Constraints for _, pkg := range append(g.glideConfig.Imports, g.glideConfig.TestImports...) { @@ -146,16 +151,16 @@ func (g *glideImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e } // Warn - if g.verbose { + if g.Verbose { if pkg.OS != "" { - g.logger.Printf(" The %s package specified an os, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.\n", pkg.Name) + g.Logger.Printf(" The %s package specified an os, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.\n", pkg.Name) } if pkg.Arch != "" { - g.logger.Printf(" The %s package specified an arch, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.\n", pkg.Name) + g.Logger.Printf(" The %s package specified an arch, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.\n", pkg.Name) } } - ip := importedPackage{ + ip := base.ImportedPackage{ Name: pkg.Name, Source: pkg.Repository, ConstraintHint: pkg.Reference, @@ -170,7 +175,7 @@ func (g *glideImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e return nil, nil, errors.New("invalid glide lock: Name is required") } - ip := importedPackage{ + ip := base.ImportedPackage{ Name: pkg.Name, Source: pkg.Repository, LockHint: pkg.Revision, @@ -178,23 +183,23 @@ func (g *glideImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e packages = append(packages, ip) } - err := g.importPackages(packages, false) + err := g.ImportPackages(packages, false) if err != nil { return nil, nil, errors.Wrap(err, "invalid glide configuration") } // Ignores - g.manifest.Ignored = append(g.manifest.Ignored, g.glideConfig.Ignores...) + g.Manifest.Ignored = append(g.Manifest.Ignored, g.glideConfig.Ignores...) if len(g.glideConfig.ExcludeDirs) > 0 { if g.glideConfig.Name != "" && g.glideConfig.Name != projectName { - g.logger.Printf(" Glide thinks the package is '%s' but dep thinks it is '%s', using dep's value.\n", g.glideConfig.Name, projectName) + g.Logger.Printf(" Glide thinks the package is '%s' but dep thinks it is '%s', using dep's value.\n", g.glideConfig.Name, projectName) } for _, dir := range g.glideConfig.ExcludeDirs { pkg := path.Join(projectName, dir) - g.manifest.Ignored = append(g.manifest.Ignored, pkg) + g.Manifest.Ignored = append(g.Manifest.Ignored, pkg) } } - return g.manifest, g.lock, nil + return g.Manifest, g.Lock, nil } diff --git a/cmd/dep/glide_importer_test.go b/internal/importers/glide/glide_importer_test.go similarity index 50% rename from cmd/dep/glide_importer_test.go rename to internal/importers/glide/glide_importer_test.go index d5d4c1b2e2..067dfa7603 100644 --- a/cmd/dep/glide_importer_test.go +++ b/internal/importers/glide/glide_importer_test.go @@ -2,116 +2,102 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package glide import ( "bytes" - "io/ioutil" "log" "path/filepath" "testing" "github.com/golang/dep" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/importertest" "github.com/golang/dep/internal/test" "github.com/pkg/errors" ) -var ( - discardLogger = log.New(ioutil.Discard, "", 0) -) - -func newTestContext(h *test.Helper) *dep.Ctx { - h.TempDir("src") - pwd := h.Path(".") - return &dep.Ctx{ - GOPATH: pwd, - Out: discardLogger, - Err: discardLogger, - } -} - func TestGlideConfig_Convert(t *testing.T) { testCases := map[string]struct { yaml glideYaml lock glideLock - convertTestCase + importertest.TestCase }{ "project": { glideYaml{ Imports: []glidePackage{ { - Name: importerTestProject, - Repository: importerTestProjectSrc, - Reference: importerTestV2Branch, + Name: importertest.Project, + Repository: importertest.ProjectSrc, + Reference: importertest.V2Branch, }, }, }, glideLock{ Imports: []glideLockedPackage{ { - Name: importerTestProject, - Repository: importerTestProjectSrc, - Revision: importerTestV2PatchRev, + Name: importertest.Project, + Repository: importertest.ProjectSrc, + Revision: importertest.V2PatchRev, }, }, }, - convertTestCase{ - wantSourceRepo: importerTestProjectSrc, - wantConstraint: importerTestV2Branch, - wantRevision: importerTestV2PatchRev, - wantVersion: importerTestV2PatchTag, + importertest.TestCase{ + WantSourceRepo: importertest.ProjectSrc, + WantConstraint: importertest.V2Branch, + WantRevision: importertest.V2PatchRev, + WantVersion: importertest.V2PatchTag, }, }, "test project": { glideYaml{ Imports: []glidePackage{ { - Name: importerTestProject, - Repository: importerTestProjectSrc, - Reference: importerTestV2Branch, + Name: importertest.Project, + Repository: importertest.ProjectSrc, + Reference: importertest.V2Branch, }, }, }, glideLock{ Imports: []glideLockedPackage{ { - Name: importerTestProject, - Repository: importerTestProjectSrc, - Revision: importerTestV2PatchRev, + Name: importertest.Project, + Repository: importertest.ProjectSrc, + Revision: importertest.V2PatchRev, }, }, }, - convertTestCase{ - wantSourceRepo: importerTestProjectSrc, - wantConstraint: importerTestV2Branch, - wantRevision: importerTestV2PatchRev, - wantVersion: importerTestV2PatchTag, + importertest.TestCase{ + WantSourceRepo: importertest.ProjectSrc, + WantConstraint: importertest.V2Branch, + WantRevision: importertest.V2PatchRev, + WantVersion: importertest.V2PatchTag, }, }, "yaml only": { glideYaml{ Imports: []glidePackage{ { - Name: importerTestProject, - Repository: importerTestProjectSrc, - Reference: importerTestV2Branch, + Name: importertest.Project, + Repository: importertest.ProjectSrc, + Reference: importertest.V2Branch, }, }, }, glideLock{}, - convertTestCase{ - wantSourceRepo: importerTestProjectSrc, - wantConstraint: importerTestV2Branch, + importertest.TestCase{ + WantSourceRepo: importertest.ProjectSrc, + WantConstraint: importertest.V2Branch, }, }, "ignored package": { glideYaml{ - Ignores: []string{importerTestProject}, + Ignores: []string{importertest.Project}, }, glideLock{}, - convertTestCase{ - wantIgnored: []string{importerTestProject}, + importertest.TestCase{ + WantIgnored: []string{importertest.Project}, }, }, "exclude dir": { @@ -119,8 +105,8 @@ func TestGlideConfig_Convert(t *testing.T) { ExcludeDirs: []string{"samples"}, }, glideLock{}, - convertTestCase{ - wantIgnored: []string{testProjectRoot + "/samples"}, + importertest.TestCase{ + WantIgnored: []string{importertest.RootProject + "/samples"}, }, }, "exclude dir ignores mismatched package name": { @@ -129,8 +115,8 @@ func TestGlideConfig_Convert(t *testing.T) { ExcludeDirs: []string{"samples"}, }, glideLock{}, - convertTestCase{ - wantIgnored: []string{testProjectRoot + "/samples"}, + importertest.TestCase{ + WantIgnored: []string{importertest.RootProject + "/samples"}, }, }, "missing package name": { @@ -138,36 +124,36 @@ func TestGlideConfig_Convert(t *testing.T) { Imports: []glidePackage{{Name: ""}}, }, glideLock{}, - convertTestCase{ - wantConvertErr: true, + importertest.TestCase{ + WantConvertErr: true, }, }, "warn unused os field": { glideYaml{ Imports: []glidePackage{ { - Name: importerTestProject, + Name: importertest.Project, OS: "windows", }, }}, glideLock{}, - convertTestCase{ - wantConstraint: "*", - wantWarning: "specified an os", + importertest.TestCase{ + WantConstraint: "*", + WantWarning: "specified an os", }, }, "warn unused arch field": { glideYaml{ Imports: []glidePackage{ { - Name: importerTestProject, + Name: importertest.Project, Arch: "i686", }, }}, glideLock{}, - convertTestCase{ - wantConstraint: "*", - wantWarning: "specified an arch", + importertest.TestCase{ + WantConstraint: "*", + WantWarning: "specified an arch", }, }, } @@ -178,11 +164,11 @@ func TestGlideConfig_Convert(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - err := testCase.Exec(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { - g := newGlideImporter(logger, true, sm) + err := testCase.Execute(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { + g := NewImporter(logger, true, sm) g.glideConfig = testCase.yaml g.glideLock = testCase.lock - return g.convert(testProjectRoot) + return g.convert(importertest.RootProject) }) if err != nil { t.Fatalf("%#v", err) @@ -195,26 +181,26 @@ func TestGlideConfig_Import(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() - ctx := newTestContext(h) + ctx := importertest.NewTestContext(h) sm, err := ctx.SourceManager() h.Must(err) defer sm.Release() - h.TempDir(filepath.Join("src", testProjectRoot)) - h.TempCopy(filepath.Join(testProjectRoot, glideYamlName), "init/glide/glide.yaml") - h.TempCopy(filepath.Join(testProjectRoot, glideLockName), "init/glide/glide.lock") - projectRoot := h.Path(testProjectRoot) + h.TempDir(filepath.Join("src", importertest.RootProject)) + h.TempCopy(filepath.Join(importertest.RootProject, glideYamlName), "glide.yaml") + h.TempCopy(filepath.Join(importertest.RootProject, glideLockName), "glide.lock") + projectRoot := h.Path(importertest.RootProject) // Capture stderr so we can verify output verboseOutput := &bytes.Buffer{} ctx.Err = log.New(verboseOutput, "", 0) - g := newGlideImporter(ctx.Err, false, sm) // Disable verbose so that we don't print values that change each test run + g := NewImporter(ctx.Err, false, sm) // Disable verbose so that we don't print values that change each test run if !g.HasDepMetadata(projectRoot) { t.Fatal("Expected the importer to detect the glide configuration files") } - m, l, err := g.Import(projectRoot, testProjectRoot) + m, l, err := g.Import(projectRoot, importertest.RootProject) h.Must(err) if m == nil { @@ -225,16 +211,16 @@ func TestGlideConfig_Import(t *testing.T) { t.Fatal("Expected the lock to be generated") } - goldenFile := "init/glide/golden.txt" + goldenFile := "golden.txt" got := verboseOutput.String() - want := h.GetTestFileString(goldenFile) - if want != got { + 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("want %s, got %s", want, got) + t.Fatalf("want %s, got %s", Want, got) } } } diff --git a/cmd/dep/testdata/init/glide/glide.lock b/internal/importers/glide/testdata/glide.lock similarity index 100% rename from cmd/dep/testdata/init/glide/glide.lock rename to internal/importers/glide/testdata/glide.lock diff --git a/cmd/dep/testdata/init/glide/glide.yaml b/internal/importers/glide/testdata/glide.yaml similarity index 100% rename from cmd/dep/testdata/init/glide/glide.yaml rename to internal/importers/glide/testdata/glide.yaml diff --git a/cmd/dep/testdata/init/glide/golden.txt b/internal/importers/glide/testdata/golden.txt similarity index 100% rename from cmd/dep/testdata/init/glide/golden.txt rename to internal/importers/glide/testdata/golden.txt diff --git a/cmd/dep/godep_importer.go b/internal/importers/godep/godep_importer.go similarity index 57% rename from cmd/dep/godep_importer.go rename to internal/importers/godep/godep_importer.go index 9d84e3e56d..e35636ec51 100644 --- a/cmd/dep/godep_importer.go +++ b/internal/importers/godep/godep_importer.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package godep import ( "encoding/json" @@ -13,18 +13,21 @@ import ( "github.com/golang/dep" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/base" "github.com/pkg/errors" ) const godepPath = "Godeps" + string(os.PathSeparator) + "Godeps.json" -type godepImporter struct { - *baseImporter +// Importer imports godep configuration into the dep configuration format. +type Importer struct { + *base.Importer json godepJSON } -func newGodepImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *godepImporter { - return &godepImporter{baseImporter: newBaseImporter(logger, verbose, sm)} +// NewImporter for godep. +func NewImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *Importer { + return &Importer{Importer: base.NewImporter(logger, verbose, sm)} } type godepJSON struct { @@ -37,11 +40,13 @@ type godepPackage struct { Comment string `json:"Comment"` } -func (g *godepImporter) Name() string { +// Name of the importer. +func (g *Importer) Name() string { return "godep" } -func (g *godepImporter) HasDepMetadata(dir string) bool { +// HasDepMetadata checks if a directory contains config that the importer can handle. +func (g *Importer) HasDepMetadata(dir string) bool { y := filepath.Join(dir, godepPath) if _, err := os.Stat(y); err != nil { return false @@ -50,7 +55,8 @@ func (g *godepImporter) HasDepMetadata(dir string) bool { return true } -func (g *godepImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { +// Import the config found in the directory. +func (g *Importer) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { err := g.load(dir) if err != nil { return nil, nil, err @@ -59,11 +65,11 @@ func (g *godepImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, * return g.convert(pr) } -func (g *godepImporter) load(projectDir string) error { - g.logger.Println("Detected godep configuration files...") +func (g *Importer) load(projectDir string) error { + g.Logger.Println("Detected godep configuration files...") j := filepath.Join(projectDir, godepPath) - if g.verbose { - g.logger.Printf(" Loading %s", j) + if g.Verbose { + g.Logger.Printf(" Loading %s", j) } jb, err := ioutil.ReadFile(j) if err != nil { @@ -77,10 +83,10 @@ func (g *godepImporter) load(projectDir string) error { return nil } -func (g *godepImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { - g.logger.Println("Converting from Godeps.json ...") +func (g *Importer) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + g.Logger.Println("Converting from Godeps.json ...") - packages := make([]importedPackage, 0, len(g.json.Imports)) + packages := make([]base.ImportedPackage, 0, len(g.json.Imports)) for _, pkg := range g.json.Imports { // Validate if pkg.ImportPath == "" { @@ -93,7 +99,7 @@ func (g *godepImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e return nil, nil, err } - ip := importedPackage{ + ip := base.ImportedPackage{ Name: pkg.ImportPath, LockHint: pkg.Rev, ConstraintHint: pkg.Comment, @@ -101,10 +107,10 @@ func (g *godepImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e packages = append(packages, ip) } - err := g.importPackages(packages, true) + err := g.ImportPackages(packages, true) if err != nil { return nil, nil, err } - return g.manifest, g.lock, nil + return g.Manifest, g.Lock, nil } diff --git a/cmd/dep/godep_importer_test.go b/internal/importers/godep/godep_importer_test.go similarity index 62% rename from cmd/dep/godep_importer_test.go rename to internal/importers/godep/godep_importer_test.go index bb231d44ac..330106f7d3 100644 --- a/cmd/dep/godep_importer_test.go +++ b/internal/importers/godep/godep_importer_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package godep import ( "bytes" @@ -12,64 +12,63 @@ import ( "github.com/golang/dep" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/importertest" "github.com/golang/dep/internal/test" "github.com/pkg/errors" ) -const testProjectRoot = "github.com/golang/notexist" - func TestGodepConfig_Convert(t *testing.T) { testCases := map[string]struct { - convertTestCase + importertest.TestCase json godepJSON }{ "package without comment": { - convertTestCase{ - wantConstraint: importerTestV1Constraint, - wantRevision: importerTestV1Rev, - wantVersion: importerTestV1Tag, + importertest.TestCase{ + WantConstraint: importertest.V1Constraint, + WantRevision: importertest.V1Rev, + WantVersion: importertest.V1Tag, }, godepJSON{ Imports: []godepPackage{ { - ImportPath: importerTestProject, - Rev: importerTestV1Rev, + ImportPath: importertest.Project, + Rev: importertest.V1Rev, }, }, }, }, "package with comment": { - convertTestCase{ - wantConstraint: importerTestV2Branch, - wantRevision: importerTestV2PatchRev, - wantVersion: importerTestV2PatchTag, + importertest.TestCase{ + WantConstraint: importertest.V2Branch, + WantRevision: importertest.V2PatchRev, + WantVersion: importertest.V2PatchTag, }, godepJSON{ Imports: []godepPackage{ { - ImportPath: importerTestProject, - Rev: importerTestV2PatchRev, - Comment: importerTestV2Branch, + ImportPath: importertest.Project, + Rev: importertest.V2PatchRev, + Comment: importertest.V2Branch, }, }, }, }, "missing package name": { - convertTestCase{ - wantConvertErr: true, + importertest.TestCase{ + WantConvertErr: true, }, godepJSON{ Imports: []godepPackage{{ImportPath: ""}}, }, }, "missing revision": { - convertTestCase{ - wantConvertErr: true, + importertest.TestCase{ + WantConvertErr: true, }, godepJSON{ Imports: []godepPackage{ { - ImportPath: importerTestProject, + ImportPath: importertest.Project, }, }, }, @@ -82,10 +81,10 @@ func TestGodepConfig_Convert(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - err := testCase.Exec(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { - g := newGodepImporter(logger, true, sm) + err := testCase.Execute(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { + g := NewImporter(logger, true, sm) g.json = testCase.json - return g.convert(testProjectRoot) + return g.convert(importertest.RootProject) }) if err != nil { t.Fatalf("%#v", err) @@ -101,10 +100,10 @@ func TestGodepConfig_Import(t *testing.T) { cacheDir := "gps-repocache" h.TempDir(cacheDir) h.TempDir("src") - h.TempDir(filepath.Join("src", testProjectRoot)) - h.TempCopy(filepath.Join(testProjectRoot, godepPath), "init/godep/Godeps.json") + h.TempDir(filepath.Join("src", importertest.RootProject)) + h.TempCopy(filepath.Join(importertest.RootProject, godepPath), "Godeps.json") - projectRoot := h.Path(testProjectRoot) + projectRoot := h.Path(importertest.RootProject) sm, err := gps.NewSourceManager(gps.SourceManagerConfig{ Cachedir: h.Path(cacheDir), Logger: log.New(test.Writer{t}, "", 0), @@ -116,12 +115,12 @@ func TestGodepConfig_Import(t *testing.T) { verboseOutput := &bytes.Buffer{} logger := log.New(verboseOutput, "", 0) - g := newGodepImporter(logger, false, sm) // Disable verbose so that we don't print values that change each test run + g := NewImporter(logger, false, sm) // Disable Verbose so that we don't print values that change each test run if !g.HasDepMetadata(projectRoot) { t.Fatal("Expected the importer to detect godep configuration file") } - m, l, err := g.Import(projectRoot, testProjectRoot) + m, l, err := g.Import(projectRoot, importertest.RootProject) h.Must(err) if m == nil { @@ -132,23 +131,23 @@ func TestGodepConfig_Import(t *testing.T) { t.Fatal("Expected the lock to be generated") } - goldenFile := "init/godep/golden.txt" + goldenFile := "golden.txt" got := verboseOutput.String() - want := h.GetTestFileString(goldenFile) - if want != got { + 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("want %s, got %s", want, got) + t.Fatalf("want %s, got %s", Want, got) } } } func TestGodepConfig_JsonLoad(t *testing.T) { // This is same as cmd/dep/testdata/init/Godeps.json - wantJSON := godepJSON{ + WantJSON := godepJSON{ Imports: []godepPackage{ { ImportPath: "github.com/sdboyer/deptest", @@ -165,20 +164,20 @@ func TestGodepConfig_JsonLoad(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() - ctx := newTestContext(h) + ctx := importertest.NewTestContext(h) - h.TempCopy(filepath.Join(testProjectRoot, godepPath), "init/godep/Godeps.json") + h.TempCopy(filepath.Join(importertest.RootProject, godepPath), "Godeps.json") - projectRoot := h.Path(testProjectRoot) + projectRoot := h.Path(importertest.RootProject) - g := newGodepImporter(ctx.Err, true, nil) + g := NewImporter(ctx.Err, true, nil) err := g.load(projectRoot) if err != nil { t.Fatalf("Error while loading... %v", err) } - if !equalImports(g.json.Imports, wantJSON.Imports) { - t.Fatalf("Expected imports to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.json.Imports, wantJSON.Imports) + if !equalImports(g.json.Imports, WantJSON.Imports) { + t.Fatalf("Expected imports to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.json.Imports, WantJSON.Imports) } } diff --git a/cmd/dep/testdata/init/godep/Godeps.json b/internal/importers/godep/testdata/Godeps.json similarity index 100% rename from cmd/dep/testdata/init/godep/Godeps.json rename to internal/importers/godep/testdata/Godeps.json diff --git a/cmd/dep/testdata/init/godep/golden.txt b/internal/importers/godep/testdata/golden.txt similarity index 100% rename from cmd/dep/testdata/init/godep/golden.txt rename to internal/importers/godep/testdata/golden.txt diff --git a/cmd/dep/govend_importer.go b/internal/importers/govend/govend_importer.go similarity index 58% rename from cmd/dep/govend_importer.go rename to internal/importers/govend/govend_importer.go index 3dc0799c65..2a49cce39f 100644 --- a/cmd/dep/govend_importer.go +++ b/internal/importers/govend/govend_importer.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package govend import ( "io/ioutil" @@ -13,6 +13,7 @@ import ( "github.com/go-yaml/yaml" "github.com/golang/dep" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/base" "github.com/pkg/errors" ) @@ -20,14 +21,15 @@ import ( // govend don't have a separate lock file. const govendYAMLName = "vendor.yml" -// govendImporter imports govend configuration in to the dep configuration format. -type govendImporter struct { - *baseImporter +// Importer imports govend configuration in to the dep configuration format. +type Importer struct { + *base.Importer yaml govendYAML } -func newGovendImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *govendImporter { - return &govendImporter{baseImporter: newBaseImporter(logger, verbose, sm)} +// NewImporter for govend. +func NewImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *Importer { + return &Importer{Importer: base.NewImporter(logger, verbose, sm)} } type govendYAML struct { @@ -39,11 +41,13 @@ type govendPackage struct { Revision string `yaml:"rev"` } -func (g *govendImporter) Name() string { +// Name of the importer. +func (g *Importer) Name() string { return "govend" } -func (g *govendImporter) HasDepMetadata(dir string) bool { +// HasDepMetadata checks if a directory contains config that the importer can handle. +func (g *Importer) HasDepMetadata(dir string) bool { y := filepath.Join(dir, govendYAMLName) if _, err := os.Stat(y); err != nil { return false @@ -52,7 +56,8 @@ func (g *govendImporter) HasDepMetadata(dir string) bool { return true } -func (g *govendImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { +// Import the config found in the directory. +func (g *Importer) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { err := g.load(dir) if err != nil { return nil, nil, err @@ -62,11 +67,11 @@ func (g *govendImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, } // load the govend configuration files. -func (g *govendImporter) load(projectDir string) error { - g.logger.Println("Detected govend configuration files...") +func (g *Importer) load(projectDir string) error { + g.Logger.Println("Detected govend configuration files...") y := filepath.Join(projectDir, govendYAMLName) - if g.verbose { - g.logger.Printf(" Loading %s", y) + if g.Verbose { + g.Logger.Printf(" Loading %s", y) } yb, err := ioutil.ReadFile(y) if err != nil { @@ -80,27 +85,27 @@ func (g *govendImporter) load(projectDir string) error { } // convert the govend configuration files into dep configuration files. -func (g *govendImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { - g.logger.Println("Converting from vendor.yaml...") +func (g *Importer) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + g.Logger.Println("Converting from vendor.yaml...") - packages := make([]importedPackage, 0, len(g.yaml.Imports)) + packages := make([]base.ImportedPackage, 0, len(g.yaml.Imports)) for _, pkg := range g.yaml.Imports { // Path must not be empty if pkg.Path == "" || pkg.Revision == "" { return nil, nil, errors.New("invalid govend configuration, path and rev are required") } - ip := importedPackage{ + ip := base.ImportedPackage{ Name: pkg.Path, LockHint: pkg.Revision, } packages = append(packages, ip) } - err := g.importPackages(packages, true) + err := g.ImportPackages(packages, true) if err != nil { return nil, nil, err } - return g.manifest, g.lock, nil + return g.Manifest, g.Lock, nil } diff --git a/cmd/dep/govend_importer_test.go b/internal/importers/govend/govend_importer_test.go similarity index 65% rename from cmd/dep/govend_importer_test.go rename to internal/importers/govend/govend_importer_test.go index 4cf9236107..4a9a0bdadb 100644 --- a/cmd/dep/govend_importer_test.go +++ b/internal/importers/govend/govend_importer_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package govend import ( "bytes" @@ -12,6 +12,7 @@ import ( "github.com/golang/dep" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/importertest" "github.com/golang/dep/internal/test" "github.com/pkg/errors" ) @@ -19,21 +20,21 @@ import ( func TestGovendConfig_Convert(t *testing.T) { testCases := map[string]struct { yaml govendYAML - convertTestCase + importertest.TestCase }{ "package": { govendYAML{ Imports: []govendPackage{ { - Path: importerTestProject, - Revision: importerTestV1Rev, + Path: importertest.Project, + Revision: importertest.V1Rev, }, }, }, - convertTestCase{ - wantConstraint: importerTestV1Constraint, - wantRevision: importerTestV1Rev, - wantVersion: importerTestV1Tag, + importertest.TestCase{ + WantConstraint: importertest.V1Constraint, + WantRevision: importertest.V1Rev, + WantVersion: importertest.V1Tag, }, }, "missing package name": { @@ -44,8 +45,8 @@ func TestGovendConfig_Convert(t *testing.T) { }, }, }, - convertTestCase{ - wantConvertErr: true, + importertest.TestCase{ + WantConvertErr: true, }, }, @@ -53,12 +54,12 @@ func TestGovendConfig_Convert(t *testing.T) { govendYAML{ Imports: []govendPackage{ { - Path: importerTestProject, + Path: importertest.Project, }, }, }, - convertTestCase{ - wantConvertErr: true, + importertest.TestCase{ + WantConvertErr: true, }, }, } @@ -69,10 +70,10 @@ func TestGovendConfig_Convert(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - err := testCase.Exec(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { - g := newGovendImporter(logger, true, sm) + err := testCase.Execute(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { + g := NewImporter(logger, true, sm) g.yaml = testCase.yaml - return g.convert(testProjectRoot) + return g.convert(importertest.RootProject) }) if err != nil { t.Fatalf("%#v", err) @@ -88,10 +89,10 @@ func TestGovendConfig_Import(t *testing.T) { cacheDir := "gps-repocache" h.TempDir(cacheDir) h.TempDir("src") - h.TempDir(filepath.Join("src", testProjectRoot)) - h.TempCopy(filepath.Join(testProjectRoot, govendYAMLName), "init/govend/vendor.yml") + h.TempDir(filepath.Join("src", importertest.RootProject)) + h.TempCopy(filepath.Join(importertest.RootProject, govendYAMLName), "vendor.yml") - projectRoot := h.Path(testProjectRoot) + projectRoot := h.Path(importertest.RootProject) sm, err := gps.NewSourceManager(gps.SourceManagerConfig{Cachedir: h.Path(cacheDir)}) h.Must(err) defer sm.Release() @@ -100,13 +101,13 @@ func TestGovendConfig_Import(t *testing.T) { verboseOutput := &bytes.Buffer{} logger := log.New(verboseOutput, "", 0) - // Disable verbose so that we don't print values that change each test run - g := newGovendImporter(logger, false, sm) + // Disable Verbose so that we don't print values that change each test run + g := NewImporter(logger, false, sm) if !g.HasDepMetadata(projectRoot) { t.Fatal("Expected the importer to detect govend configuration file") } - m, l, err := g.Import(projectRoot, testProjectRoot) + m, l, err := g.Import(projectRoot, importertest.RootProject) h.Must(err) if m == nil { @@ -117,16 +118,16 @@ func TestGovendConfig_Import(t *testing.T) { t.Fatal("Expected the lock to be generated") } - govendImportOutputFile := "init/govend/golden.txt" + govendImportOutputFile := "golden.txt" got := verboseOutput.String() - want := h.GetTestFileString(govendImportOutputFile) - if want != got { + Want := h.GetTestFileString(govendImportOutputFile) + if Want != got { if *test.UpdateGolden { if err := h.WriteTestFile(govendImportOutputFile, got); err != nil { t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", govendImportOutputFile)) } } else { - t.Fatalf("want %s, got %s", want, got) + t.Fatalf("want %s, got %s", Want, got) } } @@ -134,7 +135,7 @@ func TestGovendConfig_Import(t *testing.T) { func TestGovendConfig_YAMLLoad(t *testing.T) { // This is same as cmd/testdata/init/govend/vendor.yml - wantYAML := govendYAML{ + WantYAML := govendYAML{ Imports: []govendPackage{ { Path: "github.com/sdboyer/deptest", @@ -149,19 +150,19 @@ func TestGovendConfig_YAMLLoad(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() - ctx := newTestContext(h) - h.TempCopy(filepath.Join(testProjectRoot, govendYAMLName), "init/govend/vendor.yml") + ctx := importertest.NewTestContext(h) + h.TempCopy(filepath.Join(importertest.RootProject, govendYAMLName), "vendor.yml") - projectRoot := h.Path(testProjectRoot) + projectRoot := h.Path(importertest.RootProject) - g := newGovendImporter(ctx.Err, true, nil) + g := NewImporter(ctx.Err, true, nil) err := g.load(projectRoot) if err != nil { t.Fatalf("Error while loading %v", err) } - if !equalGovendImports(g.yaml.Imports, wantYAML.Imports) { - t.Fatalf("Expected import to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.yaml.Imports, wantYAML.Imports) + if !equalGovendImports(g.yaml.Imports, WantYAML.Imports) { + t.Fatalf("Expected import to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.yaml.Imports, WantYAML.Imports) } } diff --git a/cmd/dep/testdata/init/govend/golden.txt b/internal/importers/govend/testdata/golden.txt similarity index 100% rename from cmd/dep/testdata/init/govend/golden.txt rename to internal/importers/govend/testdata/golden.txt diff --git a/cmd/dep/testdata/init/govend/vendor.yml b/internal/importers/govend/testdata/vendor.yml similarity index 100% rename from cmd/dep/testdata/init/govend/vendor.yml rename to internal/importers/govend/testdata/vendor.yml diff --git a/internal/importers/importers.go b/internal/importers/importers.go new file mode 100644 index 0000000000..285ca5fcbf --- /dev/null +++ b/internal/importers/importers.go @@ -0,0 +1,35 @@ +package importers + +import ( + "log" + + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/glide" + "github.com/golang/dep/internal/importers/godep" + "github.com/golang/dep/internal/importers/govend" + "github.com/golang/dep/internal/importers/vndr" +) + +// Importer handles importing configuration from other dependency managers into +// the dep configuration format. +type Importer interface { + // Name of the importer. + Name() string + + // Import the config found in the directory. + Import(path string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) + + // HasDepMetadata checks if a directory contains config that the importer can handle. + HasDepMetadata(dir string) bool +} + +// BuildAll returns a slice of all the importers. +func BuildAll(logger *log.Logger, verbose bool, sm gps.SourceManager) []Importer { + return []Importer{ + glide.NewImporter(logger, verbose, sm), + godep.NewImporter(logger, verbose, sm), + vndr.NewImporter(logger, verbose, sm), + govend.NewImporter(logger, verbose, sm), + } +} diff --git a/internal/importers/importertest/testcase.go b/internal/importers/importertest/testcase.go new file mode 100644 index 0000000000..9771b37a05 --- /dev/null +++ b/internal/importers/importertest/testcase.go @@ -0,0 +1,193 @@ +// Copyright 2016 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 importertest + +import ( + "bytes" + "io/ioutil" + "log" + "sort" + "strings" + "testing" + + "github.com/golang/dep" + "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/test" + "github.com/pkg/errors" +) + +// TestCase is a common set of validations applied to the result +// of an importer converting from an external config format to dep's. +type TestCase struct { + DefaultConstraintFromLock bool + WantConvertErr bool + WantSourceRepo string + WantConstraint string + WantRevision gps.Revision + WantVersion string + WantIgnored []string + WantWarning string +} + +// NewTestContext creates a unique context with its own GOPATH for a single test. +func NewTestContext(h *test.Helper) *dep.Ctx { + h.TempDir("src") + pwd := h.Path(".") + discardLogger := log.New(ioutil.Discard, "", 0) + + return &dep.Ctx{ + GOPATH: pwd, + Out: discardLogger, + Err: discardLogger, + } +} + +// Execute and validate the test case. +func (tc TestCase) Execute(t *testing.T, convert func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error)) error { + h := test.NewHelper(t) + defer h.Cleanup() + + ctx := NewTestContext(h) + sm, err := ctx.SourceManager() + h.Must(err) + defer sm.Release() + + // Capture stderr so we can verify warnings + output := &bytes.Buffer{} + ctx.Err = log.New(output, "", 0) + + manifest, lock, convertErr := convert(ctx.Err, sm) + return tc.validate(manifest, lock, convertErr, output) +} + +// validate returns an error if any of the testcase validations failed. +func (tc TestCase) validate(manifest *dep.Manifest, lock *dep.Lock, convertErr error, output *bytes.Buffer) error { + if tc.WantConvertErr { + if convertErr == nil { + return errors.New("Expected the conversion to fail, but it did not return an error") + } + return nil + } + + if convertErr != nil { + return errors.Wrap(convertErr, "Expected the conversion to pass, but it returned an error") + } + + if !equalSlice(manifest.Ignored, tc.WantIgnored) { + return errors.Errorf("unexpected set of ignored projects: \n\t(GOT) %v \n\t(WNT) %v", + manifest.Ignored, tc.WantIgnored) + } + + WantConstraintCount := 0 + if tc.WantConstraint != "" { + WantConstraintCount = 1 + } + gotConstraintCount := len(manifest.Constraints) + if gotConstraintCount != WantConstraintCount { + return errors.Errorf("unexpected number of constraints: \n\t(GOT) %v \n\t(WNT) %v", + gotConstraintCount, WantConstraintCount) + } + + if tc.WantConstraint != "" { + d, ok := manifest.Constraints[Project] + if !ok { + return errors.Errorf("Expected the manifest to have a dependency for '%v'", + Project) + } + + gotConstraint := d.Constraint.String() + if gotConstraint != tc.WantConstraint { + return errors.Errorf("unexpected constraint: \n\t(GOT) %v \n\t(WNT) %v", + gotConstraint, tc.WantConstraint) + } + + } + + // Lock checks. + WantLockCount := 0 + if tc.WantRevision != "" { + WantLockCount = 1 + } + gotLockCount := 0 + if lock != nil { + gotLockCount = len(lock.P) + } + if gotLockCount != WantLockCount { + return errors.Errorf("unexpected number of locked projects: \n\t(GOT) %v \n\t(WNT) %v", + gotLockCount, WantLockCount) + } + + if tc.WantRevision != "" { + lp := lock.P[0] + + gotProjectRoot := lp.Ident().ProjectRoot + if gotProjectRoot != Project { + return errors.Errorf("unexpected root project in lock: \n\t(GOT) %v \n\t(WNT) %v", + gotProjectRoot, Project) + } + + gotSource := lp.Ident().Source + if gotSource != tc.WantSourceRepo { + return errors.Errorf("unexpected source repository: \n\t(GOT) %v \n\t(WNT) %v", + gotSource, tc.WantSourceRepo) + } + + // Break down the locked "version" into a version (optional) and revision + var gotVersion string + var gotRevision gps.Revision + if lpv, ok := lp.Version().(gps.PairedVersion); ok { + gotVersion = lpv.String() + gotRevision = lpv.Revision() + } else if lr, ok := lp.Version().(gps.Revision); ok { + gotRevision = lr + } else { + return errors.New("could not determine the type of the locked version") + } + + if gotRevision != tc.WantRevision { + return errors.Errorf("unexpected locked revision: \n\t(GOT) %v \n\t(WNT) %v", + gotRevision, + tc.WantRevision) + } + if gotVersion != tc.WantVersion { + return errors.Errorf("unexpected locked version: \n\t(GOT) %v \n\t(WNT) %v", + gotVersion, + tc.WantVersion) + } + } + + if tc.WantWarning != "" { + if !strings.Contains(output.String(), tc.WantWarning) { + return errors.Errorf("Expected the output to include the warning '%s'", tc.WantWarning) + } + } + + return nil +} + +// equalSlice is comparing two string slices for equality. +func equalSlice(a, b []string) bool { + if a == nil && b == nil { + return true + } + + if a == nil || b == nil { + return false + } + + if len(a) != len(b) { + return false + } + + sort.Strings(a) + sort.Strings(b) + for i := range a { + if a[i] != b[i] { + return false + } + } + + return true +} diff --git a/internal/importers/importertest/testdata.go b/internal/importers/importertest/testdata.go new file mode 100644 index 0000000000..cd7f2c0879 --- /dev/null +++ b/internal/importers/importertest/testdata.go @@ -0,0 +1,64 @@ +// Copyright 2016 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 importertest + +const ( + // RootProject is the containing project performing the import. + RootProject = "github.com/golang/notexist" + + // Project being imported. + Project = "github.com/carolynvs/deptest-importers" + + // ProjectSrc is an alternate source for the imported project. + ProjectSrc = "https://github.com/carolynvs/deptest-importers.git" + + // UntaggedRev is a revision without any tags. + UntaggedRev = "9b670d143bfb4a00f7461451d5c4a62f80e9d11d" + + // UntaggedRevAbbrv is the result of running `git describe` on UntaggedRev + UntaggedRevAbbrv = "v1.0.0-1-g9b670d1" + + // Beta1Tag is a non-semver tag. + Beta1Tag = "beta1" + + // Beta1Rev is the revision of Beta1Tag + Beta1Rev = "7913ab26988c6fb1e16225f845a178e8849dd254" + + // V2Branch is a branch that could be interpreted as a semver tag (but shouldn't). + V2Branch = "v2" + + // V2Rev is the HEAD revision of V2Branch. + V2Rev = "45dcf5a09c64b48b6e836028a3bc672b19b9d11d" + + // V2PatchTag is a prerelease semver tag on the non-default branch. + V2PatchTag = "v2.0.0-alpha1" + + // V2PatchRev is the revision of V2PatchTag. + V2PatchRev = "347760b50204948ea63e531dd6560e56a9adde8f" + + // V1Tag is a semver tag that matches V1Constraint. + V1Tag = "v1.0.0" + + // V1Rev is the revision of V1Tag. + V1Rev = "d0c29640b17f77426b111f4c1640d716591aa70e" + + // V1PatchTag is a semver tag that matches V1Constraint. + V1PatchTag = "v1.0.2" + + // V1PatchRev is the revision of V1PatchTag + V1PatchRev = "788963efe22e3e6e24c776a11a57468bb2fcd780" + + // V1Constraint is a constraint that matches multiple semver tags. + V1Constraint = "^1.0.0" + + // MultiTaggedRev is a revision with multiple tags. + MultiTaggedRev = "34cf993cc346f65601fe4356dd68bd54d20a1bfe" + + // MultiTaggedSemverTag is a semver tag on MultiTaggedRev. + MultiTaggedSemverTag = "v1.0.4" + + // MultiTaggedPlainTag is a non-semver tag on MultiTaggedRev. + MultiTaggedPlainTag = "stable" +) diff --git a/cmd/dep/testdata/init/vndr/golden.txt b/internal/importers/vndr/testdata/golden.txt similarity index 100% rename from cmd/dep/testdata/init/vndr/golden.txt rename to internal/importers/vndr/testdata/golden.txt diff --git a/cmd/dep/testdata/init/vndr/vendor.conf b/internal/importers/vndr/testdata/vendor.conf similarity index 100% rename from cmd/dep/testdata/init/vndr/vendor.conf rename to internal/importers/vndr/testdata/vendor.conf diff --git a/cmd/dep/vndr_importer.go b/internal/importers/vndr/vndr_importer.go similarity index 67% rename from cmd/dep/vndr_importer.go rename to internal/importers/vndr/vndr_importer.go index 911700a260..f24723ee0e 100644 --- a/cmd/dep/vndr_importer.go +++ b/internal/importers/vndr/vndr_importer.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package vndr import ( "bufio" @@ -13,6 +13,7 @@ import ( "github.com/golang/dep" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/base" "github.com/pkg/errors" ) @@ -20,24 +21,29 @@ func vndrFile(dir string) string { return filepath.Join(dir, "vendor.conf") } -type vndrImporter struct { - *baseImporter +// Importer imports vndr configuration into the dep configuration format. +type Importer struct { + *base.Importer packages []vndrPackage } -func newVndrImporter(log *log.Logger, verbose bool, sm gps.SourceManager) *vndrImporter { - return &vndrImporter{baseImporter: newBaseImporter(log, verbose, sm)} +// NewImporter for vndr. +func NewImporter(log *log.Logger, verbose bool, sm gps.SourceManager) *Importer { + return &Importer{Importer: base.NewImporter(log, verbose, sm)} } -func (v *vndrImporter) Name() string { return "vndr" } +// Name of the importer. +func (v *Importer) Name() string { return "vndr" } -func (v *vndrImporter) HasDepMetadata(dir string) bool { +// HasDepMetadata checks if a directory contains config that the importer can handle. +func (v *Importer) 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...") +// Import the config found in the directory. +func (v *Importer) 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 { @@ -47,8 +53,8 @@ func (v *vndrImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *d return v.convert(pr) } -func (v *vndrImporter) loadVndrFile(dir string) error { - v.logger.Printf("Converting from vendor.conf...") +func (v *Importer) loadVndrFile(dir string) error { + v.Logger.Printf("Converting from vendor.conf...") f, err := os.Open(vndrFile(dir)) if err != nil { @@ -76,8 +82,8 @@ func (v *vndrImporter) loadVndrFile(dir string) error { return nil } -func (v *vndrImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { - packages := make([]importedPackage, 0, len(v.packages)) +func (v *Importer) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { + packages := make([]base.ImportedPackage, 0, len(v.packages)) for _, pkg := range v.packages { // Validate if pkg.importPath == "" { @@ -90,19 +96,19 @@ func (v *vndrImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, er return nil, nil, err } - ip := importedPackage{ + ip := base.ImportedPackage{ Name: pkg.importPath, Source: pkg.repository, LockHint: pkg.reference, } packages = append(packages, ip) } - err := v.importPackages(packages, true) + err := v.ImportPackages(packages, true) if err != nil { return nil, nil, err } - return v.manifest, v.lock, nil + return v.Manifest, v.Lock, nil } type vndrPackage struct { diff --git a/cmd/dep/vndr_importer_test.go b/internal/importers/vndr/vndr_importer_test.go similarity index 64% rename from cmd/dep/vndr_importer_test.go rename to internal/importers/vndr/vndr_importer_test.go index 7eac9fccc4..cf970849c6 100644 --- a/cmd/dep/vndr_importer_test.go +++ b/internal/importers/vndr/vndr_importer_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package main +package vndr import ( "bytes" @@ -13,6 +13,7 @@ import ( "github.com/golang/dep" "github.com/golang/dep/internal/gps" + "github.com/golang/dep/internal/importers/importertest" "github.com/golang/dep/internal/test" "github.com/pkg/errors" ) @@ -20,35 +21,35 @@ import ( func TestVndrConfig_Convert(t *testing.T) { testCases := map[string]struct { packages []vndrPackage - convertTestCase + importertest.TestCase }{ "package": { []vndrPackage{{ - importPath: importerTestProject, - reference: importerTestV1Rev, - repository: importerTestProjectSrc, + importPath: importertest.Project, + reference: importertest.V1Rev, + repository: importertest.ProjectSrc, }}, - convertTestCase{ - wantSourceRepo: importerTestProjectSrc, - wantConstraint: importerTestV1Constraint, - wantRevision: importerTestV1Rev, - wantVersion: importerTestV1Tag, + importertest.TestCase{ + WantSourceRepo: importertest.ProjectSrc, + WantConstraint: importertest.V1Constraint, + WantRevision: importertest.V1Rev, + WantVersion: importertest.V1Tag, }, }, "missing importPath": { []vndrPackage{{ - reference: importerTestV1Tag, + reference: importertest.V1Tag, }}, - convertTestCase{ - wantConvertErr: true, + importertest.TestCase{ + WantConvertErr: true, }, }, "missing reference": { []vndrPackage{{ - importPath: importerTestProject, + importPath: importertest.Project, }}, - convertTestCase{ - wantConvertErr: true, + importertest.TestCase{ + WantConvertErr: true, }, }, } @@ -59,10 +60,10 @@ func TestVndrConfig_Convert(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - err := testCase.Exec(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { - g := newVndrImporter(logger, true, sm) + err := testCase.Execute(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) { + g := NewImporter(logger, true, sm) g.packages = testCase.packages - return g.convert(testProjectRoot) + return g.convert(importertest.RootProject) }) if err != nil { t.Fatalf("%#v", err) @@ -75,41 +76,41 @@ func TestVndrConfig_Import(t *testing.T) { h := test.NewHelper(t) defer h.Cleanup() - ctx := newTestContext(h) + ctx := importertest.NewTestContext(h) sm, err := ctx.SourceManager() h.Must(err) defer sm.Release() - h.TempDir(filepath.Join("src", testProjectRoot)) - h.TempCopy(vndrFile(testProjectRoot), "init/vndr/vendor.conf") - projectRoot := h.Path(testProjectRoot) + h.TempDir(filepath.Join("src", importertest.RootProject)) + h.TempCopy(vndrFile(importertest.RootProject), "vendor.conf") + projectRoot := h.Path(importertest.RootProject) logOutput := bytes.NewBuffer(nil) ctx.Err = log.New(logOutput, "", 0) - v := newVndrImporter(ctx.Err, false, sm) + v := NewImporter(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) + m, l, err := v.Import(projectRoot, importertest.RootProject) h.Must(err) - wantM := dep.NewManifest() + WantM := dep.NewManifest() c1, _ := gps.NewSemverConstraint("^0.8.1") - wantM.Constraints["github.com/sdboyer/deptest"] = gps.ProjectProperties{ + WantM.Constraints["github.com/sdboyer/deptest"] = gps.ProjectProperties{ Source: "https://github.com/sdboyer/deptest.git", Constraint: c1, } c2, _ := gps.NewSemverConstraint("^2.0.0") - wantM.Constraints["github.com/sdboyer/deptestdos"] = gps.ProjectProperties{ + WantM.Constraints["github.com/sdboyer/deptestdos"] = gps.ProjectProperties{ Constraint: c2, } - if !reflect.DeepEqual(wantM, m) { - t.Errorf("unexpected manifest\nhave=%+v\nwant=%+v", m, wantM) + if !reflect.DeepEqual(WantM, m) { + t.Errorf("unexpected manifest\nhave=%+v\nwant=%+v", m, WantM) } - wantL := &dep.Lock{ + WantL := &dep.Lock{ P: []gps.LockedProject{ gps.NewLockedProject( gps.ProjectIdentifier{ @@ -128,55 +129,55 @@ func TestVndrConfig_Import(t *testing.T) { ), }, } - if !reflect.DeepEqual(wantL, l) { - t.Errorf("unexpected lock\nhave=%+v\nwant=%+v", l, wantL) + if !reflect.DeepEqual(WantL, l) { + t.Errorf("unexpected lock\nhave=%+v\nwant=%+v", l, WantL) } - goldenFile := "init/vndr/golden.txt" + goldenFile := "golden.txt" got := logOutput.String() - want := h.GetTestFileString(goldenFile) - if want != got { + 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) + t.Fatalf("expected %s, got %s", Want, got) } } } func TestParseVndrLine(t *testing.T) { - testcase := func(in string, wantPkg *vndrPackage, wantErr error) func(*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: + 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) + 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) + if !reflect.DeepEqual(havePkg, WantPkg) { + t.Errorf("unexpected package, have=%v, want=%v", *havePkg, *WantPkg) } } switch { - case wantErr == nil: + 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) + 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()) + if haveErr.Error() != WantErr.Error() { + t.Errorf("expected err=%q, have err=%q", WantErr.Error(), haveErr.Error()) } } }