github.com/opsmatic/godep@v0.1.5/save_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io/ioutil"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"reflect"
    11  	"strings"
    12  	"testing"
    13  	"text/template"
    14  )
    15  
    16  // node represents a file tree or a VCS repo
    17  type node struct {
    18  	path    string      // file name or commit type
    19  	body    interface{} // file contents or commit tag
    20  	entries []*node     // nil if the entry is a file
    21  }
    22  
    23  var (
    24  	pkgtpl = template.Must(template.New("package").Parse(`package {{.Name}}
    25  
    26  import (
    27  {{range .Imports}}	{{printf "%q" .}}
    28  {{end}})
    29  `))
    30  )
    31  
    32  func pkg(name string, pkg ...string) string {
    33  	v := struct {
    34  		Name    string
    35  		Imports []string
    36  	}{name, pkg}
    37  	var buf bytes.Buffer
    38  	err := pkgtpl.Execute(&buf, v)
    39  	if err != nil {
    40  		panic(err)
    41  	}
    42  	return buf.String()
    43  }
    44  
    45  func decl(name string) string {
    46  	return "var " + name + " int\n"
    47  }
    48  
    49  func godeps(importpath string, keyval ...string) *Godeps {
    50  	g := &Godeps{
    51  		ImportPath: importpath,
    52  	}
    53  	for i := 0; i < len(keyval); i += 2 {
    54  		g.Deps = append(g.Deps, Dependency{
    55  			ImportPath: keyval[i],
    56  			Comment:    keyval[i+1],
    57  		})
    58  	}
    59  	return g
    60  }
    61  
    62  func TestSave(t *testing.T) {
    63  	var cases = []struct {
    64  		cwd      string
    65  		args     []string
    66  		flagR    bool
    67  		start    []*node
    68  		altstart []*node
    69  		want     []*node
    70  		wdep     Godeps
    71  		werr     bool
    72  	}{
    73  		{ // simple case, one dependency
    74  			cwd: "C",
    75  			start: []*node{
    76  				{
    77  					"C",
    78  					"",
    79  					[]*node{
    80  						{"main.go", pkg("main", "D"), nil},
    81  						{"+git", "", nil},
    82  					},
    83  				},
    84  				{
    85  					"D",
    86  					"",
    87  					[]*node{
    88  						{"main.go", pkg("D"), nil},
    89  						{"+git", "D1", nil},
    90  					},
    91  				},
    92  			},
    93  			want: []*node{
    94  				{"C/main.go", pkg("main", "D"), nil},
    95  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
    96  			},
    97  			wdep: Godeps{
    98  				ImportPath: "C",
    99  				Deps: []Dependency{
   100  					{ImportPath: "D", Comment: "D1"},
   101  				},
   102  			},
   103  		},
   104  		{
   105  			// dependency in same repo with existing manifest
   106  			// see bug https://github.com/tools/godep/issues/69
   107  			cwd:  "P",
   108  			args: []string{"./..."},
   109  			start: []*node{
   110  				{
   111  					"P",
   112  					"",
   113  					[]*node{
   114  						{"main.go", pkg("P", "P/Q"), nil},
   115  						{"Q/main.go", pkg("Q"), nil},
   116  						{"Godeps/Godeps.json", `{}`, nil},
   117  						{"+git", "C1", nil},
   118  					},
   119  				},
   120  			},
   121  			want: []*node{
   122  				{"P/main.go", pkg("P", "P/Q"), nil},
   123  				{"P/Q/main.go", pkg("Q"), nil},
   124  			},
   125  			wdep: Godeps{
   126  				ImportPath: "P",
   127  				Deps:       []Dependency{},
   128  			},
   129  		},
   130  		{
   131  			// dependency on parent directory in same repo
   132  			// see bug https://github.com/tools/godep/issues/70
   133  			cwd:  "P",
   134  			args: []string{"./..."},
   135  			start: []*node{
   136  				{
   137  					"P",
   138  					"",
   139  					[]*node{
   140  						{"main.go", pkg("P"), nil},
   141  						{"Q/main.go", pkg("Q", "P"), nil},
   142  						{"+git", "C1", nil},
   143  					},
   144  				},
   145  			},
   146  			want: []*node{
   147  				{"P/main.go", pkg("P"), nil},
   148  				{"P/Q/main.go", pkg("Q", "P"), nil},
   149  			},
   150  			wdep: Godeps{
   151  				ImportPath: "P",
   152  				Deps:       []Dependency{},
   153  			},
   154  		},
   155  		{ // transitive dependency
   156  			cwd: "C",
   157  			start: []*node{
   158  				{
   159  					"C",
   160  					"",
   161  					[]*node{
   162  						{"main.go", pkg("main", "D"), nil},
   163  						{"+git", "", nil},
   164  					},
   165  				},
   166  				{
   167  					"D",
   168  					"",
   169  					[]*node{
   170  						{"main.go", pkg("D", "T"), nil},
   171  						{"+git", "D1", nil},
   172  					},
   173  				},
   174  				{
   175  					"T",
   176  					"",
   177  					[]*node{
   178  						{"main.go", pkg("T"), nil},
   179  						{"+git", "T1", nil},
   180  					},
   181  				},
   182  			},
   183  			want: []*node{
   184  				{"C/main.go", pkg("main", "D"), nil},
   185  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil},
   186  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   187  			},
   188  			wdep: Godeps{
   189  				ImportPath: "C",
   190  				Deps: []Dependency{
   191  					{ImportPath: "D", Comment: "D1"},
   192  					{ImportPath: "T", Comment: "T1"},
   193  				},
   194  			},
   195  		},
   196  		{ // two packages, one in a subdirectory
   197  			cwd: "C",
   198  			start: []*node{
   199  				{
   200  					"C",
   201  					"",
   202  					[]*node{
   203  						{"main.go", pkg("main", "D", "D/P"), nil},
   204  						{"+git", "", nil},
   205  					},
   206  				},
   207  				{
   208  					"D",
   209  					"",
   210  					[]*node{
   211  						{"main.go", pkg("D"), nil},
   212  						{"P/main.go", pkg("P"), nil},
   213  						{"+git", "D1", nil},
   214  					},
   215  				},
   216  			},
   217  			want: []*node{
   218  				{"C/main.go", pkg("main", "D", "D/P"), nil},
   219  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
   220  				{"C/Godeps/_workspace/src/D/P/main.go", pkg("P"), nil},
   221  			},
   222  			wdep: Godeps{
   223  				ImportPath: "C",
   224  				Deps: []Dependency{
   225  					{ImportPath: "D", Comment: "D1"},
   226  				},
   227  			},
   228  		},
   229  		{ // repo root is not a package (no go files)
   230  			cwd: "C",
   231  			start: []*node{
   232  				{
   233  					"C",
   234  					"",
   235  					[]*node{
   236  						{"main.go", pkg("main", "D/P", "D/Q"), nil},
   237  						{"+git", "", nil},
   238  					},
   239  				},
   240  				{
   241  					"D",
   242  					"",
   243  					[]*node{
   244  						{"P/main.go", pkg("P"), nil},
   245  						{"Q/main.go", pkg("Q"), nil},
   246  						{"+git", "D1", nil},
   247  					},
   248  				},
   249  			},
   250  			want: []*node{
   251  				{"C/main.go", pkg("main", "D/P", "D/Q"), nil},
   252  				{"C/Godeps/_workspace/src/D/P/main.go", pkg("P"), nil},
   253  				{"C/Godeps/_workspace/src/D/Q/main.go", pkg("Q"), nil},
   254  			},
   255  			wdep: Godeps{
   256  				ImportPath: "C",
   257  				Deps: []Dependency{
   258  					{ImportPath: "D/P", Comment: "D1"},
   259  					{ImportPath: "D/Q", Comment: "D1"},
   260  				},
   261  			},
   262  		},
   263  		{ // symlink
   264  			cwd: "C",
   265  			start: []*node{
   266  				{
   267  					"C",
   268  					"",
   269  					[]*node{
   270  						{"main.x", pkg("main", "D"), nil},
   271  						{"main.go", "symlink:main.x", nil},
   272  						{"+git", "", nil},
   273  					},
   274  				},
   275  				{
   276  					"D",
   277  					"",
   278  					[]*node{
   279  						{"main.go", pkg("D"), nil},
   280  						{"+git", "D1", nil},
   281  					},
   282  				},
   283  			},
   284  			want: []*node{
   285  				{"C/main.go", pkg("main", "D"), nil},
   286  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
   287  			},
   288  			wdep: Godeps{
   289  				ImportPath: "C",
   290  				Deps: []Dependency{
   291  					{ImportPath: "D", Comment: "D1"},
   292  				},
   293  			},
   294  		},
   295  		{ // add one dependency; keep other dependency version
   296  			cwd: "C",
   297  			start: []*node{
   298  				{
   299  					"D",
   300  					"",
   301  					[]*node{
   302  						{"main.go", pkg("D") + decl("D1"), nil},
   303  						{"+git", "D1", nil},
   304  						{"main.go", pkg("D") + decl("D2"), nil},
   305  						{"+git", "D2", nil},
   306  					},
   307  				},
   308  				{
   309  					"E",
   310  					"",
   311  					[]*node{
   312  						{"main.go", pkg("E"), nil},
   313  						{"+git", "E1", nil},
   314  					},
   315  				},
   316  				{
   317  					"C",
   318  					"",
   319  					[]*node{
   320  						{"main.go", pkg("main", "D", "E"), nil},
   321  						{"Godeps/Godeps.json", godeps("C", "D", "D1"), nil},
   322  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   323  						{"+git", "", nil},
   324  					},
   325  				},
   326  			},
   327  			want: []*node{
   328  				{"C/main.go", pkg("main", "D", "E"), nil},
   329  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   330  				{"C/Godeps/_workspace/src/E/main.go", pkg("E"), nil},
   331  			},
   332  			wdep: Godeps{
   333  				ImportPath: "C",
   334  				Deps: []Dependency{
   335  					{ImportPath: "D", Comment: "D1"},
   336  					{ImportPath: "E", Comment: "E1"},
   337  				},
   338  			},
   339  		},
   340  		{ // remove one dependency; keep other dependency version
   341  			cwd: "C",
   342  			start: []*node{
   343  				{
   344  					"D",
   345  					"",
   346  					[]*node{
   347  						{"main.go", pkg("D") + decl("D1"), nil},
   348  						{"+git", "D1", nil},
   349  						{"main.go", pkg("D") + decl("D2"), nil},
   350  						{"+git", "D2", nil},
   351  					},
   352  				},
   353  				{
   354  					"E",
   355  					"",
   356  					[]*node{
   357  						{"main.go", pkg("E") + decl("E1"), nil},
   358  						{"+git", "E1", nil},
   359  					},
   360  				},
   361  				{
   362  					"C",
   363  					"",
   364  					[]*node{
   365  						{"main.go", pkg("main", "D"), nil},
   366  						{"Godeps/Godeps.json", godeps("C", "D", "D1", "E", "E1"), nil},
   367  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   368  						{"Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil},
   369  						{"+git", "", nil},
   370  					},
   371  				},
   372  			},
   373  			want: []*node{
   374  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   375  				{"C/Godeps/_workspace/src/E/main.go", "(absent)", nil},
   376  			},
   377  			wdep: Godeps{
   378  				ImportPath: "C",
   379  				Deps: []Dependency{
   380  					{ImportPath: "D", Comment: "D1"},
   381  				},
   382  			},
   383  		},
   384  		{ // add one dependency from same repo
   385  			cwd: "C",
   386  			start: []*node{
   387  				{
   388  					"D",
   389  					"",
   390  					[]*node{
   391  						{"A/main.go", pkg("A") + decl("A1"), nil},
   392  						{"B/main.go", pkg("B") + decl("B1"), nil},
   393  						{"+git", "D1", nil},
   394  					},
   395  				},
   396  				{
   397  					"C",
   398  					"",
   399  					[]*node{
   400  						{"main.go", pkg("main", "D/A", "D/B"), nil},
   401  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   402  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   403  						{"+git", "", nil},
   404  					},
   405  				},
   406  			},
   407  			want: []*node{
   408  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   409  				{"C/Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("B1"), nil},
   410  			},
   411  			wdep: Godeps{
   412  				ImportPath: "C",
   413  				Deps: []Dependency{
   414  					{ImportPath: "D/A", Comment: "D1"},
   415  					{ImportPath: "D/B", Comment: "D1"},
   416  				},
   417  			},
   418  		},
   419  		{ // add one dependency from same repo, require same version
   420  			cwd: "C",
   421  			start: []*node{
   422  				{
   423  					"D",
   424  					"",
   425  					[]*node{
   426  						{"A/main.go", pkg("A") + decl("A1"), nil},
   427  						{"B/main.go", pkg("B") + decl("B1"), nil},
   428  						{"+git", "D1", nil},
   429  						{"A/main.go", pkg("A") + decl("A2"), nil},
   430  						{"B/main.go", pkg("B") + decl("B2"), nil},
   431  						{"+git", "D2", nil},
   432  					},
   433  				},
   434  				{
   435  					"C",
   436  					"",
   437  					[]*node{
   438  						{"main.go", pkg("main", "D/A", "D/B"), nil},
   439  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   440  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   441  						{"+git", "", nil},
   442  					},
   443  				},
   444  			},
   445  			want: []*node{
   446  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   447  			},
   448  			wdep: Godeps{
   449  				ImportPath: "C",
   450  				Deps: []Dependency{
   451  					{ImportPath: "D/A", Comment: "D1"},
   452  				},
   453  			},
   454  			werr: true,
   455  		},
   456  		{ // replace dependency from same repo parent dir
   457  			cwd: "C",
   458  			start: []*node{
   459  				{
   460  					"D",
   461  					"",
   462  					[]*node{
   463  						{"main.go", pkg("D") + decl("D1"), nil},
   464  						{"A/main.go", pkg("A") + decl("A1"), nil},
   465  						{"+git", "D1", nil},
   466  					},
   467  				},
   468  				{
   469  					"C",
   470  					"",
   471  					[]*node{
   472  						{"main.go", pkg("main", "D"), nil},
   473  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   474  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   475  						{"+git", "", nil},
   476  					},
   477  				},
   478  			},
   479  			want: []*node{
   480  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   481  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   482  			},
   483  			wdep: Godeps{
   484  				ImportPath: "C",
   485  				Deps: []Dependency{
   486  					{ImportPath: "D", Comment: "D1"},
   487  				},
   488  			},
   489  		},
   490  		{ // replace dependency from same repo parent dir, require same version
   491  			cwd: "C",
   492  			start: []*node{
   493  				{
   494  					"D",
   495  					"",
   496  					[]*node{
   497  						{"main.go", pkg("D") + decl("D1"), nil},
   498  						{"A/main.go", pkg("A") + decl("A1"), nil},
   499  						{"+git", "D1", nil},
   500  						{"main.go", pkg("D") + decl("D2"), nil},
   501  						{"A/main.go", pkg("A") + decl("A2"), nil},
   502  						{"+git", "D2", nil},
   503  					},
   504  				},
   505  				{
   506  					"C",
   507  					"",
   508  					[]*node{
   509  						{"main.go", pkg("main", "D"), nil},
   510  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   511  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   512  						{"+git", "", nil},
   513  					},
   514  				},
   515  			},
   516  			want: []*node{
   517  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   518  			},
   519  			wdep: Godeps{
   520  				ImportPath: "C",
   521  				Deps: []Dependency{
   522  					{ImportPath: "D/A", Comment: "D1"},
   523  				},
   524  			},
   525  			werr: true,
   526  		},
   527  		{ // replace dependency from same repo child dir
   528  			cwd: "C",
   529  			start: []*node{
   530  				{
   531  					"D",
   532  					"",
   533  					[]*node{
   534  						{"main.go", pkg("D") + decl("D1"), nil},
   535  						{"A/main.go", pkg("A") + decl("A1"), nil},
   536  						{"+git", "D1", nil},
   537  					},
   538  				},
   539  				{
   540  					"C",
   541  					"",
   542  					[]*node{
   543  						{"main.go", pkg("main", "D/A"), nil},
   544  						{"Godeps/Godeps.json", godeps("C", "D", "D1"), nil},
   545  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   546  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   547  						{"+git", "", nil},
   548  					},
   549  				},
   550  			},
   551  			want: []*node{
   552  				{"C/Godeps/_workspace/src/D/main.go", "(absent)", nil},
   553  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   554  			},
   555  			wdep: Godeps{
   556  				ImportPath: "C",
   557  				Deps: []Dependency{
   558  					{ImportPath: "D/A", Comment: "D1"},
   559  				},
   560  			},
   561  		},
   562  		{ // replace dependency from same repo child dir, require same version
   563  			cwd: "C",
   564  			start: []*node{
   565  				{
   566  					"D",
   567  					"",
   568  					[]*node{
   569  						{"main.go", pkg("D") + decl("D1"), nil},
   570  						{"A/main.go", pkg("A") + decl("A1"), nil},
   571  						{"+git", "D1", nil},
   572  						{"main.go", pkg("D") + decl("D2"), nil},
   573  						{"A/main.go", pkg("A") + decl("A2"), nil},
   574  						{"+git", "D2", nil},
   575  					},
   576  				},
   577  				{
   578  					"C",
   579  					"",
   580  					[]*node{
   581  						{"main.go", pkg("main", "D/A"), nil},
   582  						{"Godeps/Godeps.json", godeps("C", "D", "D1"), nil},
   583  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   584  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   585  						{"+git", "", nil},
   586  					},
   587  				},
   588  			},
   589  			want: []*node{
   590  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   591  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   592  			},
   593  			wdep: Godeps{
   594  				ImportPath: "C",
   595  				Deps: []Dependency{
   596  					{ImportPath: "D", Comment: "D1"},
   597  				},
   598  			},
   599  			werr: true,
   600  		},
   601  		{ // Bug https://github.com/tools/godep/issues/85
   602  			cwd: "C",
   603  			start: []*node{
   604  				{
   605  					"D",
   606  					"",
   607  					[]*node{
   608  						{"A/main.go", pkg("A") + decl("A1"), nil},
   609  						{"B/main.go", pkg("B") + decl("B1"), nil},
   610  						{"+git", "D1", nil},
   611  						{"A/main.go", pkg("A") + decl("A2"), nil},
   612  						{"B/main.go", pkg("B") + decl("B2"), nil},
   613  						{"+git", "D2", nil},
   614  					},
   615  				},
   616  				{
   617  					"C",
   618  					"",
   619  					[]*node{
   620  						{"main.go", pkg("main", "D/A", "D/B"), nil},
   621  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil},
   622  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   623  						{"Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("B1"), nil},
   624  						{"+git", "", nil},
   625  					},
   626  				},
   627  			},
   628  			want: []*node{
   629  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   630  				{"C/Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("B1"), nil},
   631  			},
   632  			wdep: Godeps{
   633  				ImportPath: "C",
   634  				Deps: []Dependency{
   635  					{ImportPath: "D/A", Comment: "D1"},
   636  					{ImportPath: "D/B", Comment: "D1"},
   637  				},
   638  			},
   639  		},
   640  		{ // intermediate dependency that uses godep save -r, main -r=false
   641  			cwd: "C",
   642  			start: []*node{
   643  				{
   644  					"C",
   645  					"",
   646  					[]*node{
   647  						{"main.go", pkg("main", "D"), nil},
   648  						{"+git", "", nil},
   649  					},
   650  				},
   651  				{
   652  					"T",
   653  					"",
   654  					[]*node{
   655  						{"main.go", pkg("T"), nil},
   656  						{"+git", "T1", nil},
   657  					},
   658  				},
   659  				{
   660  					"D",
   661  					"",
   662  					[]*node{
   663  						{"main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil},
   664  						{"Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   665  						{"Godeps/Godeps.json", godeps("D", "T", "T1"), nil},
   666  						{"+git", "D1", nil},
   667  					},
   668  				},
   669  			},
   670  			want: []*node{
   671  				{"C/main.go", pkg("main", "D"), nil},
   672  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil},
   673  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   674  			},
   675  			wdep: Godeps{
   676  				ImportPath: "C",
   677  				Deps: []Dependency{
   678  					{ImportPath: "D", Comment: "D1"},
   679  					{ImportPath: "T", Comment: "T1"},
   680  				},
   681  			},
   682  		},
   683  		{ // intermediate dependency that uses godep save -r, main -r too
   684  			cwd:   "C",
   685  			flagR: true,
   686  			start: []*node{
   687  				{
   688  					"C",
   689  					"",
   690  					[]*node{
   691  						{"main.go", pkg("main", "D"), nil},
   692  						{"+git", "", nil},
   693  					},
   694  				},
   695  				{
   696  					"T",
   697  					"",
   698  					[]*node{
   699  						{"main.go", pkg("T"), nil},
   700  						{"+git", "T1", nil},
   701  					},
   702  				},
   703  				{
   704  					"D",
   705  					"",
   706  					[]*node{
   707  						{"main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil},
   708  						{"Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   709  						{"Godeps/Godeps.json", godeps("D", "T", "T1"), nil},
   710  						{"+git", "D1", nil},
   711  					},
   712  				},
   713  			},
   714  			want: []*node{
   715  				{"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil},
   716  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "C/Godeps/_workspace/src/T"), nil},
   717  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   718  			},
   719  			wdep: Godeps{
   720  				ImportPath: "C",
   721  				Deps: []Dependency{
   722  					{ImportPath: "D", Comment: "D1"},
   723  					{ImportPath: "T", Comment: "T1"},
   724  				},
   725  			},
   726  		},
   727  		{ // rewrite files under build constraints
   728  			cwd:   "C",
   729  			flagR: true,
   730  			start: []*node{
   731  				{
   732  					"C",
   733  					"",
   734  					[]*node{
   735  						{"main.go", pkg("main", "D"), nil},
   736  						{"x.go", "// +build x\n\n" + pkg("main", "D"), nil},
   737  						{"+git", "", nil},
   738  					},
   739  				},
   740  				{
   741  					"D",
   742  					"",
   743  					[]*node{
   744  						{"main.go", pkg("D"), nil},
   745  						{"+git", "D1", nil},
   746  					},
   747  				},
   748  			},
   749  			want: []*node{
   750  				{"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil},
   751  				{"C/x.go", "// +build x\n\n" + pkg("main", "C/Godeps/_workspace/src/D"), nil},
   752  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
   753  			},
   754  			wdep: Godeps{
   755  				ImportPath: "C",
   756  				Deps: []Dependency{
   757  					{ImportPath: "D", Comment: "D1"},
   758  				},
   759  			},
   760  		},
   761  		{ // exclude dependency subdirectories even when obtained by a rewritten import path
   762  			cwd: "C",
   763  			start: []*node{
   764  				{
   765  					"C",
   766  					"",
   767  					[]*node{
   768  						{"main.go", pkg("main", "D", "T"), nil},
   769  						{"+git", "", nil},
   770  					},
   771  				},
   772  				{
   773  					"T",
   774  					"",
   775  					[]*node{
   776  						{"main.go", pkg("T"), nil},
   777  						{"X/main.go", pkg("X"), nil},
   778  						{"+git", "T1", nil},
   779  					},
   780  				},
   781  				{
   782  					"D",
   783  					"",
   784  					[]*node{
   785  						{"main.go", pkg("D", "D/Godeps/_workspace/src/T/X"), nil},
   786  						{"Godeps/_workspace/src/T/X/main.go", pkg("X"), nil},
   787  						{"Godeps/Godeps.json", godeps("D", "T/X", "T1"), nil},
   788  						{"+git", "D1", nil},
   789  					},
   790  				},
   791  			},
   792  			want: []*node{
   793  				{"C/main.go", pkg("main", "D", "T"), nil},
   794  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "T/X"), nil},
   795  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   796  			},
   797  			wdep: Godeps{
   798  				ImportPath: "C",
   799  				Deps: []Dependency{
   800  					{ImportPath: "D", Comment: "D1"},
   801  					{ImportPath: "T", Comment: "T1"},
   802  				},
   803  			},
   804  		},
   805  		{ // find transitive dependencies across roots
   806  			cwd:   "C",
   807  			flagR: true,
   808  			altstart: []*node{
   809  				{
   810  					"T",
   811  					"",
   812  					[]*node{
   813  						{"main.go", pkg("T"), nil},
   814  						{"+git", "T1", nil},
   815  					},
   816  				},
   817  			},
   818  			start: []*node{
   819  				{
   820  					"C",
   821  					"",
   822  					[]*node{
   823  						{"main.go", pkg("main", "D"), nil},
   824  						{"+git", "", nil},
   825  					},
   826  				},
   827  				{
   828  					"D",
   829  					"",
   830  					[]*node{
   831  						{"main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil},
   832  						{"Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   833  						{"Godeps/Godeps.json", godeps("D", "T", "T1"), nil},
   834  						{"+git", "D1", nil},
   835  					},
   836  				},
   837  			},
   838  			want: []*node{
   839  				{"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil},
   840  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "C/Godeps/_workspace/src/T"), nil},
   841  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   842  			},
   843  			wdep: Godeps{
   844  				ImportPath: "C",
   845  				Deps: []Dependency{
   846  					{ImportPath: "D", Comment: "D1"},
   847  					{ImportPath: "T", Comment: "T1"},
   848  				},
   849  			},
   850  		},
   851  		{ // pull in minimal dependencies, see https://github.com/tools/godep/issues/93
   852  			cwd:   "C",
   853  			flagR: true,
   854  			start: []*node{
   855  				{
   856  					"C",
   857  					"",
   858  					[]*node{
   859  						{"main.go", pkg("main", "D/X"), nil},
   860  						{"+git", "", nil},
   861  					},
   862  				},
   863  				{
   864  					"T",
   865  					"",
   866  					[]*node{
   867  						{"main.go", pkg("T"), nil},
   868  						{"+git", "T1", nil},
   869  					},
   870  				},
   871  				{
   872  					"D",
   873  					"",
   874  					[]*node{
   875  						{"main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil},
   876  						{"X/main.go", pkg("X"), nil},
   877  						{"Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   878  						{"Godeps/Godeps.json", godeps("D", "T", "T1"), nil},
   879  						{"+git", "D1", nil},
   880  					},
   881  				},
   882  			},
   883  			want: []*node{
   884  				{"C/main.go", pkg("main", "C/Godeps/_workspace/src/D/X"), nil},
   885  				{"C/Godeps/_workspace/src/D/X/main.go", pkg("X"), nil},
   886  			},
   887  			wdep: Godeps{
   888  				ImportPath: "C",
   889  				Deps: []Dependency{
   890  					{ImportPath: "D/X", Comment: "D1"},
   891  				},
   892  			},
   893  		},
   894  	}
   895  
   896  	wd, err := os.Getwd()
   897  	if err != nil {
   898  		t.Fatal(err)
   899  	}
   900  	const scratch = "godeptest"
   901  	defer os.RemoveAll(scratch)
   902  	for _, test := range cases {
   903  		err = os.RemoveAll(scratch)
   904  		if err != nil {
   905  			t.Fatal(err)
   906  		}
   907  		altsrc := filepath.Join(scratch, "r2", "src")
   908  		if test.altstart != nil {
   909  			makeTree(t, &node{altsrc, "", test.altstart}, "")
   910  		}
   911  		src := filepath.Join(scratch, "r1", "src")
   912  		makeTree(t, &node{src, "", test.start}, altsrc)
   913  
   914  		dir := filepath.Join(wd, src, test.cwd)
   915  		err = os.Chdir(dir)
   916  		if err != nil {
   917  			panic(err)
   918  		}
   919  		root1 := filepath.Join(wd, scratch, "r1")
   920  		root2 := filepath.Join(wd, scratch, "r2")
   921  		err = os.Setenv("GOPATH", root1+string(os.PathListSeparator)+root2)
   922  		if err != nil {
   923  			panic(err)
   924  		}
   925  		saveR = test.flagR
   926  		err = save(test.args)
   927  		if g := err != nil; g != test.werr {
   928  			if err != nil {
   929  				t.Log(err)
   930  			}
   931  			t.Errorf("save err = %v want %v", g, test.werr)
   932  		}
   933  		err = os.Chdir(wd)
   934  		if err != nil {
   935  			panic(err)
   936  		}
   937  
   938  		checkTree(t, &node{src, "", test.want})
   939  
   940  		f, err := os.Open(filepath.Join(dir, "Godeps/Godeps.json"))
   941  		if err != nil {
   942  			t.Error(err)
   943  		}
   944  		g := new(Godeps)
   945  		err = json.NewDecoder(f).Decode(g)
   946  		if err != nil {
   947  			t.Error(err)
   948  		}
   949  		f.Close()
   950  
   951  		if g.ImportPath != test.wdep.ImportPath {
   952  			t.Errorf("ImportPath = %s want %s", g.ImportPath, test.wdep.ImportPath)
   953  		}
   954  		for i := range g.Deps {
   955  			g.Deps[i].Rev = ""
   956  		}
   957  		if !reflect.DeepEqual(g.Deps, test.wdep.Deps) {
   958  			t.Errorf("Deps = %v want %v", g.Deps, test.wdep.Deps)
   959  		}
   960  	}
   961  }
   962  
   963  func makeTree(t *testing.T, tree *node, altpath string) (gopath string) {
   964  	walkTree(tree, tree.path, func(path string, n *node) {
   965  		g, isGodeps := n.body.(*Godeps)
   966  		body, _ := n.body.(string)
   967  		switch {
   968  		case isGodeps:
   969  			for i, dep := range g.Deps {
   970  				rel := filepath.FromSlash(dep.ImportPath)
   971  				dir := filepath.Join(tree.path, rel)
   972  				if _, err := os.Stat(dir); os.IsNotExist(err) {
   973  					dir = filepath.Join(altpath, rel)
   974  				}
   975  				tag := dep.Comment
   976  				rev := strings.TrimSpace(run(t, dir, "git", "rev-parse", tag))
   977  				g.Deps[i].Rev = rev
   978  			}
   979  			os.MkdirAll(filepath.Dir(path), 0770)
   980  			f, err := os.Create(path)
   981  			if err != nil {
   982  				t.Errorf("makeTree: %v", err)
   983  				return
   984  			}
   985  			defer f.Close()
   986  			err = json.NewEncoder(f).Encode(g)
   987  			if err != nil {
   988  				t.Errorf("makeTree: %v", err)
   989  			}
   990  		case n.path == "+git":
   991  			dir := filepath.Dir(path)
   992  			run(t, dir, "git", "init") // repo might already exist, but ok
   993  			run(t, dir, "git", "add", ".")
   994  			run(t, dir, "git", "commit", "-m", "godep")
   995  			if body != "" {
   996  				run(t, dir, "git", "tag", body)
   997  			}
   998  		case n.entries == nil && strings.HasPrefix(body, "symlink:"):
   999  			target := strings.TrimPrefix(body, "symlink:")
  1000  			os.Symlink(target, path)
  1001  		case n.entries == nil && body == "(absent)":
  1002  			panic("is this gonna be forever")
  1003  		case n.entries == nil:
  1004  			os.MkdirAll(filepath.Dir(path), 0770)
  1005  			err := ioutil.WriteFile(path, []byte(body), 0660)
  1006  			if err != nil {
  1007  				t.Errorf("makeTree: %v", err)
  1008  			}
  1009  		default:
  1010  			os.MkdirAll(path, 0770)
  1011  		}
  1012  	})
  1013  	return gopath
  1014  }
  1015  
  1016  func checkTree(t *testing.T, want *node) {
  1017  	walkTree(want, want.path, func(path string, n *node) {
  1018  		body := n.body.(string)
  1019  		switch {
  1020  		case n.path == "+git":
  1021  			panic("is this real life")
  1022  		case n.entries == nil && strings.HasPrefix(body, "symlink:"):
  1023  			panic("why is this happening to me")
  1024  		case n.entries == nil && body == "(absent)":
  1025  			body, err := ioutil.ReadFile(path)
  1026  			if !os.IsNotExist(err) {
  1027  				t.Errorf("checkTree: %s = %s want absent", path, string(body))
  1028  				return
  1029  			}
  1030  		case n.entries == nil:
  1031  			gbody, err := ioutil.ReadFile(path)
  1032  			if err != nil {
  1033  				t.Errorf("checkTree: %v", err)
  1034  				return
  1035  			}
  1036  			if got := string(gbody); got != body {
  1037  				t.Errorf("%s = %s want %s", path, got, body)
  1038  			}
  1039  		default:
  1040  			os.MkdirAll(path, 0770)
  1041  		}
  1042  	})
  1043  }
  1044  
  1045  func walkTree(n *node, path string, f func(path string, n *node)) {
  1046  	f(path, n)
  1047  	for _, e := range n.entries {
  1048  		walkTree(e, filepath.Join(path, filepath.FromSlash(e.path)), f)
  1049  	}
  1050  }
  1051  
  1052  func run(t *testing.T, dir, name string, args ...string) string {
  1053  	cmd := exec.Command(name, args...)
  1054  	cmd.Dir = dir
  1055  	cmd.Stderr = os.Stderr
  1056  	out, err := cmd.Output()
  1057  	if err != nil {
  1058  		panic(name + " " + strings.Join(args, " ") + ": " + err.Error())
  1059  	}
  1060  	return string(out)
  1061  }