From 3dadaa9029296aefccc0f4b80437cd345452fefc Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Thu, 14 Sep 2023 14:57:44 +0300 Subject: [PATCH 1/7] Refine API of the testing framework These changes will allow testing providers to unify unit & integration tests. --- runtime/stdlib/contracts/test.cdc | 19 +- runtime/stdlib/test-framework.go | 5 +- runtime/stdlib/test_contract.go | 48 ++++ runtime/stdlib/test_emulatorbackend.go | 77 +++++- runtime/stdlib/test_test.go | 340 ++++++++++++++++++++----- 5 files changed, 399 insertions(+), 90 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index b6041f7b9e..458e138db7 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -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) { @@ -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 ) } @@ -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) @@ -315,8 +323,7 @@ pub contract Test { /// pub fun deployContract( name: String, - code: String, - account: Account, + path: String, arguments: [AnyStruct] ): Error? diff --git a/runtime/stdlib/test-framework.go b/runtime/stdlib/test-framework.go index 3e8d0523c2..facb02145c 100644 --- a/runtime/stdlib/test-framework.go +++ b/runtime/stdlib/test-framework.go @@ -43,6 +43,8 @@ type Blockchain interface { CreateAccount() (*Account, error) + GetAccount(interpreter.AddressValue) (*Account, error) + AddTransaction( inter *interpreter.Interpreter, code string, @@ -58,8 +60,7 @@ type Blockchain interface { DeployContract( inter *interpreter.Interpreter, name string, - code string, - account *Account, + path string, arguments []interpreter.Value, ) error diff --git a/runtime/stdlib/test_contract.go b/runtime/stdlib/test_contract.go index 2f3baf41e4..f4faa2b7c0 100644 --- a/runtime/stdlib/test_contract.go +++ b/runtime/stdlib/test_contract.go @@ -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 = ` @@ -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, @@ -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 + } + compositeValue.SetMember( + inter, + interpreter.EmptyLocationRange, + testTypeBlockchainFieldName, + blockchain, + ) + return compositeValue, nil } diff --git a/runtime/stdlib/test_emulatorbackend.go b/runtime/stdlib/test_emulatorbackend.go index aca8706e56..876e6c6858 100644 --- a/runtime/stdlib/test_emulatorbackend.go +++ b/runtime/stdlib/test_emulatorbackend.go @@ -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" @@ -49,6 +51,7 @@ type testEmulatorBackendType struct { moveTimeFunctionType *sema.FunctionType createSnapshotFunctionType *sema.FunctionType loadSnapshotFunctionType *sema.FunctionType + getAccountFunctionType *sema.FunctionType } func newTestEmulatorBackendType( @@ -124,6 +127,11 @@ func newTestEmulatorBackendType( testEmulatorBackendTypeLoadSnapshotFunctionName, ) + getAccountFunctionType := interfaceFunctionType( + blockchainBackendInterfaceType, + testEmulatorBackendTypeGetAccountFunctionName, + ) + compositeType := &sema.CompositeType{ Identifier: testEmulatorBackendTypeName, Kind: common.CompositeKindStructure, @@ -218,6 +226,12 @@ func newTestEmulatorBackendType( loadSnapshotFunctionType, testEmulatorBackendTypeLoadSnapshotFunctionDocString, ), + sema.NewUnmeteredPublicFunctionMember( + compositeType, + testEmulatorBackendTypeGetAccountFunctionName, + getAccountFunctionType, + testEmulatorBackendTypeGetAccountFunctionDocString, + ), } compositeType.Members = sema.MembersAsMap(members) @@ -239,6 +253,7 @@ func newTestEmulatorBackendType( moveTimeFunctionType: moveTimeFunctionType, createSnapshotFunctionType: createSnapshotFunctionType, loadSnapshotFunctionType: loadSnapshotFunctionType, + getAccountFunctionType: getAccountFunctionType, } } @@ -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()) + } + + 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" @@ -509,22 +565,14 @@ 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) } @@ -532,8 +580,7 @@ func (t *testEmulatorBackendType) newDeployContractFunction( err = blockchain.DeployContract( inter, name.Str, - code.Str, - account, + path.Str, args, ) @@ -878,6 +925,10 @@ func (t *testEmulatorBackendType) newEmulatorBackend( Name: testEmulatorBackendTypeLoadSnapshotFunctionName, Value: t.newLoadSnapshotFunction(blockchain), }, + { + Name: testEmulatorBackendTypeGetAccountFunctionName, + Value: t.newGetAccountFunction(blockchain), + }, } // TODO: Use SimpleCompositeValue diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index beaa3a2f68..17a3eb95bd 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -16,7 +16,8 @@ * limitations under the License. */ -package stdlib +// This is in order to avoid cyclic import errors with runtime package +package stdlib_test import ( "errors" @@ -26,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/activations" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" @@ -33,18 +35,24 @@ import ( "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/parser" "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" "github.com/onflow/cadence/runtime/tests/checker" "github.com/onflow/cadence/runtime/tests/utils" ) func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpreter, error) { - return newTestContractInterpreterWithTestFramework(t, code, nil) + testFramework := &mockedTestFramework{ + newEmulatorBackend: func() stdlib.Blockchain { + return &mockedBlockchain{} + }, + } + return newTestContractInterpreterWithTestFramework(t, code, testFramework) } func newTestContractInterpreterWithTestFramework( t *testing.T, code string, - testFramework TestFramework, + testFramework stdlib.TestFramework, ) (*interpreter.Interpreter, error) { program, err := parser.ParseProgram( nil, @@ -54,8 +62,8 @@ func newTestContractInterpreterWithTestFramework( require.NoError(t, err) activation := sema.NewVariableActivation(sema.BaseValueActivation) - activation.DeclareValue(AssertFunction) - activation.DeclareValue(PanicFunction) + activation.DeclareValue(stdlib.AssertFunction) + activation.DeclareValue(stdlib.PanicFunction) checker, err := sema.NewChecker( program, @@ -72,15 +80,15 @@ func newTestContractInterpreterWithTestFramework( sema.Import, error, ) { - if importedLocation == TestContractLocation { + if importedLocation == stdlib.TestContractLocation { return sema.ElaborationImport{ - Elaboration: GetTestContractType().Checker.Elaboration, + Elaboration: stdlib.GetTestContractType().Checker.Elaboration, }, nil } return nil, errors.New("invalid import") }, - ContractValueHandler: TestCheckerContractValueHandler, + ContractValueHandler: stdlib.TestCheckerContractValueHandler, }, ) require.NoError(t, err) @@ -90,13 +98,13 @@ func newTestContractInterpreterWithTestFramework( return nil, err } - storage := newUnmeteredInMemoryStorage() + storage := interpreter.NewInMemoryStorage(nil) var uuid uint64 = 0 baseActivation := activations.NewActivation(nil, interpreter.BaseActivation) - interpreter.Declare(baseActivation, AssertFunction) - interpreter.Declare(baseActivation, PanicFunction) + interpreter.Declare(baseActivation, stdlib.AssertFunction) + interpreter.Declare(baseActivation, stdlib.PanicFunction) inter, err := interpreter.NewInterpreter( interpreter.ProgramFromChecker(checker), @@ -105,8 +113,8 @@ func newTestContractInterpreterWithTestFramework( Storage: storage, BaseActivation: baseActivation, ImportLocationHandler: func(inter *interpreter.Interpreter, location common.Location) interpreter.Import { - if location == TestContractLocation { - program := interpreter.ProgramFromChecker(GetTestContractType().Checker) + if location == stdlib.TestContractLocation { + program := interpreter.ProgramFromChecker(stdlib.GetTestContractType().Checker) subInterpreter, err := inter.NewSubInterpreter(program, location) if err != nil { panic(err) @@ -118,7 +126,7 @@ func newTestContractInterpreterWithTestFramework( return nil }, - ContractValueHandler: NewTestInterpreterContractValueHandler(testFramework), + ContractValueHandler: stdlib.NewTestInterpreterContractValueHandler(testFramework), UUIDHandler: func() (uint64, error) { uuid++ return uuid, nil @@ -690,7 +698,7 @@ func TestAssertEqual(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) - assert.ErrorAs(t, err, &AssertionError{}) + assert.ErrorAs(t, err, &stdlib.AssertionError{}) assert.ErrorContains( t, err, @@ -714,7 +722,7 @@ func TestAssertEqual(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) - assert.ErrorAs(t, err, &AssertionError{}) + assert.ErrorAs(t, err, &stdlib.AssertionError{}) assert.ErrorContains( t, err, @@ -749,7 +757,7 @@ func TestAssertEqual(t *testing.T) { _, err = inter.Invoke("testNotEqual") require.Error(t, err) - assert.ErrorAs(t, err, &AssertionError{}) + assert.ErrorAs(t, err, &stdlib.AssertionError{}) assert.ErrorContains( t, err, @@ -792,7 +800,7 @@ func TestAssertEqual(t *testing.T) { _, err = inter.Invoke("testNotEqual") require.Error(t, err) - assert.ErrorAs(t, err, &AssertionError{}) + assert.ErrorAs(t, err, &stdlib.AssertionError{}) assert.ErrorContains( t, err, @@ -827,7 +835,7 @@ func TestAssertEqual(t *testing.T) { _, err = inter.Invoke("testNotEqual") require.Error(t, err) - assert.ErrorAs(t, err, &AssertionError{}) + assert.ErrorAs(t, err, &stdlib.AssertionError{}) assert.ErrorContains( t, err, @@ -862,11 +870,11 @@ func TestAssertEqual(t *testing.T) { _, err = inter.Invoke("testNotEqual") require.Error(t, err) - assert.ErrorAs(t, err, &AssertionError{}) + assert.ErrorAs(t, err, &stdlib.AssertionError{}) assert.ErrorContains( t, err, - "not equal: expected: {2: false, 1: true}, actual: {2: true, 1: true}", + "not equal: expected: {2: false, 1: true}, actual: {1: true, 2: true}", ) }) @@ -1721,7 +1729,7 @@ func TestTestExpect(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) - assertionErr := &AssertionError{} + assertionErr := &stdlib.AssertionError{} assert.ErrorAs(t, err, assertionErr) assert.Equal(t, "given value is: \"this string\"", assertionErr.Message) assert.Equal(t, "test", assertionErr.LocationRange.Location.String()) @@ -1744,7 +1752,7 @@ func TestTestExpect(t *testing.T) { _, err = inter.Invoke("test") require.Error(t, err) - assert.ErrorAs(t, err, &AssertionError{}) + assert.ErrorAs(t, err, &stdlib.AssertionError{}) }) t.Run("with explicit types", func(t *testing.T) { @@ -2053,7 +2061,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain let events = blockchain.events() Test.expect(events, Test.beEmpty()) @@ -2063,7 +2071,7 @@ func TestBlockchain(t *testing.T) { eventsInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ events: func(inter *interpreter.Interpreter, eventType interpreter.StaticType) interpreter.Value { eventsInvoked = true @@ -2097,7 +2105,7 @@ func TestBlockchain(t *testing.T) { pub struct Foo {} pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain // 'Foo' is not an event-type. // But we just need to test the API, so it doesn't really matter. @@ -2112,7 +2120,7 @@ func TestBlockchain(t *testing.T) { eventsInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ events: func(inter *interpreter.Interpreter, eventType interpreter.StaticType) interpreter.Value { eventsInvoked = true @@ -2149,7 +2157,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain blockchain.reset(to: 5) } ` @@ -2157,7 +2165,7 @@ func TestBlockchain(t *testing.T) { resetInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ reset: func(height uint64) { resetInvoked = true @@ -2183,7 +2191,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain blockchain.reset(to: 5.5) } ` @@ -2191,7 +2199,7 @@ func TestBlockchain(t *testing.T) { resetInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ reset: func(height uint64) { resetInvoked = true @@ -2213,7 +2221,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun testMoveForward() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain // timeDelta is the representation of 35 days, // in the form of seconds. let timeDelta = Fix64(35 * 24 * 60 * 60) @@ -2224,7 +2232,7 @@ func TestBlockchain(t *testing.T) { moveTimeInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ moveTime: func(timeDelta int64) { moveTimeInvoked = true @@ -2250,7 +2258,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun testMoveBackward() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain // timeDelta is the representation of 35 days, // in the form of seconds. let timeDelta = Fix64(35 * 24 * 60 * 60) * -1.0 @@ -2261,7 +2269,7 @@ func TestBlockchain(t *testing.T) { moveTimeInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ moveTime: func(timeDelta int64) { moveTimeInvoked = true @@ -2287,7 +2295,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun testMoveTime() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain blockchain.moveTime(by: 3000) } ` @@ -2295,7 +2303,7 @@ func TestBlockchain(t *testing.T) { moveTimeInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ moveTime: func(timeDelta int64) { moveTimeInvoked = true @@ -2310,14 +2318,14 @@ func TestBlockchain(t *testing.T) { assert.False(t, moveTimeInvoked) }) - t.Run("newEmulatorBackend", func(t *testing.T) { + t.Run("blockchain", func(t *testing.T) { t.Parallel() const script = ` import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain Test.assertEqual(Type(), blockchain.getType()) } ` @@ -2325,7 +2333,7 @@ func TestBlockchain(t *testing.T) { newEmulatorBackendInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { newEmulatorBackendInvoked = true return &mockedBlockchain{} }, @@ -2347,7 +2355,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain blockchain.createSnapshot(name: "adminCreated") } ` @@ -2355,7 +2363,7 @@ func TestBlockchain(t *testing.T) { createSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { createSnapshotInvoked = true @@ -2383,7 +2391,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain blockchain.createSnapshot(name: "adminCreated") } ` @@ -2391,7 +2399,7 @@ func TestBlockchain(t *testing.T) { createSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { createSnapshotInvoked = true @@ -2419,7 +2427,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain blockchain.createSnapshot(name: "adminCreated") blockchain.loadSnapshot(name: "adminCreated") } @@ -2428,7 +2436,7 @@ func TestBlockchain(t *testing.T) { loadSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { assert.Equal(t, "adminCreated", name) @@ -2461,7 +2469,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.newEmulatorBlockchain() + let blockchain = Test.blockchain blockchain.createSnapshot(name: "adminCreated") blockchain.loadSnapshot(name: "contractDeployed") } @@ -2470,7 +2478,7 @@ func TestBlockchain(t *testing.T) { loadSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() Blockchain { + newEmulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { assert.Equal(t, "adminCreated", name) @@ -2496,17 +2504,203 @@ func TestBlockchain(t *testing.T) { assert.True(t, loadSnapshotInvoked) }) + t.Run("deployContract", func(t *testing.T) { + t.Parallel() + + const script = ` + import Test + + pub fun test() { + let blockchain = Test.blockchain + let err = blockchain.deployContract( + name: "FooContract", + path: "./contracts/FooContract.cdc", + arguments: ["Hey, there!"] + ) + + Test.expect(err, Test.beNil()) + } + ` + + deployContractInvoked := false + + testFramework := &mockedTestFramework{ + newEmulatorBackend: func() stdlib.Blockchain { + return &mockedBlockchain{ + deployContract: func( + inter *interpreter.Interpreter, + name string, + path string, + arguments []interpreter.Value, + ) error { + deployContractInvoked = true + assert.Equal(t, "FooContract", name) + assert.Equal(t, "./contracts/FooContract.cdc", path) + assert.Equal(t, 1, len(arguments)) + argument := arguments[0].(*interpreter.StringValue) + assert.Equal(t, "Hey, there!", argument.Str) + + return nil + }, + } + }, + } + + inter, err := newTestContractInterpreterWithTestFramework(t, script, testFramework) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.NoError(t, err) + + assert.True(t, deployContractInvoked) + }) + + t.Run("deployContract with failure", func(t *testing.T) { + t.Parallel() + + const script = ` + import Test + + pub fun test() { + let blockchain = Test.blockchain + let err = blockchain.deployContract( + name: "FooContract", + path: "./contracts/FooContract.cdc", + arguments: ["Hey, there!"] + ) + + Test.assertEqual( + "failed to deploy contract: FooContract", + err!.message + ) + } + ` + + deployContractInvoked := false + + testFramework := &mockedTestFramework{ + newEmulatorBackend: func() stdlib.Blockchain { + return &mockedBlockchain{ + deployContract: func( + inter *interpreter.Interpreter, + name string, + path string, + arguments []interpreter.Value, + ) error { + deployContractInvoked = true + + return fmt.Errorf("failed to deploy contract: %s", name) + }, + } + }, + } + + inter, err := newTestContractInterpreterWithTestFramework(t, script, testFramework) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.NoError(t, err) + + assert.True(t, deployContractInvoked) + }) + + t.Run("getAccount", func(t *testing.T) { + t.Parallel() + + const script = ` + import Test + + pub fun test() { + let blockchain = Test.blockchain + let account = blockchain.getAccount(0x0000000000000009) + } + ` + + getAccountInvoked := false + + testFramework := &mockedTestFramework{ + newEmulatorBackend: func() stdlib.Blockchain { + return &mockedBlockchain{ + getAccount: func(address interpreter.AddressValue) (*stdlib.Account, error) { + getAccountInvoked = true + assert.Equal(t, "0000000000000009", address.Hex()) + addr := common.Address(address) + + return &stdlib.Account{ + Address: addr, + PublicKey: &stdlib.PublicKey{ + PublicKey: []byte{1, 2, 3}, + SignAlgo: sema.SignatureAlgorithmECDSA_P256, + }, + }, nil + }, + stdlibHandler: func() stdlib.StandardLibraryHandler { + return runtime.NewBaseInterpreterEnvironment(runtime.Config{}) + }, + } + }, + } + + inter, err := newTestContractInterpreterWithTestFramework(t, script, testFramework) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.NoError(t, err) + + assert.True(t, getAccountInvoked) + }) + + t.Run("getAccount with failure", func(t *testing.T) { + t.Parallel() + + const script = ` + import Test + + pub fun test() { + let blockchain = Test.blockchain + let account = blockchain.getAccount(0x0000000000000009) + } + ` + + getAccountInvoked := false + + testFramework := &mockedTestFramework{ + newEmulatorBackend: func() stdlib.Blockchain { + return &mockedBlockchain{ + getAccount: func(address interpreter.AddressValue) (*stdlib.Account, error) { + getAccountInvoked = true + assert.Equal(t, "0000000000000009", address.Hex()) + + return nil, fmt.Errorf("failed to retrieve account with address: %s", address) + }, + stdlibHandler: func() stdlib.StandardLibraryHandler { + return runtime.NewBaseInterpreterEnvironment(runtime.Config{}) + }, + } + }, + } + + inter, err := newTestContractInterpreterWithTestFramework(t, script, testFramework) + require.NoError(t, err) + + _, err = inter.Invoke("test") + require.Error(t, err) + assert.ErrorContains(t, err, "account with address: 0x0000000000000009 was not found") + + assert.True(t, getAccountInvoked) + }) + // TODO: Add more tests for the remaining functions. } type mockedTestFramework struct { - newEmulatorBackend func() Blockchain + newEmulatorBackend func() stdlib.Blockchain readFile func(s string) (string, error) } -var _ TestFramework = &mockedTestFramework{} +var _ stdlib.TestFramework = &mockedTestFramework{} -func (m mockedTestFramework) NewEmulatorBackend() Blockchain { +func (m mockedTestFramework) NewEmulatorBackend() stdlib.Blockchain { if m.newEmulatorBackend == nil { panic("'NewEmulatorBackend' is not implemented") } @@ -2524,15 +2718,16 @@ func (m mockedTestFramework) ReadFile(fileName string) (string, error) { type mockedBlockchain struct { runScript func(inter *interpreter.Interpreter, code string, arguments []interpreter.Value) - createAccount func() (*Account, error) - addTransaction func(inter *interpreter.Interpreter, code string, authorizers []common.Address, signers []*Account, arguments []interpreter.Value) error - executeTransaction func() *TransactionResult + createAccount func() (*stdlib.Account, error) + getAccount func(interpreter.AddressValue) (*stdlib.Account, error) + addTransaction func(inter *interpreter.Interpreter, code string, authorizers []common.Address, signers []*stdlib.Account, arguments []interpreter.Value) error + executeTransaction func() *stdlib.TransactionResult commitBlock func() error - deployContract func(inter *interpreter.Interpreter, name string, code string, account *Account, arguments []interpreter.Value) error - useConfiguration func(configuration *Configuration) - stdlibHandler func() StandardLibraryHandler + deployContract func(inter *interpreter.Interpreter, name string, path string, arguments []interpreter.Value) error + useConfiguration func(configuration *stdlib.Configuration) + stdlibHandler func() stdlib.StandardLibraryHandler logs func() []string - serviceAccount func() (*Account, error) + serviceAccount func() (*stdlib.Account, error) events func(inter *interpreter.Interpreter, eventType interpreter.StaticType) interpreter.Value reset func(uint64) moveTime func(int64) @@ -2540,13 +2735,13 @@ type mockedBlockchain struct { loadSnapshot func(string) error } -var _ Blockchain = &mockedBlockchain{} +var _ stdlib.Blockchain = &mockedBlockchain{} func (m mockedBlockchain) RunScript( inter *interpreter.Interpreter, code string, arguments []interpreter.Value, -) *ScriptResult { +) *stdlib.ScriptResult { if m.runScript == nil { panic("'RunScript' is not implemented") } @@ -2554,7 +2749,7 @@ func (m mockedBlockchain) RunScript( return m.RunScript(inter, code, arguments) } -func (m mockedBlockchain) CreateAccount() (*Account, error) { +func (m mockedBlockchain) CreateAccount() (*stdlib.Account, error) { if m.createAccount == nil { panic("'CreateAccount' is not implemented") } @@ -2562,11 +2757,19 @@ func (m mockedBlockchain) CreateAccount() (*Account, error) { return m.createAccount() } +func (m mockedBlockchain) GetAccount(address interpreter.AddressValue) (*stdlib.Account, error) { + if m.getAccount == nil { + panic("'getAccount' is not implemented") + } + + return m.getAccount(address) +} + func (m mockedBlockchain) AddTransaction( inter *interpreter.Interpreter, code string, authorizers []common.Address, - signers []*Account, + signers []*stdlib.Account, arguments []interpreter.Value, ) error { if m.addTransaction == nil { @@ -2576,7 +2779,7 @@ func (m mockedBlockchain) AddTransaction( return m.addTransaction(inter, code, authorizers, signers, arguments) } -func (m mockedBlockchain) ExecuteNextTransaction() *TransactionResult { +func (m mockedBlockchain) ExecuteNextTransaction() *stdlib.TransactionResult { if m.executeTransaction == nil { panic("'ExecuteNextTransaction' is not implemented") } @@ -2595,18 +2798,17 @@ func (m mockedBlockchain) CommitBlock() error { func (m mockedBlockchain) DeployContract( inter *interpreter.Interpreter, name string, - code string, - account *Account, + path string, arguments []interpreter.Value, ) error { if m.deployContract == nil { panic("'DeployContract' is not implemented") } - return m.deployContract(inter, name, code, account, arguments) + return m.deployContract(inter, name, path, arguments) } -func (m mockedBlockchain) UseConfiguration(configuration *Configuration) { +func (m mockedBlockchain) UseConfiguration(configuration *stdlib.Configuration) { if m.useConfiguration == nil { panic("'UseConfiguration' is not implemented") } @@ -2614,7 +2816,7 @@ func (m mockedBlockchain) UseConfiguration(configuration *Configuration) { m.useConfiguration(configuration) } -func (m mockedBlockchain) StandardLibraryHandler() StandardLibraryHandler { +func (m mockedBlockchain) StandardLibraryHandler() stdlib.StandardLibraryHandler { if m.stdlibHandler == nil { panic("'StandardLibraryHandler' is not implemented") } @@ -2630,7 +2832,7 @@ func (m mockedBlockchain) Logs() []string { return m.logs() } -func (m mockedBlockchain) ServiceAccount() (*Account, error) { +func (m mockedBlockchain) ServiceAccount() (*stdlib.Account, error) { if m.serviceAccount == nil { panic("'ServiceAccount' is not implemented") } From 24d9c4bca06d59a16bf1b0cdef2d87a3ca01e21f Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 15 Sep 2023 14:54:35 +0300 Subject: [PATCH 2/7] Remove entirely the 'Test.Blockchain' struct --- runtime/stdlib/contracts/test.cdc | 263 +++++++++++++++--------------- runtime/stdlib/test_contract.go | 175 +++----------------- runtime/stdlib/test_test.go | 83 +++------- 3 files changed, 168 insertions(+), 353 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index 458e138db7..04fe12aa36 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -2,164 +2,161 @@ /// pub contract Test { - /// Blockchain emulates a real network. + /// backend emulates a real network. /// - pub struct Blockchain { + pub let backend: AnyStruct{BlockchainBackend} - pub let backend: AnyStruct{BlockchainBackend} + init(backend: AnyStruct{BlockchainBackend}) { + self.backend = backend + } - init(backend: AnyStruct{BlockchainBackend}) { - self.backend = backend - } + /// Executes a script and returns the script return value and the status. + /// `returnValue` field of the result will be `nil` if the script failed. + /// + pub fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult { + return self.backend.executeScript(script, arguments) + } - /// Executes a script and returns the script return value and the status. - /// `returnValue` field of the result will be `nil` if the script failed. - /// - pub fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult { - return self.backend.executeScript(script, arguments) - } + /// Creates a signer account by submitting an account creation transaction. + /// The transaction is paid by the service account. + /// The returned account can be used to sign and authorize transactions. + /// + pub fun createAccount(): Account { + return self.backend.createAccount() + } - /// Creates a signer account by submitting an account creation transaction. - /// The transaction is paid by the service account. - /// The returned account can be used to sign and authorize transactions. - /// - pub fun createAccount(): Account { - return self.backend.createAccount() - } + /// Returns the account for the given address. + /// + pub fun getAccount(_ address: Address): Account { + return self.backend.getAccount(address) + } - /// 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) { + self.backend.addTransaction(tx) + } - /// Add a transaction to the current block. - /// - pub fun addTransaction(_ tx: Transaction) { - self.backend.addTransaction(tx) - } + /// Executes the next transaction in the block, if any. + /// Returns the result of the transaction, or nil if no transaction was scheduled. + /// + pub fun executeNextTransaction(): TransactionResult? { + return self.backend.executeNextTransaction() + } - /// Executes the next transaction in the block, if any. - /// Returns the result of the transaction, or nil if no transaction was scheduled. - /// - pub fun executeNextTransaction(): TransactionResult? { - return self.backend.executeNextTransaction() - } + /// Commit the current block. + /// Committing will fail if there are un-executed transactions in the block. + /// + pub fun commitBlock() { + self.backend.commitBlock() + } - /// Commit the current block. - /// Committing will fail if there are un-executed transactions in the block. - /// - pub fun commitBlock() { - self.backend.commitBlock() - } + /// Executes a given transaction and commit the current block. + /// + pub fun executeTransaction(_ tx: Transaction): TransactionResult { + self.addTransaction(tx) + let txResult = self.executeNextTransaction()! + self.commitBlock() + return txResult + } - /// Executes a given transaction and commit the current block. - /// - pub fun executeTransaction(_ tx: Transaction): TransactionResult { + /// Executes a given set of transactions and commit the current block. + /// + pub fun executeTransactions(_ transactions: [Transaction]): [TransactionResult] { + for tx in transactions { self.addTransaction(tx) - let txResult = self.executeNextTransaction()! - self.commitBlock() - return txResult } - /// Executes a given set of transactions and commit the current block. - /// - pub fun executeTransactions(_ transactions: [Transaction]): [TransactionResult] { - for tx in transactions { - self.addTransaction(tx) - } - - var results: [TransactionResult] = [] - for tx in transactions { - let txResult = self.executeNextTransaction()! - results.append(txResult) - } - - self.commitBlock() - return results + var results: [TransactionResult] = [] + for tx in transactions { + let txResult = self.executeNextTransaction()! + results.append(txResult) } - /// Deploys a given contract, and initilizes it with the arguments. - /// - pub fun deployContract( - name: String, - path: String, - arguments: [AnyStruct] - ): Error? { - return self.backend.deployContract( - name: name, - path: path, - arguments: arguments - ) - } + self.commitBlock() + return results + } - /// Set the configuration to be used by the blockchain. - /// Overrides any existing configuration. - /// - pub fun useConfiguration(_ configuration: Configuration) { - self.backend.useConfiguration(configuration) - } + /// Deploys a given contract, and initilizes it with the arguments. + /// + pub fun deployContract( + name: String, + path: String, + arguments: [AnyStruct] + ): Error? { + return self.backend.deployContract( + name: name, + path: path, + arguments: arguments + ) + } - /// Returns all the logs from the blockchain, up to the calling point. - /// - pub fun logs(): [String] { - return self.backend.logs() - } + /// Set the configuration to be used by the blockchain. + /// Overrides any existing configuration. + /// + pub fun useConfiguration(_ configuration: Configuration) { + self.backend.useConfiguration(configuration) + } - /// Returns the service account of the blockchain. Can be used to sign - /// transactions with this account. - /// - pub fun serviceAccount(): Account { - return self.backend.serviceAccount() - } + /// Returns all the logs from the blockchain, up to the calling point. + /// + pub fun logs(): [String] { + return self.backend.logs() + } - /// Returns all events emitted from the blockchain. - /// - pub fun events(): [AnyStruct] { - return self.backend.events(nil) - } + /// Returns the service account of the blockchain. Can be used to sign + /// transactions with this account. + /// + pub fun serviceAccount(): Account { + return self.backend.serviceAccount() + } - /// Returns all events emitted from the blockchain, - /// filtered by type. - /// - pub fun eventsOfType(_ type: Type): [AnyStruct] { - return self.backend.events(type) - } + /// Returns all events emitted from the blockchain. + /// + pub fun events(): [AnyStruct] { + return self.backend.events(nil) + } - /// Resets the state of the blockchain to the given height. - /// - pub fun reset(to height: UInt64) { - self.backend.reset(to: height) - } + /// Returns all events emitted from the blockchain, + /// filtered by type. + /// + pub fun eventsOfType(_ type: Type): [AnyStruct] { + return self.backend.events(type) + } - /// Moves the time of the blockchain by the given delta, - /// which should be passed in the form of seconds. - /// - pub fun moveTime(by delta: Fix64) { - self.backend.moveTime(by: delta) - } + /// Resets the state of the blockchain to the given height. + /// + pub fun reset(to height: UInt64) { + self.backend.reset(to: height) + } - /// Creates a snapshot of the blockchain, at the - /// current ledger state, with the given name. - /// - access(all) - fun createSnapshot(name: String) { - let err = self.backend.createSnapshot(name: name) - if err != nil { - panic(err!.message) - } + /// Moves the time of the blockchain by the given delta, + /// which should be passed in the form of seconds. + /// + pub fun moveTime(by delta: Fix64) { + self.backend.moveTime(by: delta) + } + + /// Creates a snapshot of the blockchain, at the + /// current ledger state, with the given name. + /// + access(all) + fun createSnapshot(name: String) { + let err = self.backend.createSnapshot(name: name) + if err != nil { + panic(err!.message) } + } - /// Loads a snapshot of the blockchain, with the - /// given name, and updates the current ledger - /// state. - /// - access(all) - fun loadSnapshot(name: String) { - let err = self.backend.loadSnapshot(name: name) - if err != nil { - panic(err!.message) - } + /// Loads a snapshot of the blockchain, with the + /// given name, and updates the current ledger + /// state. + /// + access(all) + fun loadSnapshot(name: String) { + let err = self.backend.loadSnapshot(name: name) + if err != nil { + panic(err!.message) } } diff --git a/runtime/stdlib/test_contract.go b/runtime/stdlib/test_contract.go index f4faa2b7c0..0cd3db214c 100644 --- a/runtime/stdlib/test_contract.go +++ b/runtime/stdlib/test_contract.go @@ -32,30 +32,21 @@ import ( ) type TestContractType struct { - Checker *sema.Checker - CompositeType *sema.CompositeType - InitializerTypes []sema.Type - emulatorBackendType *testEmulatorBackendType - newEmulatorBlockchainFunctionType *sema.FunctionType - expectFunction interpreter.FunctionValue - newMatcherFunction interpreter.FunctionValue - haveElementCountFunction interpreter.FunctionValue - beEmptyFunction interpreter.FunctionValue - equalFunction interpreter.FunctionValue - beGreaterThanFunction interpreter.FunctionValue - containFunction interpreter.FunctionValue - beLessThanFunction interpreter.FunctionValue - expectFailureFunction interpreter.FunctionValue + Checker *sema.Checker + CompositeType *sema.CompositeType + InitializerTypes []sema.Type + emulatorBackendType *testEmulatorBackendType + expectFunction interpreter.FunctionValue + newMatcherFunction interpreter.FunctionValue + haveElementCountFunction interpreter.FunctionValue + beEmptyFunction interpreter.FunctionValue + equalFunction interpreter.FunctionValue + beGreaterThanFunction interpreter.FunctionValue + containFunction interpreter.FunctionValue + beLessThanFunction interpreter.FunctionValue + expectFailureFunction interpreter.FunctionValue } -// 'Test.blockchain' public field - -const testTypeBlockchainFieldName = "blockchain" - -const testTypeBlockchainFieldDocString = ` -An emulator-backed blockchain. -` - // 'Test.assert' function const testTypeAssertFunctionDocString = ` @@ -388,65 +379,6 @@ func newTestTypeReadFileFunction(testFramework TestFramework) *interpreter.HostF ) } -// 'Test.newEmulatorBlockchain' function - -const testTypeNewEmulatorBlockchainFunctionDocString = ` -Creates a blockchain which is backed by a new emulator instance. -` - -const testTypeNewEmulatorBlockchainFunctionName = "newEmulatorBlockchain" - -const testBlockchainTypeName = "Blockchain" - -func newTestTypeNewEmulatorBlockchainFunctionType(blockchainType *sema.CompositeType) *sema.FunctionType { - return &sema.FunctionType{ - ReturnTypeAnnotation: sema.NewTypeAnnotation( - blockchainType, - ), - } -} - -func (t *TestContractType) newNewEmulatorBlockchainFunction( - testFramework TestFramework, -) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - t.newEmulatorBlockchainFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - inter := invocation.Interpreter - locationRange := invocation.LocationRange - - // Create an `EmulatorBackend` - emulatorBackend := t.emulatorBackendType.newEmulatorBackend( - inter, - testFramework.NewEmulatorBackend(), - locationRange, - ) - - // Create a 'Blockchain' struct value, that wraps the emulator backend, - // by calling the constructor of 'Blockchain'. - - blockchainConstructor := getNestedTypeConstructorValue( - *invocation.Self, - testBlockchainTypeName, - ) - - blockchain, err := inter.InvokeExternally( - blockchainConstructor, - blockchainConstructor.Type, - []interpreter.Value{ - emulatorBackend, - }, - ) - - if err != nil { - panic(err) - } - - return blockchain - }, - ) -} - // 'Test.NewMatcher' function. // Constructs a matcher that test only 'AnyStruct'. // Accepts test function that accepts subtype of 'AnyStruct'. @@ -1078,18 +1010,6 @@ func newTestContractType() *TestContractType { matcherType := ty.matcherType() matcherTestFunctionType := compositeFunctionType(matcherType, matcherTestFunctionName) - 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, @@ -1123,19 +1043,6 @@ func newTestContractType() *TestContractType { ), ) - // Test.newEmulatorBlockchain() - newEmulatorBlockchainFunctionType := newTestTypeNewEmulatorBlockchainFunctionType(blockchainType) - compositeType.Members.Set( - testTypeNewEmulatorBlockchainFunctionName, - sema.NewUnmeteredPublicFunctionMember( - compositeType, - testTypeNewEmulatorBlockchainFunctionName, - newEmulatorBlockchainFunctionType, - testTypeNewEmulatorBlockchainFunctionDocString, - ), - ) - ty.newEmulatorBlockchainFunctionType = newEmulatorBlockchainFunctionType - // Test.readFile() compositeType.Members.Set( testTypeReadFileFunctionName, @@ -1326,23 +1233,6 @@ func (t *TestContractType) matcherType() *sema.CompositeType { return matcherType } -func (t *TestContractType) blockchainType() *sema.CompositeType { - typ, ok := t.CompositeType.NestedTypes.Get(testBlockchainTypeName) - if !ok { - panic(typeNotFoundError(testContractTypeName, testBlockchainTypeName)) - } - - matcherType, ok := typ.(*sema.CompositeType) - if !ok || matcherType.Kind != common.CompositeKindStructure { - panic(errors.NewUnexpectedError( - "invalid type for '%s'. expected struct type", - testMatcherTypeName, - )) - } - - return matcherType -} - func (t *TestContractType) NewTestContract( inter *interpreter.Interpreter, testFramework TestFramework, @@ -1353,9 +1243,14 @@ func (t *TestContractType) NewTestContract( error, ) { initializerTypes := t.InitializerTypes + emulatorBackend := t.emulatorBackendType.newEmulatorBackend( + inter, + testFramework.NewEmulatorBackend(), + interpreter.EmptyLocationRange, + ) value, err := inter.InvokeFunctionValue( constructor, - nil, + []interpreter.Value{emulatorBackend}, initializerTypes, initializerTypes, invocationRange, @@ -1371,8 +1266,6 @@ func (t *TestContractType) NewTestContract( compositeValue.Functions[testTypeAssertEqualFunctionName] = testTypeAssertEqualFunction compositeValue.Functions[testTypeFailFunctionName] = testTypeFailFunction compositeValue.Functions[testTypeExpectFunctionName] = t.expectFunction - compositeValue.Functions[testTypeNewEmulatorBlockchainFunctionName] = - t.newNewEmulatorBlockchainFunction(testFramework) compositeValue.Functions[testTypeReadFileFunctionName] = newTestTypeReadFileFunction(testFramework) @@ -1386,35 +1279,5 @@ 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 - } - compositeValue.SetMember( - inter, - interpreter.EmptyLocationRange, - testTypeBlockchainFieldName, - blockchain, - ) - return compositeValue, nil } diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 17a3eb95bd..a47bfbea94 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -874,7 +874,7 @@ func TestAssertEqual(t *testing.T) { assert.ErrorContains( t, err, - "not equal: expected: {2: false, 1: true}, actual: {1: true, 2: true}", + "not equal: expected: {1: true, 2: false}, actual: {2: true, 1: true}", ) }) @@ -2061,8 +2061,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - let events = blockchain.events() + let events = Test.events() Test.expect(events, Test.beEmpty()) } @@ -2105,13 +2104,11 @@ func TestBlockchain(t *testing.T) { pub struct Foo {} pub fun test() { - let blockchain = Test.blockchain - // 'Foo' is not an event-type. // But we just need to test the API, so it doesn't really matter. let typ = Type() - let events = blockchain.eventsOfType(typ) + let events = Test.eventsOfType(typ) Test.expect(events, Test.beEmpty()) } @@ -2157,8 +2154,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - blockchain.reset(to: 5) + Test.reset(to: 5) } ` @@ -2191,8 +2187,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - blockchain.reset(to: 5.5) + Test.reset(to: 5.5) } ` @@ -2221,11 +2216,10 @@ func TestBlockchain(t *testing.T) { import Test pub fun testMoveForward() { - let blockchain = Test.blockchain // timeDelta is the representation of 35 days, // in the form of seconds. let timeDelta = Fix64(35 * 24 * 60 * 60) - blockchain.moveTime(by: timeDelta) + Test.moveTime(by: timeDelta) } ` @@ -2258,11 +2252,10 @@ func TestBlockchain(t *testing.T) { import Test pub fun testMoveBackward() { - let blockchain = Test.blockchain // timeDelta is the representation of 35 days, // in the form of seconds. let timeDelta = Fix64(35 * 24 * 60 * 60) * -1.0 - blockchain.moveTime(by: timeDelta) + Test.moveTime(by: timeDelta) } ` @@ -2295,8 +2288,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun testMoveTime() { - let blockchain = Test.blockchain - blockchain.moveTime(by: 3000) + Test.moveTime(by: 3000) } ` @@ -2318,36 +2310,6 @@ func TestBlockchain(t *testing.T) { assert.False(t, moveTimeInvoked) }) - t.Run("blockchain", func(t *testing.T) { - t.Parallel() - - const script = ` - import Test - - pub fun test() { - let blockchain = Test.blockchain - Test.assertEqual(Type(), blockchain.getType()) - } - ` - - newEmulatorBackendInvoked := false - - testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { - newEmulatorBackendInvoked = true - return &mockedBlockchain{} - }, - } - - inter, err := newTestContractInterpreterWithTestFramework(t, script, testFramework) - require.NoError(t, err) - - _, err = inter.Invoke("test") - require.NoError(t, err) - - assert.True(t, newEmulatorBackendInvoked) - }) - t.Run("createSnapshot", func(t *testing.T) { t.Parallel() @@ -2355,8 +2317,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - blockchain.createSnapshot(name: "adminCreated") + Test.createSnapshot(name: "adminCreated") } ` @@ -2391,8 +2352,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - blockchain.createSnapshot(name: "adminCreated") + Test.createSnapshot(name: "adminCreated") } ` @@ -2427,9 +2387,8 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - blockchain.createSnapshot(name: "adminCreated") - blockchain.loadSnapshot(name: "adminCreated") + Test.createSnapshot(name: "adminCreated") + Test.loadSnapshot(name: "adminCreated") } ` @@ -2469,9 +2428,8 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - blockchain.createSnapshot(name: "adminCreated") - blockchain.loadSnapshot(name: "contractDeployed") + Test.createSnapshot(name: "adminCreated") + Test.loadSnapshot(name: "contractDeployed") } ` @@ -2511,8 +2469,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - let err = blockchain.deployContract( + let err = Test.deployContract( name: "FooContract", path: "./contracts/FooContract.cdc", arguments: ["Hey, there!"] @@ -2562,8 +2519,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - let err = blockchain.deployContract( + let err = Test.deployContract( name: "FooContract", path: "./contracts/FooContract.cdc", arguments: ["Hey, there!"] @@ -2611,8 +2567,8 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - let account = blockchain.getAccount(0x0000000000000009) + let account = Test.getAccount(0x0000000000000009) + Test.assertEqual(0x0000000000000009 as Address, account.address) } ` @@ -2657,8 +2613,7 @@ func TestBlockchain(t *testing.T) { import Test pub fun test() { - let blockchain = Test.blockchain - let account = blockchain.getAccount(0x0000000000000009) + let account = Test.getAccount(0x0000000000000009) } ` From d39fa9eb8b1edecba066ea4c8d51a5768cc846a3 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 15 Sep 2023 16:16:01 +0300 Subject: [PATCH 3/7] Caching of initializeMemberResolvers seems to cause a bug For contracts that contain both a .cdc source file, with Cadence code, and with some functions being implemented natively, the GetMembers() does not return the natively implemented members. --- runtime/sema/type.go | 14 ++++++++++++-- runtime/stdlib/test_contract.go | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 9e2ab8b422..821b8550b4 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4262,7 +4262,11 @@ func (t *CompositeType) GetMembers() map[string]MemberResolver { } func (t *CompositeType) initializeMemberResolvers() { - t.memberResolversOnce.Do(func() { + t.memberResolversOnce.Do(t.initializerMemberResolversFunc()) +} + +func (t *CompositeType) initializerMemberResolversFunc() func() { + return func() { memberResolvers := MembersMapAsResolvers(t.Members) // Check conformances. @@ -4281,7 +4285,13 @@ func (t *CompositeType) initializeMemberResolvers() { }) t.memberResolvers = withBuiltinMembers(t, memberResolvers) - }) + } +} + +func (t *CompositeType) ResolveMembers() { + if t.Members.Len() != len(t.GetMembers()) { + t.initializerMemberResolversFunc()() + } } func (t *CompositeType) FieldPosition(name string, declaration ast.CompositeLikeDeclaration) ast.Position { diff --git a/runtime/stdlib/test_contract.go b/runtime/stdlib/test_contract.go index 0cd3db214c..36cb2980df 100644 --- a/runtime/stdlib/test_contract.go +++ b/runtime/stdlib/test_contract.go @@ -1193,6 +1193,7 @@ func newTestContractType() *TestContractType { ty.expectFailureFunction = newTestTypeExpectFailureFunction( expectFailureFunctionType, ) + compositeType.ResolveMembers() return ty } From b1b8bdd207de5d5f4d33119591589dcc4ca7200a Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Tue, 19 Sep 2023 12:02:49 +0300 Subject: [PATCH 4/7] Remove entirely the 'Test.useConfiguration' function --- runtime/stdlib/contracts/test.cdc | 12 ---- runtime/stdlib/test-framework.go | 6 -- runtime/stdlib/test.go | 2 - runtime/stdlib/test_emulatorbackend.go | 76 -------------------------- runtime/stdlib/test_test.go | 9 --- 5 files changed, 105 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index 04fe12aa36..1f9972fb1e 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -91,13 +91,6 @@ pub contract Test { ) } - /// Set the configuration to be used by the blockchain. - /// Overrides any existing configuration. - /// - pub fun useConfiguration(_ configuration: Configuration) { - self.backend.useConfiguration(configuration) - } - /// Returns all the logs from the blockchain, up to the calling point. /// pub fun logs(): [String] { @@ -324,11 +317,6 @@ pub contract Test { arguments: [AnyStruct] ): Error? - /// Set the configuration to be used by the blockchain. - /// Overrides any existing configuration. - /// - pub fun useConfiguration(_ configuration: Configuration) - /// Returns all the logs from the blockchain, up to the calling point. /// pub fun logs(): [String] diff --git a/runtime/stdlib/test-framework.go b/runtime/stdlib/test-framework.go index facb02145c..c8b68cedc1 100644 --- a/runtime/stdlib/test-framework.go +++ b/runtime/stdlib/test-framework.go @@ -64,8 +64,6 @@ type Blockchain interface { arguments []interpreter.Value, ) error - UseConfiguration(configuration *Configuration) - StandardLibraryHandler() StandardLibraryHandler Logs() []string @@ -99,7 +97,3 @@ type Account struct { PublicKey *PublicKey Address common.Address } - -type Configuration struct { - Addresses map[string]common.Address -} diff --git a/runtime/stdlib/test.go b/runtime/stdlib/test.go index db3b08ff59..f4866afc23 100644 --- a/runtime/stdlib/test.go +++ b/runtime/stdlib/test.go @@ -48,8 +48,6 @@ const accountAddressFieldName = "address" const matcherTestFunctionName = "test" -const addressesFieldName = "addresses" - const TestContractLocation = common.IdentifierLocation(testContractTypeName) var testOnce sync.Once diff --git a/runtime/stdlib/test_emulatorbackend.go b/runtime/stdlib/test_emulatorbackend.go index 876e6c6858..1e087139ea 100644 --- a/runtime/stdlib/test_emulatorbackend.go +++ b/runtime/stdlib/test_emulatorbackend.go @@ -43,7 +43,6 @@ type testEmulatorBackendType struct { executeNextTransactionFunctionType *sema.FunctionType commitBlockFunctionType *sema.FunctionType deployContractFunctionType *sema.FunctionType - useConfigFunctionType *sema.FunctionType logsFunctionType *sema.FunctionType serviceAccountFunctionType *sema.FunctionType eventsFunctionType *sema.FunctionType @@ -87,11 +86,6 @@ func newTestEmulatorBackendType( testEmulatorBackendTypeDeployContractFunctionName, ) - useConfigFunctionType := interfaceFunctionType( - blockchainBackendInterfaceType, - testEmulatorBackendTypeUseConfigFunctionName, - ) - logsFunctionType := interfaceFunctionType( blockchainBackendInterfaceType, testEmulatorBackendTypeLogsFunctionName, @@ -178,12 +172,6 @@ func newTestEmulatorBackendType( deployContractFunctionType, testEmulatorBackendTypeDeployContractFunctionDocString, ), - sema.NewUnmeteredPublicFunctionMember( - compositeType, - testEmulatorBackendTypeUseConfigFunctionName, - useConfigFunctionType, - testEmulatorBackendTypeUseConfigFunctionDocString, - ), sema.NewUnmeteredPublicFunctionMember( compositeType, testEmulatorBackendTypeLogsFunctionName, @@ -245,7 +233,6 @@ func newTestEmulatorBackendType( executeNextTransactionFunctionType: executeNextTransactionFunctionType, commitBlockFunctionType: commitBlockFunctionType, deployContractFunctionType: deployContractFunctionType, - useConfigFunctionType: useConfigFunctionType, logsFunctionType: logsFunctionType, serviceAccountFunctionType: serviceAccountFunctionType, eventsFunctionType: eventsFunctionType, @@ -589,65 +576,6 @@ func (t *testEmulatorBackendType) newDeployContractFunction( ) } -// 'EmulatorBackend.useConfiguration' function - -const testEmulatorBackendTypeUseConfigFunctionName = "useConfiguration" - -const testEmulatorBackendTypeUseConfigFunctionDocString = ` -Set the configuration to be used by the blockchain. -Overrides any existing configuration. -` - -func (t *testEmulatorBackendType) newUseConfigFunction( - blockchain Blockchain, -) *interpreter.HostFunctionValue { - return interpreter.NewUnmeteredHostFunctionValue( - t.useConfigFunctionType, - func(invocation interpreter.Invocation) interpreter.Value { - inter := invocation.Interpreter - - // configurations - configsValue, ok := invocation.Arguments[0].(*interpreter.CompositeValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - addresses, ok := configsValue.GetMember( - inter, - invocation.LocationRange, - addressesFieldName, - ).(*interpreter.DictionaryValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - mapping := make(map[string]common.Address, addresses.Count()) - - addresses.Iterate(inter, func(locationValue, addressValue interpreter.Value) bool { - location, ok := locationValue.(*interpreter.StringValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - address, ok := addressValue.(interpreter.AddressValue) - if !ok { - panic(errors.NewUnreachableError()) - } - - mapping[location.Str] = common.Address(address) - - return true - }) - - blockchain.UseConfiguration(&Configuration{ - Addresses: mapping, - }) - - return interpreter.Void - }, - ) -} - // 'EmulatorBackend.logs' function const testEmulatorBackendTypeLogsFunctionName = "logs" @@ -893,10 +821,6 @@ func (t *testEmulatorBackendType) newEmulatorBackend( Name: testEmulatorBackendTypeDeployContractFunctionName, Value: t.newDeployContractFunction(blockchain), }, - { - Name: testEmulatorBackendTypeUseConfigFunctionName, - Value: t.newUseConfigFunction(blockchain), - }, { Name: testEmulatorBackendTypeLogsFunctionName, Value: t.newLogsFunction(blockchain), diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index a47bfbea94..734c3a66d7 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -2679,7 +2679,6 @@ type mockedBlockchain struct { executeTransaction func() *stdlib.TransactionResult commitBlock func() error deployContract func(inter *interpreter.Interpreter, name string, path string, arguments []interpreter.Value) error - useConfiguration func(configuration *stdlib.Configuration) stdlibHandler func() stdlib.StandardLibraryHandler logs func() []string serviceAccount func() (*stdlib.Account, error) @@ -2763,14 +2762,6 @@ func (m mockedBlockchain) DeployContract( return m.deployContract(inter, name, path, arguments) } -func (m mockedBlockchain) UseConfiguration(configuration *stdlib.Configuration) { - if m.useConfiguration == nil { - panic("'UseConfiguration' is not implemented") - } - - m.useConfiguration(configuration) -} - func (m mockedBlockchain) StandardLibraryHandler() stdlib.StandardLibraryHandler { if m.stdlibHandler == nil { panic("'StandardLibraryHandler' is not implemented") From 34f6ebea1c4e89e48a9ade63e4722cbbe6bbb2f3 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Fri, 22 Sep 2023 12:20:55 +0300 Subject: [PATCH 5/7] Remove left-over Test.Configuration struct --- runtime/stdlib/contracts/test.cdc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index 1f9972fb1e..b2e6ba9698 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -249,17 +249,6 @@ pub contract Test { } } - /// Configuration to be used by the blockchain. - /// Can be used to set the address mappings. - /// - pub struct Configuration { - pub let addresses: {String: Address} - - init(addresses: {String: Address}) { - self.addresses = addresses - } - } - /// Transaction that can be submitted and executed on the blockchain. /// pub struct Transaction { From 72bbfe1e5d4a5ed438762cfe05a6c7b1c1f731ef Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 25 Sep 2023 12:42:56 +0300 Subject: [PATCH 6/7] Use new access modifiers in Test contract --- runtime/stdlib/contracts/test.cdc | 160 ++++++++++++++++++------------ 1 file changed, 97 insertions(+), 63 deletions(-) diff --git a/runtime/stdlib/contracts/test.cdc b/runtime/stdlib/contracts/test.cdc index b2e6ba9698..8e7a39da7d 100644 --- a/runtime/stdlib/contracts/test.cdc +++ b/runtime/stdlib/contracts/test.cdc @@ -1,10 +1,10 @@ /// Test contract is the standard library that provides testing functionality in Cadence. /// -pub contract Test { +access(all) contract Test { /// backend emulates a real network. /// - pub let backend: AnyStruct{BlockchainBackend} + access(self) let backend: AnyStruct{BlockchainBackend} init(backend: AnyStruct{BlockchainBackend}) { self.backend = backend @@ -13,7 +13,8 @@ pub contract Test { /// Executes a script and returns the script return value and the status. /// `returnValue` field of the result will be `nil` if the script failed. /// - pub fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult { + access(all) + fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult { return self.backend.executeScript(script, arguments) } @@ -21,39 +22,45 @@ pub contract Test { /// The transaction is paid by the service account. /// The returned account can be used to sign and authorize transactions. /// - pub fun createAccount(): Account { + access(all) + fun createAccount(): Account { return self.backend.createAccount() } /// Returns the account for the given address. /// - pub fun getAccount(_ address: Address): Account { + access(all) + fun getAccount(_ address: Address): Account { return self.backend.getAccount(address) } /// Add a transaction to the current block. /// - pub fun addTransaction(_ tx: Transaction) { + access(all) + fun addTransaction(_ tx: Transaction) { self.backend.addTransaction(tx) } /// Executes the next transaction in the block, if any. /// Returns the result of the transaction, or nil if no transaction was scheduled. /// - pub fun executeNextTransaction(): TransactionResult? { + access(all) + fun executeNextTransaction(): TransactionResult? { return self.backend.executeNextTransaction() } /// Commit the current block. /// Committing will fail if there are un-executed transactions in the block. /// - pub fun commitBlock() { + access(all) + fun commitBlock() { self.backend.commitBlock() } /// Executes a given transaction and commit the current block. /// - pub fun executeTransaction(_ tx: Transaction): TransactionResult { + access(all) + fun executeTransaction(_ tx: Transaction): TransactionResult { self.addTransaction(tx) let txResult = self.executeNextTransaction()! self.commitBlock() @@ -62,7 +69,8 @@ pub contract Test { /// Executes a given set of transactions and commit the current block. /// - pub fun executeTransactions(_ transactions: [Transaction]): [TransactionResult] { + access(all) + fun executeTransactions(_ transactions: [Transaction]): [TransactionResult] { for tx in transactions { self.addTransaction(tx) } @@ -79,7 +87,8 @@ pub contract Test { /// Deploys a given contract, and initilizes it with the arguments. /// - pub fun deployContract( + access(all) + fun deployContract( name: String, path: String, arguments: [AnyStruct] @@ -93,40 +102,46 @@ pub contract Test { /// Returns all the logs from the blockchain, up to the calling point. /// - pub fun logs(): [String] { + access(all) + fun logs(): [String] { return self.backend.logs() } /// Returns the service account of the blockchain. Can be used to sign /// transactions with this account. /// - pub fun serviceAccount(): Account { + access(all) + fun serviceAccount(): Account { return self.backend.serviceAccount() } /// Returns all events emitted from the blockchain. /// - pub fun events(): [AnyStruct] { + access(all) + fun events(): [AnyStruct] { return self.backend.events(nil) } /// Returns all events emitted from the blockchain, /// filtered by type. /// - pub fun eventsOfType(_ type: Type): [AnyStruct] { + access(all) + fun eventsOfType(_ type: Type): [AnyStruct] { return self.backend.events(type) } /// Resets the state of the blockchain to the given height. /// - pub fun reset(to height: UInt64) { + access(all) + fun reset(to height: UInt64) { self.backend.reset(to: height) } /// Moves the time of the blockchain by the given delta, /// which should be passed in the form of seconds. /// - pub fun moveTime(by delta: Fix64) { + access(all) + fun moveTime(by delta: Fix64) { self.backend.moveTime(by: delta) } @@ -153,18 +168,19 @@ pub contract Test { } } - pub struct Matcher { + access(all) struct Matcher { - pub let test: ((AnyStruct): Bool) + access(all) let test: ((AnyStruct): Bool) - pub init(test: ((AnyStruct): Bool)) { + init(test: ((AnyStruct): Bool)) { self.test = test } /// Combine this matcher with the given matcher. /// Returns a new matcher that succeeds if this and the given matcher succeed. /// - pub fun and(_ other: Matcher): Matcher { + access(all) + fun and(_ other: Matcher): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { return self.test(value) && other.test(value) }) @@ -174,7 +190,8 @@ pub contract Test { /// Returns a new matcher that succeeds if this or the given matcher succeed. /// If this matcher succeeds, then the other matcher would not be tested. /// - pub fun or(_ other: Matcher): Matcher { + access(all) + fun or(_ other: Matcher): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { return self.test(value) || other.test(value) }) @@ -183,29 +200,29 @@ pub contract Test { /// ResultStatus indicates status of a transaction or script execution. /// - pub enum ResultStatus: UInt8 { - pub case succeeded - pub case failed + access(all) enum ResultStatus: UInt8 { + access(all) case succeeded + access(all) case failed } /// Result is the interface to be implemented by the various execution /// operations, such as transactions and scripts. /// - pub struct interface Result { + access(all) struct interface Result { /// The result status of an executed operation. /// - pub let status: ResultStatus + access(all) let status: ResultStatus /// The optional error of an executed operation. /// - pub let error: Error? + access(all) let error: Error? } /// The result of a transaction execution. /// - pub struct TransactionResult: Result { - pub let status: ResultStatus - pub let error: Error? + access(all) struct TransactionResult: Result { + access(all) let status: ResultStatus + access(all) let error: Error? init(status: ResultStatus, error: Error?) { self.status = status @@ -215,10 +232,10 @@ pub contract Test { /// The result of a script execution. /// - pub struct ScriptResult: Result { - pub let status: ResultStatus - pub let returnValue: AnyStruct? - pub let error: Error? + access(all) struct ScriptResult: Result { + access(all) let status: ResultStatus + access(all) let returnValue: AnyStruct? + access(all) let error: Error? init(status: ResultStatus, returnValue: AnyStruct?, error: Error?) { self.status = status @@ -229,8 +246,8 @@ pub contract Test { // Error is returned if something has gone wrong. // - pub struct Error { - pub let message: String + access(all) struct Error { + access(all) let message: String init(_ message: String) { self.message = message @@ -239,9 +256,9 @@ pub contract Test { /// Account represents info about the account created on the blockchain. /// - pub struct Account { - pub let address: Address - pub let publicKey: PublicKey + access(all) struct Account { + access(all) let address: Address + access(all) let publicKey: PublicKey init(address: Address, publicKey: PublicKey) { self.address = address @@ -251,11 +268,11 @@ pub contract Test { /// Transaction that can be submitted and executed on the blockchain. /// - pub struct Transaction { - pub let code: String - pub let authorizers: [Address] - pub let signers: [Account] - pub let arguments: [AnyStruct] + access(all) struct Transaction { + access(all) let code: String + access(all) let authorizers: [Address] + access(all) let signers: [Account] + access(all) let arguments: [AnyStruct] init(code: String, authorizers: [Address], signers: [Account], arguments: [AnyStruct]) { self.code = code @@ -267,40 +284,47 @@ pub contract Test { /// BlockchainBackend is the interface to be implemented by the backend providers. /// - pub struct interface BlockchainBackend { + access(all) struct interface BlockchainBackend { /// Executes a script and returns the script return value and the status. /// `returnValue` field of the result will be `nil` if the script failed. /// - pub fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult + access(all) + fun executeScript(_ script: String, _ arguments: [AnyStruct]): ScriptResult /// Creates a signer account by submitting an account creation transaction. /// The transaction is paid by the service account. /// The returned account can be used to sign and authorize transactions. /// - pub fun createAccount(): Account + access(all) + fun createAccount(): Account /// Returns the account for the given address. /// - pub fun getAccount(_ address: Address): Account + access(all) + fun getAccount(_ address: Address): Account /// Add a transaction to the current block. /// - pub fun addTransaction(_ tx: Transaction) + access(all) + fun addTransaction(_ tx: Transaction) /// Executes the next transaction in the block, if any. /// Returns the result of the transaction, or nil if no transaction was scheduled. /// - pub fun executeNextTransaction(): TransactionResult? + access(all) + fun executeNextTransaction(): TransactionResult? /// Commit the current block. /// Committing will fail if there are un-executed transactions in the block. /// - pub fun commitBlock() + access(all) + fun commitBlock() /// Deploys a given contract, and initilizes it with the arguments. /// - pub fun deployContract( + access(all) + fun deployContract( name: String, path: String, arguments: [AnyStruct] @@ -308,26 +332,31 @@ pub contract Test { /// Returns all the logs from the blockchain, up to the calling point. /// - pub fun logs(): [String] + access(all) + fun logs(): [String] /// Returns the service account of the blockchain. Can be used to sign /// transactions with this account. /// - pub fun serviceAccount(): Account + access(all) + fun serviceAccount(): Account /// Returns all events emitted from the blockchain, optionally filtered /// by type. /// - pub fun events(_ type: Type?): [AnyStruct] + access(all) + fun events(_ type: Type?): [AnyStruct] /// Resets the state of the blockchain to the given height. /// - pub fun reset(to height: UInt64) + access(all) + fun reset(to height: UInt64) /// Moves the time of the blockchain by the given delta, /// which should be passed in the form of seconds. /// - pub fun moveTime(by delta: Fix64) + access(all) + fun moveTime(by delta: Fix64) /// Creates a snapshot of the blockchain, at the /// current ledger state, with the given name. @@ -345,7 +374,8 @@ pub contract Test { /// Returns a new matcher that negates the test of the given matcher. /// - pub fun not(_ matcher: Matcher): Matcher { + access(all) + fun not(_ matcher: Matcher): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { return !matcher.test(value) }) @@ -355,7 +385,8 @@ pub contract Test { /// a ScriptResult or TransactionResult and the ResultStatus is succeeded. /// Returns false in any other case. /// - pub fun beSucceeded(): Matcher { + access(all) + fun beSucceeded(): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { return (value as! {Result}).status == ResultStatus.succeeded }) @@ -365,7 +396,8 @@ pub contract Test { /// a ScriptResult or TransactionResult and the ResultStatus is failed. /// Returns false in any other case. /// - pub fun beFailed(): Matcher { + access(all) + fun beFailed(): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { return (value as! {Result}).status == ResultStatus.failed }) @@ -373,7 +405,8 @@ pub contract Test { /// Returns a new matcher that checks if the given test value is nil. /// - pub fun beNil(): Matcher { + access(all) + fun beNil(): Matcher { return Matcher(test: fun (value: AnyStruct): Bool { return value == nil }) @@ -383,7 +416,8 @@ pub contract Test { /// a script or transaction, has failed and contains the given error /// message. /// - pub fun assertError(_ result: {Result}, errorMessage: String) { + access(all) + fun assertError(_ result: {Result}, errorMessage: String) { pre { result.status == ResultStatus.failed: "no error was found" } From 20a58ec3dbb9637bccb42422949a4661bda287d5 Mon Sep 17 00:00:00 2001 From: Ardit Marku Date: Mon, 25 Sep 2023 12:44:02 +0300 Subject: [PATCH 7/7] Rename TestFramework's NewEmulatorBackend function to EmulatorBackend --- runtime/stdlib/test-framework.go | 2 +- runtime/stdlib/test_contract.go | 2 +- runtime/stdlib/test_test.go | 42 ++++++++++++++++---------------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/runtime/stdlib/test-framework.go b/runtime/stdlib/test-framework.go index c8b68cedc1..6496ee98fb 100644 --- a/runtime/stdlib/test-framework.go +++ b/runtime/stdlib/test-framework.go @@ -30,7 +30,7 @@ import ( // This is used as a way to inject test provider dependencies dynamically. type TestFramework interface { - NewEmulatorBackend() Blockchain + EmulatorBackend() Blockchain ReadFile(string) (string, error) } diff --git a/runtime/stdlib/test_contract.go b/runtime/stdlib/test_contract.go index 36cb2980df..3a7bcd12fb 100644 --- a/runtime/stdlib/test_contract.go +++ b/runtime/stdlib/test_contract.go @@ -1246,7 +1246,7 @@ func (t *TestContractType) NewTestContract( initializerTypes := t.InitializerTypes emulatorBackend := t.emulatorBackendType.newEmulatorBackend( inter, - testFramework.NewEmulatorBackend(), + testFramework.EmulatorBackend(), interpreter.EmptyLocationRange, ) value, err := inter.InvokeFunctionValue( diff --git a/runtime/stdlib/test_test.go b/runtime/stdlib/test_test.go index 734c3a66d7..de5545174a 100644 --- a/runtime/stdlib/test_test.go +++ b/runtime/stdlib/test_test.go @@ -42,7 +42,7 @@ import ( func newTestContractInterpreter(t *testing.T, code string) (*interpreter.Interpreter, error) { testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{} }, } @@ -2070,7 +2070,7 @@ func TestBlockchain(t *testing.T) { eventsInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ events: func(inter *interpreter.Interpreter, eventType interpreter.StaticType) interpreter.Value { eventsInvoked = true @@ -2117,7 +2117,7 @@ func TestBlockchain(t *testing.T) { eventsInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ events: func(inter *interpreter.Interpreter, eventType interpreter.StaticType) interpreter.Value { eventsInvoked = true @@ -2161,7 +2161,7 @@ func TestBlockchain(t *testing.T) { resetInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ reset: func(height uint64) { resetInvoked = true @@ -2194,7 +2194,7 @@ func TestBlockchain(t *testing.T) { resetInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ reset: func(height uint64) { resetInvoked = true @@ -2226,7 +2226,7 @@ func TestBlockchain(t *testing.T) { moveTimeInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ moveTime: func(timeDelta int64) { moveTimeInvoked = true @@ -2262,7 +2262,7 @@ func TestBlockchain(t *testing.T) { moveTimeInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ moveTime: func(timeDelta int64) { moveTimeInvoked = true @@ -2295,7 +2295,7 @@ func TestBlockchain(t *testing.T) { moveTimeInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ moveTime: func(timeDelta int64) { moveTimeInvoked = true @@ -2324,7 +2324,7 @@ func TestBlockchain(t *testing.T) { createSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { createSnapshotInvoked = true @@ -2359,7 +2359,7 @@ func TestBlockchain(t *testing.T) { createSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { createSnapshotInvoked = true @@ -2395,7 +2395,7 @@ func TestBlockchain(t *testing.T) { loadSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { assert.Equal(t, "adminCreated", name) @@ -2436,7 +2436,7 @@ func TestBlockchain(t *testing.T) { loadSnapshotInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ createSnapshot: func(name string) error { assert.Equal(t, "adminCreated", name) @@ -2482,7 +2482,7 @@ func TestBlockchain(t *testing.T) { deployContractInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ deployContract: func( inter *interpreter.Interpreter, @@ -2535,7 +2535,7 @@ func TestBlockchain(t *testing.T) { deployContractInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ deployContract: func( inter *interpreter.Interpreter, @@ -2575,7 +2575,7 @@ func TestBlockchain(t *testing.T) { getAccountInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ getAccount: func(address interpreter.AddressValue) (*stdlib.Account, error) { getAccountInvoked = true @@ -2620,7 +2620,7 @@ func TestBlockchain(t *testing.T) { getAccountInvoked := false testFramework := &mockedTestFramework{ - newEmulatorBackend: func() stdlib.Blockchain { + emulatorBackend: func() stdlib.Blockchain { return &mockedBlockchain{ getAccount: func(address interpreter.AddressValue) (*stdlib.Account, error) { getAccountInvoked = true @@ -2649,18 +2649,18 @@ func TestBlockchain(t *testing.T) { } type mockedTestFramework struct { - newEmulatorBackend func() stdlib.Blockchain - readFile func(s string) (string, error) + emulatorBackend func() stdlib.Blockchain + readFile func(s string) (string, error) } var _ stdlib.TestFramework = &mockedTestFramework{} -func (m mockedTestFramework) NewEmulatorBackend() stdlib.Blockchain { - if m.newEmulatorBackend == nil { +func (m mockedTestFramework) EmulatorBackend() stdlib.Blockchain { + if m.emulatorBackend == nil { panic("'NewEmulatorBackend' is not implemented") } - return m.newEmulatorBackend() + return m.emulatorBackend() } func (m mockedTestFramework) ReadFile(fileName string) (string, error) {