env_test.go 5.0 KB

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