github.com/matthewbelisle-wf/godep@v0.0.0-20140716191328-dba190f14fc8/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  		want  []*node
    69  		wdep  Godeps
    70  		werr  bool
    71  	}{
    72  		{ // simple case, one dependency
    73  			cwd: "C",
    74  			start: []*node{
    75  				{
    76  					"C",
    77  					"",
    78  					[]*node{
    79  						{"main.go", pkg("main", "D"), nil},
    80  						{"+git", "", nil},
    81  					},
    82  				},
    83  				{
    84  					"D",
    85  					"",
    86  					[]*node{
    87  						{"main.go", pkg("D"), nil},
    88  						{"+git", "D1", nil},
    89  					},
    90  				},
    91  			},
    92  			want: []*node{
    93  				{"C/main.go", pkg("main", "D"), nil},
    94  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
    95  			},
    96  			wdep: Godeps{
    97  				ImportPath: "C",
    98  				Deps: []Dependency{
    99  					{ImportPath: "D", Comment: "D1"},
   100  				},
   101  			},
   102  		},
   103  		{
   104  			// dependency in same repo with existing manifest
   105  			// see bug https://github.com/tools/godep/issues/69
   106  			cwd:  "P",
   107  			args: []string{"./..."},
   108  			start: []*node{
   109  				{
   110  					"P",
   111  					"",
   112  					[]*node{
   113  						{"main.go", pkg("P", "P/Q"), nil},
   114  						{"Q/main.go", pkg("Q"), nil},
   115  						{"Godeps/Godeps.json", `{}`, nil},
   116  						{"+git", "C1", nil},
   117  					},
   118  				},
   119  			},
   120  			want: []*node{
   121  				{"P/main.go", pkg("P", "P/Q"), nil},
   122  				{"P/Q/main.go", pkg("Q"), nil},
   123  			},
   124  			wdep: Godeps{
   125  				ImportPath: "P",
   126  				Deps:       []Dependency{},
   127  			},
   128  		},
   129  		{
   130  			// dependency on parent directory in same repo
   131  			// see bug https://github.com/tools/godep/issues/70
   132  			cwd:  "P",
   133  			args: []string{"./..."},
   134  			start: []*node{
   135  				{
   136  					"P",
   137  					"",
   138  					[]*node{
   139  						{"main.go", pkg("P"), nil},
   140  						{"Q/main.go", pkg("Q", "P"), nil},
   141  						{"+git", "C1", nil},
   142  					},
   143  				},
   144  			},
   145  			want: []*node{
   146  				{"P/main.go", pkg("P"), nil},
   147  				{"P/Q/main.go", pkg("Q", "P"), nil},
   148  			},
   149  			wdep: Godeps{
   150  				ImportPath: "P",
   151  				Deps:       []Dependency{},
   152  			},
   153  		},
   154  		{ // transitive dependency
   155  			cwd: "C",
   156  			start: []*node{
   157  				{
   158  					"C",
   159  					"",
   160  					[]*node{
   161  						{"main.go", pkg("main", "D"), nil},
   162  						{"+git", "", nil},
   163  					},
   164  				},
   165  				{
   166  					"D",
   167  					"",
   168  					[]*node{
   169  						{"main.go", pkg("D", "T"), nil},
   170  						{"+git", "D1", nil},
   171  					},
   172  				},
   173  				{
   174  					"T",
   175  					"",
   176  					[]*node{
   177  						{"main.go", pkg("T"), nil},
   178  						{"+git", "T1", nil},
   179  					},
   180  				},
   181  			},
   182  			want: []*node{
   183  				{"C/main.go", pkg("main", "D"), nil},
   184  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil},
   185  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   186  			},
   187  			wdep: Godeps{
   188  				ImportPath: "C",
   189  				Deps: []Dependency{
   190  					{ImportPath: "D", Comment: "D1"},
   191  					{ImportPath: "T", Comment: "T1"},
   192  				},
   193  			},
   194  		},
   195  		{ // two packages, one in a subdirectory
   196  			cwd: "C",
   197  			start: []*node{
   198  				{
   199  					"C",
   200  					"",
   201  					[]*node{
   202  						{"main.go", pkg("main", "D", "D/P"), nil},
   203  						{"+git", "", nil},
   204  					},
   205  				},
   206  				{
   207  					"D",
   208  					"",
   209  					[]*node{
   210  						{"main.go", pkg("D"), nil},
   211  						{"P/main.go", pkg("P"), nil},
   212  						{"+git", "D1", nil},
   213  					},
   214  				},
   215  			},
   216  			want: []*node{
   217  				{"C/main.go", pkg("main", "D", "D/P"), nil},
   218  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
   219  				{"C/Godeps/_workspace/src/D/P/main.go", pkg("P"), nil},
   220  			},
   221  			wdep: Godeps{
   222  				ImportPath: "C",
   223  				Deps: []Dependency{
   224  					{ImportPath: "D", Comment: "D1"},
   225  				},
   226  			},
   227  		},
   228  		{ // repo root is not a package (no go files)
   229  			cwd: "C",
   230  			start: []*node{
   231  				{
   232  					"C",
   233  					"",
   234  					[]*node{
   235  						{"main.go", pkg("main", "D/P", "D/Q"), nil},
   236  						{"+git", "", nil},
   237  					},
   238  				},
   239  				{
   240  					"D",
   241  					"",
   242  					[]*node{
   243  						{"P/main.go", pkg("P"), nil},
   244  						{"Q/main.go", pkg("Q"), nil},
   245  						{"+git", "D1", nil},
   246  					},
   247  				},
   248  			},
   249  			want: []*node{
   250  				{"C/main.go", pkg("main", "D/P", "D/Q"), nil},
   251  				{"C/Godeps/_workspace/src/D/P/main.go", pkg("P"), nil},
   252  				{"C/Godeps/_workspace/src/D/Q/main.go", pkg("Q"), nil},
   253  			},
   254  			wdep: Godeps{
   255  				ImportPath: "C",
   256  				Deps: []Dependency{
   257  					{ImportPath: "D/P", Comment: "D1"},
   258  					{ImportPath: "D/Q", Comment: "D1"},
   259  				},
   260  			},
   261  		},
   262  		{ // symlink
   263  			cwd: "C",
   264  			start: []*node{
   265  				{
   266  					"C",
   267  					"",
   268  					[]*node{
   269  						{"main.x", pkg("main", "D"), nil},
   270  						{"main.go", "symlink:main.x", nil},
   271  						{"+git", "", nil},
   272  					},
   273  				},
   274  				{
   275  					"D",
   276  					"",
   277  					[]*node{
   278  						{"main.go", pkg("D"), nil},
   279  						{"+git", "D1", nil},
   280  					},
   281  				},
   282  			},
   283  			want: []*node{
   284  				{"C/main.go", pkg("main", "D"), nil},
   285  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
   286  			},
   287  			wdep: Godeps{
   288  				ImportPath: "C",
   289  				Deps: []Dependency{
   290  					{ImportPath: "D", Comment: "D1"},
   291  				},
   292  			},
   293  		},
   294  		{ // add one dependency; keep other dependency version
   295  			cwd: "C",
   296  			start: []*node{
   297  				{
   298  					"D",
   299  					"",
   300  					[]*node{
   301  						{"main.go", pkg("D") + decl("D1"), nil},
   302  						{"+git", "D1", nil},
   303  						{"main.go", pkg("D") + decl("D2"), nil},
   304  						{"+git", "D2", nil},
   305  					},
   306  				},
   307  				{
   308  					"E",
   309  					"",
   310  					[]*node{
   311  						{"main.go", pkg("E"), nil},
   312  						{"+git", "E1", nil},
   313  					},
   314  				},
   315  				{
   316  					"C",
   317  					"",
   318  					[]*node{
   319  						{"main.go", pkg("main", "D", "E"), nil},
   320  						{"Godeps/Godeps.json", godeps("C", "D", "D1"), nil},
   321  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   322  						{"+git", "", nil},
   323  					},
   324  				},
   325  			},
   326  			want: []*node{
   327  				{"C/main.go", pkg("main", "D", "E"), nil},
   328  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   329  				{"C/Godeps/_workspace/src/E/main.go", pkg("E"), nil},
   330  			},
   331  			wdep: Godeps{
   332  				ImportPath: "C",
   333  				Deps: []Dependency{
   334  					{ImportPath: "D", Comment: "D1"},
   335  					{ImportPath: "E", Comment: "E1"},
   336  				},
   337  			},
   338  		},
   339  		{ // remove one dependency; keep other dependency version
   340  			cwd: "C",
   341  			start: []*node{
   342  				{
   343  					"D",
   344  					"",
   345  					[]*node{
   346  						{"main.go", pkg("D") + decl("D1"), nil},
   347  						{"+git", "D1", nil},
   348  						{"main.go", pkg("D") + decl("D2"), nil},
   349  						{"+git", "D2", nil},
   350  					},
   351  				},
   352  				{
   353  					"E",
   354  					"",
   355  					[]*node{
   356  						{"main.go", pkg("E") + decl("E1"), nil},
   357  						{"+git", "E1", nil},
   358  					},
   359  				},
   360  				{
   361  					"C",
   362  					"",
   363  					[]*node{
   364  						{"main.go", pkg("main", "D"), nil},
   365  						{"Godeps/Godeps.json", godeps("C", "D", "D1", "E", "E1"), nil},
   366  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   367  						{"Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil},
   368  						{"+git", "", nil},
   369  					},
   370  				},
   371  			},
   372  			want: []*node{
   373  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   374  				{"C/Godeps/_workspace/src/E/main.go", "(absent)", nil},
   375  			},
   376  			wdep: Godeps{
   377  				ImportPath: "C",
   378  				Deps: []Dependency{
   379  					{ImportPath: "D", Comment: "D1"},
   380  				},
   381  			},
   382  		},
   383  		{ // add one dependency from same repo
   384  			cwd: "C",
   385  			start: []*node{
   386  				{
   387  					"D",
   388  					"",
   389  					[]*node{
   390  						{"A/main.go", pkg("A") + decl("A1"), nil},
   391  						{"B/main.go", pkg("B") + decl("B1"), nil},
   392  						{"+git", "D1", nil},
   393  					},
   394  				},
   395  				{
   396  					"C",
   397  					"",
   398  					[]*node{
   399  						{"main.go", pkg("main", "D/A", "D/B"), nil},
   400  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   401  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   402  						{"+git", "", nil},
   403  					},
   404  				},
   405  			},
   406  			want: []*node{
   407  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   408  				{"C/Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("B1"), nil},
   409  			},
   410  			wdep: Godeps{
   411  				ImportPath: "C",
   412  				Deps: []Dependency{
   413  					{ImportPath: "D/A", Comment: "D1"},
   414  					{ImportPath: "D/B", Comment: "D1"},
   415  				},
   416  			},
   417  		},
   418  		{ // add one dependency from same repo, require same version
   419  			cwd: "C",
   420  			start: []*node{
   421  				{
   422  					"D",
   423  					"",
   424  					[]*node{
   425  						{"A/main.go", pkg("A") + decl("A1"), nil},
   426  						{"B/main.go", pkg("B") + decl("B1"), nil},
   427  						{"+git", "D1", nil},
   428  						{"A/main.go", pkg("A") + decl("A2"), nil},
   429  						{"B/main.go", pkg("B") + decl("B2"), nil},
   430  						{"+git", "D2", nil},
   431  					},
   432  				},
   433  				{
   434  					"C",
   435  					"",
   436  					[]*node{
   437  						{"main.go", pkg("main", "D/A", "D/B"), nil},
   438  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   439  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   440  						{"+git", "", nil},
   441  					},
   442  				},
   443  			},
   444  			want: []*node{
   445  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   446  			},
   447  			wdep: Godeps{
   448  				ImportPath: "C",
   449  				Deps: []Dependency{
   450  					{ImportPath: "D/A", Comment: "D1"},
   451  				},
   452  			},
   453  			werr: true,
   454  		},
   455  		{ // replace dependency from same repo parent dir
   456  			cwd: "C",
   457  			start: []*node{
   458  				{
   459  					"D",
   460  					"",
   461  					[]*node{
   462  						{"main.go", pkg("D") + decl("D1"), nil},
   463  						{"A/main.go", pkg("A") + decl("A1"), nil},
   464  						{"+git", "D1", nil},
   465  					},
   466  				},
   467  				{
   468  					"C",
   469  					"",
   470  					[]*node{
   471  						{"main.go", pkg("main", "D"), nil},
   472  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   473  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   474  						{"+git", "", nil},
   475  					},
   476  				},
   477  			},
   478  			want: []*node{
   479  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   480  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   481  			},
   482  			wdep: Godeps{
   483  				ImportPath: "C",
   484  				Deps: []Dependency{
   485  					{ImportPath: "D", Comment: "D1"},
   486  				},
   487  			},
   488  		},
   489  		{ // replace dependency from same repo parent dir, require same version
   490  			cwd: "C",
   491  			start: []*node{
   492  				{
   493  					"D",
   494  					"",
   495  					[]*node{
   496  						{"main.go", pkg("D") + decl("D1"), nil},
   497  						{"A/main.go", pkg("A") + decl("A1"), nil},
   498  						{"+git", "D1", nil},
   499  						{"main.go", pkg("D") + decl("D2"), nil},
   500  						{"A/main.go", pkg("A") + decl("A2"), nil},
   501  						{"+git", "D2", nil},
   502  					},
   503  				},
   504  				{
   505  					"C",
   506  					"",
   507  					[]*node{
   508  						{"main.go", pkg("main", "D"), nil},
   509  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1"), nil},
   510  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   511  						{"+git", "", nil},
   512  					},
   513  				},
   514  			},
   515  			want: []*node{
   516  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   517  			},
   518  			wdep: Godeps{
   519  				ImportPath: "C",
   520  				Deps: []Dependency{
   521  					{ImportPath: "D/A", Comment: "D1"},
   522  				},
   523  			},
   524  			werr: true,
   525  		},
   526  		{ // replace dependency from same repo child dir
   527  			cwd: "C",
   528  			start: []*node{
   529  				{
   530  					"D",
   531  					"",
   532  					[]*node{
   533  						{"main.go", pkg("D") + decl("D1"), nil},
   534  						{"A/main.go", pkg("A") + decl("A1"), nil},
   535  						{"+git", "D1", nil},
   536  					},
   537  				},
   538  				{
   539  					"C",
   540  					"",
   541  					[]*node{
   542  						{"main.go", pkg("main", "D/A"), nil},
   543  						{"Godeps/Godeps.json", godeps("C", "D", "D1"), nil},
   544  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   545  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   546  						{"+git", "", nil},
   547  					},
   548  				},
   549  			},
   550  			want: []*node{
   551  				{"C/Godeps/_workspace/src/D/main.go", "(absent)", nil},
   552  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   553  			},
   554  			wdep: Godeps{
   555  				ImportPath: "C",
   556  				Deps: []Dependency{
   557  					{ImportPath: "D/A", Comment: "D1"},
   558  				},
   559  			},
   560  		},
   561  		{ // replace dependency from same repo child dir, require same version
   562  			cwd: "C",
   563  			start: []*node{
   564  				{
   565  					"D",
   566  					"",
   567  					[]*node{
   568  						{"main.go", pkg("D") + decl("D1"), nil},
   569  						{"A/main.go", pkg("A") + decl("A1"), nil},
   570  						{"+git", "D1", nil},
   571  						{"main.go", pkg("D") + decl("D2"), nil},
   572  						{"A/main.go", pkg("A") + decl("A2"), nil},
   573  						{"+git", "D2", nil},
   574  					},
   575  				},
   576  				{
   577  					"C",
   578  					"",
   579  					[]*node{
   580  						{"main.go", pkg("main", "D/A"), nil},
   581  						{"Godeps/Godeps.json", godeps("C", "D", "D1"), nil},
   582  						{"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   583  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   584  						{"+git", "", nil},
   585  					},
   586  				},
   587  			},
   588  			want: []*node{
   589  				{"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil},
   590  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   591  			},
   592  			wdep: Godeps{
   593  				ImportPath: "C",
   594  				Deps: []Dependency{
   595  					{ImportPath: "D", Comment: "D1"},
   596  				},
   597  			},
   598  			werr: true,
   599  		},
   600  		{ // Bug https://github.com/tools/godep/issues/85
   601  			cwd: "C",
   602  			start: []*node{
   603  				{
   604  					"D",
   605  					"",
   606  					[]*node{
   607  						{"A/main.go", pkg("A") + decl("A1"), nil},
   608  						{"B/main.go", pkg("B") + decl("B1"), nil},
   609  						{"+git", "D1", nil},
   610  						{"A/main.go", pkg("A") + decl("A2"), nil},
   611  						{"B/main.go", pkg("B") + decl("B2"), nil},
   612  						{"+git", "D2", nil},
   613  					},
   614  				},
   615  				{
   616  					"C",
   617  					"",
   618  					[]*node{
   619  						{"main.go", pkg("main", "D/A", "D/B"), nil},
   620  						{"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil},
   621  						{"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   622  						{"Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("B1"), nil},
   623  						{"+git", "", nil},
   624  					},
   625  				},
   626  			},
   627  			want: []*node{
   628  				{"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("A1"), nil},
   629  				{"C/Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("B1"), nil},
   630  			},
   631  			wdep: Godeps{
   632  				ImportPath: "C",
   633  				Deps: []Dependency{
   634  					{ImportPath: "D/A", Comment: "D1"},
   635  					{ImportPath: "D/B", Comment: "D1"},
   636  				},
   637  			},
   638  		},
   639  		{ // intermediate dependency that uses godep save -r, main -r=false
   640  			cwd: "C",
   641  			start: []*node{
   642  				{
   643  					"C",
   644  					"",
   645  					[]*node{
   646  						{"main.go", pkg("main", "D"), nil},
   647  						{"+git", "", nil},
   648  					},
   649  				},
   650  				{
   651  					"T",
   652  					"",
   653  					[]*node{
   654  						{"main.go", pkg("T"), nil},
   655  						{"+git", "T1", nil},
   656  					},
   657  				},
   658  				{
   659  					"D",
   660  					"",
   661  					[]*node{
   662  						{"main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil},
   663  						{"Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   664  						{"Godeps/Godeps.json", godeps("D", "T", "T1"), nil},
   665  						{"+git", "D1", nil},
   666  					},
   667  				},
   668  			},
   669  			want: []*node{
   670  				{"C/main.go", pkg("main", "D"), nil},
   671  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil},
   672  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   673  			},
   674  			wdep: Godeps{
   675  				ImportPath: "C",
   676  				Deps: []Dependency{
   677  					{ImportPath: "D", Comment: "D1"},
   678  					{ImportPath: "T", Comment: "T1"},
   679  				},
   680  			},
   681  		},
   682  		{ // intermediate dependency that uses godep save -r, main -r too
   683  			cwd:   "C",
   684  			flagR: true,
   685  			start: []*node{
   686  				{
   687  					"C",
   688  					"",
   689  					[]*node{
   690  						{"main.go", pkg("main", "D"), nil},
   691  						{"+git", "", nil},
   692  					},
   693  				},
   694  				{
   695  					"T",
   696  					"",
   697  					[]*node{
   698  						{"main.go", pkg("T"), nil},
   699  						{"+git", "T1", nil},
   700  					},
   701  				},
   702  				{
   703  					"D",
   704  					"",
   705  					[]*node{
   706  						{"main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil},
   707  						{"Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   708  						{"Godeps/Godeps.json", godeps("D", "T", "T1"), nil},
   709  						{"+git", "D1", nil},
   710  					},
   711  				},
   712  			},
   713  			want: []*node{
   714  				{"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil},
   715  				{"C/Godeps/_workspace/src/D/main.go", pkg("D", "C/Godeps/_workspace/src/T"), nil},
   716  				{"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil},
   717  			},
   718  			wdep: Godeps{
   719  				ImportPath: "C",
   720  				Deps: []Dependency{
   721  					{ImportPath: "D", Comment: "D1"},
   722  					{ImportPath: "T", Comment: "T1"},
   723  				},
   724  			},
   725  		},
   726  		{ // rewrite files under build constraints
   727  			cwd:   "C",
   728  			flagR: true,
   729  			start: []*node{
   730  				{
   731  					"C",
   732  					"",
   733  					[]*node{
   734  						{"main.go", pkg("main", "D"), nil},
   735  						{"x.go", "// +build x\n\n" + pkg("main", "D"), nil},
   736  						{"+git", "", nil},
   737  					},
   738  				},
   739  				{
   740  					"D",
   741  					"",
   742  					[]*node{
   743  						{"main.go", pkg("D"), nil},
   744  						{"+git", "D1", nil},
   745  					},
   746  				},
   747  			},
   748  			want: []*node{
   749  				{"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil},
   750  				{"C/x.go", "// +build x\n\n" + pkg("main", "C/Godeps/_workspace/src/D"), nil},
   751  				{"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil},
   752  			},
   753  			wdep: Godeps{
   754  				ImportPath: "C",
   755  				Deps: []Dependency{
   756  					{ImportPath: "D", Comment: "D1"},
   757  				},
   758  			},
   759  		},
   760  	}
   761  
   762  	wd, err := os.Getwd()
   763  	if err != nil {
   764  		t.Fatal(err)
   765  	}
   766  	const gopath = "godeptest"
   767  	defer os.RemoveAll(gopath)
   768  	for _, test := range cases {
   769  		err = os.RemoveAll(gopath)
   770  		if err != nil {
   771  			t.Fatal(err)
   772  		}
   773  		src := filepath.Join(gopath, "src")
   774  		makeTree(t, &node{src, "", test.start})
   775  
   776  		dir := filepath.Join(wd, src, test.cwd)
   777  		err = os.Chdir(dir)
   778  		if err != nil {
   779  			panic(err)
   780  		}
   781  		err = os.Setenv("GOPATH", filepath.Join(wd, gopath))
   782  		if err != nil {
   783  			panic(err)
   784  		}
   785  		saveR = test.flagR
   786  		err = save(test.args)
   787  		if g := err != nil; g != test.werr {
   788  			if err != nil {
   789  				t.Log(err)
   790  			}
   791  			t.Errorf("save err = %v want %v", g, test.werr)
   792  		}
   793  		err = os.Chdir(wd)
   794  		if err != nil {
   795  			panic(err)
   796  		}
   797  
   798  		checkTree(t, &node{src, "", test.want})
   799  
   800  		f, err := os.Open(filepath.Join(dir, "Godeps/Godeps.json"))
   801  		if err != nil {
   802  			t.Error(err)
   803  		}
   804  		g := new(Godeps)
   805  		err = json.NewDecoder(f).Decode(g)
   806  		if err != nil {
   807  			t.Error(err)
   808  		}
   809  		f.Close()
   810  
   811  		if g.ImportPath != test.wdep.ImportPath {
   812  			t.Errorf("ImportPath = %s want %s", g.ImportPath, test.wdep.ImportPath)
   813  		}
   814  		for i := range g.Deps {
   815  			g.Deps[i].Rev = ""
   816  		}
   817  		if !reflect.DeepEqual(g.Deps, test.wdep.Deps) {
   818  			t.Errorf("Deps = %v want %v", g.Deps, test.wdep.Deps)
   819  		}
   820  	}
   821  }
   822  
   823  func makeTree(t *testing.T, tree *node) (gopath string) {
   824  	walkTree(tree, tree.path, func(path string, n *node) {
   825  		g, isGodeps := n.body.(*Godeps)
   826  		body, _ := n.body.(string)
   827  		_ = g
   828  		switch {
   829  		case isGodeps:
   830  			for i, dep := range g.Deps {
   831  				dir := filepath.Join(tree.path, filepath.FromSlash(dep.ImportPath))
   832  				tag := dep.Comment
   833  				rev := strings.TrimSpace(run(t, dir, "git", "rev-parse", tag))
   834  				g.Deps[i].Rev = rev
   835  			}
   836  			os.MkdirAll(filepath.Dir(path), 0770)
   837  			f, err := os.Create(path)
   838  			if err != nil {
   839  				t.Errorf("makeTree: %v", err)
   840  				return
   841  			}
   842  			defer f.Close()
   843  			err = json.NewEncoder(f).Encode(g)
   844  			if err != nil {
   845  				t.Errorf("makeTree: %v", err)
   846  			}
   847  		case n.path == "+git":
   848  			dir := filepath.Dir(path)
   849  			run(t, dir, "git", "init") // repo might already exist, but ok
   850  			run(t, dir, "git", "add", ".")
   851  			run(t, dir, "git", "commit", "-m", "godep")
   852  			if body != "" {
   853  				run(t, dir, "git", "tag", body)
   854  			}
   855  		case n.entries == nil && strings.HasPrefix(body, "symlink:"):
   856  			target := strings.TrimPrefix(body, "symlink:")
   857  			os.Symlink(target, path)
   858  		case n.entries == nil && body == "(absent)":
   859  			panic("is this gonna be forever")
   860  		case n.entries == nil:
   861  			os.MkdirAll(filepath.Dir(path), 0770)
   862  			err := ioutil.WriteFile(path, []byte(body), 0660)
   863  			if err != nil {
   864  				t.Errorf("makeTree: %v", err)
   865  			}
   866  		default:
   867  			os.MkdirAll(path, 0770)
   868  		}
   869  	})
   870  	return gopath
   871  }
   872  
   873  func checkTree(t *testing.T, want *node) {
   874  	walkTree(want, want.path, func(path string, n *node) {
   875  		body := n.body.(string)
   876  		switch {
   877  		case n.path == "+git":
   878  			panic("is this real life")
   879  		case n.entries == nil && strings.HasPrefix(body, "symlink:"):
   880  			panic("why is this happening to me")
   881  		case n.entries == nil && body == "(absent)":
   882  			body, err := ioutil.ReadFile(path)
   883  			if !os.IsNotExist(err) {
   884  				t.Errorf("checkTree: %s = %s want absent", path, string(body))
   885  				return
   886  			}
   887  		case n.entries == nil:
   888  			gbody, err := ioutil.ReadFile(path)
   889  			if err != nil {
   890  				t.Errorf("checkTree: %v", err)
   891  				return
   892  			}
   893  			if got := string(gbody); got != body {
   894  				t.Errorf("%s = %s want %s", path, got, body)
   895  			}
   896  		default:
   897  			os.MkdirAll(path, 0770)
   898  		}
   899  	})
   900  }
   901  
   902  func walkTree(n *node, path string, f func(path string, n *node)) {
   903  	f(path, n)
   904  	for _, e := range n.entries {
   905  		walkTree(e, filepath.Join(path, filepath.FromSlash(e.path)), f)
   906  	}
   907  }
   908  
   909  func run(t *testing.T, dir, name string, args ...string) string {
   910  	cmd := exec.Command(name, args...)
   911  	cmd.Dir = dir
   912  	cmd.Stderr = os.Stderr
   913  	out, err := cmd.Output()
   914  	if err != nil {
   915  		t.Fatal(err)
   916  	}
   917  	return string(out)
   918  }