Source file src/os/env_test.go

     1  // Copyright 2010 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  package os_test
     6  
     7  import (
     8  	. "os"
     9  	"reflect"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  // testGetenv gives us a controlled set of variables for testing Expand.
    15  func testGetenv(s string) string {
    16  	switch s {
    17  	case "*":
    18  		return "all the args"
    19  	case "#":
    20  		return "NARGS"
    21  	case "$":
    22  		return "PID"
    23  	case "1":
    24  		return "ARGUMENT1"
    25  	case "HOME":
    26  		return "/usr/gopher"
    27  	case "H":
    28  		return "(Value of H)"
    29  	case "home_1":
    30  		return "/usr/foo"
    31  	case "_":
    32  		return "underscore"
    33  	}
    34  	return ""
    35  }
    36  
    37  var expandTests = []struct {
    38  	in, out string
    39  }{
    40  	{"", ""},
    41  	{"$*", "all the args"},
    42  	{"$$", "PID"},
    43  	{"${*}", "all the args"},
    44  	{"$1", "ARGUMENT1"},
    45  	{"${1}", "ARGUMENT1"},
    46  	{"now is the time", "now is the time"},
    47  	{"$HOME", "/usr/gopher"},
    48  	{"$home_1", "/usr/foo"},
    49  	{"${HOME}", "/usr/gopher"},
    50  	{"${H}OME", "(Value of H)OME"},
    51  	{"A$$$#$1$H$home_1*B", "APIDNARGSARGUMENT1(Value of H)/usr/foo*B"},
    52  	{"start$+middle$^end$", "start$+middle$^end$"},
    53  	{"mixed$|bag$$$", "mixed$|bagPID$"},
    54  	{"$", "$"},
    55  	{"$}", "$}"},
    56  	{"${", ""},  // invalid syntax; eat up the characters
    57  	{"${}", ""}, // invalid syntax; eat up the characters
    58  }
    59  
    60  func TestExpand(t *testing.T) {
    61  	for _, test := range expandTests {
    62  		result := Expand(test.in, testGetenv)
    63  		if result != test.out {
    64  			t.Errorf("Expand(%q)=%q; expected %q", test.in, result, test.out)
    65  		}
    66  	}
    67  }
    68  
    69  var global any
    70  
    71  func BenchmarkExpand(b *testing.B) {
    72  	b.Run("noop", func(b *testing.B) {
    73  		var s string
    74  		b.ReportAllocs()
    75  		for i := 0; i < b.N; i++ {
    76  			s = Expand("tick tick tick tick", func(string) string { return "" })
    77  		}
    78  		global = s
    79  	})
    80  	b.Run("multiple", func(b *testing.B) {
    81  		var s string
    82  		b.ReportAllocs()
    83  		for i := 0; i < b.N; i++ {
    84  			s = Expand("$a $a $a $a", func(string) string { return "boom" })
    85  		}
    86  		global = s
    87  	})
    88  }
    89  
    90  func TestConsistentEnviron(t *testing.T) {
    91  	e0 := Environ()
    92  	for i := 0; i < 10; i++ {
    93  		e1 := Environ()
    94  		if !reflect.DeepEqual(e0, e1) {
    95  			t.Fatalf("environment changed")
    96  		}
    97  	}
    98  }
    99  
   100  func TestUnsetenv(t *testing.T) {
   101  	const testKey = "GO_TEST_UNSETENV"
   102  	set := func() bool {
   103  		prefix := testKey + "="
   104  		for _, key := range Environ() {
   105  			if strings.HasPrefix(key, prefix) {
   106  				return true
   107  			}
   108  		}
   109  		return false
   110  	}
   111  	if err := Setenv(testKey, "1"); err != nil {
   112  		t.Fatalf("Setenv: %v", err)
   113  	}
   114  	if !set() {
   115  		t.Error("Setenv didn't set TestUnsetenv")
   116  	}
   117  	if err := Unsetenv(testKey); err != nil {
   118  		t.Fatalf("Unsetenv: %v", err)
   119  	}
   120  	if set() {
   121  		t.Fatal("Unsetenv didn't clear TestUnsetenv")
   122  	}
   123  }
   124  
   125  func TestClearenv(t *testing.T) {
   126  	const testKey = "GO_TEST_CLEARENV"
   127  	const testValue = "1"
   128  
   129  	// reset env
   130  	defer func(origEnv []string) {
   131  		for _, pair := range origEnv {
   132  			// Environment variables on Windows can begin with =
   133  			// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
   134  			i := strings.Index(pair[1:], "=") + 1
   135  			if err := Setenv(pair[:i], pair[i+1:]); err != nil {
   136  				t.Errorf("Setenv(%q, %q) failed during reset: %v", pair[:i], pair[i+1:], err)
   137  			}
   138  		}
   139  	}(Environ())
   140  
   141  	if err := Setenv(testKey, testValue); err != nil {
   142  		t.Fatalf("Setenv(%q, %q) failed: %v", testKey, testValue, err)
   143  	}
   144  	if _, ok := LookupEnv(testKey); !ok {
   145  		t.Errorf("Setenv(%q, %q) didn't set $%s", testKey, testValue, testKey)
   146  	}
   147  	Clearenv()
   148  	if val, ok := LookupEnv(testKey); ok {
   149  		t.Errorf("Clearenv() didn't clear $%s, remained with value %q", testKey, val)
   150  	}
   151  }
   152  
   153  func TestLookupEnv(t *testing.T) {
   154  	const smallpox = "SMALLPOX"      // No one has smallpox.
   155  	value, ok := LookupEnv(smallpox) // Should not exist.
   156  	if ok || value != "" {
   157  		t.Fatalf("%s=%q", smallpox, value)
   158  	}
   159  	defer Unsetenv(smallpox)
   160  	err := Setenv(smallpox, "virus")
   161  	if err != nil {
   162  		t.Fatalf("failed to release smallpox virus")
   163  	}
   164  	_, ok = LookupEnv(smallpox)
   165  	if !ok {
   166  		t.Errorf("smallpox release failed; world remains safe but LookupEnv is broken")
   167  	}
   168  }
   169  
   170  // On Windows, Environ was observed to report keys with a single leading "=".
   171  // Check that they are properly reported by LookupEnv and can be set by SetEnv.
   172  // See https://golang.org/issue/49886.
   173  func TestEnvironConsistency(t *testing.T) {
   174  	t.Parallel()
   175  
   176  	for _, kv := range Environ() {
   177  		i := strings.Index(kv, "=")
   178  		if i == 0 {
   179  			// We observe in practice keys with a single leading "=" on Windows.
   180  			// TODO(#49886): Should we consume only the first leading "=" as part
   181  			// of the key, or parse through arbitrarily many of them until a non-=,
   182  			// or try each possible key/value boundary until LookupEnv succeeds?
   183  			i = strings.Index(kv[1:], "=") + 1
   184  		}
   185  		if i < 0 {
   186  			t.Errorf("Environ entry missing '=': %q", kv)
   187  		}
   188  
   189  		k := kv[:i]
   190  		v := kv[i+1:]
   191  		v2, ok := LookupEnv(k)
   192  		if ok && v == v2 {
   193  			t.Logf("LookupEnv(%q) = %q, %t", k, v2, ok)
   194  		} else {
   195  			t.Errorf("Environ contains %q, but LookupEnv(%q) = %q, %t", kv, k, v2, ok)
   196  		}
   197  
   198  		// Since k=v is already present in the environment,
   199  		// setting it should be a no-op.
   200  		if err := Setenv(k, v); err == nil {
   201  			t.Logf("Setenv(%q, %q)", k, v)
   202  		} else {
   203  			t.Errorf("Environ contains %q, but SetEnv(%q, %q) = %q", kv, k, v, err)
   204  		}
   205  	}
   206  }
   207  

View as plain text