1// Copyright 2016 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 5package work 6 7import ( 8 "bytes" 9 "fmt" 10 "io/fs" 11 "os" 12 "path/filepath" 13 "reflect" 14 "runtime" 15 "strings" 16 "testing" 17 18 "cmd/go/internal/base" 19 "cmd/go/internal/cfg" 20 "cmd/go/internal/load" 21) 22 23func TestRemoveDevNull(t *testing.T) { 24 fi, err := os.Lstat(os.DevNull) 25 if err != nil { 26 t.Skip(err) 27 } 28 if fi.Mode().IsRegular() { 29 t.Errorf("Lstat(%s).Mode().IsRegular() = true; expected false", os.DevNull) 30 } 31 mayberemovefile(os.DevNull) 32 _, err = os.Lstat(os.DevNull) 33 if err != nil { 34 t.Errorf("mayberemovefile(%s) did remove it; oops", os.DevNull) 35 } 36} 37 38func TestSplitPkgConfigOutput(t *testing.T) { 39 for _, test := range []struct { 40 in []byte 41 want []string 42 }{ 43 {[]byte(`-r:foo -L/usr/white\ space/lib -lfoo\ bar -lbar\ baz`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}}, 44 {[]byte(`-lextra\ fun\ arg\\`), []string{`-lextra fun arg\`}}, 45 {[]byte("\textra whitespace\r\n"), []string{"extra", "whitespace"}}, 46 {[]byte(" \r\n "), nil}, 47 {[]byte(`"-r:foo" "-L/usr/white space/lib" "-lfoo bar" "-lbar baz"`), []string{"-r:foo", "-L/usr/white space/lib", "-lfoo bar", "-lbar baz"}}, 48 {[]byte(`"-lextra fun arg\\"`), []string{`-lextra fun arg\`}}, 49 {[]byte(`" \r\n\ "`), []string{` \r\n\ `}}, 50 {[]byte(`""`), nil}, 51 {[]byte(``), nil}, 52 {[]byte(`"\\"`), []string{`\`}}, 53 {[]byte(`"\x"`), []string{`\x`}}, 54 {[]byte(`"\\x"`), []string{`\x`}}, 55 {[]byte(`'\\'`), []string{`\`}}, 56 {[]byte(`'\x'`), []string{`\x`}}, 57 {[]byte(`"\\x"`), []string{`\x`}}, 58 {[]byte(`-fPIC -I/test/include/foo -DQUOTED='"/test/share/doc"'`), []string{"-fPIC", "-I/test/include/foo", `-DQUOTED="/test/share/doc"`}}, 59 {[]byte(`-fPIC -I/test/include/foo -DQUOTED="/test/share/doc"`), []string{"-fPIC", "-I/test/include/foo", "-DQUOTED=/test/share/doc"}}, 60 {[]byte(`-fPIC -I/test/include/foo -DQUOTED=\"/test/share/doc\"`), []string{"-fPIC", "-I/test/include/foo", `-DQUOTED="/test/share/doc"`}}, 61 {[]byte(`-fPIC -I/test/include/foo -DQUOTED='/test/share/doc'`), []string{"-fPIC", "-I/test/include/foo", "-DQUOTED=/test/share/doc"}}, 62 {[]byte(`-DQUOTED='/te\st/share/d\oc'`), []string{`-DQUOTED=/te\st/share/d\oc`}}, 63 {[]byte(`-Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\ world`), []string{"-Dhello=10", "-Dworld=+32", "-DDEFINED_FROM_PKG_CONFIG=hello world"}}, 64 {[]byte(`"broken\"" \\\a "a"`), []string{"broken\"", "\\a", "a"}}, 65 } { 66 got, err := splitPkgConfigOutput(test.in) 67 if err != nil { 68 t.Errorf("splitPkgConfigOutput on %v failed with error %v", test.in, err) 69 continue 70 } 71 if !reflect.DeepEqual(got, test.want) { 72 t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want) 73 } 74 } 75 76 for _, test := range []struct { 77 in []byte 78 want []string 79 }{ 80 // broken quotation 81 {[]byte(`" \r\n `), nil}, 82 {[]byte(`"-r:foo" "-L/usr/white space/lib "-lfoo bar" "-lbar baz"`), nil}, 83 {[]byte(`"-lextra fun arg\\`), nil}, 84 // broken char escaping 85 {[]byte(`broken flag\`), nil}, 86 {[]byte(`extra broken flag \`), nil}, 87 {[]byte(`\`), nil}, 88 {[]byte(`"broken\"" "extra" \`), nil}, 89 } { 90 got, err := splitPkgConfigOutput(test.in) 91 if err == nil { 92 t.Errorf("splitPkgConfigOutput(%v) = %v; haven't failed with error as expected.", test.in, got) 93 } 94 if !reflect.DeepEqual(got, test.want) { 95 t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want) 96 } 97 } 98 99} 100 101func TestSharedLibName(t *testing.T) { 102 // TODO(avdva) - make these values platform-specific 103 prefix := "lib" 104 suffix := ".so" 105 testData := []struct { 106 args []string 107 pkgs []*load.Package 108 expected string 109 expectErr bool 110 rootedAt string 111 }{ 112 { 113 args: []string{"std"}, 114 pkgs: []*load.Package{}, 115 expected: "std", 116 }, 117 { 118 args: []string{"std", "cmd"}, 119 pkgs: []*load.Package{}, 120 expected: "std,cmd", 121 }, 122 { 123 args: []string{}, 124 pkgs: []*load.Package{pkgImportPath("gopkg.in/somelib")}, 125 expected: "gopkg.in-somelib", 126 }, 127 { 128 args: []string{"./..."}, 129 pkgs: []*load.Package{pkgImportPath("somelib")}, 130 expected: "somelib", 131 rootedAt: "somelib", 132 }, 133 { 134 args: []string{"../somelib", "../somelib"}, 135 pkgs: []*load.Package{pkgImportPath("somelib")}, 136 expected: "somelib", 137 }, 138 { 139 args: []string{"../lib1", "../lib2"}, 140 pkgs: []*load.Package{pkgImportPath("gopkg.in/lib1"), pkgImportPath("gopkg.in/lib2")}, 141 expected: "gopkg.in-lib1,gopkg.in-lib2", 142 }, 143 { 144 args: []string{"./..."}, 145 pkgs: []*load.Package{ 146 pkgImportPath("gopkg.in/dir/lib1"), 147 pkgImportPath("gopkg.in/lib2"), 148 pkgImportPath("gopkg.in/lib3"), 149 }, 150 expected: "gopkg.in", 151 rootedAt: "gopkg.in", 152 }, 153 { 154 args: []string{"std", "../lib2"}, 155 pkgs: []*load.Package{}, 156 expectErr: true, 157 }, 158 { 159 args: []string{"all", "./"}, 160 pkgs: []*load.Package{}, 161 expectErr: true, 162 }, 163 { 164 args: []string{"cmd", "fmt"}, 165 pkgs: []*load.Package{}, 166 expectErr: true, 167 }, 168 } 169 for _, data := range testData { 170 func() { 171 if data.rootedAt != "" { 172 tmpGopath, err := os.MkdirTemp("", "gopath") 173 if err != nil { 174 t.Fatal(err) 175 } 176 oldGopath := cfg.BuildContext.GOPATH 177 defer func() { 178 cfg.BuildContext.GOPATH = oldGopath 179 os.Chdir(base.Cwd) 180 err := os.RemoveAll(tmpGopath) 181 if err != nil { 182 t.Error(err) 183 } 184 }() 185 root := filepath.Join(tmpGopath, "src", data.rootedAt) 186 err = os.MkdirAll(root, 0755) 187 if err != nil { 188 t.Fatal(err) 189 } 190 cfg.BuildContext.GOPATH = tmpGopath 191 os.Chdir(root) 192 } 193 computed, err := libname(data.args, data.pkgs) 194 if err != nil { 195 if !data.expectErr { 196 t.Errorf("libname returned an error %q, expected a name", err.Error()) 197 } 198 } else if data.expectErr { 199 t.Errorf("libname returned %q, expected an error", computed) 200 } else { 201 expected := prefix + data.expected + suffix 202 if expected != computed { 203 t.Errorf("libname returned %q, expected %q", computed, expected) 204 } 205 } 206 }() 207 } 208} 209 210func pkgImportPath(pkgpath string) *load.Package { 211 return &load.Package{ 212 PackagePublic: load.PackagePublic{ 213 ImportPath: pkgpath, 214 }, 215 } 216} 217 218// When installing packages, the installed package directory should 219// respect the SetGID bit and group name of the destination 220// directory. 221// See https://golang.org/issue/18878. 222func TestRespectSetgidDir(t *testing.T) { 223 switch runtime.GOOS { 224 case "ios": 225 t.Skip("can't set SetGID bit with chmod on iOS") 226 case "windows", "plan9": 227 t.Skip("chown/chmod setgid are not supported on Windows or Plan 9") 228 } 229 230 var b Builder 231 232 // Check that `cp` is called instead of `mv` by looking at the output 233 // of `(*Builder).ShowCmd` afterwards as a sanity check. 234 cfg.BuildX = true 235 var cmdBuf bytes.Buffer 236 b.Print = func(a ...interface{}) (int, error) { 237 return cmdBuf.WriteString(fmt.Sprint(a...)) 238 } 239 240 setgiddir, err := os.MkdirTemp("", "SetGroupID") 241 if err != nil { 242 t.Fatal(err) 243 } 244 defer os.RemoveAll(setgiddir) 245 246 // BSD mkdir(2) inherits the parent directory group, and other platforms 247 // can inherit the parent directory group via setgid. The test setup (chmod 248 // setgid) will fail if the process does not have the group permission to 249 // the new temporary directory. 250 err = os.Chown(setgiddir, os.Getuid(), os.Getgid()) 251 if err != nil { 252 t.Fatal(err) 253 } 254 255 // Change setgiddir's permissions to include the SetGID bit. 256 if err := os.Chmod(setgiddir, 0755|fs.ModeSetgid); err != nil { 257 t.Fatal(err) 258 } 259 260 pkgfile, err := os.CreateTemp("", "pkgfile") 261 if err != nil { 262 t.Fatalf("os.CreateTemp(\"\", \"pkgfile\"): %v", err) 263 } 264 defer os.Remove(pkgfile.Name()) 265 defer pkgfile.Close() 266 267 dirGIDFile := filepath.Join(setgiddir, "setgid") 268 if err := b.moveOrCopyFile(dirGIDFile, pkgfile.Name(), 0666, true); err != nil { 269 t.Fatalf("moveOrCopyFile: %v", err) 270 } 271 272 got := strings.TrimSpace(cmdBuf.String()) 273 want := b.fmtcmd("", "cp %s %s", pkgfile.Name(), dirGIDFile) 274 if got != want { 275 t.Fatalf("moveOrCopyFile(%q, %q): want %q, got %q", dirGIDFile, pkgfile.Name(), want, got) 276 } 277} 278