diff --git a/x/logic/predicate/block_test.go b/x/logic/predicate/block_test.go new file mode 100644 index 00000000..74886ed0 --- /dev/null +++ b/x/logic/predicate/block_test.go @@ -0,0 +1,58 @@ +package predicate + +import ( + "fmt" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ichiban/prolog/engine" + "github.com/okp4/okp4d/x/logic/testutil" + "github.com/smartystreets/goconvey/convey" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmdb "github.com/tendermint/tm-db" +) + +//nolint:dupl +func TestBlock(t *testing.T) { + cases := []struct { + header tmproto.Header + implication string + ok bool + }{ + {header: tmproto.Header{Height: 102}, implication: `block_height(102)`, ok: true}, + {header: tmproto.Header{Height: 905}, implication: `block_height(102)`, ok: false}, + {header: tmproto.Header{Height: 102}, implication: `block_height(X), X == 102`, ok: true}, + {header: tmproto.Header{Height: 102}, implication: `block_height(X), X == 905`, ok: true}, + {header: tmproto.Header{Time: time.Unix(1494505756, 0)}, implication: `block_time(102)`, ok: true}, + {header: tmproto.Header{Time: time.Unix(1494505757, 0)}, implication: `block_time(102)`, ok: false}, + {header: tmproto.Header{Time: time.Unix(1494505756, 0)}, implication: `block_time(X), X == 1494505756`, ok: true}, + {header: tmproto.Header{Time: time.Unix(1494505756, 0)}, implication: `block_time(X), X == 1494505757`, ok: true}, + } + for _, tc := range cases { + convey.Convey(fmt.Sprintf("Given the clause body: %s", tc.implication), t, func() { + convey.Convey("Given a context", func() { + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + ctx := sdk.NewContext(stateStore, tc.header, false, log.NewNopLogger()) + + convey.Convey("and a vm", func() { + vm := testutil.NewVMMust(ctx) + vm.Register1(engine.NewAtom("block_height"), BlockHeight(ctx)) + testutil.CompileMust(vm, ctx, fmt.Sprintf("test :- %s.", tc.implication)) + + convey.Convey("When the predicate is called", func() { + ok, err := vm.Arrive(engine.NewAtom("test"), []engine.Term{}, engine.Success, nil).Force(ctx) + + convey.Convey("Then the result should be true and there should be no error", func() { + convey.So(err, convey.ShouldBeNil) + convey.So(ok, convey.ShouldEqual, tc.ok) + }) + }) + }) + }) + }) + } +} diff --git a/x/logic/predicate/utils.go b/x/logic/util/context.go similarity index 80% rename from x/logic/predicate/utils.go rename to x/logic/util/context.go index b2164e8a..eabc2fe5 100644 --- a/x/logic/predicate/utils.go +++ b/x/logic/util/context.go @@ -1,4 +1,4 @@ -package predicate +package util import ( "context" @@ -8,8 +8,7 @@ import ( ) // UnwrapSDKContext retrieves a Context from a context.Context instance -// attached with WrapSDKContext. It panics if a Context was not properly -// attached. +// attached with WrapSDKContext. func UnwrapSDKContext(ctx context.Context) (sdk.Context, error) { if sdkCtx, ok := ctx.(sdk.Context); ok { return sdkCtx, nil diff --git a/x/logic/util/pointer.go b/x/logic/util/pointer.go new file mode 100644 index 00000000..f5e52682 --- /dev/null +++ b/x/logic/util/pointer.go @@ -0,0 +1,20 @@ +package util + +import "reflect" + +// DerefOrDefault returns the value of the pointer if it is not nil, otherwise returns the default value. +func DerefOrDefault[T any](ptr *T, defaultValue T) T { + if ptr != nil { + return *ptr + } + return defaultValue +} + +// NonZeroOrDefault returns the value of the argument if it is not nil and not zero, otherwise returns the default value. +func NonZeroOrDefault[T any](v, defaultValue T) T { + v1 := reflect.ValueOf(v) + if v1.IsValid() && !v1.IsZero() { + return v + } + return defaultValue +}