blob: e9d02b9d06e2a0ec75a8aa9f54515fe70f2ccd83 [file] [log] [blame] [edit]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.23
package main
import (
"archive/zip"
"bytes"
"debug/buildinfo"
"flag"
"io"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
var flagUpdate = flag.Bool("update", false, "update golden files")
var moduleRoot string
func TestMain(m *testing.M) {
cmd := exec.Command("go", "list", "-m", "-f", "{{.Dir}}")
cmd.Env = append(os.Environ(), "GOWORK=off")
out, err := cmd.Output()
if err != nil {
log.Fatalf("failed to get module root for testing: %v\n", err)
}
moduleRoot = string(bytes.TrimSpace(out))
os.Exit(m.Run())
}
func TestRelease(t *testing.T) {
for _, fullCommand := range []string{
"build-vscgo -out=/tmp/artifacts",
"package -out=/tmp/artifacts",
"publish -in=/tmp/artifacts",
} {
args := strings.Fields(fullCommand)
// v0.43.0: prerelease
// v0.44.0-rc.1: release candidate of stable release
// v0.44.0: stable release
// TODO(hyangah): skip rc in favor of prerelease versions.
for _, tagName := range []string{"v0.43.0", "v0.44.0-rc.1", "v0.44.0"} {
t.Run(args[0]+"-"+tagName, func(t *testing.T) {
testRelease(t, moduleRoot, args[0], tagName, args[1:]...)
})
}
}
}
func testRelease(t *testing.T, moduleRoot, command, tagName string, extraArgs ...string) {
args := []string{"run", "-C", moduleRoot, "tools/release/release.go", command, "-n"}
args = append(args, extraArgs...)
cmd := exec.Command("go", args...)
cmd.Env = append(os.Environ(),
// Provide dummy environment variables required to run release.go commands.
"TAG_NAME="+tagName, // release tag
"GITHUB_TOKEN=dummy", // github token needed to post release notes
"VSCE_PAT=dummy", // vsce token needed to publish the extension
"COMMIT_SHA=4893cd984d190bdf2cd65e11c425b42819ae6f57", // bogus commit SHA used to post release notes
)
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("failed to run release %s: %v\n%s", command, err, output)
}
if *flagUpdate {
if err := os.WriteFile(filepath.Join("testdata", command+"-"+tagName+".golden"), output, 0644); err != nil {
t.Fatal("failed to write golden file:", err)
}
return
}
golden, err := os.ReadFile(filepath.Join("testdata", command+"-"+tagName+".golden"))
if err != nil {
t.Fatal("failed to read golden file:", err)
}
if diff := cmp.Diff(golden, output); diff != "" {
t.Error("release package output mismatch (-want +got):\n", diff)
}
}
func TestBuildVSCGO(t *testing.T) {
modulePath := "github.com/golang/vscode-go"
version := "v0.0.1"
proxyURI := createTestModuleProxy(t, modulePath, version)
gomodcache := t.TempDir()
t.Cleanup(func() {
cleanModuleCache(t, gomodcache)
})
outDir := t.TempDir()
cmd := exec.Command("go", "run", "-C", moduleRoot, "tools/release/release.go", "build-vscgo", "-out", outDir)
cmd.Env = append(os.Environ(),
"GOPROXY="+proxyURI,
"GOMODCACHE="+gomodcache,
"GONOSUMDB="+modulePath,
"TAG_NAME="+version,
)
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("failed to run build-vscgo: %v\n%s", err, out)
}
file, err := os.Open(filepath.Join(outDir, "vscgo.zip"))
if err != nil {
t.Fatal(err)
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
t.Fatal(err)
}
zipReader, err := zip.NewReader(file, stat.Size())
if err != nil {
t.Fatal(err)
}
want := map[string]bool{}
for _, platform := range targetPlatforms {
want[platform.goos+"_"+platform.goarch] = true
}
for _, f := range zipReader.File {
dirname := path.Base(path.Dir(f.Name))
if !want[dirname] {
t.Errorf("unexpected file in zip: %v", f.Name)
continue
}
// dirname must encode goos/goarch
goos, goarch, _ := strings.Cut(path.Base(path.Dir(f.Name)), "_")
bin, err := f.Open()
if err != nil {
t.Fatal(err)
}
defer bin.Close()
data, err := io.ReadAll(bin)
if err != nil {
t.Fatal(err)
}
bi, err := buildinfo.Read(bytes.NewReader(data))
if err != nil {
t.Fatal(err)
}
var gotGOOS, gotGOARCH string
for _, s := range bi.Settings {
switch s.Key {
case "GOOS":
gotGOOS = s.Value
case "GOARCH":
gotGOARCH = s.Value
}
}
if bi.Path != "github.com/golang/vscode-go/vscgo" ||
bi.Main.Path != "github.com/golang/vscode-go" ||
bi.Main.Version != version ||
gotGOOS != goos ||
gotGOARCH != goarch {
t.Errorf("%v: got %+v; want GOOS=%v/GOARCH=%v", f.Name, bi, goos, goarch)
}
}
}
func createTestModuleProxy(t *testing.T, modulePath string, version string) string {
dirPath := modulePath + "@" + version + "/"
files := map[string][]byte{
dirPath + "go.mod": []byte("module " + modulePath + "\n\ngo 1.20\n"),
dirPath + "vscgo/main.go": []byte("package main\n\nfunc main() {\n\tprintln(\"hello world\")\n}"),
}
proxyURI, err := WriteProxy(t.TempDir(), files)
if err != nil {
t.Fatal("failed to write proxy:", err)
}
return proxyURI
}
func cleanModuleCache(t *testing.T, gomodcache string) {
cmd := exec.Command("go", "clean", "-modcache")
cmd.Env = append(os.Environ(), "GOMODCACHE="+gomodcache)
if err := cmd.Run(); err != nil {
t.Errorf("failed to clean module cache: %v\n", err)
}
}