* port statediff from 9b7fd9af80/statediff/statediff.go
; minor fixes
* integrating state diff extracting, building, and persisting into geth processes
* work towards persisting created statediffs in ipfs; based off github.com/vulcanize/eth-block-extractor
* Add a state diff service
* Remove diff extractor from blockchain
* Update imports
* Move statediff on/off check to geth cmd config
* Update starting state diff service
* Add debugging logs for creating diff
* Add statediff extractor and builder tests and small refactoring
* Start to write statediff to a CSV
* Restructure statediff directory
* Pull CSV publishing methods into their own file
* Reformatting due to go fmt
* Add gomega to vendor dir
* Remove testing focuses
* Update statediff tests to use golang test pkg
instead of ginkgo
- builder_test
- extractor_test
- publisher_test
* Use hexutil.Encode instead of deprecated common.ToHex
* Remove OldValue from DiffBigInt and DiffUint64 fields
* Update builder test
* Remove old storage value from updated accounts
* Remove old values from created/deleted accounts
* Update publisher to account for only storing current account values
* Update service loop and fetching previous block
* Update testing
- remove statediff ginkgo test suite file
- move mocks to their own dir
* Updates per go fmt
* Updates to tests
* Pass statediff mode and path in through cli
* Return filename from publisher
* Remove some duplication in builder
* Remove code field from state diff output
this is the contract byte code, and it can still be obtained by querying
the db by the codeHash
* Consolidate acct diff structs for updated & updated/deleted accts
* Include block number in csv filename
* Clean up error logging
* Cleanup formatting, spelling, etc
* Address PR comments
* Add contract address and storage value to csv
* Refactor accumulating account row in csv publisher
* Add DiffStorage struct
* Add storage key to csv
* Address PR comments
* Fix publisher to include rows for accounts that don't have store updates
* Update builder test after merging in release/1.8
* Update test contract to include storage on contract intialization
- so that we're able to test that storage diffing works for created and
deleted accounts (not just updated accounts).
* Factor out a common trie iterator method in builder
195 lines
5.1 KiB
Go
195 lines
5.1 KiB
Go
package asyncassertion
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/onsi/gomega/internal/oraclematcher"
|
|
"github.com/onsi/gomega/types"
|
|
)
|
|
|
|
type AsyncAssertionType uint
|
|
|
|
const (
|
|
AsyncAssertionTypeEventually AsyncAssertionType = iota
|
|
AsyncAssertionTypeConsistently
|
|
)
|
|
|
|
type AsyncAssertion struct {
|
|
asyncType AsyncAssertionType
|
|
actualInput interface{}
|
|
timeoutInterval time.Duration
|
|
pollingInterval time.Duration
|
|
failWrapper *types.GomegaFailWrapper
|
|
offset int
|
|
}
|
|
|
|
func New(asyncType AsyncAssertionType, actualInput interface{}, failWrapper *types.GomegaFailWrapper, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
|
|
actualType := reflect.TypeOf(actualInput)
|
|
if actualType.Kind() == reflect.Func {
|
|
if actualType.NumIn() != 0 || actualType.NumOut() == 0 {
|
|
panic("Expected a function with no arguments and one or more return values.")
|
|
}
|
|
}
|
|
|
|
return &AsyncAssertion{
|
|
asyncType: asyncType,
|
|
actualInput: actualInput,
|
|
failWrapper: failWrapper,
|
|
timeoutInterval: timeoutInterval,
|
|
pollingInterval: pollingInterval,
|
|
offset: offset,
|
|
}
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
|
assertion.failWrapper.TWithHelper.Helper()
|
|
return assertion.match(matcher, true, optionalDescription...)
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
|
assertion.failWrapper.TWithHelper.Helper()
|
|
return assertion.match(matcher, false, optionalDescription...)
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string {
|
|
switch len(optionalDescription) {
|
|
case 0:
|
|
return ""
|
|
default:
|
|
return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
|
|
}
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) actualInputIsAFunction() bool {
|
|
actualType := reflect.TypeOf(assertion.actualInput)
|
|
return actualType.Kind() == reflect.Func && actualType.NumIn() == 0 && actualType.NumOut() > 0
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
|
|
if assertion.actualInputIsAFunction() {
|
|
values := reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{})
|
|
|
|
extras := []interface{}{}
|
|
for _, value := range values[1:] {
|
|
extras = append(extras, value.Interface())
|
|
}
|
|
|
|
success, message := vetExtras(extras)
|
|
|
|
if !success {
|
|
return nil, errors.New(message)
|
|
}
|
|
|
|
return values[0].Interface(), nil
|
|
}
|
|
|
|
return assertion.actualInput, nil
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
|
|
if assertion.actualInputIsAFunction() {
|
|
return true
|
|
}
|
|
|
|
return oraclematcher.MatchMayChangeInTheFuture(matcher, value)
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
|
|
timer := time.Now()
|
|
timeout := time.After(assertion.timeoutInterval)
|
|
|
|
description := assertion.buildDescription(optionalDescription...)
|
|
|
|
var matches bool
|
|
var err error
|
|
mayChange := true
|
|
value, err := assertion.pollActual()
|
|
if err == nil {
|
|
mayChange = assertion.matcherMayChange(matcher, value)
|
|
matches, err = matcher.Match(value)
|
|
}
|
|
|
|
assertion.failWrapper.TWithHelper.Helper()
|
|
|
|
fail := func(preamble string) {
|
|
errMsg := ""
|
|
message := ""
|
|
if err != nil {
|
|
errMsg = "Error: " + err.Error()
|
|
} else {
|
|
if desiredMatch {
|
|
message = matcher.FailureMessage(value)
|
|
} else {
|
|
message = matcher.NegatedFailureMessage(value)
|
|
}
|
|
}
|
|
assertion.failWrapper.TWithHelper.Helper()
|
|
assertion.failWrapper.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
|
|
}
|
|
|
|
if assertion.asyncType == AsyncAssertionTypeEventually {
|
|
for {
|
|
if err == nil && matches == desiredMatch {
|
|
return true
|
|
}
|
|
|
|
if !mayChange {
|
|
fail("No future change is possible. Bailing out early")
|
|
return false
|
|
}
|
|
|
|
select {
|
|
case <-time.After(assertion.pollingInterval):
|
|
value, err = assertion.pollActual()
|
|
if err == nil {
|
|
mayChange = assertion.matcherMayChange(matcher, value)
|
|
matches, err = matcher.Match(value)
|
|
}
|
|
case <-timeout:
|
|
fail("Timed out")
|
|
return false
|
|
}
|
|
}
|
|
} else if assertion.asyncType == AsyncAssertionTypeConsistently {
|
|
for {
|
|
if !(err == nil && matches == desiredMatch) {
|
|
fail("Failed")
|
|
return false
|
|
}
|
|
|
|
if !mayChange {
|
|
return true
|
|
}
|
|
|
|
select {
|
|
case <-time.After(assertion.pollingInterval):
|
|
value, err = assertion.pollActual()
|
|
if err == nil {
|
|
mayChange = assertion.matcherMayChange(matcher, value)
|
|
matches, err = matcher.Match(value)
|
|
}
|
|
case <-timeout:
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func vetExtras(extras []interface{}) (bool, string) {
|
|
for i, extra := range extras {
|
|
if extra != nil {
|
|
zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
|
|
if !reflect.DeepEqual(zeroValue, extra) {
|
|
message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
|
|
return false, message
|
|
}
|
|
}
|
|
}
|
|
return true, ""
|
|
}
|