github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/go/token/position_test.go (about) 1 // Copyright 2010 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 5 package token 6 7 import ( 8 "fmt" 9 "math/rand" 10 "sync" 11 "testing" 12 ) 13 14 func checkPos(t *testing.T, msg string, p, q Position) { 15 if p.Filename != q.Filename { 16 t.Errorf("%s: expected filename = %q; got %q", msg, q.Filename, p.Filename) 17 } 18 if p.Offset != q.Offset { 19 t.Errorf("%s: expected offset = %d; got %d", msg, q.Offset, p.Offset) 20 } 21 if p.Line != q.Line { 22 t.Errorf("%s: expected line = %d; got %d", msg, q.Line, p.Line) 23 } 24 if p.Column != q.Column { 25 t.Errorf("%s: expected column = %d; got %d", msg, q.Column, p.Column) 26 } 27 } 28 29 func TestNoPos(t *testing.T) { 30 if NoPos.IsValid() { 31 t.Errorf("NoPos should not be valid") 32 } 33 var fset *FileSet 34 checkPos(t, "nil NoPos", fset.Position(NoPos), Position{}) 35 fset = NewFileSet() 36 checkPos(t, "fset NoPos", fset.Position(NoPos), Position{}) 37 } 38 39 var tests = []struct { 40 filename string 41 source []byte // may be nil 42 size int 43 lines []int 44 }{ 45 {"a", []byte{}, 0, []int{}}, 46 {"b", []byte("01234"), 5, []int{0}}, 47 {"c", []byte("\n\n\n\n\n\n\n\n\n"), 9, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}}, 48 {"d", nil, 100, []int{0, 5, 10, 20, 30, 70, 71, 72, 80, 85, 90, 99}}, 49 {"e", nil, 777, []int{0, 80, 100, 120, 130, 180, 267, 455, 500, 567, 620}}, 50 {"f", []byte("package p\n\nimport \"fmt\""), 23, []int{0, 10, 11}}, 51 {"g", []byte("package p\n\nimport \"fmt\"\n"), 24, []int{0, 10, 11}}, 52 {"h", []byte("package p\n\nimport \"fmt\"\n "), 25, []int{0, 10, 11, 24}}, 53 } 54 55 func linecol(lines []int, offs int) (int, int) { 56 prevLineOffs := 0 57 for line, lineOffs := range lines { 58 if offs < lineOffs { 59 return line, offs - prevLineOffs + 1 60 } 61 prevLineOffs = lineOffs 62 } 63 return len(lines), offs - prevLineOffs + 1 64 } 65 66 func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) { 67 for offs := 0; offs < f.Size(); offs++ { 68 p := f.Pos(offs) 69 offs2 := f.Offset(p) 70 if offs2 != offs { 71 t.Errorf("%s, Offset: expected offset %d; got %d", f.Name(), offs, offs2) 72 } 73 line, col := linecol(lines, offs) 74 msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) 75 checkPos(t, msg, f.Position(f.Pos(offs)), Position{f.Name(), offs, line, col}) 76 checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col}) 77 } 78 } 79 80 func makeTestSource(size int, lines []int) []byte { 81 src := make([]byte, size) 82 for _, offs := range lines { 83 if offs > 0 { 84 src[offs-1] = '\n' 85 } 86 } 87 return src 88 } 89 90 func TestPositions(t *testing.T) { 91 const delta = 7 // a non-zero base offset increment 92 fset := NewFileSet() 93 for _, test := range tests { 94 // verify consistency of test case 95 if test.source != nil && len(test.source) != test.size { 96 t.Errorf("%s: inconsistent test case: expected file size %d; got %d", test.filename, test.size, len(test.source)) 97 } 98 99 // add file and verify name and size 100 f := fset.AddFile(test.filename, fset.Base()+delta, test.size) 101 if f.Name() != test.filename { 102 t.Errorf("expected filename %q; got %q", test.filename, f.Name()) 103 } 104 if f.Size() != test.size { 105 t.Errorf("%s: expected file size %d; got %d", f.Name(), test.size, f.Size()) 106 } 107 if fset.File(f.Pos(0)) != f { 108 t.Errorf("%s: f.Pos(0) was not found in f", f.Name()) 109 } 110 111 // add lines individually and verify all positions 112 for i, offset := range test.lines { 113 f.AddLine(offset) 114 if f.LineCount() != i+1 { 115 t.Errorf("%s, AddLine: expected line count %d; got %d", f.Name(), i+1, f.LineCount()) 116 } 117 // adding the same offset again should be ignored 118 f.AddLine(offset) 119 if f.LineCount() != i+1 { 120 t.Errorf("%s, AddLine: expected unchanged line count %d; got %d", f.Name(), i+1, f.LineCount()) 121 } 122 verifyPositions(t, fset, f, test.lines[0:i+1]) 123 } 124 125 // add lines with SetLines and verify all positions 126 if ok := f.SetLines(test.lines); !ok { 127 t.Errorf("%s: SetLines failed", f.Name()) 128 } 129 if f.LineCount() != len(test.lines) { 130 t.Errorf("%s, SetLines: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount()) 131 } 132 verifyPositions(t, fset, f, test.lines) 133 134 // add lines with SetLinesForContent and verify all positions 135 src := test.source 136 if src == nil { 137 // no test source available - create one from scratch 138 src = makeTestSource(test.size, test.lines) 139 } 140 f.SetLinesForContent(src) 141 if f.LineCount() != len(test.lines) { 142 t.Errorf("%s, SetLinesForContent: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount()) 143 } 144 verifyPositions(t, fset, f, test.lines) 145 } 146 } 147 148 func TestLineInfo(t *testing.T) { 149 fset := NewFileSet() 150 f := fset.AddFile("foo", fset.Base(), 500) 151 lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401} 152 // add lines individually and provide alternative line information 153 for _, offs := range lines { 154 f.AddLine(offs) 155 f.AddLineInfo(offs, "bar", 42) 156 } 157 // verify positions for all offsets 158 for offs := 0; offs <= f.Size(); offs++ { 159 p := f.Pos(offs) 160 _, col := linecol(lines, offs) 161 msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) 162 checkPos(t, msg, f.Position(f.Pos(offs)), Position{"bar", offs, 42, col}) 163 checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col}) 164 } 165 } 166 167 func TestFiles(t *testing.T) { 168 fset := NewFileSet() 169 for i, test := range tests { 170 base := fset.Base() 171 if i%2 == 1 { 172 // Setting a negative base is equivalent to 173 // fset.Base(), so test some of each. 174 base = -1 175 } 176 fset.AddFile(test.filename, base, test.size) 177 j := 0 178 fset.Iterate(func(f *File) bool { 179 if f.Name() != tests[j].filename { 180 t.Errorf("expected filename = %s; got %s", tests[j].filename, f.Name()) 181 } 182 j++ 183 return true 184 }) 185 if j != i+1 { 186 t.Errorf("expected %d files; got %d", i+1, j) 187 } 188 } 189 } 190 191 // FileSet.File should return nil if Pos is past the end of the FileSet. 192 func TestFileSetPastEnd(t *testing.T) { 193 fset := NewFileSet() 194 for _, test := range tests { 195 fset.AddFile(test.filename, fset.Base(), test.size) 196 } 197 if f := fset.File(Pos(fset.Base())); f != nil { 198 t.Errorf("expected nil, got %v", f) 199 } 200 } 201 202 func TestFileSetCacheUnlikely(t *testing.T) { 203 fset := NewFileSet() 204 offsets := make(map[string]int) 205 for _, test := range tests { 206 offsets[test.filename] = fset.Base() 207 fset.AddFile(test.filename, fset.Base(), test.size) 208 } 209 for file, pos := range offsets { 210 f := fset.File(Pos(pos)) 211 if f.Name() != file { 212 t.Errorf("expecting %q at position %d, got %q", file, pos, f.Name()) 213 } 214 } 215 } 216 217 // issue 4345. Test concurrent use of FileSet.Pos does not trigger a 218 // race in the FileSet position cache. 219 func TestFileSetRace(t *testing.T) { 220 fset := NewFileSet() 221 for i := 0; i < 100; i++ { 222 fset.AddFile(fmt.Sprintf("file-%d", i), fset.Base(), 1031) 223 } 224 max := int32(fset.Base()) 225 var stop sync.WaitGroup 226 r := rand.New(rand.NewSource(7)) 227 for i := 0; i < 2; i++ { 228 r := rand.New(rand.NewSource(r.Int63())) 229 stop.Add(1) 230 go func() { 231 for i := 0; i < 1000; i++ { 232 fset.Position(Pos(r.Int31n(max))) 233 } 234 stop.Done() 235 }() 236 } 237 stop.Wait() 238 }