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

tests(all modules): adds randomized simapp tests #1531

Closed
wants to merge 69 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
8099743
start to improve useability of invariants
danwt Nov 19, 2024
df7d3da
impl the sequencer keeper
danwt Nov 19, 2024
b0104ee
pre fix proposer
danwt Nov 19, 2024
5eb90fa
impl sequencer , but need to add more context
danwt Nov 19, 2024
273d396
add the template for sponsorship
danwt Nov 19, 2024
e9c478e
start on bulk of sponsorship
danwt Nov 19, 2024
8007c77
add some gpt examples
danwt Nov 19, 2024
81097fb
now doing the general sponsorship inv
danwt Nov 19, 2024
dfdded7
impl sponsorship
danwt Nov 19, 2024
4b573e3
fix delayedack
danwt Nov 19, 2024
12c8444
enhance block finalization queue
danwt Nov 19, 2024
def9264
adds invariants for IRO (use copilot)
danwt Nov 20, 2024
07275a8
adds eibc invariants
danwt Nov 20, 2024
e3c275a
pre do light client ones
danwt Nov 20, 2024
6845e4a
adds client state invar
danwt Nov 20, 2024
9082689
adds light client inv
danwt Nov 20, 2024
0a89b87
now going to refac
danwt Nov 20, 2024
37969a0
working on refac
danwt Nov 20, 2024
c7f022f
refac invar package
danwt Nov 20, 2024
9b4f4f5
rename invar to uinv
danwt Nov 20, 2024
7e818a4
need to change all the written invs
danwt Nov 20, 2024
2bbef22
refactor one
danwt Nov 20, 2024
34e6215
need to refac the rest
danwt Nov 20, 2024
d035a45
finish light client refactor
danwt Nov 20, 2024
da96e36
partially refac seq
danwt Nov 20, 2024
37a960a
finish sequencer refac
danwt Nov 20, 2024
949fdd5
start to refac sponsorship
danwt Nov 20, 2024
9c21069
lfg
danwt Nov 20, 2024
341d9ee
ready to cont with sponsorship and other refacs
danwt Nov 20, 2024
b24f3ff
clean up one
danwt Nov 20, 2024
ff41724
fix another in sponsor
danwt Nov 20, 2024
334287c
finish sponsorship cleanup
danwt Nov 20, 2024
8038a5f
fix iro
danwt Nov 20, 2024
f557c5f
fix delayedack
danwt Nov 20, 2024
be2a989
need to figure how invariants are wired into the app
danwt Nov 20, 2024
6765c57
add invariants check to test
danwt Nov 20, 2024
6d0c6a9
need to make things clearer around valset hash and proposer addr
danwt Nov 20, 2024
9ceb469
invariants seem OK - but loads of UT failures
danwt Nov 20, 2024
a79a753
pre remove auto invar checking
danwt Nov 20, 2024
3c6593f
REMOVES always running invariants on all tests, and restores original…
danwt Nov 20, 2024
e8f86e6
riding the debug train
danwt Nov 20, 2024
89b8240
trying to fix state info index invar
danwt Nov 20, 2024
deb306d
cp
danwt Nov 20, 2024
b7ae234
pre del grey code
danwt Nov 20, 2024
8545232
tests passing
danwt Nov 20, 2024
8f59fe9
restore contributing
danwt Nov 20, 2024
3c607e1
fumpt
danwt Nov 20, 2024
deb570b
linter
danwt Nov 20, 2024
1f6f52b
linter
danwt Nov 20, 2024
1d310c0
revert accidental
danwt Nov 20, 2024
4619fe9
removes empty fun
danwt Nov 20, 2024
f3caaf3
pre start addressing review comments
danwt Nov 20, 2024
8dd2d2d
few fixes
danwt Nov 20, 2024
32f7a52
fix according to comments
danwt Nov 20, 2024
d5f0cf6
adds docstrings, make sure to collect all errors while walking
danwt Nov 20, 2024
bad4faa
dcostring
danwt Nov 20, 2024
86413fe
removes test skip
danwt Nov 21, 2024
404dbe9
adds missing all invariants calls
danwt Nov 21, 2024
3abcd88
add some kiril suggestions
danwt Nov 21, 2024
4dc08be
add some kiril suggestions
danwt Nov 21, 2024
cc62ee8
gonna do iro
danwt Nov 21, 2024
d5aaddb
add something similar to what Michael said but not quite
danwt Nov 21, 2024
9351b25
think its ok
danwt Nov 21, 2024
85d6eb7
fmt
danwt Nov 21, 2024
5e75ec7
better string
danwt Nov 21, 2024
59a8e36
ready
danwt Nov 21, 2024
b380060
Improve err string
danwt Nov 21, 2024
06e46c0
fix one
danwt Nov 21, 2024
2a67270
cp
danwt Nov 21, 2024
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
9 changes: 8 additions & 1 deletion Contributing.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# CONTRIBUTING

1. [Code standards](#code-standards)
2. [Test pyramid](#test-pyramid)

## Code standards

Expand Down Expand Up @@ -206,4 +210,7 @@ type Foo struct{ }
- https://github.com/dymensionxyz/gerr-cosmos : Dymension error library
- https://cloud.google.com/apis/design/errors#error_codes : Google error handling guidelines
- https://github.com/uber-go/guide/blob/master/style.md#errors : Uber Style Guide
- https://100go.co/#error-management : 100 Go Mistakes
- https://100go.co/#error-management : 100 Go Mistakes

## Test pyramid

144 changes: 144 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,150 @@ release:

.PHONY: release-dry-run release


###############################################################################
### Tests & Simulation ###
### (This section is copied from SDK makefile and adjusted) ###
###############################################################################

test: test-unit
test-e2e:
$(MAKE) -C tests test-e2e
test-e2e-cov:
$(MAKE) -C tests test-e2e-cov
test-integration:
$(MAKE) -C tests test-integration
test-integration-cov:
$(MAKE) -C tests test-integration-cov
test-all: test-unit test-e2e test-integration test-ledger-mock test-race

TEST_PACKAGES=./...
TEST_TARGETS := test-unit test-unit-amino test-unit-proto test-ledger-mock test-race test-ledger test-race

# Test runs-specific rules. To add a new test target, just add
# a new rule, customise ARGS or TEST_PACKAGES ad libitum, and
# append the new rule to the TEST_TARGETS list.
test-unit: test_tags += cgo ledger test_ledger_mock norace
test-unit-amino: test_tags += ledger test_ledger_mock test_amino norace
test-ledger: test_tags += cgo ledger norace
test-ledger-mock: test_tags += ledger test_ledger_mock norace
test-race: test_tags += cgo ledger test_ledger_mock
test-race: ARGS=-race
test-race: TEST_PACKAGES=$(PACKAGES_NOSIMULATION)
$(TEST_TARGETS): run-tests

# check-* compiles and collects tests without running them
# note: go test -c doesn't support multiple packages yet (https://github.com/golang/go/issues/15513)
CHECK_TEST_TARGETS := check-test-unit check-test-unit-amino
check-test-unit: test_tags += cgo ledger test_ledger_mock norace
check-test-unit-amino: test_tags += ledger test_ledger_mock test_amino norace
$(CHECK_TEST_TARGETS): EXTRA_ARGS=-run=none
$(CHECK_TEST_TARGETS): run-tests

ARGS += -tags "$(test_tags)"
SUB_MODULES = $(shell find . -type f -name 'go.mod' -print0 | xargs -0 -n1 dirname | sort)
CURRENT_DIR = $(shell pwd)
run-tests:
ifneq (,$(shell which tparse 2>/dev/null))
@echo "Starting unit tests"; \
finalec=0; \
for module in $(SUB_MODULES); do \
cd ${CURRENT_DIR}/$$module; \
echo "Running unit tests for module $$module"; \
go test -mod=readonly -json $(ARGS) $(TEST_PACKAGES) ./... | tparse; \
ec=$$?; \
if [ "$$ec" -ne '0' ]; then finalec=$$ec; fi; \
done; \
exit $$finalec
else
@echo "Starting unit tests"; \
finalec=0; \
for module in $(SUB_MODULES); do \
cd ${CURRENT_DIR}/$$module; \
echo "Running unit tests for module $$module"; \
go test -mod=readonly $(ARGS) $(TEST_PACKAGES) ./... ; \
ec=$$?; \
if [ "$$ec" -ne '0' ]; then finalec=$$ec; fi; \
done; \
exit $$finalec
endif

.PHONY: run-tests test test-all $(TEST_TARGETS)

test-sim-nondeterminism:
@echo "Running non-determinism test..."
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -run TestAppStateDeterminism -Enabled=true \
-NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h

test-sim-custom-genesis-fast:
@echo "Running custom genesis simulation..."
@echo "By default, ${HOME}/.gaiad/config/genesis.json will be used."
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -run TestFullAppSimulation -Genesis=${HOME}/.gaiad/config/genesis.json \
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h

test-sim-import-export: runsim
@echo "Running application import/export simulation. This may take several minutes..."
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 5 TestAppImportExport

test-sim-after-import: runsim
@echo "Running application simulation-after-import. This may take several minutes..."
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 5 TestAppSimulationAfterImport

test-sim-custom-genesis-multi-seed: runsim
@echo "Running multi-seed custom genesis simulation..."
@echo "By default, ${HOME}/.gaiad/config/genesis.json will be used."
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Genesis=${HOME}/.gaiad/config/genesis.json -SimAppPkg=. -ExitOnFail 400 5 TestFullAppSimulation

test-sim-multi-seed-long: runsim
@echo "Running long multi-seed application simulation. This may take awhile!"
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 500 50 TestFullAppSimulation

test-sim-multi-seed-short: runsim
@echo "Running short multi-seed application simulation. This may take awhile!"
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 10 TestFullAppSimulation

test-sim-benchmark-invariants:
@echo "Running simulation invariant benchmarks..."
cd ${CURRENT_DIR}/simapp && @go test -mod=readonly -benchmem -bench=BenchmarkInvariants -run=^$ \
-Enabled=true -NumBlocks=1000 -BlockSize=200 \
-Period=1 -Commit=true -Seed=57 -v -timeout 24h

.PHONY: \
test-sim-nondeterminism \
test-sim-custom-genesis-fast \
test-sim-import-export \
test-sim-after-import \
test-sim-custom-genesis-multi-seed \
test-sim-multi-seed-short \
test-sim-multi-seed-long \
test-sim-benchmark-invariants

SIM_NUM_BLOCKS ?= 500
SIM_BLOCK_SIZE ?= 200
SIM_COMMIT ?= true

test-sim-benchmark:
@echo "Running application benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!"
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -benchmem -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \
-Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h

test-sim-profile:
@echo "Running application benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!"
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -benchmem -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \
-Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out

.PHONY: test-sim-profile test-sim-benchmark

test-rosetta:
docker build -t rosetta-ci:latest -f contrib/rosetta/rosetta-ci/Dockerfile .
docker-compose -f contrib/rosetta/docker-compose.yaml up --abort-on-container-exit --exit-code-from test_rosetta --build
.PHONY: test-rosetta

benchmark:
@go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION)
.PHONY: benchmark


###############################################################################
### Proto ###
###############################################################################
Expand Down
5 changes: 4 additions & 1 deletion app/apptesting/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,15 @@ type SetupOptions struct {
AppOpts types.AppOptions
}

// Having this enabled led to some problems because some tests use intrusive methods to modify the state, whichs breaks invariants
var InvariantCheckInterval = uint(0) // disabled

func SetupTestingApp() (*app.App, app.GenesisState) {
db := dbm.NewMemDB()
encCdc := app.MakeEncodingConfig()
params.SetAddressPrefixes()

newApp := app.New(log.NewNopLogger(), db, nil, true, map[int64]bool{}, app.DefaultNodeHome, 5, encCdc, EmptyAppOptions{}, bam.SetChainID(TestChainID))
newApp := app.New(log.NewNopLogger(), db, nil, true, map[int64]bool{}, app.DefaultNodeHome, InvariantCheckInterval, encCdc, EmptyAppOptions{}, bam.SetChainID(TestChainID))
defaultGenesisState := app.NewDefaultGenesisState(encCdc.Codec)

incentivesGenesisStateJson := defaultGenesisState[incentivestypes.ModuleName]
Expand Down
1 change: 1 addition & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ func (a *AppKeepers) InitKeepers(
appCodec,
a.keys[sequencermoduletypes.StoreKey],
a.BankKeeper,
a.AccountKeeper,
a.RollappKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
Expand Down
1 change: 0 additions & 1 deletion ibctesting/bridging_fee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func (s *bridgingFeeSuite) TestNotRollappNoBridgingFee() {
func (s *bridgingFeeSuite) TestBridgingFee() {
path := s.newTransferPath(s.hubChain(), s.rollappChain())
s.coordinator.Setup(path)
s.fundSenderAccount()
s.createRollappWithFinishedGenesis(path.EndpointA.ChannelID)
s.registerSequencer()
s.setRollappLightClientID(s.rollappCtx().ChainID(), path.EndpointA.ClientID)
Expand Down
1 change: 0 additions & 1 deletion ibctesting/transfers_enabled_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ func (s *transfersEnabledSuite) SetupTest() {
s.utilSuite.SetupTest()
path := s.newTransferPath(s.hubChain(), s.rollappChain())
s.coordinator.Setup(path)
s.fundSenderAccount()
s.createRollapp(false, nil)
s.registerSequencer()
s.path = path
Expand Down
5 changes: 1 addition & 4 deletions ibctesting/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,6 @@ func (s *utilSuite) SetupTest() {
s.coordinator.Chains[rollappChainID()] = s.newTestChainWithSingleValidator(s.T(), s.coordinator, rollappChainID())
}

func (s *utilSuite) fundSenderAccount() {
// apptesting.FundAccount(s.hubApp(), s.hubCtx(), s.hubChain().SenderAccount.GetAddress(), sdk.NewCoins(rollapptypes.DefaultRegistrationFee))
}

// CreateRollappWithFinishedGenesis creates a rollapp whose 'genesis' protocol is complete:
// that is, they have finished all genesis transfers and their bridge is enabled.
func (s *utilSuite) createRollappWithFinishedGenesis(canonicalChannelID string) {
Expand Down Expand Up @@ -251,6 +247,7 @@ func (s *utilSuite) updateRollappState(endHeight uint64) {
s.Require().NoError(err)
}

// NOTE: does not use process the queue, it uses intrusive method which breaks invariants
func (s *utilSuite) finalizeRollappState(index uint64, endHeight uint64) (sdk.Events, error) {
rollappKeeper := s.hubApp().RollappKeeper
ctx := s.hubCtx()
Expand Down
2 changes: 2 additions & 0 deletions testutil/keeper/sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/store"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
"github.com/stretchr/testify/require"

cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
Expand Down Expand Up @@ -38,6 +39,7 @@ func SequencerKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
cdc,
storeKey,
nil,
&authkeeper.AccountKeeper{},
&rollappkeeper.Keeper{},
sample.AccAddress(),
)
Expand Down
4 changes: 4 additions & 0 deletions utils/uinv/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Packet uinv provides a simple way to define and register invariants in Cosmos SDK modules.
// Invariants should be written using normal code style, by returning errors containing context.
// Use NewErr to wrap errors in a way that they can be handled as invariant breaking.
package uinv
73 changes: 73 additions & 0 deletions utils/uinv/funcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package uinv

import (
"errors"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/dymensionxyz/gerr-cosmos/gerrc"
)

var ErrBroken = gerrc.ErrInternal.Wrap("invariant broken")

// Wrap an error to mark as invariant breaking. If the error is nil, it will return nil.
func Breaking(err error) error {
if err == nil {
return nil
}
return errors.Join(ErrBroken, err)
}

// If any error that your function returns is invariant breaking, use this function to wrap
// it to reduce verbosity.
func AnyErrorIsBreaking(f Func) Func {
return func(ctx sdk.Context) error {
return Breaking(f(ctx))
}
}

// Should return an ErrBroken if invariant is broken. Other errors are logged.
type Func = func(sdk.Context) error

type NamedFunc[K any] struct {
Name string
Func func(K) Func
}

func (nf NamedFunc[K]) Exec(ctx sdk.Context, module string, keeper K) (string, bool) {
err := nf.Func(keeper)(ctx)
broken := false
var msg string
if err != nil {
broken = errorsmod.IsOf(err, ErrBroken)
if !broken {
ctx.Logger().Error("Invariant function error but not breaking.", "module", module, "name", nf.Name, "error", err)
// Note that if it is broken the SDK wil take care of logging the error somewhere else
}
msg = sdk.FormatInvariant(module, nf.Name, err.Error())
}
return msg, broken
}

type NamedFuncsList[K any] []NamedFunc[K]

func (l NamedFuncsList[K]) RegisterInvariants(module string, ir sdk.InvariantRegistry, keeper K) {
for _, f := range l {
ir.RegisterRoute(module, f.Name, func(ctx sdk.Context) (string, bool) {
return f.Exec(ctx, module, keeper)
})
}
}

// Should be called in a function func AllInvariants(k Keeper) sdk.Invariant within your own module namespace.
func (l NamedFuncsList[K]) All(module string, keeper K) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
for _, invar := range l {
s, stop := invar.Exec(ctx, module, keeper)
if stop {
return s, stop
}
}
return "", false
}
}
26 changes: 26 additions & 0 deletions utils/uinv/funcs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package uinv

import (
"errors"
"testing"

errorsmod "cosmossdk.io/errors"
"github.com/stretchr/testify/require"
)

func TestSanityCheckErrorTypes(t *testing.T) {
baseErr := errors.New("base")
var nilErr error

t.Run("breaking", func(t *testing.T) {
require.True(t, errorsmod.IsOf(Breaking(baseErr), ErrBroken))
require.False(t, errorsmod.IsOf(Breaking(nilErr), ErrBroken))
})

t.Run("join", func(t *testing.T) {
joinedBase := errors.Join(baseErr, baseErr)
joinedNil := errors.Join(nil, nil)
require.True(t, errorsmod.IsOf(Breaking(joinedBase), ErrBroken))
require.False(t, errorsmod.IsOf(Breaking(joinedNil), ErrBroken))
})
}
Loading