1
0
mirror of https://github.com/golang/go synced 2024-11-25 02:07:58 -07:00

position.go: test cases for token.Pos

- adjustments to position.go due to changed sort.Search semantics
- various minor fixes

R=rsc
CC=golang-dev, r
https://golang.org/cl/3079041
This commit is contained in:
Robert Griesemer 2010-11-17 12:17:40 -08:00
parent e38b7f4953
commit ac966ac706
2 changed files with 167 additions and 10 deletions

View File

@ -94,7 +94,14 @@ func (p Pos) IsValid() bool {
func searchFiles(a []*File, x int) int { func searchFiles(a []*File, x int) int {
return sort.Search(len(a), func(i int) bool { return a[i].base <= x }) i := sort.Search(len(a), func(i int) bool { return a[i].base < x })
// TODO(gri) The code below is really unfortunate. With the old
// semantics of sort.Search, it was possible to simply
// return i! Need to rethink the Search API.
if i == 0 || i < len(a) && a[i].base == x {
return i
}
return i - 1
} }
@ -150,9 +157,9 @@ func (f *File) AddLineInfo(offset int, filename string, line int) {
// //
type File struct { type File struct {
set *FileSet set *FileSet
base int name string // file name as provided to AddFile
size int base int // Pos value range for this file is [base...base+size]
name string size int // file size as provided to AddFile
// lines and infos are protected by set.mutex // lines and infos are protected by set.mutex
lines []int lines []int
@ -187,7 +194,7 @@ func (f *File) LineCount() int {
// //
func (f *File) AddLine(offset int) { func (f *File) AddLine(offset int) {
f.set.mutex.Lock() f.set.mutex.Lock()
if i := len(f.lines); i == 0 || f.lines[i-1] < offset && offset <= f.size { if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset <= f.size {
f.lines = append(f.lines, offset) f.lines = append(f.lines, offset)
} }
f.set.mutex.Unlock() f.set.mutex.Unlock()
@ -252,12 +259,26 @@ func (f *File) Position(offset int) Position {
func searchUints(a []int, x int) int { func searchUints(a []int, x int) int {
return sort.Search(len(a), func(i int) bool { return a[i] <= x }) i := sort.Search(len(a), func(i int) bool { return a[i] < x })
// TODO(gri) The code below is really unfortunate. With the old
// semantics of sort.Search, it was possible to simply
// return i! Need to rethink the Search API.
if i == 0 || i < len(a) && a[i] == x {
return i
}
return i - 1
} }
func searchLineInfos(a []lineInfo, x int) int { func searchLineInfos(a []lineInfo, x int) int {
return sort.Search(len(a), func(i int) bool { return a[i].offset <= x }) i := sort.Search(len(a), func(i int) bool { return a[i].offset < x })
// TODO(gri) The code below is really unfortunate. With the old
// semantics of sort.Search, it was possible to simply
// return i! Need to rethink the Search API.
if i == 0 || i < len(a) && a[i].offset == x {
return i
}
return i - 1
} }
@ -298,18 +319,18 @@ func NewFileSet() *FileSet {
// AddFile adds a new file with a given filename and file size to a the // AddFile adds a new file with a given filename and file size to a the
// file set s and returns the file. Multiple files may have the same name. // file set s and returns the file. Multiple files may have the same name.
// File.Pos may be used to create file-specifiction position values from a // File.Pos may be used to create file-specific position values from a
// file offset. // file offset.
// //
func (s *FileSet) AddFile(filename string, size int) *File { func (s *FileSet) AddFile(filename string, size int) *File {
s.mutex.Lock() s.mutex.Lock()
f := &File{s, s.base, size, filename, []int{0}, nil} defer s.mutex.Unlock()
f := &File{s, filename, s.base, size, []int{0}, nil}
s.base += size + 1 // +1 because EOF also has a position s.base += size + 1 // +1 because EOF also has a position
if s.base < 0 { if s.base < 0 {
panic("token.Pos offset overflow (> 2G of source code in file set)") panic("token.Pos offset overflow (> 2G of source code in file set)")
} }
s.index[f] = len(s.files) s.index[f] = len(s.files)
s.files = append(s.files, f) s.files = append(s.files, f)
s.mutex.Unlock()
return f return f
} }

View File

@ -0,0 +1,136 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package token
import (
"fmt"
"testing"
)
func checkPos(t *testing.T, msg string, p, q Position) {
if p.Filename != q.Filename {
t.Errorf("%s: expected filename = %q; got %q", msg, q.Filename, p.Filename)
}
if p.Offset != q.Offset {
t.Errorf("%s: expected offset = %d; got %d", msg, q.Offset, p.Offset)
}
if p.Line != q.Line {
t.Errorf("%s: expected line = %d; got %d", msg, q.Line, p.Line)
}
if p.Column != q.Column {
t.Errorf("%s: expected column = %d; got %d", msg, q.Column, p.Column)
}
}
func TestNoPos(t *testing.T) {
if NoPos.IsValid() {
t.Errorf("NoPos should not be valid")
}
var fset *FileSet
checkPos(t, "nil NoPos", fset.Position(NoPos), Position{})
fset = NewFileSet()
checkPos(t, "fset NoPos", fset.Position(NoPos), Position{})
}
var tests = []struct {
filename string
size int
lines []int
}{
{"a", 0, []int{}},
{"b", 5, []int{0}},
{"c", 10, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}},
{"d", 100, []int{0, 5, 10, 20, 30, 70, 71, 72, 80, 85, 90, 99}},
{"e", 777, []int{0, 80, 100, 120, 130, 180, 267, 455, 500, 567, 620}},
}
func linecol(lines []int, offs int) (int, int) {
prevLineOffs := 0
for line, lineOffs := range lines {
if offs < lineOffs {
return line, offs - prevLineOffs + 1
}
prevLineOffs = lineOffs
}
return len(lines), offs - prevLineOffs + 1
}
func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) {
for offs := 0; offs < f.Size(); offs++ {
p := f.Pos(offs)
offs2 := f.Offset(p)
if offs2 != offs {
t.Errorf("%s, Offset: expected offset %d; got %d", f.Name(), offs, offs2)
}
line, col := linecol(lines, offs)
msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
checkPos(t, msg, f.Position(offs), Position{f.Name(), offs, line, col})
checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col})
}
}
func TestPositions(t *testing.T) {
fset := NewFileSet()
for _, test := range tests {
// add file and verify name and size
f := fset.AddFile(test.filename, test.size)
if f.Name() != test.filename {
t.Errorf("expected filename %q; got %q", test.filename, f.Name())
}
if f.Size() != test.size {
t.Errorf("%s: expected file size %d; got %d", f.Name(), test.size, f.Size())
}
// add lines individually and verify all positions
for i, offset := range test.lines {
f.AddLine(offset)
if f.LineCount() != i+1 {
t.Errorf("%s, AddLine: expected line count %d; got %d", f.Name(), i+1, f.LineCount())
}
// adding the same offset again should be ignored
f.AddLine(offset)
if f.LineCount() != i+1 {
t.Errorf("%s, AddLine: expected unchanged line count %d; got %d", f.Name(), i+1, f.LineCount())
}
verifyPositions(t, fset, f, test.lines[0:i+1])
}
// add lines at once and verify all positions
ok := f.SetLines(test.lines)
if !ok {
t.Errorf("%s: SetLines failed", f.Name())
}
if f.LineCount() != len(test.lines) {
t.Errorf("%s, SetLines: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount())
}
verifyPositions(t, fset, f, test.lines)
}
}
func TestLineInfo(t *testing.T) {
fset := NewFileSet()
f := fset.AddFile("foo", 500)
lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401}
// add lines individually and provide alternative line information
for _, offs := range lines {
f.AddLine(offs)
f.AddLineInfo(offs, "bar", 42)
}
// verify positions for all offsets
for offs := 0; offs <= f.Size(); offs++ {
p := f.Pos(offs)
_, col := linecol(lines, offs)
msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p)
checkPos(t, msg, f.Position(offs), Position{"bar", offs, 42, col})
checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col})
}
}