1package parse
2
3import (
4	"bytes"
5	"fmt"
6	"strings"
7	"testing"
8
9	"github.com/tdewolff/test"
10)
11
12func TestPosition(t *testing.T) {
13	var newlineTests = []struct {
14		offset int
15		buf    string
16		line   int
17		col    int
18	}{
19		// \u2028, \u2029, and \u2318 are three bytes long
20		{0, "x", 1, 1},
21		{1, "xx", 1, 2},
22		{2, "x\nx", 2, 1},
23		{2, "\n\nx", 3, 1},
24		{3, "\nxxx", 2, 3},
25		{2, "\r\nx", 2, 1},
26		{1, "\rx", 2, 1},
27		{3, "\u2028x", 2, 1},
28		{3, "\u2029x", 2, 1},
29
30		// edge cases
31		{0, "", 1, 1},
32		{2, "x", 1, 2},
33		{0, "\nx", 1, 1},
34		{1, "\r\ny", 1, 1},
35		{-1, "x", 1, 1},
36		{0, "\x00a", 1, 1},
37		{2, "a\x00\n", 1, 3},
38
39		// unicode
40		{1, "x\u2028x", 1, 2},
41		{2, "x\u2028x", 1, 2},
42		{3, "x\u2028x", 1, 2},
43		{0, "x\u2318x", 1, 1},
44		{1, "x\u2318x", 1, 2},
45		{2, "x\u2318x", 1, 2},
46		{3, "x\u2318x", 1, 2},
47		{4, "x\u2318x", 1, 3},
48	}
49	for _, tt := range newlineTests {
50		t.Run(fmt.Sprint(tt.buf, " ", tt.offset), func(t *testing.T) {
51			r := bytes.NewBufferString(tt.buf)
52			line, col, _ := Position(r, tt.offset)
53			test.T(t, line, tt.line, "line")
54			test.T(t, col, tt.col, "column")
55		})
56	}
57}
58
59func TestPositionContext(t *testing.T) {
60	var newlineTests = []struct {
61		offset  int
62		buf     string
63		context string
64	}{
65		{10, "0123456789@123456789012345678901234567890123456789012345678901234567890123456789", "0123456789@1234567890123456789012345678901234567890123456..."}, // 80 characters -> 60 characters
66		{40, "0123456789012345678901234567890123456789@123456789012345678901234567890123456789", "...01234567890123456789@12345678901234567890..."},
67		{60, "012345678901234567890123456789012345678901234567890123456789@12345678901234567890", "...78901234567890123456789@12345678901234567890"},
68		{60, "012345678901234567890123456789012345678901234567890123456789@12345678901234567890123", "...01234567890123456789@12345678901234567890123"},
69		{60, "012345678901234567890123456789012345678901234567890123456789@123456789012345678901234", "...01234567890123456789@12345678901234567890..."},
70		{60, "0123456789012345678901234567890123456789ÎÎÎÎÎÎÎÎÎÎ@123456789012345678901234567890", "...0123456789ÎÎÎÎÎÎÎÎÎÎ@12345678901234567890..."},
71		{60, "012345678901234567890123456789012345678912456780123456789@12345678901234567890", "...789·12·45678·0123456789@12345678901234567890"},
72	}
73	for _, tt := range newlineTests {
74		t.Run(fmt.Sprint(tt.buf, " ", tt.offset), func(t *testing.T) {
75			r := bytes.NewBufferString(tt.buf)
76			_, _, context := Position(r, tt.offset)
77			i := strings.IndexByte(context, '\n')
78			pointer := context[i+1:]
79			context = context[:i]
80			test.T(t, context[7:], tt.context)
81
82			// check if @ and ^ are at the same position
83			j := strings.IndexByte(context, '@')
84			k := strings.IndexByte(pointer, '^')
85			test.T(t, len([]rune(pointer[:k])), len([]rune(context[:j])))
86		})
87	}
88}
89