Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
m-Peter committed Jun 5, 2023
1 parent 0456026 commit 8e2d950
Show file tree
Hide file tree
Showing 3 changed files with 326 additions and 13 deletions.
63 changes: 60 additions & 3 deletions test/emulator_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,66 @@ func (e *EmulatorBackend) ServiceAccount() (*stdlib.Account, error) {
return nil, errors.New("TODO")
}

func (e *EmulatorBackend) Events(_ *interpreter.Interpreter, _ interpreter.StaticType) interpreter.Value {
// TODO:
return nil
// Events returns all the emitted events up until the latest block,
// optionally filtered by event type.
func (e *EmulatorBackend) Events(
inter *interpreter.Interpreter,
eventType interpreter.StaticType,
) interpreter.Value {
latestBlock, err := e.blockchain.GetLatestBlock()
if err != nil {
panic(err)
}

latestBlockHeight := latestBlock.Header.Height
height := uint64(0)
values := make([]interpreter.Value, 0)
evtType, _ := eventType.(interpreter.CompositeStaticType)

for height <= latestBlockHeight {
events, err := e.blockchain.GetEventsByHeight(
height,
evtType.String(),
)
if err != nil {
panic(err)
}
sdkEvents, err := convert.FlowEventsToSDK(events)
if err != nil {
panic(err)
}
for _, event := range sdkEvents {
value, err := runtime.ImportValue(
inter,
interpreter.EmptyLocationRange,
e.stdlibHandler,
event.Value,
nil,
)
if err != nil {
panic(err)
}
values = append(values, value)

}
height += 1
}

arrayType := interpreter.NewVariableSizedStaticType(
inter,
interpreter.NewPrimitiveStaticType(
inter,
interpreter.PrimitiveStaticTypeAnyStruct,
),
)

return interpreter.NewArrayValue(
inter,
interpreter.EmptyLocationRange,
arrayType,
common.ZeroAddress,
values...,
)
}

// excludeCommonLocations excludes the common contracts from appearing
Expand Down
170 changes: 170 additions & 0 deletions test/test_framework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3111,3 +3111,173 @@ func TestCoverageReportForIntegrationTests(t *testing.T) {
coverageReport.String(),
)
}

func TestGetEventsFromIntegrationTests(t *testing.T) {
t.Parallel()

contractCode := `
pub contract FooContract {
pub let specialNumbers: {Int: String}
pub event ContractInitialized()
pub event NumberAdded(n: Int, trait: String)
init() {
self.specialNumbers = {1729: "Harshad"}
emit ContractInitialized()
}
pub fun addSpecialNumber(_ n: Int, _ trait: String) {
self.specialNumbers[n] = trait
emit NumberAdded(n: n, trait: trait)
}
pub fun getIntegerTrait(_ n: Int): String {
if self.specialNumbers.containsKey(n) {
return self.specialNumbers[n]!
}
return "Unknown"
}
}
`

scriptCode := `
import FooContract from "../contracts/FooContract.cdc"
pub fun main(): Bool {
// Act
let trait = FooContract.getIntegerTrait(1729)
// Assert
assert(trait == "Harshad")
return true
}
`

testCode := `
import Test
pub let blockchain = Test.newEmulatorBlockchain()
pub let account = blockchain.createAccount()
pub fun setup() {
let contractCode = Test.readFile("../contracts/FooContract.cdc")
let err = blockchain.deployContract(
name: "FooContract",
code: contractCode,
account: account,
arguments: []
)
if err != nil {
panic(err!.message)
}
blockchain.useConfiguration(Test.Configuration({
"../contracts/FooContract.cdc": account.address
}))
}
pub fun testGetIntegerTrait() {
let script = Test.readFile("../scripts/get_integer_traits.cdc")
let result = blockchain.executeScript(script, [])
if result.status != Test.ResultStatus.succeeded {
panic(result.error!.message)
}
assert(result.returnValue! as! Bool)
let typ = CompositeType("A.01cf0e2f2f715450.FooContract.ContractInitialized")!
let events = blockchain.eventsOfType(typ)
Test.assert(events.length == 1)
}
pub fun testAddSpecialNumber() {
let code = Test.readFile("../transactions/add_special_number.cdc")
let tx = Test.Transaction(
code: code,
authorizers: [account.address],
signers: [account],
arguments: [78557, "Sierpinski"]
)
let result = blockchain.executeTransaction(tx)
assert(result.status == Test.ResultStatus.succeeded)
let typ = CompositeType("A.01cf0e2f2f715450.FooContract.NumberAdded")!
let events = blockchain.eventsOfType(typ)
Test.assert(events.length == 1)
let evts = blockchain.events()
}
`

transactionCode := `
import FooContract from "../contracts/FooContract.cdc"
transaction(number: Int, trait: String) {
prepare(acct: AuthAccount) {}
execute {
// Act
FooContract.addSpecialNumber(number, trait)
// Assert
assert(trait == FooContract.getIntegerTrait(number))
}
}
`

fileResolver := func(path string) (string, error) {
switch path {
case "../contracts/FooContract.cdc":
return contractCode, nil
case "../scripts/get_integer_traits.cdc":
return scriptCode, nil
case "../transactions/add_special_number.cdc":
return transactionCode, nil
default:
return "", fmt.Errorf("cannot find import location: %s", path)
}
}
importResolver := func(location common.Location) (string, error) {
return "", nil
}

runner := NewTestRunner().WithFileResolver(fileResolver).WithImportResolver(importResolver)

results, err := runner.RunTests(testCode)
require.NoError(t, err)
for _, result := range results {
require.NoError(t, result.Error)
}

// events := runner.backend.Events("A.01cf0e2f2f715450.FooContract.RandomEvent")
// assert.Equal(t, 0, len(events))

// events = runner.backend.Events("A.01cf0e2f2f715450.FooContract.ContractInitialized")
// assert.Equal(t, 1, len(events))

// events = runner.backend.Events("A.01cf0e2f2f715450.FooContract.NumberAdded")
// assert.Equal(t, 1, len(events))

// n := cadence.NewInt(78557)
// trait, err := cadence.NewString("Sierpinski")
// require.NoError(t, err)

// event := events[0]
// assert.ElementsMatch(
// t,
// []cadence.Value{n, trait},
// event.Fields,
// )
// assert.ElementsMatch(
// t,
// []cadence.Field{
// cadence.NewField("n", cadence.NewIntType()),
// cadence.NewField("trait", cadence.NewStringType()),
// },
// event.EventType.Fields,
// )
}
106 changes: 96 additions & 10 deletions test/test_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/onflow/flow-go/engine/execution/testutil"
"github.com/onflow/flow-go/fvm"
"github.com/onflow/flow-go/fvm/environment"
"github.com/onflow/flow-go/model/flow"

"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/ast"
Expand Down Expand Up @@ -90,6 +91,8 @@ type TestRunner struct {
testRuntime runtime.Runtime

coverageReport *runtime.CoverageReport

backend *EmulatorBackend
}

func NewTestRunner() *TestRunner {
Expand Down Expand Up @@ -304,7 +307,78 @@ func (r *TestRunner) parseCheckAndInterpret(script string) (*interpreter.Program
env.CheckerConfig.ContractValueHandler = contractValueHandler

// Interpreter configs
env.InterpreterConfig.ImportLocationHandler = r.interpreterImportHandler(ctx)
// env.InterpreterConfig.ImportLocationHandler = r.interpreterImportHandler(ctx)
interpreterImportHandler := func(ctx runtime.Context) func(inter *interpreter.Interpreter, location common.Location) interpreter.Import {
return func(inter *interpreter.Interpreter, location common.Location) interpreter.Import {
switch location {
case stdlib.CryptoCheckerLocation:
cryptoChecker := stdlib.CryptoChecker()
program := interpreter.ProgramFromChecker(cryptoChecker)
subInterpreter, err := inter.NewSubInterpreter(program, location)
if err != nil {
panic(err)
}
return interpreter.InterpreterImport{
Interpreter: subInterpreter,
}

case stdlib.TestContractLocation:
testChecker := stdlib.GetTestContractType().Checker
program := interpreter.ProgramFromChecker(testChecker)
subInterpreter, err := inter.NewSubInterpreter(program, location)
if err != nil {
panic(err)
}
return interpreter.InterpreterImport{
Interpreter: subInterpreter,
}

default:
addressLocation, ok := location.(common.AddressLocation)
if ok {
account, _ := r.backend.blockchain.GetAccount(
flow.Address(addressLocation.Address),
)
programCode := account.Contracts[addressLocation.Name]
program, err := env.ParseAndCheckProgram(
programCode, location, true,
)
fmt.Println("GOT HERE for: ", location)
if err != nil {
fmt.Println("GOT AN err")
panic(err)
}

subInterpreter, err := inter.NewSubInterpreter(program, addressLocation)
if err != nil {
panic(err)
}
return interpreter.InterpreterImport{
Interpreter: subInterpreter,
}
}
importedProgram, importedElaboration, err := r.parseAndCheckImport(location, ctx)
if err != nil {
panic(err)
}

program := &interpreter.Program{
Program: importedProgram,
Elaboration: importedElaboration,
}

subInterpreter, err := inter.NewSubInterpreter(program, location)
if err != nil {
panic(err)
}

return interpreter.InterpreterImport{
Interpreter: subInterpreter,
}
}
}
}
env.InterpreterConfig.ImportLocationHandler = interpreterImportHandler(ctx)

// It is safe to use the test-runner's environment as the standard library handler
// in the test framework, since it is only used for value conversions (i.e: values
Expand Down Expand Up @@ -415,15 +489,15 @@ func (r *TestRunner) interpreterContractValueHandler(
return contract

case stdlib.TestContractLocation:
testFramework := NewEmulatorBackend(
r.backend = NewEmulatorBackend(
r.fileResolver,
stdlibHandler,
r.coverageReport,
)
contract, err := stdlib.GetTestContractType().
NewTestContract(
inter,
testFramework,
r.backend,
constructorGenerator(common.Address{}),
invocationRange,
)
Expand Down Expand Up @@ -522,21 +596,33 @@ func (r *TestRunner) parseAndCheckImport(
return nil, nil, ImportResolverNotProvidedError{}
}

code, err := r.importResolver(location)
if err != nil {
return nil, nil, err
}

// Create a new (child) context, with new environment.

env := runtime.NewBaseInterpreterEnvironment(runtime.Config{})

ctx := runtime.Context{
Interface: startCtx.Interface,
Location: location,
Environment: env,
}

addressLocation, ok := location.(common.AddressLocation)
if ok {
account, _ := r.backend.blockchain.GetAccount(
flow.Address(addressLocation.Address),
)
programCode := account.Contracts[addressLocation.Name]
program, err := r.testRuntime.ParseAndCheckProgram(programCode, ctx)
if err != nil {
panic(err)
}

return program.Program, program.Elaboration, nil
}

code, err := r.importResolver(location)
if err != nil {
return nil, nil, err
}

env.CheckerConfig.ImportHandler = func(
checker *sema.Checker,
importedLocation common.Location,
Expand Down

0 comments on commit 8e2d950

Please sign in to comment.