Skip to content

Commit

Permalink
Merge pull request #197 from m-Peter/reference-deployed-contract-types
Browse files Browse the repository at this point in the history
[test] Allow test scripts to import types from deployed contracts
  • Loading branch information
SupunS authored Sep 8, 2023
2 parents b63c712 + b6814fa commit d0d3d6f
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 52 deletions.
40 changes: 22 additions & 18 deletions test/emulator_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,25 @@ const helperFilePrefix = "\x00helper/"

var _ stdlib.Blockchain = &EmulatorBackend{}

type SystemClock struct {
type systemClock struct {
TimeDelta int64
}

func (sc SystemClock) Now() time.Time {
func (sc systemClock) Now() time.Time {
return time.Now().Add(time.Second * time.Duration(sc.TimeDelta)).UTC()
}

func NewSystemClock() *SystemClock {
return &SystemClock{}
func newSystemClock() *systemClock {
return &systemClock{}
}

type DeployedContractConstructorInvocation struct {
type deployedContractConstructorInvocation struct {
ConstructorArguments []interpreter.Value
ArgumentTypes []sema.Type
}

var ContractInvocations = make(
map[string]DeployedContractConstructorInvocation,
var contractInvocations = make(
map[string]deployedContractConstructorInvocation,
0,
)

Expand All @@ -96,19 +96,22 @@ type EmulatorBackend struct {

// logCollection is a hook attached in the server logger, in order
// to aggregate and expose log messages from the blockchain.
logCollection *LogCollectionHook
logCollection *logCollectionHook

// clock allows manipulating the blockchain's clock.
clock *SystemClock
clock *systemClock
}

type keyInfo struct {
accountKey *sdk.AccountKey
signer crypto.Signer
}

var chain = flow.MonotonicEmulator.Chain()

var commonContracts = emulator.NewCommonContracts(chain)

var systemContracts = func() []common.AddressLocation {
chain := flow.Emulator.Chain()
serviceAddress := chain.ServiceAddress().HexWithPrefix()
contracts := map[string]string{
"FlowServiceAccount": serviceAddress,
Expand Down Expand Up @@ -143,7 +146,7 @@ func NewEmulatorBackend(
stdlibHandler stdlib.StandardLibraryHandler,
coverageReport *runtime.CoverageReport,
) *EmulatorBackend {
logCollectionHook := NewLogCollectionHook()
logCollectionHook := newLogCollectionHook()
var blockchain *emulator.Blockchain
if coverageReport != nil {
excludeCommonLocations(coverageReport)
Expand All @@ -154,7 +157,7 @@ func NewEmulatorBackend(
} else {
blockchain = newBlockchain(logCollectionHook)
}
clock := NewSystemClock()
clock := newSystemClock()
blockchain.SetClock(clock)

return &EmulatorBackend{
Expand Down Expand Up @@ -509,7 +512,7 @@ func (e *EmulatorBackend) DeployContract(
}
argTypes = append(argTypes, argType)
}
ContractInvocations[name] = DeployedContractConstructorInvocation{
contractInvocations[name] = deployedContractConstructorInvocation{
ConstructorArguments: args,
ArgumentTypes: argTypes,
}
Expand All @@ -524,7 +527,7 @@ func (e *EmulatorBackend) Logs() []string {

// newBlockchain returns an emulator blockchain for testing.
func newBlockchain(
hook *LogCollectionHook,
hook *logCollectionHook,
opts ...emulator.Option,
) *emulator.Blockchain {
output := zerolog.ConsoleWriter{Out: os.Stdout}
Expand All @@ -536,7 +539,8 @@ func newBlockchain(
[]emulator.Option{
emulator.WithStorageLimitEnabled(false),
emulator.WithServerLogger(logger),
emulator.Contracts(emulator.CommonContracts),
emulator.Contracts(commonContracts),
emulator.WithChainID(chain.ChainID()),
},
opts...,
)...,
Expand Down Expand Up @@ -711,7 +715,7 @@ func excludeCommonLocations(coverageReport *runtime.CoverageReport) {
for _, location := range systemContracts {
coverageReport.ExcludeLocation(location)
}
for _, contract := range emulator.CommonContracts {
for _, contract := range commonContracts {
address, _ := common.HexToAddress(contract.Address.String())
location := common.AddressLocation{
Address: address,
Expand All @@ -725,7 +729,7 @@ func excludeCommonLocations(coverageReport *runtime.CoverageReport) {
// address mappings for system/common contracts.
func baseConfiguration() *stdlib.Configuration {
addresses := make(map[string]common.Address, 0)
serviceAddress, _ := common.HexToAddress("0xf8d6e0586b0a20c7")
serviceAddress := common.Address(chain.ServiceAddress())
addresses["NonFungibleToken"] = serviceAddress
addresses["MetadataViews"] = serviceAddress
addresses["ViewResolver"] = serviceAddress
Expand All @@ -734,7 +738,7 @@ func baseConfiguration() *stdlib.Configuration {
address := common.Address(addressLocation.Address)
addresses[contract] = address
}
for _, contractDescription := range emulator.CommonContracts {
for _, contractDescription := range commonContracts {
contract := contractDescription.Name
address := common.Address(contractDescription.Address)
addresses[contract] = address
Expand Down
51 changes: 28 additions & 23 deletions test/test_framework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2957,7 +2957,7 @@ func TestServiceAccount(t *testing.T) {
require.NoError(t, err)
assert.Equal(
t,
"0xf8d6e0586b0a20c7",
"0x0000000000000001",
serviceAccount.Address.HexWithPrefix(),
)
})
Expand All @@ -2977,7 +2977,7 @@ func TestServiceAccount(t *testing.T) {
// Assert
Test.assertEqual(Type<Address>(), account.address.getType())
Test.assertEqual(Type<PublicKey>(), account.publicKey.getType())
Test.assertEqual(Address(0xf8d6e0586b0a20c7), account.address)
Test.assertEqual(Address(0x0000000000000001), account.address)
}
`

Expand Down Expand Up @@ -3377,7 +3377,7 @@ func TestCoverageReportForIntegrationTests(t *testing.T) {
assert.Equal(t, result2.TestName, "testAddSpecialNumber")
require.NoError(t, result2.Error)

address, err := common.HexToAddress("0x01cf0e2f2f715450")
address, err := common.HexToAddress("0x0000000000000005")
require.NoError(t, err)
location := common.AddressLocation{
Address: address,
Expand All @@ -3400,25 +3400,25 @@ func TestCoverageReportForIntegrationTests(t *testing.T) {
assert.ElementsMatch(
t,
[]string{
"A.0ae53cb6e3f42a79.FlowToken",
"A.ee82856bf20e2aa6.FungibleToken",
"A.e5a8b7f23e8b548f.FlowFees",
"A.f8d6e0586b0a20c7.FlowStorageFees",
"A.f8d6e0586b0a20c7.FlowServiceAccount",
"A.f8d6e0586b0a20c7.FlowClusterQC",
"A.f8d6e0586b0a20c7.FlowDKG",
"A.f8d6e0586b0a20c7.FlowEpoch",
"A.f8d6e0586b0a20c7.FlowIDTableStaking",
"A.f8d6e0586b0a20c7.FlowStakingCollection",
"A.f8d6e0586b0a20c7.LockedTokens",
"A.f8d6e0586b0a20c7.NodeVersionBeacon",
"A.f8d6e0586b0a20c7.StakingProxy",
"A.0000000000000003.FlowToken",
"A.0000000000000002.FungibleToken",
"A.0000000000000004.FlowFees",
"A.0000000000000001.FlowStorageFees",
"A.0000000000000001.FlowServiceAccount",
"A.0000000000000001.FlowClusterQC",
"A.0000000000000001.FlowDKG",
"A.0000000000000001.FlowEpoch",
"A.0000000000000001.FlowIDTableStaking",
"A.0000000000000001.FlowStakingCollection",
"A.0000000000000001.LockedTokens",
"A.0000000000000001.NodeVersionBeacon",
"A.0000000000000001.StakingProxy",
"s.7465737400000000000000000000000000000000000000000000000000000000",
"I.Crypto",
"I.Test",
"A.f8d6e0586b0a20c7.ExampleNFT",
"A.f8d6e0586b0a20c7.NFTStorefrontV2",
"A.f8d6e0586b0a20c7.NFTStorefront",
"A.0000000000000001.ExampleNFT",
"A.0000000000000001.NFTStorefrontV2",
"A.0000000000000001.NFTStorefront",
},
coverageReport.ExcludedLocationIDs(),
)
Expand Down Expand Up @@ -3889,6 +3889,7 @@ func TestGetEventsFromIntegrationTests(t *testing.T) {

const testCode = `
import Test
import FooContract from 0x0000000000000005
pub let blockchain = Test.newEmulatorBlockchain()
pub let account = blockchain.createAccount()
Expand Down Expand Up @@ -3916,7 +3917,7 @@ func TestGetEventsFromIntegrationTests(t *testing.T) {
Test.expect(result, Test.beSucceeded())
Test.assert(result.returnValue! as! Bool)
let typ = CompositeType("A.01cf0e2f2f715450.FooContract.ContractInitialized")!
let typ = Type<FooContract.ContractInitialized>()
let events = blockchain.eventsOfType(typ)
Test.assertEqual(1, events.length)
}
Expand All @@ -3933,10 +3934,14 @@ func TestGetEventsFromIntegrationTests(t *testing.T) {
let result = blockchain.executeTransaction(tx)
Test.expect(result, Test.beSucceeded())
let typ = CompositeType("A.01cf0e2f2f715450.FooContract.NumberAdded")!
let typ = Type<FooContract.NumberAdded>()
let events = blockchain.eventsOfType(typ)
Test.assertEqual(1, events.length)
let event = events[0] as! FooContract.NumberAdded
Test.assertEqual(78557, event.n)
Test.assertEqual("Sierpinski", event.trait)
let evts = blockchain.events()
Test.expect(evts.length, Test.beGreaterThan(1))
}
Expand Down Expand Up @@ -4420,7 +4425,7 @@ func TestReferenceDeployedContractTypes(t *testing.T) {

const testCode = `
import Test
import FooContract from 0x01cf0e2f2f715450
import FooContract from 0x0000000000000005
pub let blockchain = Test.newEmulatorBlockchain()
pub let account = blockchain.createAccount()
Expand Down Expand Up @@ -4543,7 +4548,7 @@ func TestReferenceDeployedContractTypes(t *testing.T) {

const testCode = `
import Test
import FooContract from 0x01cf0e2f2f715450
import FooContract from 0x0000000000000005
pub let blockchain = Test.newEmulatorBlockchain()
pub let account = blockchain.createAccount()
Expand Down
23 changes: 12 additions & 11 deletions test/test_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,23 @@ type Result struct {
Error error
}

// LogCollectionHook can be attached to zerolog.Logger objects, in order
// logCollectionHook can be attached to zerolog.Logger objects, in order
// to aggregate the log messages in a string slice, containing only the
// string message.
type LogCollectionHook struct {
type logCollectionHook struct {
Logs []string
}

var _ zerolog.Hook = &LogCollectionHook{}
var _ zerolog.Hook = &logCollectionHook{}

// NewLogCollectionHook initializes and returns a *LogCollectionHook
func NewLogCollectionHook() *LogCollectionHook {
return &LogCollectionHook{
// newLogCollectionHook initializes and returns a *LogCollectionHook
func newLogCollectionHook() *logCollectionHook {
return &logCollectionHook{
Logs: make([]string, 0),
}
}

func (h *LogCollectionHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
func (h *logCollectionHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
if level != zerolog.NoLevel {
logMsg := strings.Replace(
msg,
Expand Down Expand Up @@ -144,7 +144,7 @@ type TestRunner struct {
// logCollection is a hook attached in the program logger of
// the script environment, in order to aggregate and expose
// log messages from test cases and contracts.
logCollection *LogCollectionHook
logCollection *logCollectionHook

// randomSeed is used for randomized test case execution.
randomSeed int64
Expand All @@ -155,7 +155,7 @@ type TestRunner struct {
}

func NewTestRunner() *TestRunner {
logCollectionHook := NewLogCollectionHook()
logCollectionHook := newLogCollectionHook()
output := zerolog.ConsoleWriter{Out: os.Stdout}
output.FormatMessage = func(i interface{}) string {
msg := i.(string)
Expand All @@ -169,7 +169,8 @@ func NewTestRunner() *TestRunner {
logger := zerolog.New(output).With().Timestamp().Logger().Hook(logCollectionHook)
blockchain, err := emulator.New(
emulator.WithStorageLimitEnabled(false),
emulator.Contracts(emulator.CommonContracts),
emulator.Contracts(commonContracts),
emulator.WithChainID(chain.ChainID()),
)
if err != nil {
panic(err)
Expand Down Expand Up @@ -579,7 +580,7 @@ func (r *TestRunner) interpreterContractValueHandler(

default:
if _, ok := compositeType.Location.(common.AddressLocation); ok {
invocation, found := ContractInvocations[compositeType.Identifier]
invocation, found := contractInvocations[compositeType.Identifier]
if !found {
panic(fmt.Errorf("contract invocation not found"))
}
Expand Down

0 comments on commit d0d3d6f

Please sign in to comment.