1
2
3
4
5
6
7 package main_test
8
9 import (
10 "bytes"
11 "fmt"
12 "os"
13 "os/exec"
14 "path/filepath"
15 "runtime"
16 "strconv"
17 "strings"
18 "testing"
19
20 "golang.org/x/tools/internal/testenv"
21 "golang.org/x/tools/txtar"
22 )
23
24
25
26 func Test(t *testing.T) {
27 testenv.NeedsTool(t, "go")
28 if runtime.GOOS == "android" {
29 t.Skipf("the dependencies are not available on android")
30 }
31
32 exe := buildDeadcode(t)
33
34 matches, err := filepath.Glob("testdata/*.txtar")
35 if err != nil {
36 t.Fatal(err)
37 }
38 for _, filename := range matches {
39 filename := filename
40 t.Run(filename, func(t *testing.T) {
41 t.Parallel()
42
43 ar, err := txtar.ParseFile(filename)
44 if err != nil {
45 t.Fatal(err)
46 }
47
48
49 tmpdir := t.TempDir()
50 for _, f := range ar.Files {
51 filename := filepath.Join(tmpdir, f.Name)
52 if err := os.MkdirAll(filepath.Dir(filename), 0777); err != nil {
53 t.Fatal(err)
54 }
55 if err := os.WriteFile(filename, f.Data, 0666); err != nil {
56 t.Fatal(err)
57 }
58 }
59
60
61
62
63
64
65
66 type testcase struct {
67 linenum int
68 args []string
69 wantErr bool
70 want map[string]bool
71 }
72 var cases []*testcase
73 var current *testcase
74 for i, line := range strings.Split(string(ar.Comment), "\n") {
75 line = strings.TrimSpace(line)
76 if line == "" || line[0] == '#' {
77 continue
78 }
79
80 words, err := words(line)
81 if err != nil {
82 t.Fatalf("cannot break line into words: %v (%s)", err, line)
83 }
84 switch kind := words[0]; kind {
85 case "deadcode", "!deadcode":
86 current = &testcase{
87 linenum: i + 1,
88 want: make(map[string]bool),
89 args: words[1:],
90 wantErr: kind[0] == '!',
91 }
92 cases = append(cases, current)
93 case "want", "!want":
94 if current == nil {
95 t.Fatalf("'want' directive must be after 'deadcode'")
96 }
97 if len(words) != 2 {
98 t.Fatalf("'want' directive needs argument <<%s>>", line)
99 }
100 current.want[words[1]] = kind[0] != '!'
101 default:
102 t.Fatalf("%s: invalid directive %q", filename, kind)
103 }
104 }
105
106 for _, tc := range cases {
107 t.Run(fmt.Sprintf("L%d", tc.linenum), func(t *testing.T) {
108
109 cmd := exec.Command(exe, tc.args...)
110 cmd.Stdout = new(bytes.Buffer)
111 cmd.Stderr = new(bytes.Buffer)
112 cmd.Dir = tmpdir
113 cmd.Env = append(os.Environ(), "GOPROXY=", "GO111MODULE=on")
114 var got string
115 if err := cmd.Run(); err != nil {
116 if !tc.wantErr {
117 t.Fatalf("deadcode failed: %v (stderr=%s)", err, cmd.Stderr)
118 }
119 got = fmt.Sprint(cmd.Stderr)
120 } else {
121 if tc.wantErr {
122 t.Fatalf("deadcode succeeded unexpectedly (stdout=%s)", cmd.Stdout)
123 }
124 got = fmt.Sprint(cmd.Stdout)
125 }
126
127
128 for str, sense := range tc.want {
129 ok := true
130 if strings.Contains(got, str) != sense {
131 if sense {
132 t.Errorf("missing %q", str)
133 } else {
134 t.Errorf("unwanted %q", str)
135 }
136 ok = false
137 }
138 if !ok {
139 t.Errorf("got: <<%s>>", got)
140 }
141 }
142 })
143 }
144 })
145 }
146 }
147
148
149
150 func buildDeadcode(t *testing.T) string {
151 bin := filepath.Join(t.TempDir(), "deadcode")
152 if runtime.GOOS == "windows" {
153 bin += ".exe"
154 }
155 cmd := exec.Command("go", "build", "-o", bin)
156 if out, err := cmd.CombinedOutput(); err != nil {
157 t.Fatalf("Building deadcode: %v\n%s", err, out)
158 }
159 return bin
160 }
161
162
163
164 func words(s string) ([]string, error) {
165 var words []string
166 for s != "" {
167 s = strings.TrimSpace(s)
168 var word string
169 if s[0] == '"' || s[0] == '`' {
170 prefix, err := strconv.QuotedPrefix(s)
171 if err != nil {
172 return nil, err
173 }
174 s = s[len(prefix):]
175 word, _ = strconv.Unquote(prefix)
176 } else {
177 prefix, rest, _ := strings.Cut(s, " ")
178 s = rest
179 word = prefix
180 }
181 words = append(words, word)
182 }
183 return words, nil
184 }
185
View as plain text