Source file src/syscall/env_unix_test.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build unix || (js && wasm) || plan9 || wasip1
     6  
     7  package syscall_test
     8  
     9  import (
    10  	"fmt"
    11  	"strconv"
    12  	"strings"
    13  	"syscall"
    14  	"testing"
    15  )
    16  
    17  type env struct {
    18  	name, val string
    19  }
    20  
    21  func genDummyEnv(tb testing.TB, size int) []env {
    22  	tb.Helper()
    23  	envList := make([]env, size)
    24  	for idx := range size {
    25  		envList[idx] = env{
    26  			name: fmt.Sprintf("DUMMY_VAR_%d", idx),
    27  			val:  fmt.Sprintf("val-%d", idx*100),
    28  		}
    29  	}
    30  	return envList
    31  }
    32  
    33  func setDummyEnv(tb testing.TB, envList []env) {
    34  	tb.Helper()
    35  	for _, env := range envList {
    36  		if err := syscall.Setenv(env.name, env.val); err != nil {
    37  			tb.Fatalf("setenv %s=%q failed: %v", env.name, env.val, err)
    38  		}
    39  	}
    40  }
    41  
    42  func setupEnvCleanup(tb testing.TB) {
    43  	tb.Helper()
    44  	originalEnv := map[string]string{}
    45  	for _, env := range syscall.Environ() {
    46  		fields := strings.SplitN(env, "=", 2)
    47  		name, val := fields[0], fields[1]
    48  		originalEnv[name] = val
    49  	}
    50  	tb.Cleanup(func() {
    51  		syscall.Clearenv()
    52  		for name, val := range originalEnv {
    53  			if err := syscall.Setenv(name, val); err != nil {
    54  				tb.Fatalf("could not reset env %s=%q: %v", name, val, err)
    55  			}
    56  		}
    57  	})
    58  }
    59  
    60  func TestClearenv(t *testing.T) {
    61  	setupEnvCleanup(t)
    62  
    63  	t.Run("DummyVars-4096", func(t *testing.T) {
    64  		envList := genDummyEnv(t, 4096)
    65  		setDummyEnv(t, envList)
    66  
    67  		if env := syscall.Environ(); len(env) < 4096 {
    68  			t.Fatalf("env is missing dummy variables: %v", env)
    69  		}
    70  		for idx := range 4096 {
    71  			name := fmt.Sprintf("DUMMY_VAR_%d", idx)
    72  			if _, ok := syscall.Getenv(name); !ok {
    73  				t.Fatalf("env is missing dummy variable %s", name)
    74  			}
    75  		}
    76  
    77  		syscall.Clearenv()
    78  
    79  		if env := syscall.Environ(); len(env) != 0 {
    80  			t.Fatalf("clearenv should've cleared all variables: %v still set", env)
    81  		}
    82  		for idx := range 4096 {
    83  			name := fmt.Sprintf("DUMMY_VAR_%d", idx)
    84  			if val, ok := syscall.Getenv(name); ok {
    85  				t.Fatalf("clearenv should've cleared all variables: %s=%q still set", name, val)
    86  			}
    87  		}
    88  	})
    89  
    90  	// Test that GODEBUG getting cleared by Clearenv also resets the behaviour.
    91  	t.Run("GODEBUG", func(t *testing.T) {
    92  		envList := genDummyEnv(t, 100)
    93  		setDummyEnv(t, envList)
    94  
    95  		doNilPanic := func() (ret any) {
    96  			defer func() {
    97  				ret = recover()
    98  			}()
    99  			panic(nil)
   100  		}
   101  
   102  		// Allow panic(nil).
   103  		if err := syscall.Setenv("GODEBUG", "panicnil=1"); err != nil {
   104  			t.Fatalf("setenv GODEBUG=panicnil=1 failed: %v", err)
   105  		}
   106  
   107  		got := doNilPanic()
   108  		if got != nil {
   109  			t.Fatalf("GODEBUG=panicnil=1 did not allow for nil panic: got %#v", got)
   110  		}
   111  
   112  		// Disallow panic(nil).
   113  		syscall.Clearenv()
   114  
   115  		if env := syscall.Environ(); len(env) != 0 {
   116  			t.Fatalf("clearenv should've cleared all variables: %v still set", env)
   117  		}
   118  
   119  		got = doNilPanic()
   120  		if got == nil {
   121  			t.Fatalf("GODEBUG=panicnil=1 being unset didn't reset panicnil behaviour")
   122  		}
   123  		if godebug, ok := syscall.Getenv("GODEBUG"); ok {
   124  			t.Fatalf("GODEBUG still exists in environment despite being unset: GODEBUG=%q", godebug)
   125  		}
   126  	})
   127  }
   128  
   129  func BenchmarkClearenv(b *testing.B) {
   130  	setupEnvCleanup(b)
   131  	b.ResetTimer()
   132  	for _, size := range []int{100, 1000, 10000} {
   133  		b.Run(strconv.Itoa(size), func(b *testing.B) {
   134  			envList := genDummyEnv(b, size)
   135  			for b.Loop() {
   136  				// Ideally we would use b.StopTimer() for the setDummyEnv
   137  				// portion, but this causes the benchmark time to get confused
   138  				// and take forever. See <https://go.dev/issue/27217>.
   139  				setDummyEnv(b, envList)
   140  				syscall.Clearenv()
   141  			}
   142  		})
   143  	}
   144  }
   145  

View as plain text