| 1463 | } |
| 1464 | |
| 1465 | func TestVM_StackUnderflow(t *testing.T) { |
| 1466 | tests := []struct { |
| 1467 | name string |
| 1468 | bytecode []vm.Opcode |
| 1469 | args []int |
| 1470 | expectError string |
| 1471 | }{ |
| 1472 | { |
| 1473 | name: "pop after push", |
| 1474 | bytecode: []vm.Opcode{vm.OpInt, vm.OpPop}, |
| 1475 | args: []int{42, 0}, |
| 1476 | }, |
| 1477 | { |
| 1478 | name: "underflow after valid operations", |
| 1479 | bytecode: []vm.Opcode{vm.OpInt, vm.OpInt, vm.OpPop, vm.OpPop, vm.OpPop}, |
| 1480 | args: []int{1, 2, 0, 0, 0}, |
| 1481 | expectError: "stack underflow", |
| 1482 | }, |
| 1483 | { |
| 1484 | name: "pop on empty stack", |
| 1485 | bytecode: []vm.Opcode{vm.OpPop}, |
| 1486 | args: []int{0}, |
| 1487 | expectError: "stack underflow", |
| 1488 | }, |
| 1489 | { |
| 1490 | name: "pop after push", |
| 1491 | bytecode: []vm.Opcode{vm.OpInt, vm.OpPop}, |
| 1492 | args: []int{123, 0}, |
| 1493 | }, |
| 1494 | } |
| 1495 | |
| 1496 | for _, tt := range tests { |
| 1497 | t.Run(tt.name, func(t *testing.T) { |
| 1498 | program := &vm.Program{ |
| 1499 | Bytecode: tt.bytecode, |
| 1500 | Arguments: tt.args, |
| 1501 | Constants: []any{}, |
| 1502 | } |
| 1503 | |
| 1504 | _, err := vm.Run(program, nil) |
| 1505 | if tt.expectError != "" { |
| 1506 | require.Error(t, err) |
| 1507 | require.Contains(t, err.Error(), tt.expectError) |
| 1508 | } else { |
| 1509 | require.NoError(t, err) |
| 1510 | } |
| 1511 | }) |
| 1512 | } |
| 1513 | } |
| 1514 | |
| 1515 | func TestVM_EnvNotCallable(t *testing.T) { |
| 1516 | // $env is the environment, not a function. |