sealevel: use JSON-based test framework

This commit is contained in:
Richard Patel 2022-09-05 10:16:52 +02:00
parent a940a46699
commit 0a3c7853fd
4 changed files with 109 additions and 61 deletions

View File

@ -10,14 +10,19 @@ import (
"github.com/stretchr/testify/require"
)
// Load returns the fixture at the given path.
func Load(t *testing.T, strs ...string) []byte {
// Path returns the absolute path for the fixture at the given relative path.
func Path(t *testing.T, strs ...string) string {
_, file, _, ok := runtime.Caller(0)
require.True(t, ok, "runtime.Caller failed")
parts := make([]string, 1, 1+len(strs))
parts[0] = filepath.Dir(file)
parts = append(parts, strs...)
data, err := os.ReadFile(filepath.Join(parts...))
return filepath.Join(parts...)
}
// Load returns the fixture at the given path.
func Load(t *testing.T, strs ...string) []byte {
data, err := os.ReadFile(Path(t, strs...))
require.NoError(t, err)
return data
}

View File

@ -0,0 +1,39 @@
{
"Name": "SPLToken_InitializeMint",
"Program": "sealevel/TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.so",
"Params": {
"Accounts": [
{
"IsDuplicate": false,
"DuplicateIndex": 255,
"IsSigner": true,
"IsWritable": true,
"IsExecutable": false,
"Key": "4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM",
"Owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"Lamports": 10000000,
"Data": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Padding": 0,
"RentEpoch": 0
},
{
"IsDuplicate": false,
"DuplicateIndex": 255,
"IsSigner": true,
"IsWritable": true,
"IsExecutable": false,
"Key": "SysvarRent111111111111111111111111111111111",
"Owner": "Sysvar1111111111111111111111111111111111111",
"Lamports": 10092,
"Data": "mA0AAAAAAAAAAAAAAAAAQGQ=",
"Padding": 0,
"RentEpoch": 0
}
],
"Data": "AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"ProgramID": "11111111111111111111111111111111"
},
"Logs": [
"Instruction: InitializeMint"
]
}

View File

@ -5,13 +5,15 @@ import (
"encoding/binary"
"fmt"
"io"
"github.com/gagliardetto/solana-go"
)
// Params is the data passed to programs via the Sealevel VM input segment.
type Params struct {
Accounts []AccountParam
Data []byte // per-instruction data
ProgramID [32]byte
ProgramID solana.PublicKey
}
// ReallocSpace is the allowed length by which an account is allowed to grow.
@ -27,8 +29,8 @@ type AccountParam struct {
IsSigner bool
IsWritable bool
IsExecutable bool
Key [32]byte
Owner [32]byte
Key solana.PublicKey
Owner solana.PublicKey
Lamports uint64
Data []byte
Padding int // ignored, written by serializer

View File

@ -2,7 +2,11 @@ package sealevel
import (
_ "embed"
"log"
"encoding/json"
"io/fs"
"os"
"path/filepath"
"strings"
"testing"
"github.com/certusone/radiance/fixtures"
@ -78,64 +82,27 @@ func TestInterpreter_Noop(t *testing.T) {
})
}
func TestExecute_Token(t *testing.T) {
loader, err := loader.NewLoaderFromBytes(fixtures.Load(t, "sealevel", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.so"))
require.NoError(t, err)
require.NotNil(t, loader)
type executeCase struct {
Name string
Program string
Params Params
Logs []string
}
program, err := loader.Load()
func (e *executeCase) run(t *testing.T) {
ld, err := loader.NewLoaderFromBytes(fixtures.Load(t, e.Program))
require.NoError(t, err)
require.NotNil(t, ld)
program, err := ld.Load()
require.NoError(t, err)
require.NotNil(t, program)
require.NoError(t, program.Verify())
tx := TxContext{}
opts := tx.newVMOpts(&Params{
Accounts: []AccountParam{
// Mint
{
IsDuplicate: false,
DuplicateIndex: 0xFF,
IsSigner: true,
IsWritable: true,
IsExecutable: false,
Key: [32]byte{0x1},
Owner: [32]byte{6, 221, 246, 225, 215, 101, 161, 147, 217, 203, 225, 70, 206, 235, 121, 172, 28, 180, 133, 237, 95, 91, 55, 145, 58, 140, 245, 133, 126, 255, 0, 169},
Lamports: 10000000,
Data: make([]byte, 82),
RentEpoch: 0,
},
// Rent sysvar
{
IsDuplicate: false,
DuplicateIndex: 0xFF,
IsSigner: true,
IsWritable: true,
IsExecutable: false,
Key: [32]byte{0x06, 0xa7, 0xd5, 0x17, 0x19, 0x2c, 0x5c, 0x51, 0x21, 0x8c, 0xc9, 0x4c, 0x3d, 0x4a, 0xf1, 0x7f, 0x58, 0xda, 0xee, 0x08, 0x9b, 0xa1, 0xfd, 0x44, 0xe3, 0xdb, 0xd9, 0x8a, 0x00, 0x00, 0x00, 0x00},
Owner: [32]byte{0x06, 0xa7, 0xd5, 0x17, 0x18, 0x75, 0xf7, 0x29, 0xc7, 0x3d, 0x93, 0x40, 0x8f, 0x21, 0x61, 0x20, 0x06, 0x7e, 0xd8, 0x8c, 0x76, 0xe0, 0x8c, 0x28, 0x7f, 0xc1, 0x94, 0x60, 0x00, 0x00, 0x00, 0x00},
Lamports: 10092,
Data: []byte{
0x98, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x64,
},
RentEpoch: 0,
},
},
Data: []byte{
0, 0, 0, 0,
1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
},
ProgramID: [32]byte{},
})
logger := log.Default()
logger.SetFlags(0)
opts.Tracer = logger
opts := tx.newVMOpts(&e.Params)
opts.Tracer = testLogger{t}
interpreter := sbf.NewInterpreter(program, opts)
require.NotNil(t, interpreter)
@ -144,7 +111,42 @@ func TestExecute_Token(t *testing.T) {
assert.NoError(t, err)
logs := opts.Context.(*Execution).Log.(*LogRecorder).Logs
assert.Equal(t, logs, []string{
"Instruction: InitializeMint",
})
assert.Equal(t, logs, e.Logs)
}
func TestExecute(t *testing.T) {
// Collect test cases
var cases []executeCase
err := filepath.WalkDir(fixtures.Path(t, "sealevel"), func(path string, entry fs.DirEntry, err error) error {
if !entry.Type().IsRegular() ||
!strings.HasPrefix(filepath.Base(path), "test_") ||
filepath.Ext(path) != ".json" {
return nil
}
buf, err := os.ReadFile(path)
require.NoError(t, err, path)
var _case executeCase
require.NoError(t, json.Unmarshal(buf, &_case), path)
cases = append(cases, _case)
return nil
})
require.NoError(t, err)
for _, _case := range cases {
t.Run(_case.Name, func(t *testing.T) {
t.Parallel()
_case.run(t)
})
}
}
type testLogger struct {
t *testing.T
}
func (t testLogger) Printf(format string, args ...any) {
t.t.Logf(format, args...)
}