-
Notifications
You must be signed in to change notification settings - Fork 74
/
parser_test.go
324 lines (304 loc) · 16.1 KB
/
parser_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
package main
import (
"fmt"
"path/filepath"
"regexp"
"runtime"
"sort"
"strconv"
"testing"
)
var goVersionRegexp = regexp.MustCompile(`^go1(?:\.(\d+))?`)
// This type is used to implement the sort.Interface interface
// in order to be able to sort an array of Tag
type TagSlice []Tag
// Return the len of the array
func (t TagSlice) Len() int {
return len(t)
}
// Compare two elements of a tag array
func (t TagSlice) Less(i, j int) bool {
return t[i].String() < t[j].String()
}
// Swap two elements of the underlying array
func (t TagSlice) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
// Dump the names of the tags in a TagSlice
func (t TagSlice) Dump() {
for idx, val := range t {
fmt.Println(idx, val.Name)
}
}
type F map[TagField]string
var testCases = []struct {
filename string
relative bool
basepath string
minversion int
withExtraSymbols bool
tags []Tag
}{
{filename: "testdata/const.go", tags: []Tag{
tag("Test", 1, "p", F{}),
tag("Constant", 3, "c", F{"access": "public", "type": "string"}),
tag("OtherConst", 4, "c", F{"access": "public"}),
tag("A", 7, "c", F{"access": "public"}),
tag("B", 8, "c", F{"access": "public"}),
tag("C", 8, "c", F{"access": "public"}),
tag("D", 9, "c", F{"access": "public"}),
}},
{filename: "testdata/const.go", withExtraSymbols: true, tags: []Tag{
tag("Test", 1, "p", F{}),
tag("Constant", 3, "c", F{"access": "public", "type": "string"}),
tag("OtherConst", 4, "c", F{"access": "public"}),
tag("A", 7, "c", F{"access": "public"}),
tag("B", 8, "c", F{"access": "public"}),
tag("C", 8, "c", F{"access": "public"}),
tag("D", 9, "c", F{"access": "public"}),
tag("Test.Constant", 3, "c", F{"access": "public", "type": "string"}),
tag("Test.OtherConst", 4, "c", F{"access": "public"}),
tag("Test.A", 7, "c", F{"access": "public"}),
tag("Test.B", 8, "c", F{"access": "public"}),
tag("Test.C", 8, "c", F{"access": "public"}),
tag("Test.D", 9, "c", F{"access": "public"}),
}},
{filename: "testdata/func.go", tags: []Tag{
tag("Test", 1, "p", F{}),
tag("Function1", 3, "f", F{"access": "public", "signature": "()", "type": "string"}),
tag("function2", 6, "f", F{"access": "private", "signature": "(p1, p2 int, p3 *string)"}),
tag("function3", 9, "f", F{"access": "private", "signature": "()", "type": "bool"}),
tag("function4", 12, "f", F{"access": "private", "signature": "(p interface{})", "type": "interface{}"}),
tag("function5", 15, "f", F{"access": "private", "signature": "()", "type": "string, string, error"}),
tag("function6", 18, "f", F{"access": "private", "signature": "(v ...interface{})"}),
tag("function7", 21, "f", F{"access": "private", "signature": "(s ...string)"}),
}},
{filename: "testdata/func.go", withExtraSymbols: true, tags: []Tag{
tag("Test", 1, "p", F{}),
tag("Test.Function1", 3, "f", F{"access": "public", "signature": "()", "type": "string"}),
tag("Test.function2", 6, "f", F{"access": "private", "signature": "(p1, p2 int, p3 *string)"}),
tag("Test.function3", 9, "f", F{"access": "private", "signature": "()", "type": "bool"}),
tag("Test.function4", 12, "f", F{"access": "private", "signature": "(p interface{})", "type": "interface{}"}),
tag("Test.function5", 15, "f", F{"access": "private", "signature": "()", "type": "string, string, error"}),
tag("Test.function6", 18, "f", F{"access": "private", "signature": "(v ...interface{})"}),
tag("Test.function7", 21, "f", F{"access": "private", "signature": "(s ...string)"}),
tag("Function1", 3, "f", F{"access": "public", "signature": "()", "type": "string"}),
tag("function2", 6, "f", F{"access": "private", "signature": "(p1, p2 int, p3 *string)"}),
tag("function3", 9, "f", F{"access": "private", "signature": "()", "type": "bool"}),
tag("function4", 12, "f", F{"access": "private", "signature": "(p interface{})", "type": "interface{}"}),
tag("function5", 15, "f", F{"access": "private", "signature": "()", "type": "string, string, error"}),
tag("function6", 18, "f", F{"access": "private", "signature": "(v ...interface{})"}),
tag("function7", 21, "f", F{"access": "private", "signature": "(s ...string)"}),
}},
{filename: "testdata/import.go", tags: []Tag{
tag("Test", 1, "p", F{}),
tag("fmt", 3, "i", F{}),
tag("go/ast", 6, "i", F{}),
tag("go/parser", 7, "i", F{}),
}},
{filename: "testdata/import.go", withExtraSymbols: true, tags: []Tag{
tag("Test", 1, "p", F{}),
tag("fmt", 3, "i", F{}),
tag("go/ast", 6, "i", F{}),
tag("go/parser", 7, "i", F{}),
}},
{filename: "testdata/interface.go", tags: []Tag{
tag("Test", 1, "p", F{}),
tag("InterfaceMethod", 4, "m", F{"access": "public", "signature": "(int)", "ntype": "Interface", "type": "string"}),
tag("OtherMethod", 5, "m", F{"access": "public", "signature": "()", "ntype": "Interface"}),
tag("io.Reader", 6, "e", F{"access": "public", "ntype": "Interface"}),
tag("Interface", 3, "n", F{"access": "public", "type": "interface"}),
}},
{filename: "testdata/interface.go", withExtraSymbols: true, tags: []Tag{
tag("Test", 1, "p", F{}),
tag("InterfaceMethod", 4, "m", F{"access": "public", "signature": "(int)", "ntype": "Interface", "type": "string"}),
tag("OtherMethod", 5, "m", F{"access": "public", "signature": "()", "ntype": "Interface"}),
tag("io.Reader", 6, "e", F{"access": "public", "ntype": "Interface"}),
tag("Interface", 3, "n", F{"access": "public", "type": "interface"}),
tag("Test.Interface", 3, "n", F{"access": "public", "type": "interface"}),
}},
{filename: "testdata/struct.go", tags: []Tag{
tag("Test", 1, "p", F{}),
tag("Field1", 4, "w", F{"access": "public", "ctype": "Struct", "type": "int"}),
tag("Field2", 4, "w", F{"access": "public", "ctype": "Struct", "type": "int"}),
tag("field3", 5, "w", F{"access": "private", "ctype": "Struct", "type": "string"}),
tag("field4", 6, "w", F{"access": "private", "ctype": "Struct", "type": "*bool"}),
tag("Struct", 3, "t", F{"access": "public", "type": "struct"}),
tag("Struct", 20, "e", F{"access": "public", "ctype": "TestEmbed", "type": "Struct"}),
tag("*io.Writer", 21, "e", F{"access": "public", "ctype": "TestEmbed", "type": "*io.Writer"}),
tag("TestEmbed", 19, "t", F{"access": "public", "type": "struct"}),
tag("Struct2", 27, "t", F{"access": "public", "type": "struct"}),
tag("Connection", 36, "t", F{"access": "public", "type": "struct"}),
tag("NewStruct", 9, "f", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "*Struct"}),
tag("F1", 13, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "[]bool, [2]*string"}),
tag("F2", 16, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "bool"}),
tag("NewTestEmbed", 24, "f", F{"access": "public", "ctype": "TestEmbed", "signature": "()", "type": "TestEmbed"}),
tag("NewStruct2", 30, "f", F{"access": "public", "ctype": "Struct2", "signature": "()", "type": "*Struct2, error"}),
tag("Dial", 33, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, error"}),
tag("Dial2", 39, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, *Struct2"}),
tag("Dial3", 42, "f", F{"access": "public", "signature": "()", "type": "*Connection, *Connection"}),
}},
{filename: "testdata/struct.go", withExtraSymbols: true, tags: []Tag{
tag("Test", 1, "p", F{}),
tag("Field1", 4, "w", F{"access": "public", "ctype": "Struct", "type": "int"}),
tag("Field2", 4, "w", F{"access": "public", "ctype": "Struct", "type": "int"}),
tag("field3", 5, "w", F{"access": "private", "ctype": "Struct", "type": "string"}),
tag("field4", 6, "w", F{"access": "private", "ctype": "Struct", "type": "*bool"}),
tag("Struct", 3, "t", F{"access": "public", "type": "struct"}),
tag("Test.Struct", 3, "t", F{"access": "public", "type": "struct"}),
tag("Struct", 20, "e", F{"access": "public", "ctype": "TestEmbed", "type": "Struct"}),
tag("*io.Writer", 21, "e", F{"access": "public", "ctype": "TestEmbed", "type": "*io.Writer"}),
tag("TestEmbed", 19, "t", F{"access": "public", "type": "struct"}),
tag("Test.TestEmbed", 19, "t", F{"access": "public", "type": "struct"}),
tag("Struct2", 27, "t", F{"access": "public", "type": "struct"}),
tag("Test.Struct2", 27, "t", F{"access": "public", "type": "struct"}),
tag("Connection", 36, "t", F{"access": "public", "type": "struct"}),
tag("Test.Connection", 36, "t", F{"access": "public", "type": "struct"}),
tag("NewStruct", 9, "f", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "*Struct"}),
tag("Test.NewStruct", 9, "f", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "*Struct"}),
tag("F1", 13, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "[]bool, [2]*string"}),
tag("Struct.F1", 13, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "[]bool, [2]*string"}),
tag("Test.F1", 13, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "[]bool, [2]*string"}),
tag("Test.Struct.F1", 13, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "[]bool, [2]*string"}),
tag("F2", 16, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "bool"}),
tag("Struct.F2", 16, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "bool"}),
tag("Test.Struct.F2", 16, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "bool"}),
tag("Test.F2", 16, "m", F{"access": "public", "ctype": "Struct", "signature": "()", "type": "bool"}),
tag("NewTestEmbed", 24, "f", F{"access": "public", "ctype": "TestEmbed", "signature": "()", "type": "TestEmbed"}),
tag("Test.NewTestEmbed", 24, "f", F{"access": "public", "ctype": "TestEmbed", "signature": "()", "type": "TestEmbed"}),
tag("NewStruct2", 30, "f", F{"access": "public", "ctype": "Struct2", "signature": "()", "type": "*Struct2, error"}),
tag("Test.NewStruct2", 30, "f", F{"access": "public", "ctype": "Struct2", "signature": "()", "type": "*Struct2, error"}),
tag("Dial", 33, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, error"}),
tag("Test.Dial", 33, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, error"}),
tag("Dial2", 39, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, *Struct2"}),
tag("Test.Dial2", 39, "f", F{"access": "public", "ctype": "Connection", "signature": "()", "type": "*Connection, *Struct2"}),
tag("Dial3", 42, "f", F{"access": "public", "signature": "()", "type": "*Connection, *Connection"}),
tag("Test.Dial3", 42, "f", F{"access": "public", "signature": "()", "type": "*Connection, *Connection"}),
}},
{filename: "testdata/type.go", tags: []Tag{
tag("Test", 1, "p", F{}),
tag("testType", 3, "t", F{"access": "private", "type": "int"}),
tag("testArrayType", 4, "t", F{"access": "private", "type": "[4]int"}),
tag("testSliceType", 5, "t", F{"access": "private", "type": "[]int"}),
tag("testPointerType", 6, "t", F{"access": "private", "type": "*string"}),
tag("testFuncType1", 7, "t", F{"access": "private", "type": "func()"}),
tag("testFuncType2", 8, "t", F{"access": "private", "type": "func(int) string"}),
tag("testMapType", 9, "t", F{"access": "private", "type": "map[string]bool"}),
tag("testChanType", 10, "t", F{"access": "private", "type": "chan bool"}),
}},
{filename: "testdata/type.go", withExtraSymbols: true, tags: []Tag{
tag("Test", 1, "p", F{}),
tag("testType", 3, "t", F{"access": "private", "type": "int"}),
tag("testArrayType", 4, "t", F{"access": "private", "type": "[4]int"}),
tag("testSliceType", 5, "t", F{"access": "private", "type": "[]int"}),
tag("testPointerType", 6, "t", F{"access": "private", "type": "*string"}),
tag("testFuncType1", 7, "t", F{"access": "private", "type": "func()"}),
tag("testFuncType2", 8, "t", F{"access": "private", "type": "func(int) string"}),
tag("testMapType", 9, "t", F{"access": "private", "type": "map[string]bool"}),
tag("testChanType", 10, "t", F{"access": "private", "type": "chan bool"}),
tag("Test.testType", 3, "t", F{"access": "private", "type": "int"}),
tag("Test.testArrayType", 4, "t", F{"access": "private", "type": "[4]int"}),
tag("Test.testSliceType", 5, "t", F{"access": "private", "type": "[]int"}),
tag("Test.testPointerType", 6, "t", F{"access": "private", "type": "*string"}),
tag("Test.testFuncType1", 7, "t", F{"access": "private", "type": "func()"}),
tag("Test.testFuncType2", 8, "t", F{"access": "private", "type": "func(int) string"}),
tag("Test.testMapType", 9, "t", F{"access": "private", "type": "map[string]bool"}),
tag("Test.testChanType", 10, "t", F{"access": "private", "type": "chan bool"}),
}},
{filename: "testdata/var.go", tags: []Tag{
tag("Test", 1, "p", F{}),
tag("variable1", 3, "v", F{"access": "private", "type": "int"}),
tag("variable2", 4, "v", F{"access": "private", "type": "string"}),
tag("A", 7, "v", F{"access": "public"}),
tag("B", 8, "v", F{"access": "public"}),
tag("C", 8, "v", F{"access": "public"}),
tag("D", 9, "v", F{"access": "public"}),
}},
{filename: "testdata/var.go", withExtraSymbols: true, tags: []Tag{
tag("Test", 1, "p", F{}),
tag("variable1", 3, "v", F{"access": "private", "type": "int"}),
tag("variable2", 4, "v", F{"access": "private", "type": "string"}),
tag("A", 7, "v", F{"access": "public"}),
tag("B", 8, "v", F{"access": "public"}),
tag("C", 8, "v", F{"access": "public"}),
tag("D", 9, "v", F{"access": "public"}),
tag("Test.variable1", 3, "v", F{"access": "private", "type": "int"}),
tag("Test.variable2", 4, "v", F{"access": "private", "type": "string"}),
tag("Test.A", 7, "v", F{"access": "public"}),
tag("Test.B", 8, "v", F{"access": "public"}),
tag("Test.C", 8, "v", F{"access": "public"}),
tag("Test.D", 9, "v", F{"access": "public"}),
}},
{filename: "testdata/simple.go", relative: true, basepath: "dir", tags: []Tag{
{Name: "main", File: "../testdata/simple.go", Address: "1", Type: "p", Fields: F{"line": "1"}},
}},
{filename: "testdata/simple.go", withExtraSymbols: true, relative: true, basepath: "dir", tags: []Tag{
{Name: "main", File: "../testdata/simple.go", Address: "1", Type: "p", Fields: F{"line": "1"}},
}},
{filename: "testdata/range.go", minversion: 4, tags: []Tag{
tag("main", 1, "p", F{}),
tag("fmt", 3, "i", F{}),
tag("main", 5, "f", F{"access": "private", "signature": "()"}),
}},
{filename: "testdata/range.go", withExtraSymbols: true, minversion: 4, tags: []Tag{
tag("main", 1, "p", F{}),
tag("fmt", 3, "i", F{}),
tag("main", 5, "f", F{"access": "private", "signature": "()"}),
tag("main.main", 5, "f", F{"access": "private", "signature": "()"}),
}},
}
func TestParse(t *testing.T) {
for _, testCase := range testCases {
if testCase.minversion > 0 && extractVersionCode(runtime.Version()) < testCase.minversion {
t.Skipf("[%s] skipping test. Version is %s, but test requires at least go1.%d", testCase.filename, runtime.Version(), testCase.minversion)
continue
}
basepath, err := filepath.Abs(testCase.basepath)
if err != nil {
t.Errorf("[%s] could not determine base path: %s\n", testCase.filename, err)
continue
}
var extra FieldSet
if testCase.withExtraSymbols {
extra = FieldSet{ExtraTags: true}
}
tags, err := Parse(testCase.filename, testCase.relative, basepath, extra)
if err != nil {
t.Errorf("[%s] Parse error: %s", testCase.filename, err)
continue
}
sort.Sort(TagSlice(tags))
sort.Sort(TagSlice(testCase.tags))
if len(tags) != len(testCase.tags) {
t.Errorf("[%s] len(tags) == %d, want %d", testCase.filename, len(tags), len(testCase.tags))
continue
}
for i, tag := range testCase.tags {
if len(tag.File) == 0 {
tag.File = testCase.filename
}
if tags[i].String() != tag.String() {
t.Errorf("[%s] tag(%d)\n is:%s\nwant:%s", testCase.filename, i, tags[i].String(), tag.String())
}
}
}
}
func tag(n string, l int, t TagType, fields F) (tag Tag) {
tag = Tag{
Name: n,
File: "",
Address: strconv.Itoa(l),
Type: t,
Fields: fields,
}
tag.Fields["line"] = tag.Address
return
}
func extractVersionCode(version string) int {
matches := goVersionRegexp.FindAllStringSubmatch(version, -1)
if len(matches) == 0 || len(matches[0]) < 2 {
return 0
}
n, _ := strconv.Atoi(matches[0][1])
return n
}