Source file
src/go/token/position_test.go
1
2
3
4
5 package token
6
7 import (
8 "fmt"
9 "math/rand"
10 "reflect"
11 "sync"
12 "testing"
13 )
14
15 func checkPos(t *testing.T, msg string, got, want Position) {
16 if got.Filename != want.Filename {
17 t.Errorf("%s: got filename = %q; want %q", msg, got.Filename, want.Filename)
18 }
19 if got.Offset != want.Offset {
20 t.Errorf("%s: got offset = %d; want %d", msg, got.Offset, want.Offset)
21 }
22 if got.Line != want.Line {
23 t.Errorf("%s: got line = %d; want %d", msg, got.Line, want.Line)
24 }
25 if got.Column != want.Column {
26 t.Errorf("%s: got column = %d; want %d", msg, got.Column, want.Column)
27 }
28 }
29
30 func TestNoPos(t *testing.T) {
31 if NoPos.IsValid() {
32 t.Errorf("NoPos should not be valid")
33 }
34 var fset *FileSet
35 checkPos(t, "nil NoPos", fset.Position(NoPos), Position{})
36 fset = NewFileSet()
37 checkPos(t, "fset NoPos", fset.Position(NoPos), Position{})
38 }
39
40 var tests = []struct {
41 filename string
42 source []byte
43 size int
44 lines []int
45 }{
46 {"a", []byte{}, 0, []int{}},
47 {"b", []byte("01234"), 5, []int{0}},
48 {"c", []byte("\n\n\n\n\n\n\n\n\n"), 9, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}},
49 {"d", nil, 100, []int{0, 5, 10, 20, 30, 70, 71, 72, 80, 85, 90, 99}},
50 {"e", nil, 777, []int{0, 80, 100, 120, 130, 180, 267, 455, 500, 567, 620}},
51 {"f", []byte("package p\n\nimport \"fmt\""), 23, []int{0, 10, 11}},
52 {"g", []byte("package p\n\nimport \"fmt\"\n"), 24, []int{0, 10, 11}},
53 {"h", []byte("package p\n\nimport \"fmt\"\n "), 25, []int{0, 10, 11, 24}},
54 }
55
56 func linecol(lines []int, offs int) (int, int) {
57 prevLineOffs := 0
58 for line, lineOffs := range lines {
59 if offs < lineOffs {
60 return line, offs - prevLineOffs + 1
61 }
62 prevLineOffs = lineOffs
63 }
64 return len(lines), offs - prevLineOffs + 1
65 }
66
67 func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) {
68 for offs := 0; offs < f.Size(); offs++ {
69 p := f.Pos(offs)
70 offs2 := f.Offset(p)
71 if offs2 != offs {
72 t.Errorf("%s, Offset: got offset %d; want %d", f.Name(), offs2, offs)
73 }
74 line, col := linecol(lines, offs)
75 msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
76 checkPos(t, msg, f.Position(f.Pos(offs)), Position{f.Name(), offs, line, col})
77 checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col})
78 }
79 }
80
81 func makeTestSource(size int, lines []int) []byte {
82 src := make([]byte, size)
83 for _, offs := range lines {
84 if offs > 0 {
85 src[offs-1] = '\n'
86 }
87 }
88 return src
89 }
90
91 func TestPositions(t *testing.T) {
92 const delta = 7
93 fset := NewFileSet()
94 for _, test := range tests {
95
96 if test.source != nil && len(test.source) != test.size {
97 t.Errorf("%s: inconsistent test case: got file size %d; want %d", test.filename, len(test.source), test.size)
98 }
99
100
101 f := fset.AddFile(test.filename, fset.Base()+delta, test.size)
102 if f.Name() != test.filename {
103 t.Errorf("got filename %q; want %q", f.Name(), test.filename)
104 }
105 if f.Size() != test.size {
106 t.Errorf("%s: got file size %d; want %d", f.Name(), f.Size(), test.size)
107 }
108 if fset.File(f.Pos(0)) != f {
109 t.Errorf("%s: f.Pos(0) was not found in f", f.Name())
110 }
111
112
113 for i, offset := range test.lines {
114 f.AddLine(offset)
115 if f.LineCount() != i+1 {
116 t.Errorf("%s, AddLine: got line count %d; want %d", f.Name(), f.LineCount(), i+1)
117 }
118
119 f.AddLine(offset)
120 if f.LineCount() != i+1 {
121 t.Errorf("%s, AddLine: got unchanged line count %d; want %d", f.Name(), f.LineCount(), i+1)
122 }
123 verifyPositions(t, fset, f, test.lines[0:i+1])
124 }
125
126
127 if ok := f.SetLines(test.lines); !ok {
128 t.Errorf("%s: SetLines failed", f.Name())
129 }
130 if f.LineCount() != len(test.lines) {
131 t.Errorf("%s, SetLines: got line count %d; want %d", f.Name(), f.LineCount(), len(test.lines))
132 }
133 verifyPositions(t, fset, f, test.lines)
134
135
136 src := test.source
137 if src == nil {
138
139 src = makeTestSource(test.size, test.lines)
140 }
141 f.SetLinesForContent(src)
142 if f.LineCount() != len(test.lines) {
143 t.Errorf("%s, SetLinesForContent: got line count %d; want %d", f.Name(), f.LineCount(), len(test.lines))
144 }
145 verifyPositions(t, fset, f, test.lines)
146 }
147 }
148
149 func TestLineInfo(t *testing.T) {
150 fset := NewFileSet()
151 f := fset.AddFile("foo", fset.Base(), 500)
152 lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401}
153
154 for _, offs := range lines {
155 f.AddLine(offs)
156 f.AddLineInfo(offs, "bar", 42)
157 }
158
159 for offs := 0; offs <= f.Size(); offs++ {
160 p := f.Pos(offs)
161 _, col := linecol(lines, offs)
162 msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
163 checkPos(t, msg, f.Position(f.Pos(offs)), Position{"bar", offs, 42, col})
164 checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col})
165 }
166 }
167
168 func TestFiles(t *testing.T) {
169 fset := NewFileSet()
170 for i, test := range tests {
171 base := fset.Base()
172 if i%2 == 1 {
173
174
175 base = -1
176 }
177 fset.AddFile(test.filename, base, test.size)
178 j := 0
179 fset.Iterate(func(f *File) bool {
180 if f.Name() != tests[j].filename {
181 t.Errorf("got filename = %s; want %s", f.Name(), tests[j].filename)
182 }
183 j++
184 return true
185 })
186 if j != i+1 {
187 t.Errorf("got %d files; want %d", j, i+1)
188 }
189 }
190 }
191
192
193 func TestFileSetPastEnd(t *testing.T) {
194 fset := NewFileSet()
195 for _, test := range tests {
196 fset.AddFile(test.filename, fset.Base(), test.size)
197 }
198 if f := fset.File(Pos(fset.Base())); f != nil {
199 t.Errorf("got %v, want nil", f)
200 }
201 }
202
203 func TestFileSetCacheUnlikely(t *testing.T) {
204 fset := NewFileSet()
205 offsets := make(map[string]int)
206 for _, test := range tests {
207 offsets[test.filename] = fset.Base()
208 fset.AddFile(test.filename, fset.Base(), test.size)
209 }
210 for file, pos := range offsets {
211 f := fset.File(Pos(pos))
212 if f.Name() != file {
213 t.Errorf("got %q at position %d, want %q", f.Name(), pos, file)
214 }
215 }
216 }
217
218
219
220 func TestFileSetRace(t *testing.T) {
221 fset := NewFileSet()
222 for i := 0; i < 100; i++ {
223 fset.AddFile(fmt.Sprintf("file-%d", i), fset.Base(), 1031)
224 }
225 max := int32(fset.Base())
226 var stop sync.WaitGroup
227 r := rand.New(rand.NewSource(7))
228 for i := 0; i < 2; i++ {
229 r := rand.New(rand.NewSource(r.Int63()))
230 stop.Add(1)
231 go func() {
232 for i := 0; i < 1000; i++ {
233 fset.Position(Pos(r.Int31n(max)))
234 }
235 stop.Done()
236 }()
237 }
238 stop.Wait()
239 }
240
241
242
243 func TestFileSetRace2(t *testing.T) {
244 const N = 1e3
245 var (
246 fset = NewFileSet()
247 file = fset.AddFile("", -1, N)
248 ch = make(chan int, 2)
249 )
250
251 go func() {
252 for i := 0; i < N; i++ {
253 file.AddLine(i)
254 }
255 ch <- 1
256 }()
257
258 go func() {
259 pos := file.Pos(0)
260 for i := 0; i < N; i++ {
261 fset.PositionFor(pos, false)
262 }
263 ch <- 1
264 }()
265
266 <-ch
267 <-ch
268 }
269
270 func TestPositionFor(t *testing.T) {
271 src := []byte(`
272 foo
273 b
274 ar
275 //line :100
276 foobar
277 //line bar:3
278 done
279 `)
280
281 const filename = "foo"
282 fset := NewFileSet()
283 f := fset.AddFile(filename, fset.Base(), len(src))
284 f.SetLinesForContent(src)
285
286
287 for i, offs := range f.lines {
288 got1 := f.PositionFor(f.Pos(offs), false)
289 got2 := f.PositionFor(f.Pos(offs), true)
290 got3 := f.Position(f.Pos(offs))
291 want := Position{filename, offs, i + 1, 1}
292 checkPos(t, "1. PositionFor unadjusted", got1, want)
293 checkPos(t, "1. PositionFor adjusted", got2, want)
294 checkPos(t, "1. Position", got3, want)
295 }
296
297
298 const l1, l2 = 5, 7
299 f.AddLineInfo(f.lines[l1-1], "", 100)
300 f.AddLineInfo(f.lines[l2-1], "bar", 3)
301
302
303 for i, offs := range f.lines {
304 got1 := f.PositionFor(f.Pos(offs), false)
305 want := Position{filename, offs, i + 1, 1}
306 checkPos(t, "2. PositionFor unadjusted", got1, want)
307 }
308
309
310 for i, offs := range f.lines {
311 got2 := f.PositionFor(f.Pos(offs), true)
312 got3 := f.Position(f.Pos(offs))
313 want := Position{filename, offs, i + 1, 1}
314
315 line := want.Line
316 if i+1 >= l1 {
317 want.Filename = ""
318 want.Line = line - l1 + 100
319 }
320 if i+1 >= l2 {
321 want.Filename = "bar"
322 want.Line = line - l2 + 3
323 }
324 checkPos(t, "3. PositionFor adjusted", got2, want)
325 checkPos(t, "3. Position", got3, want)
326 }
327 }
328
329 func TestLineStart(t *testing.T) {
330 const src = "one\ntwo\nthree\n"
331 fset := NewFileSet()
332 f := fset.AddFile("input", -1, len(src))
333 f.SetLinesForContent([]byte(src))
334
335 for line := 1; line <= 3; line++ {
336 pos := f.LineStart(line)
337 position := fset.Position(pos)
338 if position.Line != line || position.Column != 1 {
339 t.Errorf("LineStart(%d) returned wrong pos %d: %s", line, pos, position)
340 }
341 }
342 }
343
344 func TestRemoveFile(t *testing.T) {
345 contentA := []byte("this\nis\nfileA")
346 contentB := []byte("this\nis\nfileB")
347 fset := NewFileSet()
348 a := fset.AddFile("fileA", -1, len(contentA))
349 a.SetLinesForContent(contentA)
350 b := fset.AddFile("fileB", -1, len(contentB))
351 b.SetLinesForContent(contentB)
352
353 checkPos := func(pos Pos, want string) {
354 if got := fset.Position(pos).String(); got != want {
355 t.Errorf("Position(%d) = %s, want %s", pos, got, want)
356 }
357 }
358 checkNumFiles := func(want int) {
359 got := 0
360 fset.Iterate(func(*File) bool { got++; return true })
361 if got != want {
362 t.Errorf("Iterate called %d times, want %d", got, want)
363 }
364 }
365
366 apos3 := a.Pos(3)
367 bpos3 := b.Pos(3)
368 checkPos(apos3, "fileA:1:4")
369 checkPos(bpos3, "fileB:1:4")
370 checkNumFiles(2)
371
372
373 fset.RemoveFile(a)
374 checkPos(apos3, "-")
375 checkPos(bpos3, "fileB:1:4")
376 checkNumFiles(1)
377
378
379 fset.RemoveFile(a)
380 checkPos(apos3, "-")
381 checkPos(bpos3, "fileB:1:4")
382 checkNumFiles(1)
383 }
384
385 func TestFileAddLineColumnInfo(t *testing.T) {
386 const (
387 filename = "test.go"
388 filesize = 100
389 )
390
391 tests := []struct {
392 name string
393 infos []lineInfo
394 want []lineInfo
395 }{
396 {
397 name: "normal",
398 infos: []lineInfo{
399 {Offset: 10, Filename: filename, Line: 2, Column: 1},
400 {Offset: 50, Filename: filename, Line: 3, Column: 1},
401 {Offset: 80, Filename: filename, Line: 4, Column: 2},
402 },
403 want: []lineInfo{
404 {Offset: 10, Filename: filename, Line: 2, Column: 1},
405 {Offset: 50, Filename: filename, Line: 3, Column: 1},
406 {Offset: 80, Filename: filename, Line: 4, Column: 2},
407 },
408 },
409 {
410 name: "offset1 == file size",
411 infos: []lineInfo{
412 {Offset: filesize, Filename: filename, Line: 2, Column: 1},
413 },
414 want: nil,
415 },
416 {
417 name: "offset1 > file size",
418 infos: []lineInfo{
419 {Offset: filesize + 1, Filename: filename, Line: 2, Column: 1},
420 },
421 want: nil,
422 },
423 {
424 name: "offset2 == file size",
425 infos: []lineInfo{
426 {Offset: 10, Filename: filename, Line: 2, Column: 1},
427 {Offset: filesize, Filename: filename, Line: 3, Column: 1},
428 },
429 want: []lineInfo{
430 {Offset: 10, Filename: filename, Line: 2, Column: 1},
431 },
432 },
433 {
434 name: "offset2 > file size",
435 infos: []lineInfo{
436 {Offset: 10, Filename: filename, Line: 2, Column: 1},
437 {Offset: filesize + 1, Filename: filename, Line: 3, Column: 1},
438 },
439 want: []lineInfo{
440 {Offset: 10, Filename: filename, Line: 2, Column: 1},
441 },
442 },
443 {
444 name: "offset2 == offset1",
445 infos: []lineInfo{
446 {Offset: 10, Filename: filename, Line: 2, Column: 1},
447 {Offset: 10, Filename: filename, Line: 3, Column: 1},
448 },
449 want: []lineInfo{
450 {Offset: 10, Filename: filename, Line: 2, Column: 1},
451 },
452 },
453 {
454 name: "offset2 < offset1",
455 infos: []lineInfo{
456 {Offset: 10, Filename: filename, Line: 2, Column: 1},
457 {Offset: 9, Filename: filename, Line: 3, Column: 1},
458 },
459 want: []lineInfo{
460 {Offset: 10, Filename: filename, Line: 2, Column: 1},
461 },
462 },
463 }
464
465 for _, test := range tests {
466 t.Run(test.name, func(t *testing.T) {
467 fs := NewFileSet()
468 f := fs.AddFile(filename, -1, filesize)
469 for _, info := range test.infos {
470 f.AddLineColumnInfo(info.Offset, info.Filename, info.Line, info.Column)
471 }
472 if !reflect.DeepEqual(f.infos, test.want) {
473 t.Errorf("\ngot %+v, \nwant %+v", f.infos, test.want)
474 }
475 })
476 }
477 }
478
View as plain text