github.com/joshdk/godel@v0.0.0-20170529232908-862138a45aee/apps/distgo/cmd/artifacts/artifacts_test.go (about)

     1  // Copyright 2016 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package artifacts_test
    16  
    17  import (
    18  	"io/ioutil"
    19  	"path"
    20  	"path/filepath"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/nmiyake/pkg/dirs"
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  
    28  	"github.com/palantir/godel/apps/distgo/cmd/artifacts"
    29  	"github.com/palantir/godel/apps/distgo/cmd/build"
    30  	"github.com/palantir/godel/apps/distgo/params"
    31  	"github.com/palantir/godel/apps/distgo/pkg/osarch"
    32  )
    33  
    34  func TestBuildArtifacts(t *testing.T) {
    35  	tmpDir, cleanup, err := dirs.TempDir("", "")
    36  	defer cleanup()
    37  	require.NoError(t, err)
    38  
    39  	for i, currCase := range []struct {
    40  		specs   func(projectDir string) []params.ProductBuildSpecWithDeps
    41  		osArchs []osarch.OSArch
    42  		want    map[string][]string
    43  	}{
    44  		// empty spec
    45  		{
    46  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
    47  				return []params.ProductBuildSpecWithDeps{}
    48  			},
    49  			want: map[string][]string{},
    50  		},
    51  		// returns paths for all OS/arch combinations if requested osArchs is empty
    52  		{
    53  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
    54  				return []params.ProductBuildSpecWithDeps{
    55  					createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
    56  						{OS: "darwin", Arch: "amd64"},
    57  						{OS: "darwin", Arch: "386"},
    58  						{OS: "linux", Arch: "amd64"},
    59  					}, &params.SLSDistInfo{}),
    60  				}
    61  			},
    62  			want: map[string][]string{
    63  				"foo": {
    64  					path.Join("build", "darwin-amd64", "foo"),
    65  					path.Join("build", "darwin-386", "foo"),
    66  					path.Join("build", "linux-amd64", "foo"),
    67  				},
    68  			},
    69  		},
    70  		// returns only path to requested OS/arch
    71  		{
    72  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
    73  				return []params.ProductBuildSpecWithDeps{
    74  					createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
    75  						{OS: "darwin", Arch: "amd64"},
    76  						{OS: "linux", Arch: "amd64"},
    77  					}, &params.SLSDistInfo{}),
    78  				}
    79  			},
    80  			osArchs: []osarch.OSArch{{OS: "darwin", Arch: "amd64"}},
    81  			want: map[string][]string{
    82  				"foo": {
    83  					path.Join("build", "darwin-amd64", "foo"),
    84  				},
    85  			},
    86  		},
    87  		// path to windows executable includes ".exe"
    88  		{
    89  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
    90  				return []params.ProductBuildSpecWithDeps{
    91  					createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
    92  						{OS: "windows", Arch: "amd64"},
    93  					}, &params.SLSDistInfo{}),
    94  				}
    95  			},
    96  			want: map[string][]string{
    97  				"foo": {
    98  					path.Join("build", "windows-amd64", "foo.exe"),
    99  				},
   100  			},
   101  		},
   102  		// returns empty if os/arch that is not part of the spec is requested
   103  		{
   104  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
   105  				return []params.ProductBuildSpecWithDeps{
   106  					createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
   107  						{OS: "darwin", Arch: "amd64"},
   108  						{OS: "linux", Arch: "amd64"},
   109  					}, &params.SLSDistInfo{}),
   110  				}
   111  			},
   112  			osArchs: []osarch.OSArch{{OS: "windows", Arch: "amd64"}},
   113  			want:    map[string][]string{},
   114  		},
   115  	} {
   116  		currProjectDir, err := ioutil.TempDir(tmpDir, "")
   117  		require.NoError(t, err)
   118  
   119  		// relative path
   120  		got, err := artifacts.BuildArtifacts(currCase.specs(currProjectDir), artifacts.BuildArtifactsParams{
   121  			OSArchs: currCase.osArchs,
   122  		})
   123  		require.NoError(t, err, "Case %d", i)
   124  		assert.Equal(t, currCase.want, toMap(got), "Case %d", i)
   125  
   126  		// absolute path
   127  		got, err = artifacts.BuildArtifacts(currCase.specs(currProjectDir), artifacts.BuildArtifactsParams{
   128  			AbsPath: true,
   129  			OSArchs: currCase.osArchs,
   130  		})
   131  		require.NoError(t, err, "Case %d", i)
   132  		assert.Equal(t, toAbs(currCase.want, currProjectDir), toMap(got), "Case %d", i)
   133  	}
   134  }
   135  
   136  func TestBuildArtifactsRequiresBuild(t *testing.T) {
   137  	tmpDir, cleanup, err := dirs.TempDir(".", "")
   138  	defer cleanup()
   139  	require.NoError(t, err)
   140  
   141  	tmpDir, err = filepath.Abs(tmpDir)
   142  	require.NoError(t, err)
   143  
   144  	for i, currCase := range []struct {
   145  		specs         func(projectDir string) params.ProductBuildSpecWithDeps
   146  		osArchs       []osarch.OSArch
   147  		requiresBuild bool
   148  		beforeAction  func(projectDir string, specs []params.ProductBuildSpec)
   149  		want          map[string][]string
   150  	}{
   151  		// returns paths to all artifacts if build has not happened
   152  		{
   153  			specs: func(projectDir string) params.ProductBuildSpecWithDeps {
   154  				return createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
   155  					{OS: "darwin", Arch: "amd64"},
   156  					{OS: "darwin", Arch: "386"},
   157  					{OS: "linux", Arch: "amd64"},
   158  				}, &params.SLSDistInfo{})
   159  			},
   160  			want: map[string][]string{
   161  				"foo": {
   162  					path.Join("build", "darwin-amd64", "foo"),
   163  					path.Join("build", "darwin-386", "foo"),
   164  					path.Join("build", "linux-amd64", "foo"),
   165  				},
   166  			},
   167  		},
   168  		// returns empty if all artifacts exist and are up-to-date
   169  		{
   170  			specs: func(projectDir string) params.ProductBuildSpecWithDeps {
   171  				return createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
   172  					{OS: "darwin", Arch: "amd64"},
   173  					{OS: "darwin", Arch: "386"},
   174  					{OS: "linux", Arch: "amd64"},
   175  				}, &params.SLSDistInfo{})
   176  			},
   177  			beforeAction: func(projectDir string, specs []params.ProductBuildSpec) {
   178  				// build products
   179  				err = build.Run(specs, nil, build.Context{
   180  					Parallel: false,
   181  				}, ioutil.Discard)
   182  				require.NoError(t, err)
   183  			},
   184  			want: map[string][]string{},
   185  		},
   186  		// returns paths to all artifacts if input source file has been modified
   187  		{
   188  			specs: func(projectDir string) params.ProductBuildSpecWithDeps {
   189  				return createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
   190  					{OS: "darwin", Arch: "amd64"},
   191  					{OS: "darwin", Arch: "386"},
   192  					{OS: "linux", Arch: "amd64"},
   193  				}, &params.SLSDistInfo{})
   194  			},
   195  			beforeAction: func(projectDir string, specs []params.ProductBuildSpec) {
   196  				// build products
   197  				err := build.Run(specs, nil, build.Context{
   198  					Parallel: false,
   199  				}, ioutil.Discard)
   200  				require.NoError(t, err)
   201  
   202  				// sleep to ensure that modification time will differ
   203  				time.Sleep(time.Second)
   204  
   205  				// update source file
   206  				err = ioutil.WriteFile(path.Join(projectDir, "main.go"), []byte("package main; func main(){}"), 0644)
   207  				require.NoError(t, err)
   208  			},
   209  			want: map[string][]string{
   210  				"foo": {
   211  					path.Join("build", "darwin-amd64", "foo"),
   212  					path.Join("build", "darwin-386", "foo"),
   213  					path.Join("build", "linux-amd64", "foo"),
   214  				},
   215  			},
   216  		},
   217  		// if OS/Archs are specified, results are filtered base on that
   218  		{
   219  			specs: func(projectDir string) params.ProductBuildSpecWithDeps {
   220  				return createSpec(projectDir, "foo", "0.1.0", []osarch.OSArch{
   221  					{OS: "darwin", Arch: "amd64"},
   222  					{OS: "darwin", Arch: "386"},
   223  					{OS: "linux", Arch: "amd64"},
   224  				}, &params.SLSDistInfo{})
   225  			},
   226  			osArchs: []osarch.OSArch{
   227  				{OS: "windows", Arch: "amd64"},
   228  			},
   229  			want: map[string][]string{},
   230  		},
   231  	} {
   232  		currProjectDir, err := ioutil.TempDir(tmpDir, "")
   233  		require.NoError(t, err)
   234  
   235  		err = ioutil.WriteFile(path.Join(currProjectDir, "main.go"), []byte("package main; func main(){}"), 0644)
   236  		require.NoError(t, err)
   237  
   238  		specWithDeps := currCase.specs(currProjectDir)
   239  		if currCase.beforeAction != nil {
   240  			currCase.beforeAction(currProjectDir, specWithDeps.AllSpecs())
   241  		}
   242  
   243  		got, err := artifacts.BuildArtifacts([]params.ProductBuildSpecWithDeps{specWithDeps}, artifacts.BuildArtifactsParams{
   244  			RequiresBuild: true,
   245  			OSArchs:       currCase.osArchs,
   246  		})
   247  		require.NoError(t, err, "Case %d", i)
   248  		assert.Equal(t, currCase.want, toMap(got), "Case %d", i)
   249  	}
   250  }
   251  
   252  func TestDistArtifacts(t *testing.T) {
   253  	tmpDir, cleanup, err := dirs.TempDir("", "")
   254  	defer cleanup()
   255  	require.NoError(t, err)
   256  
   257  	for i, currCase := range []struct {
   258  		specs func(projectDir string) []params.ProductBuildSpecWithDeps
   259  		want  map[string][]string
   260  	}{
   261  		{
   262  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
   263  				return []params.ProductBuildSpecWithDeps{}
   264  			},
   265  			want: map[string][]string{},
   266  		},
   267  		{
   268  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
   269  				return []params.ProductBuildSpecWithDeps{
   270  					createSpec(projectDir, "foo", "0.1.0", nil, &params.SLSDistInfo{}),
   271  				}
   272  			},
   273  			want: map[string][]string{
   274  				"foo": {"foo-0.1.0.sls.tgz"},
   275  			},
   276  		},
   277  		{
   278  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
   279  				return []params.ProductBuildSpecWithDeps{
   280  					createSpec(projectDir, "foo", "0.1.0", nil, &params.SLSDistInfo{}),
   281  					createSpec(projectDir, "bar", "unspecified", nil, &params.SLSDistInfo{}),
   282  				}
   283  			},
   284  			want: map[string][]string{
   285  				"foo": {"foo-0.1.0.sls.tgz"},
   286  				"bar": {"bar-unspecified.sls.tgz"},
   287  			},
   288  		},
   289  		{
   290  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
   291  				return []params.ProductBuildSpecWithDeps{
   292  					createSpecWithDists(projectDir, "foo", "0.1.0", nil, params.Dist{Info: &params.SLSDistInfo{}}, params.Dist{Info: &params.BinDistInfo{}}),
   293  				}
   294  			},
   295  			want: map[string][]string{
   296  				"foo": {
   297  					"foo-0.1.0.sls.tgz",
   298  					"foo-0.1.0.tgz",
   299  				},
   300  			},
   301  		},
   302  		{
   303  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
   304  				return []params.ProductBuildSpecWithDeps{
   305  					createSpecWithDists(projectDir, "foo", "0.1.0", nil, params.Dist{Info: &params.OSArchsBinDistInfo{
   306  						OSArchs: []osarch.OSArch{
   307  							{
   308  								OS:   "darwin",
   309  								Arch: "amd64",
   310  							},
   311  						},
   312  					}}, params.Dist{Info: &params.OSArchsBinDistInfo{
   313  						OSArchs: []osarch.OSArch{
   314  							{
   315  								OS:   "linux",
   316  								Arch: "amd64",
   317  							},
   318  						},
   319  					}}),
   320  				}
   321  			},
   322  			want: map[string][]string{
   323  				"foo": {
   324  					"foo-0.1.0-darwin-amd64.tgz",
   325  					"foo-0.1.0-linux-amd64.tgz",
   326  				},
   327  			},
   328  		},
   329  		{
   330  			specs: func(projectDir string) []params.ProductBuildSpecWithDeps {
   331  				return []params.ProductBuildSpecWithDeps{
   332  					createSpec(projectDir, "foo", "0.1.0", nil, &params.OSArchsBinDistInfo{
   333  						OSArchs: []osarch.OSArch{
   334  							{
   335  								OS:   "darwin",
   336  								Arch: "amd64",
   337  							},
   338  							{
   339  								OS:   "linux",
   340  								Arch: "amd64",
   341  							},
   342  						},
   343  					}),
   344  				}
   345  			},
   346  			want: map[string][]string{
   347  				"foo": {
   348  					"foo-0.1.0-darwin-amd64.tgz",
   349  					"foo-0.1.0-linux-amd64.tgz",
   350  				},
   351  			},
   352  		},
   353  	} {
   354  		currProjectDir, err := ioutil.TempDir(tmpDir, "")
   355  		require.NoError(t, err)
   356  
   357  		// relative path
   358  		got, err := artifacts.DistArtifacts(currCase.specs(currProjectDir), false)
   359  		require.NoError(t, err, "Case %d", i)
   360  		assert.Equal(t, currCase.want, toMap(got), "Case %d", i)
   361  
   362  		// absolute path
   363  		got, err = artifacts.DistArtifacts(currCase.specs(currProjectDir), true)
   364  		require.NoError(t, err, "Case %d", i)
   365  		assert.Equal(t, toAbs(currCase.want, currProjectDir), toMap(got), "Case %d", i)
   366  	}
   367  }
   368  
   369  func toAbs(input map[string][]string, baseDir string) map[string][]string {
   370  	absWant := make(map[string][]string, len(input))
   371  	for k, v := range input {
   372  		absWant[k] = make([]string, len(v))
   373  		for i := range v {
   374  			absWant[k][i] = path.Join(baseDir, v[i])
   375  		}
   376  	}
   377  	return absWant
   378  }
   379  
   380  func createSpec(projectDir, productName, productVersion string, osArchs []osarch.OSArch, distInfo params.DistInfo) params.ProductBuildSpecWithDeps {
   381  	return createSpecWithDists(projectDir, productName, productVersion, osArchs, params.Dist{Info: distInfo})
   382  }
   383  
   384  func createSpecWithDists(projectDir, productName, productVersion string, osArchs []osarch.OSArch, dists ...params.Dist) params.ProductBuildSpecWithDeps {
   385  	return params.ProductBuildSpecWithDeps{
   386  		Spec: params.ProductBuildSpec{
   387  			Product: params.Product{
   388  				Build: params.Build{
   389  					OutputDir: "build",
   390  					OSArchs:   osArchs,
   391  				},
   392  				Dist: dists,
   393  			},
   394  			ProjectDir:     projectDir,
   395  			ProductName:    productName,
   396  			ProductVersion: productVersion,
   397  		},
   398  	}
   399  }
   400  
   401  func toMap(input map[string]artifacts.OrderedStringSliceMap) map[string][]string {
   402  	output := make(map[string][]string, len(input))
   403  	for product, m := range input {
   404  		keys := m.Keys()
   405  		var values []string
   406  		for _, k := range keys {
   407  			values = append(values, m.Get(k)...)
   408  		}
   409  		output[product] = values
   410  	}
   411  	return output
   412  }