blob: a0050606099c0124ca288c3fbf1162e4f8d575cd [file] [log] [blame] [edit]
// Copyright 2025 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 unix || (js && wasm) || plan9 || wasip1
package syscall_test
import (
"fmt"
"strconv"
"strings"
"syscall"
"testing"
)
type env struct {
name, val string
}
func genDummyEnv(tb testing.TB, size int) []env {
tb.Helper()
envList := make([]env, size)
for idx := range size {
envList[idx] = env{
name: fmt.Sprintf("DUMMY_VAR_%d", idx),
val: fmt.Sprintf("val-%d", idx*100),
}
}
return envList
}
func setDummyEnv(tb testing.TB, envList []env) {
tb.Helper()
for _, env := range envList {
if err := syscall.Setenv(env.name, env.val); err != nil {
tb.Fatalf("setenv %s=%q failed: %v", env.name, env.val, err)
}
}
}
func setupEnvCleanup(tb testing.TB) {
tb.Helper()
originalEnv := map[string]string{}
for _, env := range syscall.Environ() {
fields := strings.SplitN(env, "=", 2)
name, val := fields[0], fields[1]
originalEnv[name] = val
}
tb.Cleanup(func() {
syscall.Clearenv()
for name, val := range originalEnv {
if err := syscall.Setenv(name, val); err != nil {
tb.Fatalf("could not reset env %s=%q: %v", name, val, err)
}
}
})
}
func TestClearenv(t *testing.T) {
setupEnvCleanup(t)
t.Run("DummyVars-4096", func(t *testing.T) {
envList := genDummyEnv(t, 4096)
setDummyEnv(t, envList)
if env := syscall.Environ(); len(env) < 4096 {
t.Fatalf("env is missing dummy variables: %v", env)
}
for idx := range 4096 {
name := fmt.Sprintf("DUMMY_VAR_%d", idx)
if _, ok := syscall.Getenv(name); !ok {
t.Fatalf("env is missing dummy variable %s", name)
}
}
syscall.Clearenv()
if env := syscall.Environ(); len(env) != 0 {
t.Fatalf("clearenv should've cleared all variables: %v still set", env)
}
for idx := range 4096 {
name := fmt.Sprintf("DUMMY_VAR_%d", idx)
if val, ok := syscall.Getenv(name); ok {
t.Fatalf("clearenv should've cleared all variables: %s=%q still set", name, val)
}
}
})
// Test that GODEBUG getting cleared by Clearenv also resets the behaviour.
t.Run("GODEBUG", func(t *testing.T) {
envList := genDummyEnv(t, 100)
setDummyEnv(t, envList)
doNilPanic := func() (ret any) {
defer func() {
ret = recover()
}()
panic(nil)
}
// Allow panic(nil).
if err := syscall.Setenv("GODEBUG", "panicnil=1"); err != nil {
t.Fatalf("setenv GODEBUG=panicnil=1 failed: %v", err)
}
got := doNilPanic()
if got != nil {
t.Fatalf("GODEBUG=panicnil=1 did not allow for nil panic: got %#v", got)
}
// Disallow panic(nil).
syscall.Clearenv()
if env := syscall.Environ(); len(env) != 0 {
t.Fatalf("clearenv should've cleared all variables: %v still set", env)
}
got = doNilPanic()
if got == nil {
t.Fatalf("GODEBUG=panicnil=1 being unset didn't reset panicnil behaviour")
}
if godebug, ok := syscall.Getenv("GODEBUG"); ok {
t.Fatalf("GODEBUG still exists in environment despite being unset: GODEBUG=%q", godebug)
}
})
}
func BenchmarkClearenv(b *testing.B) {
setupEnvCleanup(b)
b.ResetTimer()
for _, size := range []int{100, 1000, 10000} {
b.Run(strconv.Itoa(size), func(b *testing.B) {
envList := genDummyEnv(b, size)
for b.Loop() {
// Ideally we would use b.StopTimer() for the setDummyEnv
// portion, but this causes the benchmark time to get confused
// and take forever. See <https://go.dev/issue/27217>.
setDummyEnv(b, envList)
syscall.Clearenv()
}
})
}
}