1// Copyright 2019 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 5package span_test 6 7import ( 8 "strings" 9 "testing" 10 11 "golang.org/x/tools/internal/span" 12) 13 14// The funny character below is 4 bytes long in UTF-8; two UTF-16 code points 15var funnyString = []byte("23\n45") 16 17var toUTF16Tests = []struct { 18 scenario string 19 input []byte 20 line int // 1-indexed count 21 col int // 1-indexed byte position in line 22 offset int // 0-indexed byte offset into input 23 resUTF16col int // 1-indexed UTF-16 col number 24 pre string // everything before the cursor on the line 25 post string // everything from the cursor onwards 26 err string // expected error string in call to ToUTF16Column 27 issue *bool 28}{ 29 { 30 scenario: "cursor missing content", 31 input: nil, 32 err: "ToUTF16Column: point is missing position", 33 }, 34 { 35 scenario: "cursor missing position", 36 input: funnyString, 37 line: -1, 38 col: -1, 39 err: "ToUTF16Column: point is missing position", 40 }, 41 { 42 scenario: "cursor missing offset", 43 input: funnyString, 44 line: 1, 45 col: 1, 46 offset: -1, 47 err: "ToUTF16Column: point is missing offset", 48 }, 49 { 50 scenario: "zero length input; cursor at first col, first line", 51 input: []byte(""), 52 line: 1, 53 col: 1, 54 offset: 0, 55 resUTF16col: 1, 56 }, 57 { 58 scenario: "cursor before funny character; first line", 59 input: funnyString, 60 line: 1, 61 col: 1, 62 offset: 0, 63 resUTF16col: 1, 64 pre: "", 65 post: "23", 66 }, 67 { 68 scenario: "cursor after funny character; first line", 69 input: funnyString, 70 line: 1, 71 col: 5, // 4 + 1 (1-indexed) 72 offset: 4, 73 resUTF16col: 3, // 2 + 1 (1-indexed) 74 pre: "", 75 post: "23", 76 }, 77 { 78 scenario: "cursor after last character on first line", 79 input: funnyString, 80 line: 1, 81 col: 7, // 4 + 1 + 1 + 1 (1-indexed) 82 offset: 6, // 4 + 1 + 1 83 resUTF16col: 5, // 2 + 1 + 1 + 1 (1-indexed) 84 pre: "23", 85 post: "", 86 }, 87 { 88 scenario: "cursor before funny character; second line", 89 input: funnyString, 90 line: 2, 91 col: 1, 92 offset: 7, // length of first line 93 resUTF16col: 1, 94 pre: "", 95 post: "45", 96 }, 97 { 98 scenario: "cursor after funny character; second line", 99 input: funnyString, 100 line: 1, 101 col: 5, // 4 + 1 (1-indexed) 102 offset: 11, // 7 (length of first line) + 4 103 resUTF16col: 3, // 2 + 1 (1-indexed) 104 pre: "", 105 post: "45", 106 }, 107 { 108 scenario: "cursor after last character on second line", 109 input: funnyString, 110 line: 2, 111 col: 7, // 4 + 1 + 1 + 1 (1-indexed) 112 offset: 13, // 7 (length of first line) + 4 + 1 + 1 113 resUTF16col: 5, // 2 + 1 + 1 + 1 (1-indexed) 114 pre: "45", 115 post: "", 116 }, 117 { 118 scenario: "cursor beyond end of file", 119 input: funnyString, 120 line: 2, 121 col: 8, // 4 + 1 + 1 + 1 + 1 (1-indexed) 122 offset: 14, // 4 + 1 + 1 + 1 123 err: "ToUTF16Column: offsets 7-14 outside file contents (13)", 124 }, 125} 126 127var fromUTF16Tests = []struct { 128 scenario string 129 input []byte 130 line int // 1-indexed line number (isn't actually used) 131 offset int // 0-indexed byte offset to beginning of line 132 utf16col int // 1-indexed UTF-16 col number 133 resCol int // 1-indexed byte position in line 134 resOffset int // 0-indexed byte offset into input 135 pre string // everything before the cursor on the line 136 post string // everything from the cursor onwards 137 err string // expected error string in call to ToUTF16Column 138}{ 139 { 140 scenario: "zero length input; cursor at first col, first line", 141 input: []byte(""), 142 line: 1, 143 offset: 0, 144 utf16col: 1, 145 resCol: 1, 146 resOffset: 0, 147 pre: "", 148 post: "", 149 }, 150 { 151 scenario: "missing offset", 152 input: funnyString, 153 line: 1, 154 offset: -1, 155 err: "FromUTF16Column: point is missing offset", 156 }, 157 { 158 scenario: "cursor before funny character", 159 input: funnyString, 160 line: 1, 161 utf16col: 1, 162 resCol: 1, 163 resOffset: 0, 164 pre: "", 165 post: "23", 166 }, 167 { 168 scenario: "cursor after funny character", 169 input: funnyString, 170 line: 1, 171 utf16col: 3, 172 resCol: 5, 173 resOffset: 4, 174 pre: "", 175 post: "23", 176 }, 177 { 178 scenario: "cursor after last character on line", 179 input: funnyString, 180 line: 1, 181 utf16col: 5, 182 resCol: 7, 183 resOffset: 6, 184 pre: "23", 185 post: "", 186 }, 187 { 188 scenario: "cursor beyond last character on line", 189 input: funnyString, 190 line: 1, 191 offset: 0, 192 utf16col: 6, 193 resCol: 7, 194 resOffset: 6, 195 pre: "23", 196 post: "", 197 }, 198 { 199 scenario: "cursor before funny character; second line", 200 input: funnyString, 201 line: 2, 202 offset: 7, // length of first line 203 utf16col: 1, 204 resCol: 1, 205 resOffset: 7, 206 pre: "", 207 post: "45", 208 }, 209 { 210 scenario: "cursor after funny character; second line", 211 input: funnyString, 212 line: 2, 213 offset: 7, // length of first line 214 utf16col: 3, // 2 + 1 (1-indexed) 215 resCol: 5, // 4 + 1 (1-indexed) 216 resOffset: 11, // 7 (length of first line) + 4 217 pre: "", 218 post: "45", 219 }, 220 { 221 scenario: "cursor after last character on second line", 222 input: funnyString, 223 line: 2, 224 offset: 7, // length of first line 225 utf16col: 5, // 2 + 1 + 1 + 1 (1-indexed) 226 resCol: 7, // 4 + 1 + 1 + 1 (1-indexed) 227 resOffset: 13, // 7 (length of first line) + 4 + 1 + 1 228 pre: "45", 229 post: "", 230 }, 231 { 232 scenario: "cursor beyond end of file", 233 input: funnyString, 234 line: 2, 235 offset: 7, 236 utf16col: 6, // 2 + 1 + 1 + 1 + 1(1-indexed) 237 resCol: 8, // 4 + 1 + 1 + 1 + 1 (1-indexed) 238 resOffset: 14, // 7 (length of first line) + 4 + 1 + 1 + 1 239 err: "FromUTF16Column: chr goes beyond the content", 240 }, 241 { 242 scenario: "offset beyond end of file", 243 input: funnyString, 244 line: 2, 245 offset: 14, 246 utf16col: 2, 247 err: "FromUTF16Column: offset (14) greater than length of content (13)", 248 }, 249} 250 251func TestToUTF16(t *testing.T) { 252 for _, e := range toUTF16Tests { 253 t.Run(e.scenario, func(t *testing.T) { 254 if e.issue != nil && !*e.issue { 255 t.Skip("expected to fail") 256 } 257 p := span.NewPoint(e.line, e.col, e.offset) 258 got, err := span.ToUTF16Column(p, e.input) 259 if err != nil { 260 if err.Error() != e.err { 261 t.Fatalf("expected error %v; got %v", e.err, err) 262 } 263 return 264 } 265 if e.err != "" { 266 t.Fatalf("unexpected success; wanted %v", e.err) 267 } 268 if got != e.resUTF16col { 269 t.Fatalf("expected result %v; got %v", e.resUTF16col, got) 270 } 271 pre, post := getPrePost(e.input, p.Offset()) 272 if string(pre) != e.pre { 273 t.Fatalf("expected #%d pre %q; got %q", p.Offset(), e.pre, pre) 274 } 275 if string(post) != e.post { 276 t.Fatalf("expected #%d, post %q; got %q", p.Offset(), e.post, post) 277 } 278 }) 279 } 280} 281 282func TestFromUTF16(t *testing.T) { 283 for _, e := range fromUTF16Tests { 284 t.Run(e.scenario, func(t *testing.T) { 285 p := span.NewPoint(e.line, 1, e.offset) 286 p, err := span.FromUTF16Column(p, e.utf16col, []byte(e.input)) 287 if err != nil { 288 if err.Error() != e.err { 289 t.Fatalf("expected error %v; got %v", e.err, err) 290 } 291 return 292 } 293 if e.err != "" { 294 t.Fatalf("unexpected success; wanted %v", e.err) 295 } 296 if p.Column() != e.resCol { 297 t.Fatalf("expected resulting col %v; got %v", e.resCol, p.Column()) 298 } 299 if p.Offset() != e.resOffset { 300 t.Fatalf("expected resulting offset %v; got %v", e.resOffset, p.Offset()) 301 } 302 pre, post := getPrePost(e.input, p.Offset()) 303 if string(pre) != e.pre { 304 t.Fatalf("expected #%d pre %q; got %q", p.Offset(), e.pre, pre) 305 } 306 if string(post) != e.post { 307 t.Fatalf("expected #%d post %q; got %q", p.Offset(), e.post, post) 308 } 309 }) 310 } 311} 312 313func getPrePost(content []byte, offset int) (string, string) { 314 pre, post := string(content)[:offset], string(content)[offset:] 315 if i := strings.LastIndex(pre, "\n"); i >= 0 { 316 pre = pre[i+1:] 317 } 318 if i := strings.IndexRune(post, '\n'); i >= 0 { 319 post = post[:i] 320 } 321 return pre, post 322} 323