Skip to content

Commit

Permalink
Refine API of the testing framework
Browse files Browse the repository at this point in the history
These changes will allow testing providers to unify
unit & integration tests.
  • Loading branch information
m-Peter committed Sep 14, 2023
1 parent b761511 commit eecc40a
Show file tree
Hide file tree
Showing 5 changed files with 399 additions and 90 deletions.
19 changes: 13 additions & 6 deletions runtime/stdlib/contracts/test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ pub contract Test {
return self.backend.createAccount()
}

/// Returns the account for the given address.
///
pub fun getAccount(_ address: Address): Account {
return self.backend.getAccount(address)
}

/// Add a transaction to the current block.
///
pub fun addTransaction(_ tx: Transaction) {
Expand Down Expand Up @@ -77,14 +83,12 @@ pub contract Test {
///
pub fun deployContract(
name: String,
code: String,
account: Account,
path: String,
arguments: [AnyStruct]
): Error? {
return self.backend.deployContract(
name: name,
code: code,
account: account,
path: path,
arguments: arguments
)
}
Expand Down Expand Up @@ -297,6 +301,10 @@ pub contract Test {
///
pub fun createAccount(): Account

/// Returns the account for the given address.
///
pub fun getAccount(_ address: Address): Account

/// Add a transaction to the current block.
///
pub fun addTransaction(_ tx: Transaction)
Expand All @@ -315,8 +323,7 @@ pub contract Test {
///
pub fun deployContract(
name: String,
code: String,
account: Account,
path: String,
arguments: [AnyStruct]
): Error?

Expand Down
5 changes: 3 additions & 2 deletions runtime/stdlib/test-framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type Blockchain interface {

CreateAccount() (*Account, error)

GetAccount(interpreter.AddressValue) (*Account, error)

AddTransaction(
inter *interpreter.Interpreter,
code string,
Expand All @@ -58,8 +60,7 @@ type Blockchain interface {
DeployContract(
inter *interpreter.Interpreter,
name string,
code string,
account *Account,
path string,
arguments []interpreter.Value,
) error

Expand Down
48 changes: 48 additions & 0 deletions runtime/stdlib/test_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ type TestContractType struct {
expectFailureFunction interpreter.FunctionValue
}

// 'Test.blockchain' public field

const testTypeBlockchainFieldName = "blockchain"

const testTypeBlockchainFieldDocString = `
An emulator-backed blockchain.
`

// 'Test.assert' function

const testTypeAssertFunctionDocString = `
Expand Down Expand Up @@ -1072,6 +1080,16 @@ func newTestContractType() *TestContractType {

blockchainType := ty.blockchainType()

compositeType.Fields = []string{testTypeBlockchainFieldName}
blockchainField := sema.NewPublicConstantFieldMember(
nil,
compositeType,
testTypeBlockchainFieldName,
blockchainType,
testTypeBlockchainFieldDocString,
)
compositeType.Members.Set(testTypeBlockchainFieldName, blockchainField)

// Test.assert()
compositeType.Members.Set(
testTypeAssertFunctionName,
Expand Down Expand Up @@ -1368,5 +1386,35 @@ func (t *TestContractType) NewTestContract(
compositeValue.Functions[testTypeBeLessThanFunctionName] = t.beLessThanFunction
compositeValue.Functions[testExpectFailureFunctionName] = t.expectFailureFunction

// Create an `EmulatorBackend`
emulatorBackend := t.emulatorBackendType.newEmulatorBackend(
inter,
testFramework.NewEmulatorBackend(),
interpreter.EmptyLocationRange,
)

// Create a 'Blockchain' struct value, that wraps the emulator backend,
// by calling the constructor of 'Blockchain'.
blockchainConstructor := getNestedTypeConstructorValue(
compositeValue,
testBlockchainTypeName,
)
blockchain, err := inter.InvokeExternally(
blockchainConstructor,
blockchainConstructor.Type,
[]interpreter.Value{
emulatorBackend,
},
)
if err != nil {
return nil, err
}

Check warning on line 1411 in runtime/stdlib/test_contract.go

View check run for this annotation

Codecov / codecov/patch

runtime/stdlib/test_contract.go#L1410-L1411

Added lines #L1410 - L1411 were not covered by tests
compositeValue.SetMember(
inter,
interpreter.EmptyLocationRange,
testTypeBlockchainFieldName,
blockchain,
)

return compositeValue, nil
}
77 changes: 64 additions & 13 deletions runtime/stdlib/test_emulatorbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
package stdlib

import (
"fmt"

"github.com/onflow/cadence/runtime/common"
"github.com/onflow/cadence/runtime/errors"
"github.com/onflow/cadence/runtime/interpreter"
Expand Down Expand Up @@ -49,6 +51,7 @@ type testEmulatorBackendType struct {
moveTimeFunctionType *sema.FunctionType
createSnapshotFunctionType *sema.FunctionType
loadSnapshotFunctionType *sema.FunctionType
getAccountFunctionType *sema.FunctionType
}

func newTestEmulatorBackendType(
Expand Down Expand Up @@ -124,6 +127,11 @@ func newTestEmulatorBackendType(
testEmulatorBackendTypeLoadSnapshotFunctionName,
)

getAccountFunctionType := interfaceFunctionType(
blockchainBackendInterfaceType,
testEmulatorBackendTypeGetAccountFunctionName,
)

compositeType := &sema.CompositeType{
Identifier: testEmulatorBackendTypeName,
Kind: common.CompositeKindStructure,
Expand Down Expand Up @@ -218,6 +226,12 @@ func newTestEmulatorBackendType(
loadSnapshotFunctionType,
testEmulatorBackendTypeLoadSnapshotFunctionDocString,
),
sema.NewUnmeteredPublicFunctionMember(
compositeType,
testEmulatorBackendTypeGetAccountFunctionName,
getAccountFunctionType,
testEmulatorBackendTypeGetAccountFunctionDocString,
),
}

compositeType.Members = sema.MembersAsMap(members)
Expand All @@ -239,6 +253,7 @@ func newTestEmulatorBackendType(
moveTimeFunctionType: moveTimeFunctionType,
createSnapshotFunctionType: createSnapshotFunctionType,
loadSnapshotFunctionType: loadSnapshotFunctionType,
getAccountFunctionType: getAccountFunctionType,
}
}

Expand Down Expand Up @@ -348,6 +363,47 @@ func newTestAccountValue(
return accountValue
}

// 'EmulatorBackend.getAccount' function

const testEmulatorBackendTypeGetAccountFunctionName = "getAccount"

const testEmulatorBackendTypeGetAccountFunctionDocString = `
Returns the account for the given address.
`

func (t *testEmulatorBackendType) newGetAccountFunction(
blockchain Blockchain,
) *interpreter.HostFunctionValue {
return interpreter.NewUnmeteredHostFunctionValue(
t.getAccountFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
address, ok := invocation.Arguments[0].(interpreter.AddressValue)
if !ok {
panic(errors.NewUnreachableError())

Check warning on line 382 in runtime/stdlib/test_emulatorbackend.go

View check run for this annotation

Codecov / codecov/patch

runtime/stdlib/test_emulatorbackend.go#L382

Added line #L382 was not covered by tests
}

account, err := blockchain.GetAccount(address)
if err != nil {
msg := fmt.Sprintf("account with address: %s was not found", address)
panic(PanicError{
Message: msg,
LocationRange: invocation.LocationRange,
})
}

inter := invocation.Interpreter
locationRange := invocation.LocationRange

return newTestAccountValue(
blockchain,
inter,
locationRange,
account,
)
},
)
}

// 'EmulatorBackend.addTransaction' function

const testEmulatorBackendTypeAddTransactionFunctionName = "addTransaction"
Expand Down Expand Up @@ -509,31 +565,22 @@ func (t *testEmulatorBackendType) newDeployContractFunction(
panic(errors.NewUnreachableError())
}

// Contract code
code, ok := invocation.Arguments[1].(*interpreter.StringValue)
if !ok {
panic(errors.NewUnreachableError())
}

// authorizer
accountValue, ok := invocation.Arguments[2].(interpreter.MemberAccessibleValue)
// Contract file path
path, ok := invocation.Arguments[1].(*interpreter.StringValue)
if !ok {
panic(errors.NewUnreachableError())
}

account := accountFromValue(inter, accountValue, invocation.LocationRange)

// Contract init arguments
args, err := arrayValueToSlice(inter, invocation.Arguments[3])
args, err := arrayValueToSlice(inter, invocation.Arguments[2])
if err != nil {
panic(err)
}

err = blockchain.DeployContract(
inter,
name.Str,
code.Str,
account,
path.Str,
args,
)

Expand Down Expand Up @@ -878,6 +925,10 @@ func (t *testEmulatorBackendType) newEmulatorBackend(
Name: testEmulatorBackendTypeLoadSnapshotFunctionName,
Value: t.newLoadSnapshotFunction(blockchain),
},
{
Name: testEmulatorBackendTypeGetAccountFunctionName,
Value: t.newGetAccountFunction(blockchain),
},
}

// TODO: Use SimpleCompositeValue
Expand Down
Loading

0 comments on commit eecc40a

Please sign in to comment.