github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/vm/stackitem/item_test.go (about) 1 package stackitem 2 3 import ( 4 "math/big" 5 "testing" 6 7 "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" 8 "github.com/nspcc-dev/neo-go/pkg/util" 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 ) 12 13 var makeStackItemTestCases = []struct { 14 input any 15 result Item 16 }{ 17 { 18 input: int64(3), 19 result: (*BigInteger)(big.NewInt(3)), 20 }, 21 { 22 input: int16(3), 23 result: (*BigInteger)(big.NewInt(3)), 24 }, 25 { 26 input: 3, 27 result: (*BigInteger)(big.NewInt(3)), 28 }, 29 { 30 input: uint8(3), 31 result: (*BigInteger)(big.NewInt(3)), 32 }, 33 { 34 input: uint16(3), 35 result: (*BigInteger)(big.NewInt(3)), 36 }, 37 { 38 input: uint32(3), 39 result: (*BigInteger)(big.NewInt(3)), 40 }, 41 { 42 input: uint64(3), 43 result: (*BigInteger)(big.NewInt(3)), 44 }, 45 { 46 input: big.NewInt(3), 47 result: (*BigInteger)(big.NewInt(3)), 48 }, 49 { 50 input: []byte{1, 2, 3, 4}, 51 result: NewByteArray([]byte{1, 2, 3, 4}), 52 }, 53 { 54 input: []byte{}, 55 result: NewByteArray([]byte{}), 56 }, 57 { 58 input: "bla", 59 result: NewByteArray([]byte("bla")), 60 }, 61 { 62 input: "", 63 result: NewByteArray([]byte{}), 64 }, 65 { 66 input: true, 67 result: Bool(true), 68 }, 69 { 70 input: false, 71 result: Bool(false), 72 }, 73 { 74 input: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}, 75 result: &Array{value: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}}, 76 }, 77 { 78 input: []int{1, 2, 3}, 79 result: &Array{value: []Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}}, 80 }, 81 { 82 input: nil, 83 result: Null{}, 84 }, 85 { 86 input: &util.Uint160{1, 2, 3}, 87 result: NewByteArray(util.Uint160{1, 2, 3}.BytesBE()), 88 }, 89 { 90 input: &util.Uint256{1, 2, 3}, 91 result: NewByteArray(util.Uint256{1, 2, 3}.BytesBE()), 92 }, 93 { 94 input: (*util.Uint160)(nil), 95 result: Null{}, 96 }, 97 { 98 input: (*util.Uint256)(nil), 99 result: Null{}, 100 }, 101 } 102 103 var makeStackItemErrorCases = []struct { 104 input any 105 }{ 106 { 107 input: map[int]int{1: 2}, 108 }, 109 } 110 111 func TestMakeStackItem(t *testing.T) { 112 for _, testCase := range makeStackItemTestCases { 113 assert.Equal(t, testCase.result, Make(testCase.input)) 114 } 115 for _, errorCase := range makeStackItemErrorCases { 116 assert.Panics(t, func() { Make(errorCase.input) }) 117 } 118 } 119 120 var stringerTestCases = []struct { 121 input Item 122 result string 123 }{ 124 { 125 input: NewStruct([]Item{}), 126 result: "Struct", 127 }, 128 { 129 input: NewBigInteger(big.NewInt(3)), 130 result: "BigInteger", 131 }, 132 { 133 input: NewBool(true), 134 result: "Boolean", 135 }, 136 { 137 input: NewByteArray([]byte{}), 138 result: "ByteString", 139 }, 140 { 141 input: NewArray([]Item{}), 142 result: "Array", 143 }, 144 { 145 input: NewMap(), 146 result: "Map", 147 }, 148 { 149 input: NewInterop(nil), 150 result: "InteropInterface", 151 }, 152 { 153 input: NewPointer(0, nil), 154 result: "Pointer", 155 }, 156 } 157 158 func TestStringer(t *testing.T) { 159 for _, testCase := range stringerTestCases { 160 assert.Equal(t, testCase.result, testCase.input.String()) 161 } 162 } 163 164 var equalsTestCases = map[string][]struct { 165 item1 Item 166 item2 Item 167 result bool 168 panics bool 169 }{ 170 "struct": { 171 { 172 item1: NewStruct(nil), 173 item2: nil, 174 result: false, 175 }, 176 { 177 item1: NewStruct(nil), 178 item2: NewBigInteger(big.NewInt(1)), 179 result: false, 180 }, 181 { 182 item1: NewStruct(nil), 183 item2: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), 184 result: false, 185 }, 186 { 187 item1: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), 188 item2: NewStruct([]Item{NewBigInteger(big.NewInt(2))}), 189 result: false, 190 }, 191 { 192 item1: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), 193 item2: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), 194 result: true, 195 }, 196 { 197 item1: NewStruct([]Item{NewBigInteger(big.NewInt(1)), NewStruct([]Item{})}), 198 item2: NewStruct([]Item{NewBigInteger(big.NewInt(1)), NewStruct([]Item{})}), 199 result: true, 200 }, 201 }, 202 "bigint": { 203 { 204 item1: NewBigInteger(big.NewInt(2)), 205 item2: nil, 206 result: false, 207 }, 208 { 209 item1: NewBigInteger(big.NewInt(2)), 210 item2: NewBigInteger(big.NewInt(2)), 211 result: true, 212 }, 213 { 214 item1: NewBigInteger(big.NewInt(2)), 215 item2: NewBool(false), 216 result: false, 217 }, 218 { 219 item1: NewBigInteger(big.NewInt(0)), 220 item2: NewBool(false), 221 result: false, 222 }, 223 { 224 item1: NewBigInteger(big.NewInt(2)), 225 item2: Make(int32(2)), 226 result: true, 227 }, 228 }, 229 "bool": { 230 { 231 item1: NewBool(true), 232 item2: nil, 233 result: false, 234 }, 235 { 236 item1: NewBool(true), 237 item2: NewBool(true), 238 result: true, 239 }, 240 { 241 item1: NewBool(true), 242 item2: NewBigInteger(big.NewInt(1)), 243 result: false, 244 }, 245 { 246 item1: NewBool(true), 247 item2: NewBool(false), 248 result: false, 249 }, 250 { 251 item1: NewBool(true), 252 item2: Make(true), 253 result: true, 254 }, 255 }, 256 "bytearray": { 257 { 258 item1: NewByteArray(nil), 259 item2: nil, 260 result: false, 261 }, 262 { 263 item1: NewByteArray([]byte{1, 2, 3}), 264 item2: NewByteArray([]byte{1, 2, 3}), 265 result: true, 266 }, 267 { 268 item1: NewByteArray([]byte{1}), 269 item2: NewBigInteger(big.NewInt(1)), 270 result: false, 271 }, 272 { 273 item1: NewByteArray([]byte{1, 2, 3}), 274 item2: NewByteArray([]byte{1, 2, 4}), 275 result: false, 276 }, 277 { 278 item1: NewByteArray([]byte{1, 2, 3}), 279 item2: Make([]byte{1, 2, 3}), 280 result: true, 281 }, 282 { 283 item1: NewByteArray(make([]byte, MaxByteArrayComparableSize+1)), 284 item2: NewByteArray([]byte{1, 2, 3}), 285 panics: true, 286 }, 287 { 288 item1: NewByteArray([]byte{1, 2, 3}), 289 item2: NewByteArray(make([]byte, MaxByteArrayComparableSize+1)), 290 panics: true, 291 }, 292 { 293 item1: NewByteArray(make([]byte, MaxByteArrayComparableSize+1)), 294 item2: NewByteArray(make([]byte, MaxByteArrayComparableSize+1)), 295 panics: true, 296 }, 297 }, 298 "array": { 299 { 300 item1: NewArray(nil), 301 item2: nil, 302 result: false, 303 }, 304 { 305 item1: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}), 306 item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}), 307 result: false, 308 }, 309 { 310 item1: NewArray([]Item{(*BigInteger)(big.NewInt(1))}), 311 item2: NewBigInteger(big.NewInt(1)), 312 result: false, 313 }, 314 { 315 item1: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}), 316 item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(4))}), 317 result: false, 318 }, 319 }, 320 "map": { 321 { 322 item1: NewMap(), 323 item2: nil, 324 result: false, 325 }, 326 { 327 item1: NewMap(), 328 item2: NewMap(), 329 result: false, 330 }, 331 { 332 item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, 333 item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, 334 result: false, 335 }, 336 { 337 item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, 338 item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{3})}}}, 339 result: false, 340 }, 341 }, 342 "interop": { 343 { 344 item1: NewInterop(nil), 345 item2: nil, 346 result: false, 347 }, 348 { 349 item1: NewInterop(nil), 350 item2: NewInterop(nil), 351 result: true, 352 }, 353 { 354 item1: NewInterop(2), 355 item2: NewInterop(3), 356 result: false, 357 }, 358 { 359 item1: NewInterop(3), 360 item2: NewInterop(3), 361 result: true, 362 }, 363 }, 364 "pointer": { 365 { 366 item1: NewPointer(0, []byte{}), 367 result: false, 368 }, 369 { 370 item1: NewPointer(1, []byte{1}), 371 item2: NewPointer(1, []byte{1}), 372 result: true, 373 }, 374 { 375 item1: NewPointer(1, []byte{1}), 376 item2: NewPointer(2, []byte{1}), 377 result: false, 378 }, 379 { 380 item1: NewPointer(1, []byte{1}), 381 item2: NewPointer(1, []byte{2}), 382 result: false, 383 }, 384 { 385 item1: NewPointer(0, []byte{}), 386 item2: NewBigInteger(big.NewInt(0)), 387 result: false, 388 }, 389 }, 390 } 391 392 func TestEquals(t *testing.T) { 393 for name, testBatch := range equalsTestCases { 394 for _, testCase := range testBatch { 395 t.Run(name, func(t *testing.T) { 396 if testCase.panics { 397 assert.Panics(t, func() { 398 testCase.item1.Equals(testCase.item2) 399 }) 400 } else { 401 assert.Equal(t, testCase.result, testCase.item1.Equals(testCase.item2)) 402 // Reference equals 403 assert.Equal(t, true, testCase.item1.Equals(testCase.item1)) 404 } 405 }) 406 } 407 } 408 } 409 410 func TestEqualsDeepStructure(t *testing.T) { 411 const perStruct = 4 412 var items = []Item{} 413 var num int 414 for i := 0; i < perStruct; i++ { 415 items = append(items, Make(0)) 416 num++ 417 } 418 var layerUp = func(sa *Struct, num int) (*Struct, int) { 419 items := []Item{} 420 for i := 0; i < perStruct; i++ { 421 clon, err := sa.Clone() 422 require.NoError(t, err) 423 items = append(items, clon) 424 } 425 num *= perStruct 426 num++ 427 return NewStruct(items), num 428 } 429 var sa = NewStruct(items) 430 for i := 0; i < 4; i++ { 431 sa, num = layerUp(sa, num) 432 } 433 require.Less(t, num, MaxComparableNumOfItems) 434 sb, err := sa.Clone() 435 require.NoError(t, err) 436 require.True(t, sa.Equals(sb)) 437 sa, num = layerUp(sa, num) 438 sb, num = layerUp(sb, num) 439 440 require.Less(t, MaxComparableNumOfItems, num) 441 require.Panics(t, func() { sa.Equals(sb) }) 442 } 443 444 var marshalJSONTestCases = []struct { 445 input Item 446 result []byte 447 }{ 448 { 449 input: NewBigInteger(big.NewInt(2)), 450 result: []byte(`2`), 451 }, 452 { 453 input: NewBool(true), 454 result: []byte(`true`), 455 }, 456 { 457 input: NewByteArray([]byte{1, 2, 3}), 458 result: []byte(`"010203"`), 459 }, 460 { 461 input: NewBuffer([]byte{1, 2, 3}), 462 result: []byte(`"010203"`), 463 }, 464 { 465 input: &Array{value: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}}, 466 result: []byte(`[3,"010203"]`), 467 }, 468 { 469 input: &Interop{value: 3}, 470 result: []byte(`3`), 471 }, 472 } 473 474 func TestMarshalJSON(t *testing.T) { 475 var ( 476 actual []byte 477 err error 478 ) 479 for _, testCase := range marshalJSONTestCases { 480 switch testCase.input.(type) { 481 case *BigInteger: 482 actual, err = testCase.input.(*BigInteger).MarshalJSON() 483 case Bool: 484 actual, err = testCase.input.(Bool).MarshalJSON() 485 case *ByteArray: 486 actual, err = testCase.input.(*ByteArray).MarshalJSON() 487 case *Array: 488 actual, err = testCase.input.(*Array).MarshalJSON() 489 case *Interop: 490 actual, err = testCase.input.(*Interop).MarshalJSON() 491 default: 492 continue 493 } 494 495 assert.NoError(t, err) 496 assert.Equal(t, testCase.result, actual) 497 } 498 } 499 500 func TestNewVeryBigInteger(t *testing.T) { 501 check := func(ok bool, v *big.Int) { 502 bs := bigint.ToBytes(v) 503 if ok { 504 assert.True(t, len(bs)*8 <= MaxBigIntegerSizeBits) 505 } else { 506 assert.True(t, len(bs)*8 > MaxBigIntegerSizeBits) 507 assert.Panics(t, func() { NewBigInteger(v) }) 508 } 509 } 510 511 maxBitSet := big.NewInt(1) 512 maxBitSet.Lsh(maxBitSet, MaxBigIntegerSizeBits-1) 513 514 check(false, maxBitSet) 515 check(true, new(big.Int).Neg(maxBitSet)) 516 517 minus1 := new(big.Int).Sub(maxBitSet, big.NewInt(1)) 518 check(true, minus1) 519 check(true, new(big.Int).Neg(minus1)) 520 521 plus1 := new(big.Int).Add(maxBitSet, big.NewInt(1)) 522 check(false, plus1) 523 check(false, new(big.Int).Neg(plus1)) 524 525 check(false, new(big.Int).Mul(maxBitSet, big.NewInt(2))) 526 } 527 528 func TestStructClone(t *testing.T) { 529 st0 := Struct{} 530 st := Struct{value: []Item{&st0}} 531 for i := 0; i < MaxClonableNumOfItems-1; i++ { 532 nst, err := st.Clone() 533 require.NoError(t, err) 534 st = Struct{value: []Item{nst}} 535 } 536 _, err := st.Clone() 537 require.Error(t, err) 538 } 539 540 func TestDeepCopy(t *testing.T) { 541 testCases := []struct { 542 name string 543 item Item 544 }{ 545 {"Integer", NewBigInteger(big.NewInt(1))}, 546 {"ByteArray", NewByteArray([]byte{1, 2, 3})}, 547 {"Buffer", NewBuffer([]byte{1, 2, 3})}, 548 {"Bool", NewBool(true)}, 549 {"Pointer", NewPointer(1, []byte{1, 2, 3})}, 550 {"Interop", NewInterop(&[]byte{1, 2})}, 551 } 552 for _, tc := range testCases { 553 t.Run(tc.name, func(t *testing.T) { 554 actual := DeepCopy(tc.item, false) 555 if immut, ok := tc.item.(Immutable); ok { 556 immut.MarkAsReadOnly() // tiny hack for test to be able to compare object references. 557 } 558 require.Equal(t, tc.item, actual) 559 if tc.item.Type() != BooleanT { 560 require.False(t, actual == tc.item) 561 } 562 }) 563 } 564 565 t.Run("Null", func(t *testing.T) { 566 require.Equal(t, Null{}, DeepCopy(Null{}, false)) 567 }) 568 569 t.Run("Array", func(t *testing.T) { 570 arr := NewArray(make([]Item, 2)) 571 arr.value[0] = NewBool(true) 572 arr.value[1] = arr 573 574 actual := DeepCopy(arr, false) 575 arr.isReadOnly = true // tiny hack for test to be able to compare object references. 576 require.Equal(t, arr, actual) 577 require.False(t, arr == actual) 578 require.True(t, actual == actual.(*Array).value[1]) 579 }) 580 581 t.Run("Struct", func(t *testing.T) { 582 arr := NewStruct(make([]Item, 2)) 583 arr.value[0] = NewBool(true) 584 arr.value[1] = arr 585 586 actual := DeepCopy(arr, false) 587 arr.isReadOnly = true // tiny hack for test to be able to compare object references. 588 require.Equal(t, arr, actual) 589 require.False(t, arr == actual) 590 require.True(t, actual == actual.(*Struct).value[1]) 591 }) 592 593 t.Run("Map", func(t *testing.T) { 594 m := NewMapWithValue(make([]MapElement, 2)) 595 m.value[0] = MapElement{Key: NewBool(true), Value: m} 596 m.value[1] = MapElement{Key: NewBigInteger(big.NewInt(1)), Value: NewByteArray([]byte{1, 2, 3})} 597 598 actual := DeepCopy(m, false) 599 m.isReadOnly = true // tiny hack for test to be able to compare object references. 600 require.Equal(t, m, actual) 601 require.False(t, m == actual) 602 require.True(t, actual == actual.(*Map).value[0].Value) 603 }) 604 }