LEFT | RIGHT |
1 // Copyright 2013 The Go Authors. All rights reserved. | 1 // Copyright 2013 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package oracle_test | 5 package oracle_test |
6 | 6 |
7 // This file defines a test framework for oracle queries. | 7 // This file defines a test framework for oracle queries. |
8 // | 8 // |
9 // The files beneath testdata/src/main contain Go programs containing | 9 // The files beneath testdata/src/main contain Go programs containing |
10 // query annotations of the form: | 10 // query annotations of the form: |
11 // | 11 // |
12 // @verb id "select" | 12 // @verb id "select" |
13 // | 13 // |
14 // where verb is the query mode (e.g. "callers"), id is a unique name | 14 // where verb is the query mode (e.g. "callers"), id is a unique name |
15 // for this query, and "select" is a regular expression matching the | 15 // for this query, and "select" is a regular expression matching the |
16 // substring of the current line that is the query's input selection. | 16 // substring of the current line that is the query's input selection. |
17 // | 17 // |
18 // The expected output for each query is provided in the accompanying | 18 // The expected output for each query is provided in the accompanying |
19 // .golden file. | 19 // .golden file. |
20 // | 20 // |
21 // (Location information is not included because it's too fragile to | 21 // (Location information is not included because it's too fragile to |
22 // display as text. TODO(adonovan): think about how we can test its | 22 // display as text. TODO(adonovan): think about how we can test its |
23 // correctness, since it is critical information.) | 23 // correctness, since it is critical information.) |
24 // | 24 // |
25 // Run this test with: | 25 // Run this test with: |
26 // % go test code.google.com/p/go.tools/oracle -update | 26 // % go test code.google.com/p/go.tools/oracle -update |
27 // to update the golden files. | 27 // to update the golden files. |
28 | 28 |
29 // TODO(adonovan): improve coverage: | |
30 // - output of @callgraph is nondeterministic. | |
31 // - as are lists of labels. | |
32 | |
33 import ( | 29 import ( |
34 "bytes" | 30 "bytes" |
| 31 "encoding/json" |
35 "flag" | 32 "flag" |
36 "fmt" | 33 "fmt" |
37 "go/build" | 34 "go/build" |
38 "go/parser" | 35 "go/parser" |
39 "go/token" | 36 "go/token" |
40 "io" | 37 "io" |
41 "io/ioutil" | 38 "io/ioutil" |
42 "os" | 39 "os" |
43 "os/exec" | 40 "os/exec" |
44 "regexp" | 41 "regexp" |
| 42 "runtime" |
45 "strconv" | 43 "strconv" |
46 "strings" | 44 "strings" |
47 "testing" | 45 "testing" |
48 | 46 |
49 "code.google.com/p/go.tools/oracle" | 47 "code.google.com/p/go.tools/oracle" |
50 ) | 48 ) |
51 | 49 |
52 var updateFlag = flag.Bool("update", false, "Update the golden files.") | 50 var updateFlag = flag.Bool("update", false, "Update the golden files.") |
53 | 51 |
54 type query struct { | 52 type query struct { |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 // stripLocation removes a "file:line: " prefix. | 149 // stripLocation removes a "file:line: " prefix. |
152 func stripLocation(line string) string { | 150 func stripLocation(line string) string { |
153 if i := strings.Index(line, ": "); i >= 0 { | 151 if i := strings.Index(line, ": "); i >= 0 { |
154 line = line[i+2:] | 152 line = line[i+2:] |
155 } | 153 } |
156 return line | 154 return line |
157 } | 155 } |
158 | 156 |
159 // doQuery poses query q to the oracle and writes its response and | 157 // doQuery poses query q to the oracle and writes its response and |
160 // error (if any) to out. | 158 // error (if any) to out. |
161 func doQuery(out io.Writer, q *query) { | 159 func doQuery(out io.Writer, q *query, useJson bool) { |
162 fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id) | 160 fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id) |
163 | 161 |
164 capture := new(bytes.Buffer) // capture standard output | |
165 var buildContext = build.Default | 162 var buildContext = build.Default |
166 buildContext.GOPATH = "testdata" | 163 buildContext.GOPATH = "testdata" |
167 » err := oracle.Main([]string{q.filename}, | 164 » res, err := oracle.Query([]string{q.filename}, |
168 q.verb, | 165 q.verb, |
169 » » fmt.Sprintf("%s %d-%d", q.filename, q.start, q.end), | 166 » » fmt.Sprintf("%s:%d-%d", q.filename, q.start, q.end), |
170 » » /*PTA-log=*/ nil, capture, &buildContext) | 167 » » /*PTA-log=*/ nil, &buildContext) |
171 | 168 » if err != nil { |
172 » for _, line := range strings.Split(capture.String(), "\n") { | 169 » » fmt.Fprintf(out, "\nError: %s\n", stripLocation(err.Error())) |
173 » » fmt.Fprintf(out, "%s\n", stripLocation(line)) | 170 » » return |
174 » } | 171 » } |
175 | 172 |
176 » if err != nil { | 173 » if useJson { |
177 » » fmt.Fprintf(out, "Error: %s\n", stripLocation(err.Error())) | 174 » » // JSON output |
| 175 » » b, err := json.Marshal(res) |
| 176 » » if err != nil { |
| 177 » » » fmt.Fprintf(out, "JSON error: %s\n", err.Error()) |
| 178 » » » return |
| 179 » » } |
| 180 » » var buf bytes.Buffer |
| 181 » » if err := json.Indent(&buf, b, "", "\t"); err != nil { |
| 182 » » » fmt.Fprintf(out, "json.Indent failed: %s", err) |
| 183 » » » return |
| 184 » » } |
| 185 » » out.Write(buf.Bytes()) |
| 186 » } else { |
| 187 » » // "plain" (compiler diagnostic format) output |
| 188 » » capture := new(bytes.Buffer) // capture standard output |
| 189 » » res.WriteTo(capture) |
| 190 » » for _, line := range strings.Split(capture.String(), "\n") { |
| 191 » » » fmt.Fprintf(out, "%s\n", stripLocation(line)) |
| 192 » » } |
178 } | 193 } |
179 } | 194 } |
180 | 195 |
181 func TestOracle(t *testing.T) { | 196 func TestOracle(t *testing.T) { |
| 197 switch runtime.GOOS { |
| 198 case "windows": |
| 199 t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS) |
| 200 } |
| 201 |
182 for _, filename := range []string{ | 202 for _, filename := range []string{ |
183 "testdata/src/main/calls.go", | 203 "testdata/src/main/calls.go", |
184 "testdata/src/main/callgraph.go", | 204 "testdata/src/main/callgraph.go", |
185 "testdata/src/main/describe.go", | 205 "testdata/src/main/describe.go", |
186 "testdata/src/main/freevars.go", | 206 "testdata/src/main/freevars.go", |
187 "testdata/src/main/implements.go", | 207 "testdata/src/main/implements.go", |
188 "testdata/src/main/imports.go", | 208 "testdata/src/main/imports.go", |
189 "testdata/src/main/peers.go", | 209 "testdata/src/main/peers.go", |
| 210 // JSON: |
| 211 "testdata/src/main/callgraph-json.go", |
| 212 "testdata/src/main/calls-json.go", |
| 213 "testdata/src/main/peers-json.go", |
| 214 "testdata/src/main/describe-json.go", |
190 } { | 215 } { |
| 216 useJson := strings.HasSuffix(filename, "-json.go") |
191 queries := parseQueries(t, filename) | 217 queries := parseQueries(t, filename) |
192 golden := filename + "lden" | 218 golden := filename + "lden" |
193 got := filename + "t" | 219 got := filename + "t" |
194 gotfh, err := os.Create(got) | 220 gotfh, err := os.Create(got) |
195 if err != nil { | 221 if err != nil { |
196 t.Errorf("Create(%s) failed: %s", got, err) | 222 t.Errorf("Create(%s) failed: %s", got, err) |
197 continue | 223 continue |
198 } | 224 } |
199 defer gotfh.Close() | 225 defer gotfh.Close() |
200 | 226 |
201 // Run the oracle on each query, redirecting its output | 227 // Run the oracle on each query, redirecting its output |
202 // and error (if any) to the foo.got file. | 228 // and error (if any) to the foo.got file. |
203 for _, q := range queries { | 229 for _, q := range queries { |
204 » » » doQuery(gotfh, q) | 230 » » » doQuery(gotfh, q, useJson) |
205 } | 231 } |
206 | 232 |
207 // Compare foo.got with foo.golden. | 233 // Compare foo.got with foo.golden. |
208 » » cmd := exec.Command("/usr/bin/diff", "-u3", golden, got) // assu
mes POSIX | 234 » » cmd := exec.Command("/usr/bin/diff", "-u", golden, got) // assum
es POSIX |
209 buf := new(bytes.Buffer) | 235 buf := new(bytes.Buffer) |
210 cmd.Stdout = buf | 236 cmd.Stdout = buf |
211 if err := cmd.Run(); err != nil { | 237 if err := cmd.Run(); err != nil { |
212 t.Errorf("Oracle tests for %s failed: %s.\n%s\n", | 238 t.Errorf("Oracle tests for %s failed: %s.\n%s\n", |
213 filename, err, buf) | 239 filename, err, buf) |
214 | 240 |
215 if *updateFlag { | 241 if *updateFlag { |
216 t.Logf("Updating %s...", golden) | 242 t.Logf("Updating %s...", golden) |
217 if err := exec.Command("/bin/cp", got, golden).R
un(); err != nil { | 243 if err := exec.Command("/bin/cp", got, golden).R
un(); err != nil { |
218 t.Errorf("Update failed: %s", err) | 244 t.Errorf("Update failed: %s", err) |
219 } | 245 } |
220 } | 246 } |
221 } | 247 } |
222 } | 248 } |
223 } | 249 } |
LEFT | RIGHT |