Merge pull request #461 from Masterminds/feat/resolve-test-imports

Resolve Test Imports
diff --git a/action/config_wizard.go b/action/config_wizard.go
index 2d774f8..1257980 100644
--- a/action/config_wizard.go
+++ b/action/config_wizard.go
@@ -51,6 +51,11 @@
 			deps = append(deps, dep)
 		}
 	}
+	for _, dep := range conf.DevImports {
+		if wizardLookInto(dep) {
+			deps = append(deps, dep)
+		}
+	}
 
 	msg.Info("Gathering information on each dependency")
 	msg.Info("--> This may take a moment. Especially on a codebase with many dependencies")
diff --git a/action/create.go b/action/create.go
index 4d1c0bb..5d42199 100644
--- a/action/create.go
+++ b/action/create.go
@@ -108,15 +108,19 @@
 		msg.Die("Error creating a dependency resolver: %s", err)
 	}
 
+	// When creating resolve the test dependencies as well as the application ones.
+	r.ResolveTest = true
+
 	h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}}
 	r.Handler = h
 
-	sortable, err := r.ResolveLocal(false)
+	sortable, testSortable, err := r.ResolveLocal(false)
 	if err != nil {
 		msg.Die("Error resolving local dependencies: %s", err)
 	}
 
 	sort.Strings(sortable)
+	sort.Strings(testSortable)
 
 	vpath := r.VendorDir
 	if !strings.HasSuffix(vpath, "/") {
@@ -127,7 +131,7 @@
 		n := strings.TrimPrefix(pa, vpath)
 		root, subpkg := util.NormalizeName(n)
 
-		if !config.HasDependency(root) && root != config.Name {
+		if !config.Imports.Has(root) && root != config.Name {
 			msg.Info("--> Found reference to %s\n", n)
 			d := &cfg.Dependency{
 				Name: root,
@@ -136,7 +140,7 @@
 				d.Subpackages = []string{subpkg}
 			}
 			config.Imports = append(config.Imports, d)
-		} else if config.HasDependency(root) {
+		} else if config.Imports.Has(root) {
 			if len(subpkg) > 0 {
 				subpkg = strings.TrimPrefix(subpkg, "/")
 				d := config.Imports.Get(root)
@@ -148,6 +152,33 @@
 		}
 	}
 
+	for _, pa := range testSortable {
+		n := strings.TrimPrefix(pa, vpath)
+		root, subpkg := util.NormalizeName(n)
+
+		if config.Imports.Has(root) && root != config.Name {
+			msg.Debug("--> Found test reference to %s already listed as an import", n)
+		} else if !config.DevImports.Has(root) && root != config.Name {
+			msg.Info("--> Found test reference to %s", n)
+			d := &cfg.Dependency{
+				Name: root,
+			}
+			if len(subpkg) > 0 {
+				d.Subpackages = []string{subpkg}
+			}
+			config.DevImports = append(config.DevImports, d)
+		} else if config.DevImports.Has(root) {
+			if len(subpkg) > 0 {
+				subpkg = strings.TrimPrefix(subpkg, "/")
+				d := config.DevImports.Get(root)
+				if !d.HasSubpackage(subpkg) {
+					msg.Info("--> Adding test sub-package %s to %s\n", subpkg, root)
+					d.Subpackages = append(d.Subpackages, subpkg)
+				}
+			}
+		}
+	}
+
 	if len(config.Imports) == importLen && importLen != 0 {
 		msg.Info("--> Code scanning found no additional imports")
 	}
diff --git a/action/get.go b/action/get.go
index 5d4e143..5b0b72e 100644
--- a/action/get.go
+++ b/action/get.go
@@ -18,7 +18,7 @@
 // Get fetches one or more dependencies and installs.
 //
 // This includes resolving dependency resolution and re-generating the lock file.
-func Get(names []string, installer *repo.Installer, insecure, skipRecursive, strip, stripVendor, nonInteract bool) {
+func Get(names []string, installer *repo.Installer, insecure, skipRecursive, strip, stripVendor, nonInteract, testDeps bool) {
 	if installer.UseCache {
 		cache.SystemLock()
 	}
@@ -33,7 +33,7 @@
 	}
 
 	// Add the packages to the config.
-	if count, err2 := addPkgsToConfig(conf, names, insecure, nonInteract); err2 != nil {
+	if count, err2 := addPkgsToConfig(conf, names, insecure, nonInteract, testDeps); err2 != nil {
 		msg.Die("Failed to get new packages: %s", err2)
 	} else if count == 0 {
 		msg.Warn("Nothing to do")
@@ -43,7 +43,7 @@
 	// Fetch the new packages. Can't resolve versions via installer.Update if
 	// get is called while the vendor/ directory is empty so we checkout
 	// everything.
-	err = installer.Checkout(conf, false)
+	err = installer.Checkout(conf)
 	if err != nil {
 		msg.Die("Failed to checkout packages: %s", err)
 	}
@@ -68,7 +68,7 @@
 	}
 
 	// Set Reference
-	if err := repo.SetReference(confcopy); err != nil {
+	if err := repo.SetReference(confcopy, installer.ResolveTest); err != nil {
 		msg.Err("Failed to set references: %s", err)
 	}
 
@@ -112,7 +112,7 @@
 	if err != nil {
 		msg.Die("Failed to generate config hash. Unable to generate lock file.")
 	}
-	lock := cfg.NewLockfile(confcopy.Imports, hash)
+	lock := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
 	if err := lock.WriteFile(filepath.Join(base, gpath.LockFile)); err != nil {
 		msg.Die("Failed to write glide lock file: %s", err)
 	}
@@ -127,7 +127,7 @@
 // - separates repo from packages
 // - sets up insecure repo URLs where necessary
 // - generates a list of subpackages
-func addPkgsToConfig(conf *cfg.Config, names []string, insecure, nonInteract bool) (int, error) {
+func addPkgsToConfig(conf *cfg.Config, names []string, insecure, nonInteract, testDeps bool) (int, error) {
 
 	if len(names) == 1 {
 		msg.Info("Preparing to install %d package.", len(names))
@@ -152,17 +152,38 @@
 
 		if conf.HasDependency(root) {
 
+			var moved bool
+			var dep *cfg.Dependency
+			// Move from DevImports to Imports
+			if !testDeps && !conf.Imports.Has(root) && conf.DevImports.Has(root) {
+				dep = conf.DevImports.Get(root)
+				conf.Imports = append(conf.Imports, dep)
+				conf.DevImports = conf.DevImports.Remove(root)
+				moved = true
+				numAdded++
+				msg.Info("--> Moving %s from testImport to import", root)
+			} else if testDeps && conf.Imports.Has(root) {
+				msg.Warn("--> Test dependency %s already listed as import", root)
+			}
+
 			// Check if the subpackage is present.
 			if subpkg != "" {
-				dep := conf.Imports.Get(root)
+				if dep == nil {
+					dep = conf.Imports.Get(root)
+					if dep == nil && testDeps {
+						dep = conf.DevImports.Get(root)
+					}
+				}
 				if dep.HasSubpackage(subpkg) {
-					msg.Warn("--> Package %q is already in glide.yaml. Skipping", name)
+					if !moved {
+						msg.Warn("--> Package %q is already in glide.yaml. Skipping", name)
+					}
 				} else {
 					dep.Subpackages = append(dep.Subpackages, subpkg)
 					msg.Info("--> Adding sub-package %s to existing import %s", subpkg, root)
 					numAdded++
 				}
-			} else {
+			} else if !moved {
 				msg.Warn("--> Package %q is already in glide.yaml. Skipping", root)
 			}
 			continue
@@ -196,10 +217,14 @@
 		if dep.Reference != "" {
 			msg.Info("--> Adding %s to your configuration with the version %s", dep.Name, dep.Reference)
 		} else {
-			msg.Info("--> Adding %s to your configuration %s", dep.Name)
+			msg.Info("--> Adding %s to your configuration", dep.Name)
 		}
 
-		conf.Imports = append(conf.Imports, dep)
+		if testDeps {
+			conf.DevImports = append(conf.DevImports, dep)
+		} else {
+			conf.Imports = append(conf.Imports, dep)
+		}
 		numAdded++
 	}
 	return numAdded, nil
diff --git a/action/get_test.go b/action/get_test.go
index b61490d..07dba0f 100644
--- a/action/get_test.go
+++ b/action/get_test.go
@@ -24,7 +24,7 @@
 		"github.com/Masterminds/semver",
 	}
 
-	addPkgsToConfig(conf, names, false, true)
+	addPkgsToConfig(conf, names, false, true, false)
 
 	if !conf.HasDependency("github.com/Masterminds/semver") {
 		t.Error("addPkgsToConfig failed to add github.com/Masterminds/semver")
diff --git a/action/install.go b/action/install.go
index 25a5f08..3deb9d2 100644
--- a/action/install.go
+++ b/action/install.go
@@ -53,7 +53,7 @@
 	msg.Info("Setting references.")
 
 	// Set reference
-	if err := repo.SetReference(newConf); err != nil {
+	if err := repo.SetReference(newConf, installer.ResolveTest); err != nil {
 		msg.Err("Failed to set references: %s (Skip to cleanup)", err)
 	}
 
diff --git a/action/list.go b/action/list.go
index 8ff6c69..f4b3379 100644
--- a/action/list.go
+++ b/action/list.go
@@ -28,7 +28,7 @@
 	h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}}
 	r.Handler = h
 
-	localPkgs, err := r.ResolveLocal(deep)
+	localPkgs, _, err := r.ResolveLocal(deep)
 	if err != nil {
 		msg.Die("Error listing dependencies: %s", err)
 	}
diff --git a/action/remove.go b/action/remove.go
index bdf831b..fc305f5 100644
--- a/action/remove.go
+++ b/action/remove.go
@@ -27,7 +27,7 @@
 
 	confcopy.Imports = inst.List(confcopy)
 
-	if err := repo.SetReference(confcopy); err != nil {
+	if err := repo.SetReference(confcopy, inst.ResolveTest); err != nil {
 		msg.Err("Failed to set references: %s", err)
 	}
 
diff --git a/action/update.go b/action/update.go
index 23d3f4f..cb33e63 100644
--- a/action/update.go
+++ b/action/update.go
@@ -30,13 +30,13 @@
 	}
 
 	// Try to check out the initial dependencies.
-	if err := installer.Checkout(conf, false); err != nil {
+	if err := installer.Checkout(conf); err != nil {
 		msg.Die("Failed to do initial checkout of config: %s", err)
 	}
 
 	// Set the versions for the initial dependencies so that resolved dependencies
 	// are rooted in the correct version of the base.
-	if err := repo.SetReference(conf); err != nil {
+	if err := repo.SetReference(conf, installer.ResolveTest); err != nil {
 		msg.Die("Failed to set initial config references: %s", err)
 	}
 
@@ -51,15 +51,11 @@
 			msg.Die("Could not update packages: %s", err)
 		}
 
-		// TODO: There is no support here for importing Godeps, GPM, and GB files.
-		// I think that all we really need to do now is hunt for these files, and then
-		// roll their version numbers into the config file.
-
 		// Set references. There may be no remaining references to set since the
 		// installer set them as it went to make sure it parsed the right imports
 		// from the right version of the package.
 		msg.Info("Setting references for remaining imports")
-		if err := repo.SetReference(confcopy); err != nil {
+		if err := repo.SetReference(confcopy, installer.ResolveTest); err != nil {
 			msg.Err("Failed to set references: %s (Skip to cleanup)", err)
 		}
 	}
@@ -90,7 +86,7 @@
 		if err != nil {
 			msg.Die("Failed to generate config hash. Unable to generate lock file.")
 		}
-		lock := cfg.NewLockfile(confcopy.Imports, hash)
+		lock := cfg.NewLockfile(confcopy.Imports, confcopy.DevImports, hash)
 		wl := true
 		if gpath.HasLock(base) {
 			yml, err := ioutil.ReadFile(filepath.Join(base, gpath.LockFile))
diff --git a/cfg/config.go b/cfg/config.go
index afeed9a..4b24fca 100644
--- a/cfg/config.go
+++ b/cfg/config.go
@@ -51,7 +51,7 @@
 
 	// DevImports contains the test or other development imports for a project.
 	// See the Dependency type for more details on how this is recorded.
-	DevImports Dependencies `yaml:"devimport,omitempty"`
+	DevImports Dependencies `yaml:"testImport,omitempty"`
 }
 
 // A transitive representation of a dependency for importing and exporting to yaml.
@@ -64,7 +64,7 @@
 	Ignore      []string     `yaml:"ignore,omitempty"`
 	Exclude     []string     `yaml:"excludeDirs,omitempty"`
 	Imports     Dependencies `yaml:"import"`
-	DevImports  Dependencies `yaml:"devimport,omitempty"`
+	DevImports  Dependencies `yaml:"testImport,omitempty"`
 }
 
 // ConfigFromYaml returns an instance of Config from YAML
@@ -298,6 +298,33 @@
 	return nil
 }
 
+// Has checks if a dependency is on a list of dependencies such as import or devimport
+func (d Dependencies) Has(name string) bool {
+	for _, dep := range d {
+		if dep.Name == name {
+			return true
+		}
+	}
+	return false
+}
+
+// Remove removes a dependency from a list of dependencies
+func (d Dependencies) Remove(name string) Dependencies {
+	found := -1
+	for i, dep := range d {
+		if dep.Name == name {
+			found = i
+		}
+	}
+
+	if found >= 0 {
+		copy(d[found:], d[found+1:])
+		d[len(d)-1] = nil
+		return d[:len(d)-1]
+	}
+	return d
+}
+
 // Clone performs a deep clone of Dependencies
 func (d Dependencies) Clone() Dependencies {
 	n := make(Dependencies, 0, len(d))
diff --git a/cfg/lock.go b/cfg/lock.go
index 517d3ba..2b2c4c8 100644
--- a/cfg/lock.go
+++ b/cfg/lock.go
@@ -15,7 +15,7 @@
 	Hash       string    `yaml:"hash"`
 	Updated    time.Time `yaml:"updated"`
 	Imports    Locks     `yaml:"imports"`
-	DevImports Locks     `yaml:"devImports"`
+	DevImports Locks     `yaml:"testImports"`
 }
 
 // LockfileFromYaml returns an instance of Lockfile from YAML
@@ -131,11 +131,12 @@
 }
 
 // NewLockfile is used to create an instance of Lockfile.
-func NewLockfile(ds Dependencies, hash string) *Lockfile {
+func NewLockfile(ds, tds Dependencies, hash string) *Lockfile {
 	lf := &Lockfile{
-		Hash:    hash,
-		Updated: time.Now(),
-		Imports: make([]*Lock, len(ds)),
+		Hash:       hash,
+		Updated:    time.Now(),
+		Imports:    make([]*Lock, len(ds)),
+		DevImports: make([]*Lock, len(tds)),
 	}
 
 	for i := 0; i < len(ds); i++ {
@@ -152,6 +153,20 @@
 
 	sort.Sort(lf.Imports)
 
+	for i := 0; i < len(tds); i++ {
+		lf.DevImports[i] = &Lock{
+			Name:        tds[i].Name,
+			Version:     tds[i].Pin,
+			Repository:  tds[i].Repository,
+			VcsType:     tds[i].VcsType,
+			Subpackages: tds[i].Subpackages,
+			Arch:        tds[i].Arch,
+			Os:          tds[i].Os,
+		}
+	}
+
+	sort.Sort(lf.DevImports)
+
 	return lf
 }
 
diff --git a/dependency/resolver.go b/dependency/resolver.go
index 473209a..06dd2dc 100644
--- a/dependency/resolver.go
+++ b/dependency/resolver.go
@@ -31,7 +31,7 @@
 	// When it returns false with no error, this indicates that the handler did
 	// its job, but the resolver should not do any additional work on the
 	// package.
-	NotFound(pkg string) (bool, error)
+	NotFound(pkg string, addTest bool) (bool, error)
 
 	// OnGopath is called when the Resolver finds a dependency, but it's only on GOPATH.
 	//
@@ -45,12 +45,12 @@
 	//
 	// An error indicates that OnGopath cannot complete its intended operation.
 	// Not all false results are errors.
-	OnGopath(pkg string) (bool, error)
+	OnGopath(pkg string, addTest bool) (bool, error)
 
 	// InVendor is called when the Resolver finds a dependency in the vendor/ directory.
 	//
 	// This can be used update a project found in the vendor/ folder.
-	InVendor(pkg string) error
+	InVendor(pkg string, addTest bool) error
 }
 
 // DefaultMissingPackageHandler is the default handler for missing packages.
@@ -65,21 +65,21 @@
 // NotFound prints a warning and then stores the package name in Missing.
 //
 // It never returns an error, and it always returns false.
-func (d *DefaultMissingPackageHandler) NotFound(pkg string) (bool, error) {
+func (d *DefaultMissingPackageHandler) NotFound(pkg string, addTest bool) (bool, error) {
 	msg.Warn("Package %s is not installed", pkg)
 	d.Missing = append(d.Missing, pkg)
 	return false, nil
 }
 
 // OnGopath is run when a package is missing from vendor/ but found in the GOPATH
-func (d *DefaultMissingPackageHandler) OnGopath(pkg string) (bool, error) {
+func (d *DefaultMissingPackageHandler) OnGopath(pkg string, addTest bool) (bool, error) {
 	msg.Warn("Package %s is only on GOPATH.", pkg)
 	d.Gopath = append(d.Gopath, pkg)
 	return false, nil
 }
 
 // InVendor is run when a package is found in the vendor/ folder
-func (d *DefaultMissingPackageHandler) InVendor(pkg string) error {
+func (d *DefaultMissingPackageHandler) InVendor(pkg string, addTest bool) error {
 	msg.Info("Package %s found in vendor/ folder", pkg)
 	return nil
 }
@@ -96,7 +96,7 @@
 
 	// SetVersion sets the version for a package. An error is returned if there
 	// was a problem setting the version.
-	SetVersion(pkg string) error
+	SetVersion(pkg string, testDep bool) error
 }
 
 // DefaultVersionHandler is the default handler for setting the version.
@@ -113,7 +113,7 @@
 
 // SetVersion here sends a message when a package is found noting that it
 // did not set the version.
-func (d *DefaultVersionHandler) SetVersion(pkg string) error {
+func (d *DefaultVersionHandler) SetVersion(pkg string, testDep bool) error {
 	msg.Warn("Version not set for package %s", pkg)
 	return nil
 }
@@ -139,6 +139,9 @@
 	// import tree.
 	ResolveAllFiles bool
 
+	// ResolveTest sets if test dependencies should be resolved.
+	ResolveTest bool
+
 	// Items already in the queue.
 	alreadyQ map[string]bool
 
@@ -220,9 +223,9 @@
 
 	// In this mode, walk the entire tree.
 	if r.ResolveAllFiles {
-		return r.resolveList(l)
+		return r.resolveList(l, false, false)
 	}
-	return r.resolveImports(l)
+	return r.resolveImports(l, false, false)
 }
 
 // dirHasPrefix tests whether the directory dir begins with prefix.
@@ -240,12 +243,14 @@
 // If the deep flag is set to true, this will then resolve all of the dependencies
 // of the dependencies it has found. If not, it will return just the packages that
 // the base project relies upon.
-func (r *Resolver) ResolveLocal(deep bool) ([]string, error) {
+func (r *Resolver) ResolveLocal(deep bool) ([]string, []string, error) {
 	// We build a list of local source to walk, then send this list
 	// to resolveList.
 	msg.Debug("Resolving local dependencies")
 	l := list.New()
+	tl := list.New()
 	alreadySeen := map[string]bool{}
+	talreadySeen := map[string]bool{}
 	err := filepath.Walk(r.basedir, func(path string, fi os.FileInfo, err error) error {
 		if err != nil && err != filepath.SkipDir {
 			return err
@@ -266,6 +271,7 @@
 		// Scan for dependencies, and anything that's not part of the local
 		// package gets added to the scan list.
 		var imps []string
+		var testImps []string
 		p, err := r.BuildContext.ImportDir(path, 0)
 		if err != nil {
 			if strings.HasPrefix(err.Error(), "no buildable Go source") {
@@ -275,7 +281,7 @@
 				// declared. This is often because of an example with a package
 				// or main but +build ignore as a build tag. In that case we
 				// try to brute force the packages with a slower scan.
-				imps, err = IterativeScan(path)
+				imps, testImps, err = IterativeScan(path)
 				if err != nil {
 					return err
 				}
@@ -284,6 +290,7 @@
 			}
 		} else {
 			imps = p.Imports
+			testImps = p.TestImports
 		}
 
 		// We are only looking for dependencies in vendor. No root, cgo, etc.
@@ -314,19 +321,55 @@
 			}
 		}
 
+		if r.ResolveTest {
+			for _, imp := range testImps {
+				if talreadySeen[imp] {
+					continue
+				}
+				talreadySeen[imp] = true
+				info := r.FindPkg(imp)
+				switch info.Loc {
+				case LocUnknown, LocVendor:
+					tl.PushBack(filepath.Join(r.VendorDir, filepath.FromSlash(imp))) // Do we need a path on this?
+				case LocGopath:
+					if !dirHasPrefix(info.Path, r.basedir) {
+						// FIXME: This is a package outside of the project we're
+						// scanning. It should really be on vendor. But we don't
+						// want it to reference GOPATH. We want it to be detected
+						// and moved.
+						tl.PushBack(filepath.Join(r.VendorDir, filepath.FromSlash(imp)))
+					}
+				case LocRelative:
+					if strings.HasPrefix(imp, "./"+gpath.VendorDir) {
+						msg.Warn("Go package resolving will resolve %s without the ./%s/ prefix", imp, gpath.VendorDir)
+					}
+				}
+			}
+		}
+
 		return nil
 	})
 
 	if err != nil {
 		msg.Err("Failed to build an initial list of packages to scan: %s", err)
-		return []string{}, err
+		return []string{}, []string{}, err
 	}
 
 	if deep {
 		if r.ResolveAllFiles {
-			return r.resolveList(l)
+			re, err := r.resolveList(l, false, false)
+			if err != nil {
+				return []string{}, []string{}, err
+			}
+			tre, err := r.resolveList(l, false, true)
+			return re, tre, err
 		}
-		return r.resolveImports(l)
+		re, err := r.resolveImports(l, false, false)
+		if err != nil {
+			return []string{}, []string{}, err
+		}
+		tre, err := r.resolveImports(tl, true, true)
+		return re, tre, err
 	}
 
 	// If we're not doing a deep scan, we just convert the list into an
@@ -335,7 +378,14 @@
 	for e := l.Front(); e != nil; e = e.Next() {
 		res = append(res, e.Value.(string))
 	}
-	return res, nil
+	tres := make([]string, 0, l.Len())
+	if r.ResolveTest {
+		for e := tl.Front(); e != nil; e = e.Next() {
+			tres = append(tres, e.Value.(string))
+		}
+	}
+
+	return res, tres, nil
 }
 
 // ResolveAll takes a list of packages and returns an inclusive list of all
@@ -350,31 +400,18 @@
 //
 // If one of the passed in packages does not exist in the vendor directory,
 // an error is returned.
-func (r *Resolver) ResolveAll(deps []*cfg.Dependency) ([]string, error) {
-	var queue *list.List
-	if r.ResolveAllFiles {
-		queue = sliceToQueue(deps, r.VendorDir)
-	} else {
-		queue = list.New()
-	}
+func (r *Resolver) ResolveAll(deps []*cfg.Dependency, addTest bool) ([]string, error) {
 
-	loc, err := r.ResolveLocal(false)
-	if err != nil {
-		return []string{}, err
-	}
-	for _, l := range loc {
-		msg.Debug("Adding local Import %s to queue", l)
-		queue.PushBack(l)
-	}
+	queue := sliceToQueue(deps, r.VendorDir)
 
 	if r.ResolveAllFiles {
-		return r.resolveList(queue)
+		return r.resolveList(queue, false, addTest)
 	}
-	return r.resolveImports(queue)
+	return r.resolveImports(queue, false, addTest)
 }
 
-// stripv strips the vendor/ prefix from vendored packages.
-func (r *Resolver) stripv(str string) string {
+// Stripv strips the vendor/ prefix from vendored packages.
+func (r *Resolver) Stripv(str string) string {
 	return strings.TrimPrefix(str, r.VendorDir+string(os.PathSeparator))
 }
 
@@ -395,14 +432,25 @@
 //
 // The resolver's handler is used in the cases where a package cannot be
 // located.
-func (r *Resolver) resolveImports(queue *list.List) ([]string, error) {
+//
+// testDeps specifies if the test dependencies should be resolved and addTest
+// specifies if the dependencies should be added to the Config.DevImports. This
+// is important because we may resolve normal dependencies of test deps and add
+// them to the DevImports list.
+func (r *Resolver) resolveImports(queue *list.List, testDeps, addTest bool) ([]string, error) {
 	msg.Debug("Resolving import path")
+
+	// When test deps passed in but not resolving return empty.
+	if (testDeps || addTest) && !r.ResolveTest {
+		return []string{}, nil
+	}
+
 	for e := queue.Front(); e != nil; e = e.Next() {
 		vdep := e.Value.(string)
-		dep := r.stripv(vdep)
-
+		dep := r.Stripv(vdep)
 		// Check if marked in the Q and then explicitly mark it. We want to know
 		// if it had previously been marked and ensure it for the future.
+
 		_, foundQ := r.alreadyQ[dep]
 		r.alreadyQ[dep] = true
 
@@ -419,7 +467,6 @@
 			continue
 		}
 		r.VersionHandler.Process(dep)
-
 		// Here, we want to import the package and see what imports it has.
 		msg.Debug("Trying to open %s", vdep)
 		var imps []string
@@ -430,7 +477,12 @@
 			// or main but +build ignore as a build tag. In that case we
 			// try to brute force the packages with a slower scan.
 			msg.Debug("Using Iterative Scanning for %s", dep)
-			imps, err = IterativeScan(vdep)
+			if testDeps {
+				_, imps, err = IterativeScan(vdep)
+			} else {
+				imps, _, err = IterativeScan(vdep)
+			}
+
 			if err != nil {
 				msg.Err("Iterative scanning error %s: %s", dep, err)
 				continue
@@ -448,13 +500,13 @@
 				// not to get stuck in a recursion.
 
 				// If the location doesn't exist try to fetch it.
-				if ok, err2 := r.Handler.NotFound(dep); ok {
+				if ok, err2 := r.Handler.NotFound(dep, addTest); ok {
 					r.alreadyQ[dep] = true
 
 					// By adding to the queue it will get reprocessed now that
 					// it exists.
 					queue.PushBack(r.vpath(dep))
-					r.VersionHandler.SetVersion(dep)
+					r.VersionHandler.SetVersion(dep, addTest)
 				} else if err2 != nil {
 					r.hadError[dep] = true
 					msg.Err("Error looking for %s: %s", dep, err2)
@@ -470,7 +522,12 @@
 			}
 			continue
 		} else {
-			imps = pkg.Imports
+			if testDeps {
+				imps = pkg.TestImports
+			} else {
+				imps = pkg.Imports
+			}
+
 		}
 
 		// Range over all of the identified imports and see which ones we
@@ -489,20 +546,20 @@
 				msg.Debug("In vendor: %s", imp)
 				if _, ok := r.alreadyQ[imp]; !ok {
 					msg.Debug("Marking %s to be scanned.", imp)
-					r.alreadyQ[imp] = true
+					r.alreadyQ[dep] = true
 					queue.PushBack(r.vpath(imp))
-					if err := r.Handler.InVendor(imp); err == nil {
-						r.VersionHandler.SetVersion(imp)
+					if err := r.Handler.InVendor(imp, addTest); err == nil {
+						r.VersionHandler.SetVersion(imp, addTest)
 					} else {
 						msg.Warn("Error updating %s: %s", imp, err)
 					}
 				}
 			case LocUnknown:
 				msg.Debug("Missing %s. Trying to resolve.", imp)
-				if ok, err := r.Handler.NotFound(imp); ok {
-					r.alreadyQ[imp] = true
+				if ok, err := r.Handler.NotFound(imp, addTest); ok {
+					r.alreadyQ[dep] = true
 					queue.PushBack(r.vpath(imp))
-					r.VersionHandler.SetVersion(imp)
+					r.VersionHandler.SetVersion(imp, addTest)
 				} else if err != nil {
 					r.hadError[dep] = true
 					msg.Warn("Error looking for %s: %s", imp, err)
@@ -514,10 +571,10 @@
 				msg.Debug("Found on GOPATH, not vendor: %s", imp)
 				if _, ok := r.alreadyQ[imp]; !ok {
 					// Only scan it if it gets moved into vendor/
-					if ok, _ := r.Handler.OnGopath(imp); ok {
-						r.alreadyQ[imp] = true
+					if ok, _ := r.Handler.OnGopath(imp, addTest); ok {
+						r.alreadyQ[dep] = true
 						queue.PushBack(r.vpath(imp))
-						r.VersionHandler.SetVersion(imp)
+						r.VersionHandler.SetVersion(imp, addTest)
 					}
 				}
 			}
@@ -530,7 +587,7 @@
 
 	// In addition to generating a list
 	for e := queue.Front(); e != nil; e = e.Next() {
-		t := r.stripv(e.Value.(string))
+		t := r.Stripv(e.Value.(string))
 		root, sp := util.NormalizeName(t)
 
 		// Skip ignored packages
@@ -541,6 +598,9 @@
 
 		// TODO(mattfarina): Need to eventually support devImport
 		existing := r.Config.Imports.Get(root)
+		if existing == nil && addTest {
+			existing = r.Config.DevImports.Get(root)
+		}
 		if existing != nil {
 			if sp != "" && !existing.HasSubpackage(sp) {
 				existing.Subpackages = append(existing.Subpackages, sp)
@@ -553,7 +613,11 @@
 				newDep.Subpackages = []string{sp}
 			}
 
-			r.Config.Imports = append(r.Config.Imports, newDep)
+			if addTest {
+				r.Config.DevImports = append(r.Config.DevImports, newDep)
+			} else {
+				r.Config.Imports = append(r.Config.Imports, newDep)
+			}
 		}
 		res = append(res, t)
 	}
@@ -566,7 +630,11 @@
 // This walks the entire file tree for the given dependencies, not just the
 // parts that are imported directly. Using this will discover dependencies
 // regardless of OS, and arch.
-func (r *Resolver) resolveList(queue *list.List) ([]string, error) {
+func (r *Resolver) resolveList(queue *list.List, testDeps, addTest bool) ([]string, error) {
+	// When test deps passed in but not resolving return empty.
+	if testDeps && !r.ResolveTest {
+		return []string{}, nil
+	}
 
 	var failedDep string
 	for e := queue.Front(); e != nil; e = e.Next() {
@@ -599,7 +667,7 @@
 			// Anything that comes through here has already been through
 			// the queue.
 			r.alreadyQ[path] = true
-			e := r.queueUnseen(path, queue)
+			e := r.queueUnseen(path, queue, testDeps, addTest)
 			if err != nil {
 				failedDep = path
 				//msg.Err("Failed to fetch dependency %s: %s", path, err)
@@ -619,8 +687,11 @@
 		t := strings.TrimPrefix(e.Value.(string), r.VendorDir+string(os.PathSeparator))
 		root, sp := util.NormalizeName(t)
 
-		// TODO(mattfarina): Need to eventually support devImport
 		existing := r.Config.Imports.Get(root)
+		if existing == nil && addTest {
+			existing = r.Config.DevImports.Get(root)
+		}
+
 		if existing != nil {
 			if sp != "" && !existing.HasSubpackage(sp) {
 				existing.Subpackages = append(existing.Subpackages, sp)
@@ -633,7 +704,11 @@
 				newDep.Subpackages = []string{sp}
 			}
 
-			r.Config.Imports = append(r.Config.Imports, newDep)
+			if addTest {
+				r.Config.DevImports = append(r.Config.DevImports, newDep)
+			} else {
+				r.Config.Imports = append(r.Config.Imports, newDep)
+			}
 		}
 		res = append(res, e.Value.(string))
 	}
@@ -643,7 +718,7 @@
 
 // queueUnseenImports scans a package's imports and adds any new ones to the
 // processing queue.
-func (r *Resolver) queueUnseen(pkg string, queue *list.List) error {
+func (r *Resolver) queueUnseen(pkg string, queue *list.List, testDeps, addTest bool) error {
 	// A pkg is marked "seen" as soon as we have inspected it the first time.
 	// Seen means that we have added all of its imports to the list.
 
@@ -651,7 +726,7 @@
 	// or intentionally not put it in the queue for fatal reasons (e.g. no
 	// buildable source).
 
-	deps, err := r.imports(pkg)
+	deps, err := r.imports(pkg, testDeps, addTest)
 	if err != nil && !strings.HasPrefix(err.Error(), "no buildable Go source") {
 		msg.Err("Could not find %s: %s", pkg, err)
 		return err
@@ -676,7 +751,7 @@
 // If the package is in GOROOT, this will return an empty list (but not
 // an error).
 // If it cannot resolve the pkg, it will return an error.
-func (r *Resolver) imports(pkg string) ([]string, error) {
+func (r *Resolver) imports(pkg string, testDeps, addTest bool) ([]string, error) {
 
 	if r.Config.HasIgnore(pkg) {
 		msg.Debug("Ignoring %s", pkg)
@@ -698,14 +773,23 @@
 		// declared. This is often because of an example with a package
 		// or main but +build ignore as a build tag. In that case we
 		// try to brute force the packages with a slower scan.
-		imps, err = IterativeScan(pkg)
+		if testDeps {
+			_, imps, err = IterativeScan(pkg)
+		} else {
+			imps, _, err = IterativeScan(pkg)
+		}
+
 		if err != nil {
 			return []string{}, err
 		}
 	} else if err != nil {
 		return []string{}, err
 	} else {
-		imps = p.Imports
+		if testDeps {
+			imps = p.TestImports
+		} else {
+			imps = p.Imports
+		}
 	}
 
 	// It is okay to scan a package more than once. In some cases, this is
@@ -730,26 +814,26 @@
 		switch info.Loc {
 		case LocUnknown:
 			// Do we resolve here?
-			found, err := r.Handler.NotFound(imp)
+			found, err := r.Handler.NotFound(imp, addTest)
 			if err != nil {
 				msg.Err("Failed to fetch %s: %s", imp, err)
 			}
 			if found {
 				buf = append(buf, filepath.Join(r.VendorDir, filepath.FromSlash(imp)))
-				r.VersionHandler.SetVersion(imp)
+				r.VersionHandler.SetVersion(imp, addTest)
 				continue
 			}
 			r.seen[info.Path] = true
 		case LocVendor:
 			//msg.Debug("Vendored: %s", imp)
 			buf = append(buf, info.Path)
-			if err := r.Handler.InVendor(imp); err == nil {
-				r.VersionHandler.SetVersion(imp)
+			if err := r.Handler.InVendor(imp, addTest); err == nil {
+				r.VersionHandler.SetVersion(imp, addTest)
 			} else {
 				msg.Warn("Error updating %s: %s", imp, err)
 			}
 		case LocGopath:
-			found, err := r.Handler.OnGopath(imp)
+			found, err := r.Handler.OnGopath(imp, addTest)
 			if err != nil {
 				msg.Err("Failed to fetch %s: %s", imp, err)
 			}
@@ -758,7 +842,7 @@
 			// in a less-than-perfect, but functional, situation.
 			if found {
 				buf = append(buf, filepath.Join(r.VendorDir, filepath.FromSlash(imp)))
-				r.VersionHandler.SetVersion(imp)
+				r.VersionHandler.SetVersion(imp, addTest)
 				continue
 			}
 			msg.Warn("Package %s is on GOPATH, but not vendored. Ignoring.", imp)
@@ -777,7 +861,20 @@
 func sliceToQueue(deps []*cfg.Dependency, basepath string) *list.List {
 	l := list.New()
 	for _, e := range deps {
-		l.PushBack(filepath.Join(basepath, filepath.FromSlash(e.Name)))
+		if len(e.Subpackages) > 0 {
+			for _, v := range e.Subpackages {
+				ip := e.Name
+				if v != "." && v != "" {
+					ip = ip + "/" + v
+				}
+				msg.Debug("Adding local Import %s to queue", ip)
+				l.PushBack(filepath.Join(basepath, filepath.FromSlash(ip)))
+			}
+		} else {
+			msg.Debug("Adding local Import %s to queue", e.Name)
+			l.PushBack(filepath.Join(basepath, filepath.FromSlash(e.Name)))
+		}
+
 	}
 	return l
 }
diff --git a/dependency/resolver_test.go b/dependency/resolver_test.go
index bb455c2..1867cee 100644
--- a/dependency/resolver_test.go
+++ b/dependency/resolver_test.go
@@ -15,7 +15,7 @@
 		t.Fatal(err)
 	}
 
-	l, err := r.ResolveLocal(false)
+	l, _, err := r.ResolveLocal(false)
 	if err != nil {
 		t.Fatalf("Failed to resolve: %s", err)
 	}
@@ -47,7 +47,7 @@
 		t.Fatal(err)
 	}
 
-	l, err := r.ResolveLocal(true)
+	l, _, err := r.ResolveLocal(true)
 	if err != nil {
 		t.Fatalf("Failed to resolve: %s", err)
 	}
@@ -91,7 +91,7 @@
 	if err != nil {
 		t.Fatalf("No new resolver: %s", err)
 	}
-	l, err := r.ResolveAll(deps)
+	l, err := r.ResolveAll(deps, false)
 	if err != nil {
 		t.Fatalf("Failed to resolve: %s", err)
 	}
diff --git a/dependency/scan.go b/dependency/scan.go
index c1bad5c..71f4865 100644
--- a/dependency/scan.go
+++ b/dependency/scan.go
@@ -35,7 +35,7 @@
 // Note, there are cases where multiple packages are in the same directory. This
 // usually happens with an example that has a main package and a +build tag
 // of ignore. This is a bit of a hack. It causes UseAllFiles to have errors.
-func IterativeScan(path string) ([]string, error) {
+func IterativeScan(path string) ([]string, []string, error) {
 
 	// TODO(mattfarina): Add support for release tags.
 
@@ -44,6 +44,7 @@
 	tgs = append(tgs, "")
 
 	var pkgs []string
+	var testPkgs []string
 	for _, tt := range tgs {
 
 		// split the tag combination to look at permutations.
@@ -84,7 +85,7 @@
 
 		b, err := util.GetBuildContext()
 		if err != nil {
-			return []string{}, err
+			return []string{}, []string{}, err
 		}
 
 		// Make sure use all files is off
@@ -110,7 +111,7 @@
 			continue
 		} else if err != nil {
 			msg.Debug("Problem parsing package at %s for %s %s", path, ops, arch)
-			return []string{}, err
+			return []string{}, []string{}, err
 		}
 
 		for _, dep := range pk.Imports {
@@ -124,9 +125,21 @@
 				pkgs = append(pkgs, dep)
 			}
 		}
+
+		for _, dep := range pk.TestImports {
+			found := false
+			for _, p := range pkgs {
+				if p == dep {
+					found = true
+				}
+			}
+			if !found {
+				testPkgs = append(testPkgs, dep)
+			}
+		}
 	}
 
-	return pkgs, nil
+	return pkgs, testPkgs, nil
 }
 
 func readBuildTags(p string) ([]string, error) {
diff --git a/docs/glide.yaml.md b/docs/glide.yaml.md
index 3aba801..1b87990 100644
--- a/docs/glide.yaml.md
+++ b/docs/glide.yaml.md
@@ -44,4 +44,4 @@
     - `subpackages`: A record of packages being used within a repository. This does not include all packages within a repository but rather those being used.
     - `os`: A list of operating systems used for filtering. If set it will compare the current runtime OS to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOOS` environment variable.
     - `arch`: A list of architectures used for filtering. If set it will compare the current runtime architecture to the one specified and only fetch the dependency if there is a match. If not set filtering is skipped. The names are the same used in build flags and `GOARCH` environment variable.
-- `devImport`: A list of development packages. Each package has the same details as those listed under import.
+- `testImport`: A list of packages used in tests that are not already listed in `import`. Each package has the same details as those listed under import.
diff --git a/glide.go b/glide.go
index 61b6e2b..1190885 100644
--- a/glide.go
+++ b/glide.go
@@ -203,6 +203,10 @@
    folder.`,
 			Flags: []cli.Flag{
 				cli.BoolFlag{
+					Name:  "test",
+					Usage: "Add test dependencies.",
+				},
+				cli.BoolFlag{
 					Name:  "insecure",
 					Usage: "Use http:// rather than https:// to retrieve pacakges.",
 				},
@@ -250,6 +254,10 @@
 					Name:  "non-interactive",
 					Usage: "Disable interactive prompts.",
 				},
+				cli.BoolFlag{
+					Name:  "skip-test",
+					Usage: "Resolve dependencies in test files.",
+				},
 			},
 			Action: func(c *cli.Context) {
 				if c.Bool("strip-vendor") && !c.Bool("strip-vcs") {
@@ -273,9 +281,10 @@
 				inst.UseCacheGopath = c.Bool("cache-gopath")
 				inst.UpdateVendored = c.Bool("update-vendored")
 				inst.ResolveAllFiles = c.Bool("all-dependencies")
+				inst.ResolveTest = !c.Bool("skip-test")
 				packages := []string(c.Args())
 				insecure := c.Bool("insecure")
-				action.Get(packages, inst, insecure, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor"), c.Bool("non-interactive"))
+				action.Get(packages, inst, insecure, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor"), c.Bool("non-interactive"), c.Bool("test"))
 			},
 		},
 		{
@@ -456,6 +465,10 @@
 					Name:  "strip-vendor, v",
 					Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.",
 				},
+				cli.BoolFlag{
+					Name:  "skip-test",
+					Usage: "Resolve dependencies in test files.",
+				},
 			},
 			Action: func(c *cli.Context) {
 				if c.Bool("strip-vendor") && !c.Bool("strip-vcs") {
@@ -470,6 +483,7 @@
 				installer.UpdateVendored = c.Bool("update-vendored")
 				installer.Home = c.GlobalString("home")
 				installer.DeleteUnused = c.Bool("delete")
+				installer.ResolveTest = !c.Bool("skip-test")
 
 				action.Install(installer, c.Bool("strip-vcs"), c.Bool("strip-vendor"))
 			},
@@ -563,6 +577,10 @@
 					Name:  "strip-vendor, v",
 					Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.",
 				},
+				cli.BoolFlag{
+					Name:  "skip-test",
+					Usage: "Resolve dependencies in test files.",
+				},
 			},
 			Action: func(c *cli.Context) {
 				if c.Bool("strip-vendor") && !c.Bool("strip-vcs") {
@@ -583,6 +601,7 @@
 				installer.ResolveAllFiles = c.Bool("all-dependencies")
 				installer.Home = c.GlobalString("home")
 				installer.DeleteUnused = c.Bool("delete")
+				installer.ResolveTest = !c.Bool("skip-test")
 
 				action.Update(installer, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor"))
 			},
diff --git a/glide.lock b/glide.lock
index 8bb73d6..572094a 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,5 +1,5 @@
 hash: ebc39e5b2036ba2235316f2897fb9f2e696c6a7d5389416812722cc8ed3dfa21
-updated: 2016-05-05T09:44:44.751721442-04:00
+updated: 2016-06-10T16:27:29.24243625-04:00
 imports:
 - name: github.com/codegangsta/cli
   version: 71f57d300dd6a780ac1856c005c4b518cfd498ec
@@ -9,4 +9,4 @@
   version: 7af28b64c5ec41b1558f5514fd938379822c237c
 - name: gopkg.in/yaml.v2
   version: a83829b6f1293c91addabc89d0571c246397bbf4
-devImports: []
+testImports: []
diff --git a/repo/installer.go b/repo/installer.go
index 1469042..6f4b2d0 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -51,6 +51,9 @@
 	// packages.
 	ResolveAllFiles bool
 
+	// ResolveTest sets if test dependencies should be resolved.
+	ResolveTest bool
+
 	// Updated tracks the packages that have been remotely fetched.
 	Updated *UpdateTracker
 }
@@ -121,6 +124,8 @@
 		return newConf, nil
 	}
 
+	msg.Info("Downloading dependencies. Please wait...")
+
 	ConcurrentUpdate(newConf.Imports, cwd, i, newConf)
 	ConcurrentUpdate(newConf.DevImports, cwd, i, newConf)
 	return newConf, nil
@@ -130,15 +135,17 @@
 //
 // This is used when initializing an empty vendor directory, or when updating a
 // vendor directory based on changed config.
-func (i *Installer) Checkout(conf *cfg.Config, useDev bool) error {
+func (i *Installer) Checkout(conf *cfg.Config) error {
 
 	dest := i.VendorPath()
 
+	msg.Info("Downloading dependencies. Please wait...")
+
 	if err := ConcurrentUpdate(conf.Imports, dest, i, conf); err != nil {
 		return err
 	}
 
-	if useDev {
+	if i.ResolveTest {
 		return ConcurrentUpdate(conf.DevImports, dest, i, conf)
 	}
 
@@ -182,6 +189,7 @@
 
 	// Update imports
 	res, err := dependency.NewResolver(base)
+	res.ResolveTest = i.ResolveTest
 	if err != nil {
 		msg.Die("Failed to create a resolver: %s", err)
 	}
@@ -190,18 +198,81 @@
 	res.VersionHandler = v
 	res.ResolveAllFiles = i.ResolveAllFiles
 	msg.Info("Resolving imports")
-	_, err = allPackages(conf.Imports, res)
+
+	imps, timps, err := res.ResolveLocal(false)
+	if err != nil {
+		msg.Die("Failed to resolve local packages: %s", err)
+	}
+	var deps cfg.Dependencies
+	var tdeps cfg.Dependencies
+	for _, v := range imps {
+		n := res.Stripv(v)
+		rt, sub := util.NormalizeName(n)
+		if sub == "" {
+			sub = "."
+		}
+		d := deps.Get(rt)
+		if d == nil {
+			nd := &cfg.Dependency{
+				Name:        rt,
+				Subpackages: []string{sub},
+			}
+			deps = append(deps, nd)
+		} else if !d.HasSubpackage(sub) {
+			d.Subpackages = append(d.Subpackages, sub)
+		}
+	}
+	if i.ResolveTest {
+		for _, v := range timps {
+			n := res.Stripv(v)
+			rt, sub := util.NormalizeName(n)
+			if sub == "" {
+				sub = "."
+			}
+			d := deps.Get(rt)
+			if d == nil {
+				d = tdeps.Get(rt)
+			}
+			if d == nil {
+				nd := &cfg.Dependency{
+					Name:        rt,
+					Subpackages: []string{sub},
+				}
+				tdeps = append(tdeps, nd)
+			} else if !d.HasSubpackage(sub) {
+				d.Subpackages = append(d.Subpackages, sub)
+			}
+		}
+	}
+
+	_, err = allPackages(deps, res, false)
 	if err != nil {
 		msg.Die("Failed to retrieve a list of dependencies: %s", err)
 	}
 
-	if len(conf.DevImports) > 0 {
-		msg.Warn("dev imports not resolved.")
+	if i.ResolveTest {
+		msg.Debug("Resolving test dependencies")
+		_, err = allPackages(tdeps, res, true)
+		if err != nil {
+			msg.Die("Failed to retrieve a list of test dependencies: %s", err)
+		}
 	}
 
-	err = ConcurrentUpdate(conf.Imports, vpath, i, conf)
+	msg.Info("Downloading dependencies. Please wait...")
 
-	return err
+	err = ConcurrentUpdate(conf.Imports, vpath, i, conf)
+	if err != nil {
+		return err
+	}
+
+	if i.ResolveTest {
+		err = ConcurrentUpdate(conf.DevImports, vpath, i, conf)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
 }
 
 // List resolves the complete dependency tree and returns a list of dependencies.
@@ -229,7 +300,12 @@
 	res.ResolveAllFiles = i.ResolveAllFiles
 
 	msg.Info("Resolving imports")
-	_, err = allPackages(conf.Imports, res)
+	_, _, err = res.ResolveLocal(false)
+	if err != nil {
+		msg.Die("Failed to resolve local packages: %s", err)
+	}
+
+	_, err = allPackages(conf.Imports, res, false)
 	if err != nil {
 		msg.Die("Failed to retrieve a list of dependencies: %s", err)
 	}
@@ -249,8 +325,6 @@
 	var lock sync.Mutex
 	var returnErr error
 
-	msg.Info("Downloading dependencies. Please wait...")
-
 	for ii := 0; ii < concurrentWorkers; ii++ {
 		go func(ch <-chan *cfg.Dependency) {
 			for {
@@ -307,7 +381,7 @@
 }
 
 // allPackages gets a list of all packages required to satisfy the given deps.
-func allPackages(deps []*cfg.Dependency, res *dependency.Resolver) ([]string, error) {
+func allPackages(deps []*cfg.Dependency, res *dependency.Resolver, addTest bool) ([]string, error) {
 	if len(deps) == 0 {
 		return []string{}, nil
 	}
@@ -317,7 +391,7 @@
 		return []string{}, err
 	}
 	vdir += string(os.PathSeparator)
-	ll, err := res.ResolveAll(deps)
+	ll, err := res.ResolveAll(deps, addTest)
 	if err != nil {
 		return []string{}, err
 	}
@@ -344,9 +418,8 @@
 
 // NotFound attempts to retrieve a package when not found in the local vendor/
 // folder. It will attempt to get it from the remote location info.
-func (m *MissingPackageHandler) NotFound(pkg string) (bool, error) {
+func (m *MissingPackageHandler) NotFound(pkg string, addTest bool) (bool, error) {
 	root := util.GetRootFromPackage(pkg)
-
 	// Skip any references to the root package.
 	if root == m.Config.Name {
 		return false, nil
@@ -379,6 +452,10 @@
 	msg.Info("Fetching %s into %s", pkg, m.destination)
 
 	d := m.Config.Imports.Get(root)
+	if d == nil && addTest {
+		d = m.Config.DevImports.Get(root)
+	}
+
 	// If the dependency is nil it means the Config doesn't yet know about it.
 	if d == nil {
 		d, _ = m.Use.Get(root)
@@ -386,8 +463,11 @@
 		if d == nil {
 			d = &cfg.Dependency{Name: root}
 		}
-
-		m.Config.Imports = append(m.Config.Imports, d)
+		if addTest {
+			m.Config.DevImports = append(m.Config.DevImports, d)
+		} else {
+			m.Config.Imports = append(m.Config.Imports, d)
+		}
 	}
 	if err := VcsGet(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath); err != nil {
 		return false, err
@@ -398,11 +478,11 @@
 // OnGopath will either copy a package, already found in the GOPATH, to the
 // vendor/ directory or download it from the internet. This is dependent if
 // useGopath on the installer is set to true to copy from the GOPATH.
-func (m *MissingPackageHandler) OnGopath(pkg string) (bool, error) {
+func (m *MissingPackageHandler) OnGopath(pkg string, addTest bool) (bool, error) {
 	// If useGopath is false, we fall back to the strategy of fetching from
 	// remote.
 	if !m.useGopath {
-		return m.NotFound(pkg)
+		return m.NotFound(pkg, addTest)
 	}
 
 	root := util.GetRootFromPackage(pkg)
@@ -435,9 +515,8 @@
 
 // InVendor updates a package in the vendor/ directory to make sure the latest
 // is available.
-func (m *MissingPackageHandler) InVendor(pkg string) error {
+func (m *MissingPackageHandler) InVendor(pkg string, addTest bool) error {
 	root := util.GetRootFromPackage(pkg)
-
 	// Skip any references to the root package.
 	if root == m.Config.Name {
 		return nil
@@ -446,6 +525,10 @@
 	dest := filepath.Join(m.destination, root)
 
 	d := m.Config.Imports.Get(root)
+	if d == nil && addTest {
+		d = m.Config.DevImports.Get(root)
+	}
+
 	// If the dependency is nil it means the Config doesn't yet know about it.
 	if d == nil {
 		d, _ = m.Use.Get(root)
@@ -454,7 +537,11 @@
 			d = &cfg.Dependency{Name: root}
 		}
 
-		m.Config.Imports = append(m.Config.Imports, d)
+		if addTest {
+			m.Config.DevImports = append(m.Config.DevImports, d)
+		} else {
+			m.Config.Imports = append(m.Config.Imports, d)
+		}
 	}
 
 	if err := VcsUpdate(d, dest, m.home, m.cache, m.cacheGopath, m.useGopath, m.force, m.updateVendored, m.updated); err != nil {
@@ -524,7 +611,7 @@
 // - keeping the already set version
 // - proviting messaging about the version conflict
 // TODO(mattfarina): The way version setting happens can be improved. Currently not optimal.
-func (d *VersionHandler) SetVersion(pkg string) (e error) {
+func (d *VersionHandler) SetVersion(pkg string, addTest bool) (e error) {
 	root := util.GetRootFromPackage(pkg)
 
 	// Skip any references to the root package.
@@ -533,6 +620,20 @@
 	}
 
 	v := d.Config.Imports.Get(root)
+	if addTest {
+		if v == nil {
+			v = d.Config.DevImports.Get(root)
+		} else if d.Config.DevImports.Has(root) {
+			// Both imports and test imports lists the same dependency.
+			// There are import chains (because the import tree is resolved
+			// before the test tree) that can cause this.
+			tempD := d.Config.DevImports.Get(root)
+			if tempD.Reference != v.Reference {
+				msg.Warn("Using import %s (version %s) for test instead of testImport (version %s).", v.Name, v.Reference, tempD.Reference)
+			}
+			// TODO(mattfarina): Note repo difference in a warning.
+		}
+	}
 
 	dep, req := d.Use.Get(root)
 	if dep != nil && v != nil {
@@ -553,7 +654,11 @@
 	} else if dep != nil {
 		// We've got an imported dependency to use and don't already have a
 		// record of it. Append it to the Imports.
-		d.Config.Imports = append(d.Config.Imports, dep)
+		if addTest {
+			d.Config.DevImports = append(d.Config.DevImports, dep)
+		} else {
+			d.Config.Imports = append(d.Config.Imports, dep)
+		}
 	} else {
 		// If we've gotten here we don't have any depenency objects.
 		r, sp := util.NormalizeName(pkg)
@@ -563,7 +668,11 @@
 		if sp != "" {
 			dep.Subpackages = []string{sp}
 		}
-		d.Config.Imports = append(d.Config.Imports, dep)
+		if addTest {
+			d.Config.DevImports = append(d.Config.DevImports, dep)
+		} else {
+			d.Config.Imports = append(d.Config.Imports, dep)
+		}
 	}
 
 	err := VcsVersion(dep, d.Destination)
diff --git a/repo/set_reference.go b/repo/set_reference.go
index 2856b89..4363459 100644
--- a/repo/set_reference.go
+++ b/repo/set_reference.go
@@ -10,14 +10,14 @@
 
 // SetReference is a command to set the VCS reference (commit id, tag, etc) for
 // a project.
-func SetReference(conf *cfg.Config) error {
+func SetReference(conf *cfg.Config, resolveTest bool) error {
 
 	cwd, err := gpath.Vendor()
 	if err != nil {
 		return err
 	}
 
-	if len(conf.Imports) == 0 {
+	if len(conf.Imports) == 0 && len(conf.DevImports) == 0 {
 		msg.Info("No references set.\n")
 		return nil
 	}
@@ -49,6 +49,15 @@
 		}
 	}
 
+	if resolveTest {
+		for _, dep := range conf.DevImports {
+			if !conf.HasIgnore(dep.Name) {
+				wg.Add(1)
+				in <- dep
+			}
+		}
+	}
+
 	wg.Wait()
 	// Close goroutines setting the version
 	for i := 0; i < concurrentWorkers; i++ {
diff --git a/repo/vcs.go b/repo/vcs.go
index b2d471f..31bed50 100644
--- a/repo/vcs.go
+++ b/repo/vcs.go
@@ -36,7 +36,7 @@
 	}
 	updated.Add(dep.Name)
 
-	msg.Info("Fetching updates for %s.\n", dep.Name)
+	msg.Info("--> Fetching updates for %s.", dep.Name)
 
 	if filterArchOs(dep) {
 		msg.Info("%s is not used for %s/%s.\n", dep.Name, runtime.GOOS, runtime.GOARCH)
@@ -207,7 +207,7 @@
 		// If there is a ^ prefix we assume it's a semver constraint rather than
 		// part of the git/VCS commit id.
 		if repo.IsReference(ver) && !strings.HasPrefix(ver, "^") {
-			msg.Info("Setting version for %s to %s.\n", dep.Name, ver)
+			msg.Info("--> Setting version for %s to %s.\n", dep.Name, ver)
 		} else {
 
 			// Create the constraing first to make sure it's valid before
@@ -242,9 +242,9 @@
 				}
 			}
 			if found {
-				msg.Info("Detected semantic version. Setting version for %s to %s.\n", dep.Name, ver)
+				msg.Info("--> Detected semantic version. Setting version for %s to %s.", dep.Name, ver)
 			} else {
-				msg.Warn("Unable to find semantic version for constraint %s %s\n", dep.Name, ver)
+				msg.Warn("--> Unable to find semantic version for constraint %s %s", dep.Name, ver)
 			}
 		}
 		if err := repo.UpdateVersion(ver); err != nil {
diff --git a/tree/tree.go b/tree/tree.go
index 5b19336..0dc3df7 100644
--- a/tree/tree.go
+++ b/tree/tree.go
@@ -59,7 +59,7 @@
 			// declared. This is often because of an example with a package
 			// or main but +build ignore as a build tag. In that case we
 			// try to brute force the packages with a slower scan.
-			imps, err = dependency.IterativeScan(path)
+			imps, _, err = dependency.IterativeScan(path)
 			if err != nil {
 				msg.Err("Error walking dependencies for %s: %s", path, err)
 				return err