github.com/tahsinrahman/goreleaser@v0.79.1/internal/builders/golang/build_test.go (about)

     1  package golang
     2  
     3  import (
     4  	"io/ioutil"
     5  	"path/filepath"
     6  	"runtime"
     7  	"strings"
     8  	"testing"
     9  
    10  	api "github.com/goreleaser/goreleaser/build"
    11  	"github.com/goreleaser/goreleaser/config"
    12  	"github.com/goreleaser/goreleaser/context"
    13  	"github.com/goreleaser/goreleaser/internal/artifact"
    14  	"github.com/goreleaser/goreleaser/internal/testlib"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  var runtimeTarget = runtime.GOOS + "_" + runtime.GOARCH
    19  
    20  func TestWithDefaults(t *testing.T) {
    21  	for name, testcase := range map[string]struct {
    22  		build   config.Build
    23  		targets []string
    24  	}{
    25  		"full": {
    26  			build: config.Build{
    27  				Binary: "foo",
    28  				Goos: []string{
    29  					"linux",
    30  					"windows",
    31  					"darwin",
    32  				},
    33  				Goarch: []string{
    34  					"amd64",
    35  					"arm",
    36  				},
    37  				Goarm: []string{
    38  					"6",
    39  				},
    40  			},
    41  			targets: []string{
    42  				"linux_amd64",
    43  				"darwin_amd64",
    44  				"windows_amd64",
    45  				"linux_arm_6",
    46  			},
    47  		},
    48  		"empty": {
    49  			build: config.Build{
    50  				Binary: "foo",
    51  			},
    52  			targets: []string{
    53  				"linux_amd64",
    54  				"linux_386",
    55  				"darwin_amd64",
    56  				"darwin_386",
    57  			},
    58  		},
    59  	} {
    60  		t.Run(name, func(tt *testing.T) {
    61  			var config = config.Project{
    62  				Builds: []config.Build{
    63  					testcase.build,
    64  				},
    65  			}
    66  			var ctx = context.New(config)
    67  			var build = Default.WithDefaults(ctx.Config.Builds[0])
    68  			assert.ElementsMatch(t, build.Targets, testcase.targets)
    69  		})
    70  	}
    71  }
    72  
    73  func TestBuild(t *testing.T) {
    74  	folder, back := testlib.Mktmp(t)
    75  	defer back()
    76  	writeGoodMain(t, folder)
    77  	var config = config.Project{
    78  		Builds: []config.Build{
    79  			{
    80  				Binary: "foo",
    81  				Targets: []string{
    82  					"linux_amd64",
    83  					"darwin_amd64",
    84  					"windows_amd64",
    85  					"linux_arm_6",
    86  				},
    87  				Asmflags: []string{".=", "all="},
    88  				Gcflags:  []string{"all="},
    89  			},
    90  		},
    91  	}
    92  	var ctx = context.New(config)
    93  	var build = ctx.Config.Builds[0]
    94  	for _, target := range build.Targets {
    95  		var ext string
    96  		if strings.HasPrefix(target, "windows") {
    97  			ext = ".exe"
    98  		}
    99  		var err = Default.Build(ctx, build, api.Options{
   100  			Target: target,
   101  			Name:   build.Binary,
   102  			Path:   filepath.Join(folder, "dist", target, build.Binary),
   103  			Ext:    ext,
   104  		})
   105  		assert.NoError(t, err)
   106  	}
   107  	assert.ElementsMatch(t, ctx.Artifacts.List(), []artifact.Artifact{
   108  		{
   109  			Name:   "foo",
   110  			Path:   filepath.Join(folder, "dist", "linux_amd64", "foo"),
   111  			Goos:   "linux",
   112  			Goarch: "amd64",
   113  			Type:   artifact.Binary,
   114  			Extra: map[string]string{
   115  				"Ext":    "",
   116  				"Binary": "foo",
   117  			},
   118  		},
   119  		{
   120  			Name:   "foo",
   121  			Path:   filepath.Join(folder, "dist", "darwin_amd64", "foo"),
   122  			Goos:   "darwin",
   123  			Goarch: "amd64",
   124  			Type:   artifact.Binary,
   125  			Extra: map[string]string{
   126  				"Ext":    "",
   127  				"Binary": "foo",
   128  			},
   129  		},
   130  		{
   131  			Name:   "foo",
   132  			Path:   filepath.Join(folder, "dist", "linux_arm_6", "foo"),
   133  			Goos:   "linux",
   134  			Goarch: "arm",
   135  			Goarm:  "6",
   136  			Type:   artifact.Binary,
   137  			Extra: map[string]string{
   138  				"Ext":    "",
   139  				"Binary": "foo",
   140  			},
   141  		},
   142  		{
   143  			Name:   "foo",
   144  			Path:   filepath.Join(folder, "dist", "windows_amd64", "foo"),
   145  			Goos:   "windows",
   146  			Goarch: "amd64",
   147  			Type:   artifact.Binary,
   148  			Extra: map[string]string{
   149  				"Ext":    ".exe",
   150  				"Binary": "foo",
   151  			},
   152  		},
   153  	})
   154  }
   155  
   156  func TestBuildFailed(t *testing.T) {
   157  	folder, back := testlib.Mktmp(t)
   158  	defer back()
   159  	writeGoodMain(t, folder)
   160  	var config = config.Project{
   161  		Builds: []config.Build{
   162  			{
   163  				Flags: []string{"-flag-that-dont-exists-to-force-failure"},
   164  				Targets: []string{
   165  					runtimeTarget,
   166  				},
   167  			},
   168  		},
   169  	}
   170  	var ctx = context.New(config)
   171  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   172  		Target: "darwin_amd64",
   173  	})
   174  	assertContainsError(t, err, `flag provided but not defined: -flag-that-dont-exists-to-force-failure`)
   175  	assert.Empty(t, ctx.Artifacts.List())
   176  }
   177  
   178  func TestBuildInvalidTarget(t *testing.T) {
   179  	folder, back := testlib.Mktmp(t)
   180  	defer back()
   181  	writeGoodMain(t, folder)
   182  	var target = "linux"
   183  	var config = config.Project{
   184  		Builds: []config.Build{
   185  			{
   186  				Binary:  "foo",
   187  				Targets: []string{target},
   188  			},
   189  		},
   190  	}
   191  	var ctx = context.New(config)
   192  	var build = ctx.Config.Builds[0]
   193  	var err = Default.Build(ctx, build, api.Options{
   194  		Target: target,
   195  		Name:   build.Binary,
   196  		Path:   filepath.Join(folder, "dist", target, build.Binary),
   197  	})
   198  	assert.EqualError(t, err, "linux is not a valid build target")
   199  	assert.Len(t, ctx.Artifacts.List(), 0)
   200  }
   201  
   202  func TestRunInvalidAsmflags(t *testing.T) {
   203  	folder, back := testlib.Mktmp(t)
   204  	defer back()
   205  	writeGoodMain(t, folder)
   206  	var config = config.Project{
   207  		Builds: []config.Build{
   208  			{
   209  				Binary:   "nametest",
   210  				Asmflags: []string{"{{.Version}"},
   211  				Targets: []string{
   212  					runtimeTarget,
   213  				},
   214  			},
   215  		},
   216  	}
   217  	var ctx = context.New(config)
   218  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   219  		Target: runtimeTarget,
   220  	})
   221  	assert.EqualError(t, err, `template: asmflags:1: unexpected "}" in operand`)
   222  }
   223  
   224  func TestRunInvalidGcflags(t *testing.T) {
   225  	folder, back := testlib.Mktmp(t)
   226  	defer back()
   227  	writeGoodMain(t, folder)
   228  	var config = config.Project{
   229  		Builds: []config.Build{
   230  			{
   231  				Binary:  "nametest",
   232  				Gcflags: []string{"{{.Version}"},
   233  				Targets: []string{
   234  					runtimeTarget,
   235  				},
   236  			},
   237  		},
   238  	}
   239  	var ctx = context.New(config)
   240  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   241  		Target: runtimeTarget,
   242  	})
   243  	assert.EqualError(t, err, `template: gcflags:1: unexpected "}" in operand`)
   244  }
   245  
   246  func TestRunInvalidLdflags(t *testing.T) {
   247  	folder, back := testlib.Mktmp(t)
   248  	defer back()
   249  	writeGoodMain(t, folder)
   250  	var config = config.Project{
   251  		Builds: []config.Build{
   252  			{
   253  				Binary:  "nametest",
   254  				Flags:   []string{"-v"},
   255  				Ldflags: []string{"-s -w -X main.version={{.Version}"},
   256  				Targets: []string{
   257  					runtimeTarget,
   258  				},
   259  			},
   260  		},
   261  	}
   262  	var ctx = context.New(config)
   263  	var err = Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   264  		Target: runtimeTarget,
   265  	})
   266  	assert.EqualError(t, err, `template: ldflags:1: unexpected "}" in operand`)
   267  }
   268  
   269  func TestRunPipeWithoutMainFunc(t *testing.T) {
   270  	folder, back := testlib.Mktmp(t)
   271  	defer back()
   272  	writeMainWithoutMainFunc(t, folder)
   273  	var config = config.Project{
   274  		Builds: []config.Build{
   275  			{
   276  				Binary: "no-main",
   277  				Hooks:  config.Hooks{},
   278  				Targets: []string{
   279  					runtimeTarget,
   280  				},
   281  			},
   282  		},
   283  	}
   284  	var ctx = context.New(config)
   285  	t.Run("empty", func(t *testing.T) {
   286  		ctx.Config.Builds[0].Main = ""
   287  		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   288  			Target: runtimeTarget,
   289  		}), `build for no-main does not contain a main function`)
   290  	})
   291  	t.Run("not main.go", func(t *testing.T) {
   292  		ctx.Config.Builds[0].Main = "foo.go"
   293  		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   294  			Target: runtimeTarget,
   295  		}), `stat foo.go: no such file or directory`)
   296  	})
   297  	t.Run("glob", func(t *testing.T) {
   298  		ctx.Config.Builds[0].Main = "."
   299  		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   300  			Target: runtimeTarget,
   301  		}), `build for no-main does not contain a main function`)
   302  	})
   303  	t.Run("fixed main.go", func(t *testing.T) {
   304  		ctx.Config.Builds[0].Main = "main.go"
   305  		assert.EqualError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   306  			Target: runtimeTarget,
   307  		}), `build for no-main does not contain a main function`)
   308  	})
   309  }
   310  
   311  func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
   312  	folder, back := testlib.Mktmp(t)
   313  	defer back()
   314  	assert.NoError(t, ioutil.WriteFile(
   315  		filepath.Join(folder, "foo.go"),
   316  		[]byte("package main\nfunc main() {println(0)}"),
   317  		0644,
   318  	))
   319  	var config = config.Project{
   320  		Builds: []config.Build{
   321  			{
   322  				Binary: "foo",
   323  				Hooks:  config.Hooks{},
   324  				Targets: []string{
   325  					runtimeTarget,
   326  				},
   327  			},
   328  		},
   329  	}
   330  	var ctx = context.New(config)
   331  	t.Run("empty", func(t *testing.T) {
   332  		ctx.Config.Builds[0].Main = ""
   333  		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   334  			Target: runtimeTarget,
   335  		}))
   336  	})
   337  	t.Run("foo.go", func(t *testing.T) {
   338  		ctx.Config.Builds[0].Main = "foo.go"
   339  		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   340  			Target: runtimeTarget,
   341  		}))
   342  	})
   343  	t.Run("glob", func(t *testing.T) {
   344  		ctx.Config.Builds[0].Main = "."
   345  		assert.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
   346  			Target: runtimeTarget,
   347  		}))
   348  	})
   349  }
   350  
   351  func TestLdFlagsFullTemplate(t *testing.T) {
   352  	var config = config.Project{
   353  		Builds: []config.Build{
   354  			{
   355  				Ldflags: []string{
   356  					`-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}" -X main.time={{ time "20060102" }}`,
   357  				},
   358  			},
   359  		},
   360  	}
   361  	var ctx = &context.Context{
   362  		Git: context.GitInfo{
   363  			CurrentTag: "v1.2.3",
   364  			Commit:     "123",
   365  		},
   366  		Version: "1.2.3",
   367  		Config:  config,
   368  		Env:     map[string]string{"FOO": "123"},
   369  	}
   370  	flags, err := processField(ctx, ctx.Config.Builds[0].Ldflags[0], "ldflags")
   371  	assert.NoError(t, err)
   372  	assert.Contains(t, flags, "-s -w")
   373  	assert.Contains(t, flags, "-X main.version=1.2.3")
   374  	assert.Contains(t, flags, "-X main.tag=v1.2.3")
   375  	assert.Contains(t, flags, "-X main.commit=123")
   376  	// TODO: this will break in 2019
   377  	assert.Contains(t, flags, "-X main.date=2018")
   378  	assert.Contains(t, flags, "-X main.time=2018")
   379  	assert.Contains(t, flags, `-X "main.foo=123"`)
   380  }
   381  
   382  func TestInvalidTemplate(t *testing.T) {
   383  	for template, eerr := range map[string]string{
   384  		"{{ .Nope }":    `template: ldflags:1: unexpected "}" in operand`,
   385  		"{{.Env.NOPE}}": `template: ldflags:1:6: executing "ldflags" at <.Env.NOPE>: map has no entry for key "NOPE"`,
   386  	} {
   387  		t.Run(template, func(tt *testing.T) {
   388  			var config = config.Project{
   389  				Builds: []config.Build{
   390  					{Ldflags: []string{template}},
   391  				},
   392  			}
   393  			var ctx = &context.Context{
   394  				Config: config,
   395  			}
   396  			flags, err := processField(ctx, template, "ldflags")
   397  			assert.EqualError(tt, err, eerr)
   398  			assert.Empty(tt, flags)
   399  		})
   400  	}
   401  }
   402  
   403  func TestProcessFlags(t *testing.T) {
   404  	var ctx = &context.Context{
   405  		Version: "1.2.3",
   406  	}
   407  
   408  	var source = []string{
   409  		"{{.Version}}",
   410  		"flag",
   411  	}
   412  
   413  	var expected = []string{
   414  		"-testflag=1.2.3",
   415  		"-testflag=flag",
   416  	}
   417  
   418  	flags, err := processFlags(ctx, source, "testflag", "-testflag=")
   419  	assert.NoError(t, err)
   420  	assert.Len(t, flags, 2)
   421  	assert.Equal(t, expected, flags)
   422  }
   423  
   424  func TestProcessFlagsInvalid(t *testing.T) {
   425  	var ctx = &context.Context{}
   426  
   427  	var source = []string{
   428  		"{{.Version}",
   429  	}
   430  
   431  	var expected = `template: testflag:1: unexpected "}" in operand`
   432  
   433  	flags, err := processFlags(ctx, source, "testflag", "-testflag=")
   434  	assert.EqualError(t, err, expected)
   435  	assert.Nil(t, flags)
   436  }
   437  
   438  //
   439  // Helpers
   440  //
   441  
   442  func writeMainWithoutMainFunc(t *testing.T, folder string) {
   443  	assert.NoError(t, ioutil.WriteFile(
   444  		filepath.Join(folder, "main.go"),
   445  		[]byte("package main\nconst a = 2\nfunc notMain() {println(0)}"),
   446  		0644,
   447  	))
   448  }
   449  
   450  func writeGoodMain(t *testing.T, folder string) {
   451  	assert.NoError(t, ioutil.WriteFile(
   452  		filepath.Join(folder, "main.go"),
   453  		[]byte("package main\nvar a = 1\nfunc main() {println(0)}"),
   454  		0644,
   455  	))
   456  }
   457  
   458  func assertContainsError(t *testing.T, err error, s string) {
   459  	assert.Error(t, err)
   460  	assert.Contains(t, err.Error(), s)
   461  }