Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[test] Allow setting blockchain's clock #196

Merged
merged 4 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions test/emulator_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"fmt"
"os"
"strings"
"time"

"github.com/onflow/cadence"
"github.com/onflow/cadence/encoding/json"
Expand Down Expand Up @@ -52,6 +53,18 @@ const helperFilePrefix = "\x00helper/"

var _ stdlib.TestFramework = &EmulatorBackend{}

type SystemClock struct {
TimeDelta int64
}

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

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

// EmulatorBackend is the emulator-backed implementation of the interpreter.TestFramework.
type EmulatorBackend struct {
blockchain *emulator.Blockchain
Expand All @@ -76,6 +89,9 @@ 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

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

type keyInfo struct {
Expand Down Expand Up @@ -130,6 +146,8 @@ func NewEmulatorBackend(
} else {
blockchain = newBlockchain(logCollectionHook)
}
clock := NewSystemClock()
blockchain.SetClock(clock)

return &EmulatorBackend{
blockchain: blockchain,
Expand All @@ -139,6 +157,7 @@ func NewEmulatorBackend(
configuration: baseConfiguration(),
stdlibHandler: stdlibHandler,
logCollection: logCollectionHook,
clock: clock,
}
}

Expand Down Expand Up @@ -668,6 +687,14 @@ func (e *EmulatorBackend) Events(
)
}

// Moves the time of the Blockchain's clock, by the
// given time delta, in the form of seconds.
func (e *EmulatorBackend) MoveTime(timeDelta int64) {
e.clock.TimeDelta += timeDelta
e.blockchain.SetClock(e.clock)
e.CommitBlock()
}

// excludeCommonLocations excludes the common contracts from appearing
// in the coverage report, as they skew the coverage metrics.
func excludeCommonLocations(coverageReport *runtime.CoverageReport) {
Expand Down
4 changes: 2 additions & 2 deletions test/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ go 1.18

require (
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/onflow/cadence v0.40.1-0.20230808215334-fcb488b659bf
github.com/onflow/cadence v0.40.1-0.20230828191216-1bbc078efdb3
github.com/onflow/flow-emulator v0.54.0
github.com/onflow/flow-go v0.31.1-0.20230808172820-f074502a67e3
github.com/onflow/flow-go v0.31.1-0.20230901090702-eeeef3a7bd58
github.com/onflow/flow-go-sdk v0.41.10
github.com/rs/zerolog v1.29.0
github.com/stretchr/testify v1.8.4
Expand Down
8 changes: 4 additions & 4 deletions test/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,8 @@ github.com/onflow/atree v0.1.0-beta1.0.20211027184039-559ee654ece9/go.mod h1:+6x
github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c=
github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc=
github.com/onflow/cadence v0.20.1/go.mod h1:7mzUvPZUIJztIbr9eTvs+fQjWWHTF8veC+yk4ihcNIA=
github.com/onflow/cadence v0.40.1-0.20230808215334-fcb488b659bf h1:XdC0uYL2jjnyGk+XhX9klfNOi0/ewCpjmI8ZwoRVaE0=
github.com/onflow/cadence v0.40.1-0.20230808215334-fcb488b659bf/go.mod h1:2ggmhENvPPZXRnv9SmqN2xiYvluu4wx/96GCpeRh8lU=
github.com/onflow/cadence v0.40.1-0.20230828191216-1bbc078efdb3 h1:Ncadvbf2t4IRY1wJpGgTDs7GEpGO5RgT5HmKSifhoaQ=
github.com/onflow/cadence v0.40.1-0.20230828191216-1bbc078efdb3/go.mod h1:2ggmhENvPPZXRnv9SmqN2xiYvluu4wx/96GCpeRh8lU=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d h1:B7PdhdUNkve5MVrekWDuQf84XsGBxNZ/D3x+QQ8XeVs=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20230703193002-53362441b57d/go.mod h1:xAiV/7TKhw863r6iO3CS5RnQ4F+pBY1TxD272BsILlo=
github.com/onflow/flow-core-contracts/lib/go/templates v1.2.3 h1:X25A1dNajNUtE+KoV76wQ6BR6qI7G65vuuRXxDDqX7E=
Expand All @@ -529,8 +529,8 @@ github.com/onflow/flow-emulator v0.54.0 h1:GzqMPIjsNweiyBORs8naUXhgs3PhD0X4Ep4j/
github.com/onflow/flow-emulator v0.54.0/go.mod h1:cPKNx2eaxUDtXNHN9nnrt/qydWUHNQRTa/9QnsaCSpo=
github.com/onflow/flow-ft/lib/go/contracts v0.7.0 h1:XEKE6qJUw3luhsYmIOteXP53gtxNxrwTohgxJXCYqBE=
github.com/onflow/flow-ft/lib/go/contracts v0.7.0/go.mod h1:kTMFIySzEJJeupk+7EmXs0EJ6CBWY/MV9fv9iYQk+RU=
github.com/onflow/flow-go v0.31.1-0.20230808172820-f074502a67e3 h1:3iDV59Das0YkeFnjI0UkOZMz+gS1JKpTNZ4oMGH4bDM=
github.com/onflow/flow-go v0.31.1-0.20230808172820-f074502a67e3/go.mod h1:PdmGmlNDu9HOhg31NYAKLrIhmuTvFDgCS56CTs0af9Y=
github.com/onflow/flow-go v0.31.1-0.20230901090702-eeeef3a7bd58 h1:SJS/WqckE6lcXmCOL/7Gp39zY66mCcEhy9xfmyomKrw=
github.com/onflow/flow-go v0.31.1-0.20230901090702-eeeef3a7bd58/go.mod h1:PdmGmlNDu9HOhg31NYAKLrIhmuTvFDgCS56CTs0af9Y=
github.com/onflow/flow-go-sdk v0.24.0/go.mod h1:IoptMLPyFXWvyd9yYA6/4EmSeeozl6nJoIv4FaEMg74=
github.com/onflow/flow-go-sdk v0.41.10 h1:Cio6GJhtx532TUY+cqrqWglD5sZCXkWeM5QvaRha3p4=
github.com/onflow/flow-go-sdk v0.41.10/go.mod h1:0a0LiQFbFt8RW/ptoMUU7YkvW9ArVcbjLE0XS78uz1E=
Expand Down
125 changes: 125 additions & 0 deletions test/test_framework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4181,3 +4181,128 @@ func TestTestFunctionValidSignature(t *testing.T) {
assert.ErrorContains(t, err, "test functions should have no return values")
})
}

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

const contractCode = `
pub contract TimeLocker {
pub let lockPeriod: UFix64
pub let lockedAt: UFix64

init(lockedAt: UFix64) {
self.lockedAt = lockedAt
// Lock period is 30 days, in the form of seconds.
self.lockPeriod = UFix64(30 * 24 * 60 * 60)
}

pub fun isOpen(): Bool {
let currentTime = getCurrentBlock().timestamp
return currentTime > (self.lockedAt + self.lockPeriod)
}
}
`

const scriptCode = `
import "TimeLocker"

pub fun main(): Bool {
return TimeLocker.isOpen()
}
`

const currentBlockTimestamp = `
pub fun main(): UFix64 {
return getCurrentBlock().timestamp
}
`

const testCode = `
import Test

pub let blockchain = Test.newEmulatorBlockchain()
pub let account = blockchain.createAccount()
pub var lockedAt: UFix64 = 0.0

pub fun setup() {
let currentBlockTimestamp = Test.readFile("current_block_timestamp.cdc")
let result = blockchain.executeScript(currentBlockTimestamp, [])
lockedAt = result.returnValue! as! UFix64

let contractCode = Test.readFile("TimeLocker.cdc")
let err = blockchain.deployContract(
name: "TimeLocker",
code: contractCode,
account: account,
arguments: [lockedAt]
)

Test.expect(err, Test.beNil())

blockchain.useConfiguration(Test.Configuration({
"TimeLocker": account.address
}))
}

pub fun testIsNotOpen() {
let isLockerOpen = Test.readFile("is_locker_open.cdc")
let result = blockchain.executeScript(isLockerOpen, [])

Test.expect(result, Test.beSucceeded())
Test.assertEqual(false, result.returnValue! as! Bool)
}

pub fun testIsOpen() {
// timeDelta is the representation of 20 days, in seconds
let timeDelta = Fix64(20 * 24 * 60 * 60)
blockchain.moveTime(by: timeDelta)

let isLockerOpen = Test.readFile("is_locker_open.cdc")
var result = blockchain.executeScript(isLockerOpen, [])

Test.expect(result, Test.beSucceeded())
Test.assertEqual(false, result.returnValue! as! Bool)

// We move time forward by another 20 days
blockchain.moveTime(by: timeDelta)

result = blockchain.executeScript(isLockerOpen, [])

Test.assertEqual(true, result.returnValue! as! Bool)

// We move time backward by 20 days
blockchain.moveTime(by: timeDelta * -1.0)

result = blockchain.executeScript(isLockerOpen, [])

Test.assertEqual(false, result.returnValue! as! Bool)
}
`

fileResolver := func(path string) (string, error) {
switch path {
case "TimeLocker.cdc":
return contractCode, nil
case "is_locker_open.cdc":
return scriptCode, nil
case "current_block_timestamp.cdc":
return currentBlockTimestamp, nil
default:
return "", fmt.Errorf("cannot find import location: %s", path)
}
}

importResolver := func(location common.Location) (string, error) {
return "", nil
}

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

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