github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/std_test.go (about) 1 package native 2 3 import ( 4 "encoding/base64" 5 "encoding/hex" 6 "math" 7 "math/big" 8 "strings" 9 "testing" 10 11 "github.com/mr-tron/base58" 12 "github.com/nspcc-dev/neo-go/pkg/core/dao" 13 "github.com/nspcc-dev/neo-go/pkg/core/interop" 14 base58neogo "github.com/nspcc-dev/neo-go/pkg/encoding/base58" 15 "github.com/nspcc-dev/neo-go/pkg/vm" 16 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestStdLibItoaAtoi(t *testing.T) { 22 s := newStd() 23 ic := &interop.Context{VM: vm.New(), DAO: &dao.Simple{}} 24 var actual stackitem.Item 25 26 t.Run("itoa-atoi", func(t *testing.T) { 27 var testCases = []struct { 28 num *big.Int 29 base *big.Int 30 result string 31 }{ 32 {big.NewInt(0), big.NewInt(10), "0"}, 33 {big.NewInt(0), big.NewInt(16), "0"}, 34 {big.NewInt(1), big.NewInt(10), "1"}, 35 {big.NewInt(-1), big.NewInt(10), "-1"}, 36 {big.NewInt(1), big.NewInt(16), "1"}, 37 {big.NewInt(7), big.NewInt(16), "7"}, 38 {big.NewInt(8), big.NewInt(16), "08"}, 39 {big.NewInt(65535), big.NewInt(16), "0ffff"}, 40 {big.NewInt(15), big.NewInt(16), "0f"}, 41 {big.NewInt(-1), big.NewInt(16), "f"}, 42 } 43 44 for _, tc := range testCases { 45 require.NotPanics(t, func() { 46 actual = s.itoa(ic, []stackitem.Item{stackitem.Make(tc.num), stackitem.Make(tc.base)}) 47 }) 48 require.Equal(t, stackitem.Make(tc.result), actual) 49 50 require.NotPanics(t, func() { 51 actual = s.atoi(ic, []stackitem.Item{stackitem.Make(tc.result), stackitem.Make(tc.base)}) 52 }) 53 require.Equal(t, stackitem.Make(tc.num), actual) 54 55 if tc.base.Int64() == 10 { 56 require.NotPanics(t, func() { 57 actual = s.itoa10(ic, []stackitem.Item{stackitem.Make(tc.num)}) 58 }) 59 require.Equal(t, stackitem.Make(tc.result), actual) 60 61 require.NotPanics(t, func() { 62 actual = s.atoi10(ic, []stackitem.Item{stackitem.Make(tc.result)}) 63 }) 64 require.Equal(t, stackitem.Make(tc.num), actual) 65 if tc.result[0] != '-' { 66 require.NotPanics(t, func() { 67 actual = s.atoi10(ic, []stackitem.Item{stackitem.Make("+" + tc.result)}) 68 }) 69 require.Equal(t, stackitem.Make(tc.num), actual) 70 } 71 } 72 } 73 74 t.Run("-1", func(t *testing.T) { 75 for _, str := range []string{"FF", "FFF", "FFFF"} { 76 require.NotPanics(t, func() { 77 actual = s.atoi(ic, []stackitem.Item{stackitem.Make(str), stackitem.Make(16)}) 78 }) 79 80 require.Equal(t, stackitem.Make(-1), actual) 81 } 82 }) 83 }) 84 85 t.Run("itoa error", func(t *testing.T) { 86 var testCases = []struct { 87 num *big.Int 88 base *big.Int 89 err error 90 }{ 91 {big.NewInt(1), big.NewInt(13), ErrInvalidBase}, 92 {big.NewInt(-1), new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(10)), ErrInvalidBase}, 93 } 94 95 for _, tc := range testCases { 96 require.PanicsWithError(t, tc.err.Error(), func() { 97 _ = s.itoa(ic, []stackitem.Item{stackitem.Make(tc.num), stackitem.Make(tc.base)}) 98 }) 99 } 100 }) 101 102 t.Run("atoi error", func(t *testing.T) { 103 var testCases = []struct { 104 num string 105 base *big.Int 106 err error 107 }{ 108 {"1", big.NewInt(13), ErrInvalidBase}, 109 {"1", new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(16)), ErrInvalidBase}, 110 {"1_000", big.NewInt(10), ErrInvalidFormat}, 111 {" 1", big.NewInt(10), ErrInvalidFormat}, 112 {"1 ", big.NewInt(10), ErrInvalidFormat}, 113 {"FE", big.NewInt(10), ErrInvalidFormat}, 114 {"XD", big.NewInt(16), ErrInvalidFormat}, 115 {strings.Repeat("0", stdMaxInputLength+1), big.NewInt(10), ErrTooBigInput}, 116 } 117 118 for _, tc := range testCases { 119 require.PanicsWithError(t, tc.err.Error(), func() { 120 _ = s.atoi(ic, []stackitem.Item{stackitem.Make(tc.num), stackitem.Make(tc.base)}) 121 }) 122 } 123 }) 124 } 125 126 func TestStdLibJSON(t *testing.T) { 127 s := newStd() 128 ic := &interop.Context{VM: vm.New()} 129 var actual stackitem.Item 130 131 t.Run("JSONSerialize", func(t *testing.T) { 132 t.Run("Good", func(t *testing.T) { 133 require.NotPanics(t, func() { 134 actual = s.jsonSerialize(ic, []stackitem.Item{stackitem.Make(42)}) 135 }) 136 137 require.Equal(t, stackitem.Make([]byte("42")), actual) 138 }) 139 140 t.Run("Bad", func(t *testing.T) { 141 arr := stackitem.NewArray([]stackitem.Item{ 142 stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), 143 stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), 144 }) 145 require.Panics(t, func() { 146 _ = s.jsonSerialize(ic, []stackitem.Item{arr}) 147 }) 148 }) 149 }) 150 151 t.Run("JSONDeserialize", func(t *testing.T) { 152 t.Run("Good", func(t *testing.T) { 153 require.NotPanics(t, func() { 154 actual = s.jsonDeserialize(ic, []stackitem.Item{stackitem.Make("42")}) 155 }) 156 157 require.Equal(t, stackitem.Make(42), actual) 158 }) 159 t.Run("Bad", func(t *testing.T) { 160 require.Panics(t, func() { 161 _ = s.jsonDeserialize(ic, []stackitem.Item{stackitem.Make("{]")}) 162 }) 163 require.Panics(t, func() { 164 _ = s.jsonDeserialize(ic, []stackitem.Item{stackitem.NewInterop(nil)}) 165 }) 166 }) 167 }) 168 } 169 170 func TestStdLibEncodeDecode(t *testing.T) { 171 s := newStd() 172 original := []byte("my pretty string") 173 encoded64 := base64.StdEncoding.EncodeToString(original) 174 encoded58 := base58.Encode(original) 175 encoded58Check := base58neogo.CheckEncode(original) 176 ic := &interop.Context{VM: vm.New()} 177 var actual stackitem.Item 178 179 bigInputArgs := []stackitem.Item{stackitem.Make(strings.Repeat("6", stdMaxInputLength+1))} 180 181 t.Run("Encode64", func(t *testing.T) { 182 require.NotPanics(t, func() { 183 actual = s.base64Encode(ic, []stackitem.Item{stackitem.Make(original)}) 184 }) 185 require.Equal(t, stackitem.Make(encoded64), actual) 186 }) 187 t.Run("Encode64/error", func(t *testing.T) { 188 require.PanicsWithError(t, ErrTooBigInput.Error(), 189 func() { s.base64Encode(ic, bigInputArgs) }) 190 }) 191 t.Run("Encode58", func(t *testing.T) { 192 require.NotPanics(t, func() { 193 actual = s.base58Encode(ic, []stackitem.Item{stackitem.Make(original)}) 194 }) 195 require.Equal(t, stackitem.Make(encoded58), actual) 196 }) 197 t.Run("Encode58/error", func(t *testing.T) { 198 require.PanicsWithError(t, ErrTooBigInput.Error(), 199 func() { s.base58Encode(ic, bigInputArgs) }) 200 }) 201 t.Run("CheckEncode58", func(t *testing.T) { 202 require.NotPanics(t, func() { 203 actual = s.base58CheckEncode(ic, []stackitem.Item{stackitem.Make(original)}) 204 }) 205 require.Equal(t, stackitem.Make(encoded58Check), actual) 206 }) 207 t.Run("CheckEncode58/error", func(t *testing.T) { 208 require.PanicsWithError(t, ErrTooBigInput.Error(), 209 func() { s.base58CheckEncode(ic, bigInputArgs) }) 210 }) 211 t.Run("Decode64/positive", func(t *testing.T) { 212 require.NotPanics(t, func() { 213 actual = s.base64Decode(ic, []stackitem.Item{stackitem.Make(encoded64)}) 214 }) 215 require.Equal(t, stackitem.Make(original), actual) 216 }) 217 t.Run("Decode64/error", func(t *testing.T) { 218 require.Panics(t, func() { 219 _ = s.base64Decode(ic, []stackitem.Item{stackitem.Make(encoded64 + "%")}) 220 }) 221 require.Panics(t, func() { 222 _ = s.base64Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)}) 223 }) 224 require.PanicsWithError(t, ErrTooBigInput.Error(), 225 func() { s.base64Decode(ic, bigInputArgs) }) 226 }) 227 t.Run("Decode58/positive", func(t *testing.T) { 228 require.NotPanics(t, func() { 229 actual = s.base58Decode(ic, []stackitem.Item{stackitem.Make(encoded58)}) 230 }) 231 require.Equal(t, stackitem.Make(original), actual) 232 }) 233 t.Run("Decode58/error", func(t *testing.T) { 234 require.Panics(t, func() { 235 _ = s.base58Decode(ic, []stackitem.Item{stackitem.Make(encoded58 + "%")}) 236 }) 237 require.Panics(t, func() { 238 _ = s.base58Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)}) 239 }) 240 require.PanicsWithError(t, ErrTooBigInput.Error(), 241 func() { s.base58Decode(ic, bigInputArgs) }) 242 }) 243 t.Run("CheckDecode58/positive", func(t *testing.T) { 244 require.NotPanics(t, func() { 245 actual = s.base58CheckDecode(ic, []stackitem.Item{stackitem.Make(encoded58Check)}) 246 }) 247 require.Equal(t, stackitem.Make(original), actual) 248 }) 249 t.Run("CheckDecode58/error", func(t *testing.T) { 250 require.Panics(t, func() { 251 _ = s.base58CheckDecode(ic, []stackitem.Item{stackitem.Make(encoded58 + "%")}) 252 }) 253 require.Panics(t, func() { 254 _ = s.base58CheckDecode(ic, []stackitem.Item{stackitem.NewInterop(nil)}) 255 }) 256 require.PanicsWithError(t, ErrTooBigInput.Error(), 257 func() { s.base58CheckDecode(ic, bigInputArgs) }) 258 }) 259 } 260 261 func TestStdLibSerialize(t *testing.T) { 262 s := newStd() 263 ic := &interop.Context{VM: vm.New(), DAO: &dao.Simple{}} 264 265 t.Run("recursive", func(t *testing.T) { 266 arr := stackitem.NewArray(nil) 267 arr.Append(arr) 268 require.Panics(t, func() { 269 _ = s.serialize(ic, []stackitem.Item{arr}) 270 }) 271 }) 272 t.Run("big item", func(t *testing.T) { 273 require.Panics(t, func() { 274 _ = s.serialize(ic, []stackitem.Item{stackitem.NewByteArray(make([]byte, stackitem.MaxSize))}) 275 }) 276 }) 277 t.Run("good", func(t *testing.T) { 278 var ( 279 actualSerialized stackitem.Item 280 actualDeserialized stackitem.Item 281 ) 282 require.NotPanics(t, func() { 283 actualSerialized = s.serialize(ic, []stackitem.Item{stackitem.Make(42)}) 284 }) 285 286 encoded, err := stackitem.Serialize(stackitem.Make(42)) 287 require.NoError(t, err) 288 require.Equal(t, stackitem.Make(encoded), actualSerialized) 289 290 require.NotPanics(t, func() { 291 actualDeserialized = s.deserialize(ic, []stackitem.Item{actualSerialized}) 292 }) 293 require.Equal(t, stackitem.Make(42), actualDeserialized) 294 295 t.Run("bad", func(t *testing.T) { 296 encoded[0] ^= 0xFF 297 require.Panics(t, func() { 298 _ = s.deserialize(ic, []stackitem.Item{stackitem.Make(encoded)}) 299 }) 300 }) 301 }) 302 } 303 304 func TestStdLibSerializeDeserialize(t *testing.T) { 305 s := newStd() 306 ic := &interop.Context{VM: vm.New(), DAO: &dao.Simple{}} 307 var actual stackitem.Item 308 309 checkSerializeDeserialize := func(t *testing.T, value any, expected stackitem.Item) { 310 require.NotPanics(t, func() { 311 actual = s.serialize(ic, []stackitem.Item{stackitem.Make(value)}) 312 }) 313 require.NotPanics(t, func() { 314 actual = s.deserialize(ic, []stackitem.Item{actual}) 315 }) 316 require.Equal(t, expected, actual) 317 } 318 319 t.Run("Bool", func(t *testing.T) { 320 checkSerializeDeserialize(t, true, stackitem.NewBool(true)) 321 }) 322 t.Run("ByteArray", func(t *testing.T) { 323 checkSerializeDeserialize(t, []byte{1, 2, 3}, stackitem.NewByteArray([]byte{1, 2, 3})) 324 }) 325 t.Run("Integer", func(t *testing.T) { 326 checkSerializeDeserialize(t, 48, stackitem.NewBigInteger(big.NewInt(48))) 327 }) 328 t.Run("Array", func(t *testing.T) { 329 arr := stackitem.NewArray([]stackitem.Item{ 330 stackitem.Make(true), 331 stackitem.Make(123), 332 stackitem.NewMap()}) 333 checkSerializeDeserialize(t, arr, arr) 334 }) 335 t.Run("Struct", func(t *testing.T) { 336 st := stackitem.NewStruct([]stackitem.Item{ 337 stackitem.Make(true), 338 stackitem.Make(123), 339 stackitem.NewMap(), 340 }) 341 checkSerializeDeserialize(t, st, st) 342 }) 343 t.Run("Map", func(t *testing.T) { 344 item := stackitem.NewMap() 345 item.Add(stackitem.Make(true), stackitem.Make([]byte{1, 2, 3})) 346 item.Add(stackitem.Make([]byte{0}), stackitem.Make(false)) 347 checkSerializeDeserialize(t, item, item) 348 }) 349 t.Run("Serialize MapCompat", func(t *testing.T) { 350 resHex := "480128036b6579280576616c7565" 351 res, err := hex.DecodeString(resHex) 352 require.NoError(t, err) 353 354 item := stackitem.NewMap() 355 item.Add(stackitem.Make([]byte("key")), stackitem.Make([]byte("value"))) 356 require.NotPanics(t, func() { 357 actual = s.serialize(ic, []stackitem.Item{stackitem.Make(item)}) 358 }) 359 bytes, err := actual.TryBytes() 360 require.NoError(t, err) 361 assert.Equal(t, res, bytes) 362 }) 363 t.Run("Serialize Interop", func(t *testing.T) { 364 require.Panics(t, func() { 365 actual = s.serialize(ic, []stackitem.Item{stackitem.NewInterop("kek")}) 366 }) 367 }) 368 t.Run("Serialize Array bad", func(t *testing.T) { 369 item := stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true), stackitem.NewBool(true)}) 370 item.Value().([]stackitem.Item)[1] = item 371 require.Panics(t, func() { 372 actual = s.serialize(ic, []stackitem.Item{item}) 373 }) 374 }) 375 t.Run("Deserialize unknown", func(t *testing.T) { 376 data, err := stackitem.Serialize(stackitem.NewBigInteger(big.NewInt(123))) 377 require.NoError(t, err) 378 379 data[0] = 0xFF 380 require.Panics(t, func() { 381 actual = s.deserialize(ic, []stackitem.Item{stackitem.Make(data)}) 382 }) 383 }) 384 t.Run("Deserialize not a byte array", func(t *testing.T) { 385 require.Panics(t, func() { 386 actual = s.deserialize(ic, []stackitem.Item{stackitem.NewInterop(nil)}) 387 }) 388 }) 389 } 390 391 func TestMemoryCompare(t *testing.T) { 392 s := newStd() 393 ic := &interop.Context{VM: vm.New(), DAO: &dao.Simple{}} 394 395 check := func(t *testing.T, result int64, s1, s2 string) { 396 actual := s.memoryCompare(ic, []stackitem.Item{stackitem.Make(s1), stackitem.Make(s2)}) 397 require.Equal(t, big.NewInt(result), actual.Value()) 398 } 399 400 check(t, -1, "a", "ab") 401 check(t, 1, "ab", "a") 402 check(t, 0, "ab", "ab") 403 check(t, -1, "", "a") 404 check(t, 0, "", "") 405 406 t.Run("C# compatibility", func(t *testing.T) { 407 // These tests are taken from C# node. 408 check(t, -1, "abc", "c") 409 check(t, -1, "abc", "d") 410 check(t, 0, "abc", "abc") 411 check(t, -1, "abc", "abcd") 412 }) 413 414 t.Run("big arguments", func(t *testing.T) { 415 s1 := stackitem.Make(strings.Repeat("x", stdMaxInputLength+1)) 416 s2 := stackitem.Make("xxx") 417 418 require.PanicsWithError(t, ErrTooBigInput.Error(), 419 func() { s.memoryCompare(ic, []stackitem.Item{s1, s2}) }) 420 421 require.PanicsWithError(t, ErrTooBigInput.Error(), 422 func() { s.memoryCompare(ic, []stackitem.Item{s2, s1}) }) 423 }) 424 } 425 426 func TestMemorySearch(t *testing.T) { 427 s := newStd() 428 ic := &interop.Context{VM: vm.New()} 429 430 check := func(t *testing.T, result int64, args ...any) { 431 items := make([]stackitem.Item, len(args)) 432 for i := range args { 433 items[i] = stackitem.Make(args[i]) 434 } 435 436 var actual stackitem.Item 437 switch len(items) { 438 case 2: 439 actual = s.memorySearch2(ic, items) 440 case 3: 441 actual = s.memorySearch3(ic, items) 442 case 4: 443 actual = s.memorySearch4(ic, items) 444 default: 445 panic("invalid args length") 446 } 447 require.Equal(t, big.NewInt(result), actual.Value()) 448 } 449 450 t.Run("C# compatibility", func(t *testing.T) { 451 // These tests are taken from C# node. 452 check(t, 2, "abc", "c", 0) 453 check(t, 2, "abc", "c", 1) 454 check(t, 2, "abc", "c", 2) 455 check(t, -1, "abc", "c", 3) 456 check(t, -1, "abc", "d", 0) 457 458 check(t, 2, "abc", "c", 0, false) 459 check(t, 2, "abc", "c", 1, false) 460 check(t, 2, "abc", "c", 2, false) 461 check(t, -1, "abc", "c", 3, false) 462 check(t, -1, "abc", "d", 0, false) 463 464 check(t, -1, "abc", "c", 0, true) 465 check(t, -1, "abc", "c", 1, true) 466 check(t, -1, "abc", "c", 2, true) 467 check(t, 2, "abc", "c", 3, true) 468 check(t, -1, "abc", "d", 0, true) 469 }) 470 471 t.Run("boundary indices", func(t *testing.T) { 472 arg := stackitem.Make("aaa") 473 require.Panics(t, func() { 474 s.memorySearch3(ic, []stackitem.Item{arg, arg, stackitem.Make(-1)}) 475 }) 476 require.Panics(t, func() { 477 s.memorySearch3(ic, []stackitem.Item{arg, arg, stackitem.Make(4)}) 478 }) 479 t.Run("still in capacity", func(t *testing.T) { 480 require.Panics(t, func() { 481 arr := stackitem.NewByteArray(make([]byte, 5, 10)) 482 s.memorySearch3(ic, []stackitem.Item{arr, arg, stackitem.Make(7)}) 483 }) 484 require.Panics(t, func() { 485 arr := stackitem.NewByteArray(make([]byte, 5, 10)) 486 s.memorySearch4(ic, []stackitem.Item{arr, arg, 487 stackitem.Make(7), stackitem.Make(true)}) 488 }) 489 }) 490 }) 491 492 t.Run("big arguments", func(t *testing.T) { 493 s1 := stackitem.Make(strings.Repeat("x", stdMaxInputLength+1)) 494 s2 := stackitem.Make("xxx") 495 start := stackitem.Make(1) 496 b := stackitem.Make(true) 497 498 require.PanicsWithError(t, ErrTooBigInput.Error(), 499 func() { s.memorySearch2(ic, []stackitem.Item{s1, s2}) }) 500 501 require.PanicsWithError(t, ErrTooBigInput.Error(), 502 func() { s.memorySearch2(ic, []stackitem.Item{s2, s1}) }) 503 504 require.PanicsWithError(t, ErrTooBigInput.Error(), 505 func() { s.memorySearch3(ic, []stackitem.Item{s1, s2, start}) }) 506 507 require.PanicsWithError(t, ErrTooBigInput.Error(), 508 func() { s.memorySearch3(ic, []stackitem.Item{s2, s1, start}) }) 509 510 require.PanicsWithError(t, ErrTooBigInput.Error(), 511 func() { s.memorySearch4(ic, []stackitem.Item{s1, s2, start, b}) }) 512 513 require.PanicsWithError(t, ErrTooBigInput.Error(), 514 func() { s.memorySearch4(ic, []stackitem.Item{s2, s1, start, b}) }) 515 }) 516 } 517 518 func TestStringSplit(t *testing.T) { 519 s := newStd() 520 ic := &interop.Context{VM: vm.New()} 521 522 check := func(t *testing.T, result []string, str, sep string, remove any) { 523 args := []stackitem.Item{stackitem.Make(str), stackitem.Make(sep)} 524 var actual stackitem.Item 525 if remove == nil { 526 actual = s.stringSplit2(ic, args) 527 } else { 528 args = append(args, stackitem.NewBool(remove.(bool))) 529 actual = s.stringSplit3(ic, args) 530 } 531 532 arr, ok := actual.Value().([]stackitem.Item) 533 require.True(t, ok) 534 require.Equal(t, len(result), len(arr)) 535 for i := range result { 536 require.Equal(t, stackitem.Make(result[i]), arr[i]) 537 } 538 } 539 540 check(t, []string{"a", "b", "c"}, "abc", "", nil) 541 check(t, []string{"a", "b", "c"}, "abc", "", true) 542 check(t, []string{"a", "c", "", "", "d"}, "abcbbbd", "b", nil) 543 check(t, []string{"a", "c", "", "", "d"}, "abcbbbd", "b", false) 544 check(t, []string{"a", "c", "d"}, "abcbbbd", "b", true) 545 check(t, []string{""}, "", "abc", nil) 546 check(t, []string{}, "", "abc", true) 547 548 t.Run("C# compatibility", func(t *testing.T) { 549 // These tests are taken from C# node. 550 check(t, []string{"a", "b"}, "a,b", ",", nil) 551 }) 552 553 t.Run("big arguments", func(t *testing.T) { 554 s1 := stackitem.Make(strings.Repeat("x", stdMaxInputLength+1)) 555 s2 := stackitem.Make("xxx") 556 557 require.PanicsWithError(t, ErrTooBigInput.Error(), 558 func() { s.stringSplit2(ic, []stackitem.Item{s1, s2}) }) 559 }) 560 } 561 562 func TestStd_StrLen(t *testing.T) { 563 s := newStd() 564 ic := &interop.Context{VM: vm.New()} 565 566 check := func(t *testing.T, expected int64, str string) { 567 args := []stackitem.Item{stackitem.Make(str)} 568 actual := s.strLen(ic, args) 569 l, ok := actual.Value().(*big.Int) 570 require.True(t, ok) 571 require.Equal(t, expected, l.Int64()) 572 } 573 574 // These tests are taken from https://github.com/neo-project/neo/pull/2854/files. 575 check(t, 1, "🦆") 576 check(t, 1, "ã") 577 check(t, 1, "a") 578 579 bad := string(rune(0xff)) 580 check(t, 1, bad) 581 check(t, 3, bad+"ab") 582 }