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 }