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/ioutil"
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(`broken flag\`), []string{"broken", "flag"}},
46		{[]byte("\textra     whitespace\r\n"), []string{"extra", "whitespace"}},
47		{[]byte("     \r\n      "), nil},
48	} {
49		got := splitPkgConfigOutput(test.in)
50		if !reflect.DeepEqual(got, test.want) {
51			t.Errorf("splitPkgConfigOutput(%v) = %v; want %v", test.in, got, test.want)
52		}
53	}
54}
55
56func TestSharedLibName(t *testing.T) {
57	// TODO(avdva) - make these values platform-specific
58	prefix := "lib"
59	suffix := ".so"
60	testData := []struct {
61		args      []string
62		pkgs      []*load.Package
63		expected  string
64		expectErr bool
65		rootedAt  string
66	}{
67		{
68			args:     []string{"std"},
69			pkgs:     []*load.Package{},
70			expected: "std",
71		},
72		{
73			args:     []string{"std", "cmd"},
74			pkgs:     []*load.Package{},
75			expected: "std,cmd",
76		},
77		{
78			args:     []string{},
79			pkgs:     []*load.Package{pkgImportPath("gopkg.in/somelib")},
80			expected: "gopkg.in-somelib",
81		},
82		{
83			args:     []string{"./..."},
84			pkgs:     []*load.Package{pkgImportPath("somelib")},
85			expected: "somelib",
86			rootedAt: "somelib",
87		},
88		{
89			args:     []string{"../somelib", "../somelib"},
90			pkgs:     []*load.Package{pkgImportPath("somelib")},
91			expected: "somelib",
92		},
93		{
94			args:     []string{"../lib1", "../lib2"},
95			pkgs:     []*load.Package{pkgImportPath("gopkg.in/lib1"), pkgImportPath("gopkg.in/lib2")},
96			expected: "gopkg.in-lib1,gopkg.in-lib2",
97		},
98		{
99			args: []string{"./..."},
100			pkgs: []*load.Package{
101				pkgImportPath("gopkg.in/dir/lib1"),
102				pkgImportPath("gopkg.in/lib2"),
103				pkgImportPath("gopkg.in/lib3"),
104			},
105			expected: "gopkg.in",
106			rootedAt: "gopkg.in",
107		},
108		{
109			args:      []string{"std", "../lib2"},
110			pkgs:      []*load.Package{},
111			expectErr: true,
112		},
113		{
114			args:      []string{"all", "./"},
115			pkgs:      []*load.Package{},
116			expectErr: true,
117		},
118		{
119			args:      []string{"cmd", "fmt"},
120			pkgs:      []*load.Package{},
121			expectErr: true,
122		},
123	}
124	for _, data := range testData {
125		func() {
126			if data.rootedAt != "" {
127				tmpGopath, err := ioutil.TempDir("", "gopath")
128				if err != nil {
129					t.Fatal(err)
130				}
131				oldGopath := cfg.BuildContext.GOPATH
132				defer func() {
133					cfg.BuildContext.GOPATH = oldGopath
134					os.Chdir(base.Cwd)
135					err := os.RemoveAll(tmpGopath)
136					if err != nil {
137						t.Error(err)
138					}
139				}()
140				root := filepath.Join(tmpGopath, "src", data.rootedAt)
141				err = os.MkdirAll(root, 0755)
142				if err != nil {
143					t.Fatal(err)
144				}
145				cfg.BuildContext.GOPATH = tmpGopath
146				os.Chdir(root)
147			}
148			computed, err := libname(data.args, data.pkgs)
149			if err != nil {
150				if !data.expectErr {
151					t.Errorf("libname returned an error %q, expected a name", err.Error())
152				}
153			} else if data.expectErr {
154				t.Errorf("libname returned %q, expected an error", computed)
155			} else {
156				expected := prefix + data.expected + suffix
157				if expected != computed {
158					t.Errorf("libname returned %q, expected %q", computed, expected)
159				}
160			}
161		}()
162	}
163}
164
165func pkgImportPath(pkgpath string) *load.Package {
166	return &load.Package{
167		PackagePublic: load.PackagePublic{
168			ImportPath: pkgpath,
169		},
170	}
171}
172
173// When installing packages, the installed package directory should
174// respect the SetGID bit and group name of the destination
175// directory.
176// See https://golang.org/issue/18878.
177func TestRespectSetgidDir(t *testing.T) {
178	if runtime.GOOS == "nacl" {
179		t.Skip("can't set SetGID bit with chmod on nacl")
180	}
181
182	var b Builder
183
184	// Check that `cp` is called instead of `mv` by looking at the output
185	// of `(*Builder).ShowCmd` afterwards as a sanity check.
186	cfg.BuildX = true
187	var cmdBuf bytes.Buffer
188	b.Print = func(a ...interface{}) (int, error) {
189		return cmdBuf.WriteString(fmt.Sprint(a...))
190	}
191
192	setgiddir, err := ioutil.TempDir("", "SetGroupID")
193	if err != nil {
194		t.Fatal(err)
195	}
196	defer os.RemoveAll(setgiddir)
197
198	if runtime.GOOS == "freebsd" {
199		err = os.Chown(setgiddir, os.Getuid(), os.Getgid())
200		if err != nil {
201			t.Fatal(err)
202		}
203	}
204
205	// Change setgiddir's permissions to include the SetGID bit.
206	if err := os.Chmod(setgiddir, 0755|os.ModeSetgid); err != nil {
207		t.Fatal(err)
208	}
209
210	pkgfile, err := ioutil.TempFile("", "pkgfile")
211	if err != nil {
212		t.Fatalf("ioutil.TempFile(\"\", \"pkgfile\"): %v", err)
213	}
214	defer os.Remove(pkgfile.Name())
215	defer pkgfile.Close()
216
217	dirGIDFile := filepath.Join(setgiddir, "setgid")
218	if err := b.moveOrCopyFile(nil, dirGIDFile, pkgfile.Name(), 0666, true); err != nil {
219		t.Fatalf("moveOrCopyFile: %v", err)
220	}
221
222	got := strings.TrimSpace(cmdBuf.String())
223	want := b.fmtcmd("", "cp %s %s", pkgfile.Name(), dirGIDFile)
224	if got != want {
225		t.Fatalf("moveOrCopyFile(%q, %q): want %q, got %q", dirGIDFile, pkgfile.Name(), want, got)
226	}
227}
228