RangeScanner has an interface similar to bufio.Scanner for partitioning a buffer into tokens, but it returns the hcl.Range of each token along with that token so that the caller can see where the token fits in relation to the entire source file. The main intended use-case for this is to partition a source file into lines for the purpose of printing a source code snippet in diagnostic output. Having the source location information is important in that case to recognize which lines belong to the subject and context of each diagnostic.
194 lines
4.0 KiB
Go
194 lines
4.0 KiB
Go
package hcl
|
|
|
|
import (
|
|
"bufio"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
)
|
|
|
|
func TestPosScanner(t *testing.T) {
|
|
tests := map[string]struct {
|
|
Input string
|
|
Want []Range
|
|
WantToks [][]byte
|
|
}{
|
|
"empty": {
|
|
"",
|
|
[]Range{},
|
|
[][]byte{},
|
|
},
|
|
"single line": {
|
|
"hello",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 5, Line: 1, Column: 6},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("hello"),
|
|
},
|
|
},
|
|
"single line with trailing UNIX newline": {
|
|
"hello\n",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 5, Line: 1, Column: 6},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("hello"),
|
|
},
|
|
},
|
|
"single line with trailing Windows newline": {
|
|
"hello\r\n",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 5, Line: 1, Column: 6},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("hello"),
|
|
},
|
|
},
|
|
"two lines with UNIX newline": {
|
|
"hello\nworld",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 5, Line: 1, Column: 6},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 6, Line: 2, Column: 1},
|
|
End: Pos{Byte: 11, Line: 2, Column: 6},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("hello"),
|
|
[]byte("world"),
|
|
},
|
|
},
|
|
"two lines with Windows newline": {
|
|
"hello\r\nworld",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 5, Line: 1, Column: 6},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 7, Line: 2, Column: 1},
|
|
End: Pos{Byte: 12, Line: 2, Column: 6},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("hello"),
|
|
[]byte("world"),
|
|
},
|
|
},
|
|
"blank line with UNIX newlines": {
|
|
"hello\n\nworld",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 5, Line: 1, Column: 6},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 6, Line: 2, Column: 1},
|
|
End: Pos{Byte: 6, Line: 2, Column: 1},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 7, Line: 3, Column: 1},
|
|
End: Pos{Byte: 12, Line: 3, Column: 6},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("hello"),
|
|
[]byte(""),
|
|
[]byte("world"),
|
|
},
|
|
},
|
|
"blank line with Windows newlines": {
|
|
"hello\r\n\r\nworld",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 5, Line: 1, Column: 6},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 7, Line: 2, Column: 1},
|
|
End: Pos{Byte: 7, Line: 2, Column: 1},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 9, Line: 3, Column: 1},
|
|
End: Pos{Byte: 14, Line: 3, Column: 6},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("hello"),
|
|
[]byte(""),
|
|
[]byte("world"),
|
|
},
|
|
},
|
|
"two lines with combiner and UNIX newline": {
|
|
"foo \U0001f469\U0001f3ff bar\nbaz",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 16, Line: 1, Column: 10},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 17, Line: 2, Column: 1},
|
|
End: Pos{Byte: 20, Line: 2, Column: 4},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("foo \U0001f469\U0001f3ff bar"),
|
|
[]byte("baz"),
|
|
},
|
|
},
|
|
"two lines with combiner and Windows newline": {
|
|
"foo \U0001f469\U0001f3ff bar\r\nbaz",
|
|
[]Range{
|
|
{
|
|
Start: Pos{Byte: 0, Line: 1, Column: 1},
|
|
End: Pos{Byte: 16, Line: 1, Column: 10},
|
|
},
|
|
{
|
|
Start: Pos{Byte: 18, Line: 2, Column: 1},
|
|
End: Pos{Byte: 21, Line: 2, Column: 4},
|
|
},
|
|
},
|
|
[][]byte{
|
|
[]byte("foo \U0001f469\U0001f3ff bar"),
|
|
[]byte("baz"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
src := []byte(test.Input)
|
|
sc := NewRangeScanner(src, "", bufio.ScanLines)
|
|
got := make([]Range, 0)
|
|
gotToks := make([][]byte, 0)
|
|
for sc.Scan() {
|
|
got = append(got, sc.Range())
|
|
gotToks = append(gotToks, sc.Bytes())
|
|
}
|
|
if sc.Err() != nil {
|
|
t.Fatalf("unexpected error: %s", sc.Err())
|
|
}
|
|
if !reflect.DeepEqual(got, test.Want) {
|
|
t.Errorf("incorrect ranges\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(test.Want))
|
|
}
|
|
if !reflect.DeepEqual(gotToks, test.WantToks) {
|
|
t.Errorf("incorrect tokens\ngot: %swant: %s", spew.Sdump(gotToks), spew.Sdump(test.WantToks))
|
|
}
|
|
})
|
|
}
|
|
}
|