github.com/golang/dep@v0.5.4/internal/importers/importertest/testcase.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package importertest
     6  
     7  import (
     8  	"bytes"
     9  	"io/ioutil"
    10  	"log"
    11  	"sort"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/golang/dep"
    16  	"github.com/golang/dep/gps"
    17  	"github.com/golang/dep/internal/test"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  // TestCase is a common set of validations applied to the result
    22  // of an importer converting from an external config format to dep's.
    23  type TestCase struct {
    24  	DefaultConstraintFromLock bool
    25  	WantSourceRepo            string
    26  	WantConstraint            string
    27  	WantRevision              gps.Revision
    28  	WantVersion               string
    29  	WantIgnored               []string
    30  	WantRequired              []string
    31  	WantWarning               string
    32  }
    33  
    34  // NewTestContext creates a unique context with its own GOPATH for a single test.
    35  func NewTestContext(h *test.Helper) *dep.Ctx {
    36  	h.TempDir("src")
    37  	pwd := h.Path(".")
    38  	discardLogger := log.New(ioutil.Discard, "", 0)
    39  
    40  	return &dep.Ctx{
    41  		GOPATH: pwd,
    42  		Out:    discardLogger,
    43  		Err:    discardLogger,
    44  	}
    45  }
    46  
    47  // Execute and validate the test case.
    48  func (tc TestCase) Execute(t *testing.T, convert func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock)) error {
    49  	h := test.NewHelper(t)
    50  	defer h.Cleanup()
    51  	// Disable parallel tests until we can resolve this error on the Windows builds:
    52  	// "remote repository at https://github.com/carolynvs/deptest-importers does not exist, or is inaccessible"
    53  	//h.Parallel()
    54  
    55  	ctx := NewTestContext(h)
    56  	sm, err := ctx.SourceManager()
    57  	h.Must(err)
    58  	defer sm.Release()
    59  
    60  	// Capture stderr so we can verify warnings
    61  	output := &bytes.Buffer{}
    62  	ctx.Err = log.New(output, "", 0)
    63  
    64  	manifest, lock := convert(ctx.Err, sm)
    65  	return tc.validate(manifest, lock, output)
    66  }
    67  
    68  // validate returns an error if any of the testcase validations failed.
    69  func (tc TestCase) validate(manifest *dep.Manifest, lock *dep.Lock, output *bytes.Buffer) error {
    70  	if !equalSlice(manifest.Ignored, tc.WantIgnored) {
    71  		return errors.Errorf("unexpected set of ignored projects: \n\t(GOT) %#v \n\t(WNT) %#v",
    72  			manifest.Ignored, tc.WantIgnored)
    73  	}
    74  
    75  	if !equalSlice(manifest.Required, tc.WantRequired) {
    76  		return errors.Errorf("unexpected set of required projects: \n\t(GOT) %#v \n\t(WNT) %#v",
    77  			manifest.Required, tc.WantRequired)
    78  	}
    79  
    80  	wantConstraintCount := 0
    81  	if tc.WantConstraint != "" {
    82  		wantConstraintCount = 1
    83  	}
    84  	gotConstraintCount := len(manifest.Constraints)
    85  	if gotConstraintCount != wantConstraintCount {
    86  		return errors.Errorf("unexpected number of constraints: \n\t(GOT) %v \n\t(WNT) %v",
    87  			gotConstraintCount, wantConstraintCount)
    88  	}
    89  
    90  	if tc.WantConstraint != "" {
    91  		d, ok := manifest.Constraints[Project]
    92  		if !ok {
    93  			return errors.Errorf("Expected the manifest to have a dependency for '%v'",
    94  				Project)
    95  		}
    96  
    97  		gotConstraint := d.Constraint.String()
    98  		if gotConstraint != tc.WantConstraint {
    99  			return errors.Errorf("unexpected constraint: \n\t(GOT) %v \n\t(WNT) %v",
   100  				gotConstraint, tc.WantConstraint)
   101  		}
   102  
   103  	}
   104  
   105  	// Lock checks.
   106  	wantLockCount := 0
   107  	if tc.WantRevision != "" {
   108  		wantLockCount = 1
   109  	}
   110  	gotLockCount := 0
   111  	if lock != nil {
   112  		gotLockCount = len(lock.P)
   113  	}
   114  	if gotLockCount != wantLockCount {
   115  		return errors.Errorf("unexpected number of locked projects: \n\t(GOT) %v \n\t(WNT) %v",
   116  			gotLockCount, wantLockCount)
   117  	}
   118  
   119  	if tc.WantRevision != "" {
   120  		lp := lock.P[0]
   121  
   122  		gotProjectRoot := lp.Ident().ProjectRoot
   123  		if gotProjectRoot != Project {
   124  			return errors.Errorf("unexpected root project in lock: \n\t(GOT) %v \n\t(WNT) %v",
   125  				gotProjectRoot, Project)
   126  		}
   127  
   128  		gotSource := lp.Ident().Source
   129  		if gotSource != tc.WantSourceRepo {
   130  			return errors.Errorf("unexpected source repository: \n\t(GOT) %v \n\t(WNT) %v",
   131  				gotSource, tc.WantSourceRepo)
   132  		}
   133  
   134  		// Break down the locked "version" into a version (optional) and revision
   135  		var gotVersion string
   136  		var gotRevision gps.Revision
   137  		if lpv, ok := lp.Version().(gps.PairedVersion); ok {
   138  			gotVersion = lpv.String()
   139  			gotRevision = lpv.Revision()
   140  		} else if lr, ok := lp.Version().(gps.Revision); ok {
   141  			gotRevision = lr
   142  		} else {
   143  			return errors.New("could not determine the type of the locked version")
   144  		}
   145  
   146  		if gotRevision != tc.WantRevision {
   147  			return errors.Errorf("unexpected locked revision: \n\t(GOT) %v \n\t(WNT) %v",
   148  				gotRevision,
   149  				tc.WantRevision)
   150  		}
   151  		if gotVersion != tc.WantVersion {
   152  			return errors.Errorf("unexpected locked version: \n\t(GOT) %v \n\t(WNT) %v",
   153  				gotVersion,
   154  				tc.WantVersion)
   155  		}
   156  	}
   157  
   158  	if tc.WantWarning != "" {
   159  		gotWarning := output.String()
   160  		if !strings.Contains(gotWarning, tc.WantWarning) {
   161  			return errors.Errorf("Expected the output to include the warning '%s' but got '%s'\n", tc.WantWarning, gotWarning)
   162  		}
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  // equalSlice is comparing two string slices for equality.
   169  func equalSlice(a, b []string) bool {
   170  	if a == nil && b == nil {
   171  		return true
   172  	}
   173  
   174  	if a == nil || b == nil {
   175  		return false
   176  	}
   177  
   178  	if len(a) != len(b) {
   179  		return false
   180  	}
   181  
   182  	sort.Strings(a)
   183  	sort.Strings(b)
   184  	for i := range a {
   185  		if a[i] != b[i] {
   186  			return false
   187  		}
   188  	}
   189  
   190  	return true
   191  }