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

Added support for context.Done() #201

Merged
merged 1 commit into from
Dec 7, 2018
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
16 changes: 14 additions & 2 deletions pkg/runtime/core/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,24 @@ func (s *RootScope) Close() error {

s.closed = true

var errors []error

// close all values implemented io.Close
for _, c := range s.disposables {
c.Close()
if err := c.Close(); err != nil {
if errors == nil {
errors = make([]error, 0, len(s.disposables))
}

errors = append(errors, err)
}
}

return nil
if errors == nil {
return nil
}

return Errors(errors...)
}

func newScope(root *RootScope, parent *Scope) *Scope {
Expand Down
34 changes: 22 additions & 12 deletions pkg/runtime/expressions/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,33 @@ func (exp *BlockExpression) Add(stmt core.Expression) {
}

func (exp *BlockExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
for _, stmt := range exp.statements {
_, err := stmt.Exec(ctx, scope)

if err != nil {
return values.None, err
select {
case <-ctx.Done():
return values.None, core.ErrTerminated
default:
for _, stmt := range exp.statements {
_, err := stmt.Exec(ctx, scope)

if err != nil {
return values.None, err
}
}
}

return values.None, nil
return values.None, nil
}
}

func (exp *BlockExpression) Iterate(ctx context.Context, scope *core.Scope) (collections.Iterator, error) {
iter, err := exp.values.Iterate(ctx, scope)
select {
case <-ctx.Done():
return nil, core.ErrTerminated
default:
iter, err := exp.values.Iterate(ctx, scope)

if err != nil {
return nil, err
}
if err != nil {
return nil, err
}

return collections.NewTapIterator(iter, exp)
return collections.NewTapIterator(iter, exp)
}
}
158 changes: 158 additions & 0 deletions pkg/runtime/expressions/block_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package expressions_test

import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/collections"
"testing"

"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/expressions"
"github.com/MontFerret/ferret/pkg/runtime/values"
. "github.com/smartystreets/goconvey/convey"
)

type IterableFn func(ctx context.Context, scope *core.Scope) (collections.Iterator, error)

func (f IterableFn) Iterate(ctx context.Context, scope *core.Scope) (collections.Iterator, error) {
return f(ctx, scope)
}

type ExpressionFn func(ctx context.Context, scope *core.Scope) (core.Value, error)

func (f ExpressionFn) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
return f(ctx, scope)
}

func TestBlock(t *testing.T) {
newExp := func(values []core.Value) (*expressions.BlockExpression, error) {
iter, err := collections.NewDefaultSliceIterator(values)

if err != nil {
return nil, err
}

return expressions.NewBlockExpression(IterableFn(func(ctx context.Context, scope *core.Scope) (collections.Iterator, error) {
return iter, nil
}))
}

Convey("Should create a block expression", t, func() {
s, err := newExp(make([]core.Value, 0, 10))

So(err, ShouldBeNil)
So(s, ShouldHaveSameTypeAs, &expressions.BlockExpression{})
})

Convey("Should add a new expression of a default type", t, func() {
s, _ := newExp(make([]core.Value, 0, 10))

sourceMap := core.NewSourceMap("test", 1, 1)
exp, err := expressions.NewVariableExpression(sourceMap, "testExp")
So(err, ShouldBeNil)

s.Add(exp)
})

Convey("Should exec a block expression", t, func() {
s, _ := newExp(make([]core.Value, 0, 10))

sourceMap := core.NewSourceMap("test", 1, 1)
exp, err := expressions.NewVariableDeclarationExpression(sourceMap, "test", ExpressionFn(func(ctx context.Context, scope *core.Scope) (core.Value, error) {
return values.NewString("value"), nil
}))
So(err, ShouldBeNil)

s.Add(exp)

rootScope, _ := core.NewRootScope()
scope := rootScope.Fork()

_, err = s.Exec(context.Background(), scope)
So(err, ShouldBeNil)

val, err := scope.GetVariable("test")
So(err, ShouldBeNil)

So(val, ShouldEqual, "value")
})

Convey("Should not exec a nil block expression", t, func() {
s, _ := newExp(make([]core.Value, 0, 10))

sourceMap := core.NewSourceMap("test", 1, 1)
exp, err := expressions.NewVariableExpression(sourceMap, "test")
So(err, ShouldBeNil)

s.Add(exp)
So(err, ShouldBeNil)

rootScope, fn := core.NewRootScope()
scope := rootScope.Fork()
scope.SetVariable("test", values.NewString("value"))
fn()

value, err := s.Exec(context.Background(), scope)
So(err, ShouldBeNil)
So(value, ShouldHaveSameTypeAs, values.None)
})

Convey("Should return an iterator", t, func() {
s, _ := newExp([]core.Value{
values.NewInt(1),
values.NewInt(2),
values.NewInt(3),
})
sourceMap := core.NewSourceMap("test", 1, 1)
exp, _ := expressions.NewVariableExpression(sourceMap, "test")
s.Add(exp)

rootScope, _ := core.NewRootScope()
scope := rootScope.Fork()
scope.SetVariable("test", values.NewString("value"))

iter, err := s.Iterate(context.Background(), scope)
So(err, ShouldBeNil)

items, err := collections.ToSlice(context.Background(), scope, iter)
So(err, ShouldBeNil)
So(items, ShouldHaveLength, 3)
})

Convey("Should stop an execution when context is cancelled", t, func() {
s, _ := newExp(make([]core.Value, 0, 10))
sourceMap := core.NewSourceMap("test", 1, 1)
exp, _ := expressions.NewVariableExpression(sourceMap, "test")
s.Add(exp)

rootScope, _ := core.NewRootScope()
scope := rootScope.Fork()
scope.SetVariable("test", values.NewString("value"))

ctx, cancel := context.WithCancel(context.Background())
cancel()

_, err := s.Exec(ctx, scope)
So(err, ShouldEqual, core.ErrTerminated)
})

Convey("Should stop an execution when context is cancelled 2", t, func() {
s, _ := newExp([]core.Value{
values.NewInt(1),
values.NewInt(2),
values.NewInt(3),
})
sourceMap := core.NewSourceMap("test", 1, 1)
exp, _ := expressions.NewVariableExpression(sourceMap, "test")
s.Add(exp)

rootScope, _ := core.NewRootScope()
scope := rootScope.Fork()
scope.SetVariable("test", values.NewString("value"))

ctx, cancel := context.WithCancel(context.Background())
cancel()

_, err := s.Iterate(ctx, scope)
So(err, ShouldEqual, core.ErrTerminated)
})
}
6 changes: 6 additions & 0 deletions pkg/runtime/expressions/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func (b *BodyExpression) Add(exp core.Expression) error {
}

func (b *BodyExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
select {
case <-ctx.Done():
return values.None, core.ErrTerminated
default:
}

for _, exp := range b.statements {
if _, err := exp.Exec(ctx, scope); err != nil {
return values.None, err
Expand Down
31 changes: 18 additions & 13 deletions pkg/runtime/expressions/body_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import (
. "github.com/smartystreets/goconvey/convey"
)

func TestNewBodyExpression(t *testing.T) {
func TestBody(t *testing.T) {
Convey("Should create a block expression", t, func() {
s := expressions.NewBodyExpression(1)

So(s, ShouldHaveSameTypeAs, &expressions.BodyExpression{})
})
}

func TestBlockExpressionAddVariableExpression(t *testing.T) {
Convey("Should add a new expression of a default type", t, func() {
s := expressions.NewBodyExpression(0)

Expand All @@ -29,9 +27,7 @@ func TestBlockExpressionAddVariableExpression(t *testing.T) {
err = s.Add(exp)
So(err, ShouldBeNil)
})
}

func TestBlockExpressionAddReturnExpression(t *testing.T) {
Convey("Should add a new Return expression", t, func() {
s := expressions.NewBodyExpression(0)

Expand All @@ -45,9 +41,7 @@ func TestBlockExpressionAddReturnExpression(t *testing.T) {
err = s.Add(exp)
So(err, ShouldBeNil)
})
}

func TestBlockExpressionAddReturnExpressionFailed(t *testing.T) {
Convey("Should not add an already defined Return expression", t, func() {
s := expressions.NewBodyExpression(0)

Expand All @@ -65,9 +59,7 @@ func TestBlockExpressionAddReturnExpressionFailed(t *testing.T) {
So(err, ShouldBeError)
So(err.Error(), ShouldEqual, "invalid operation: return expression is already defined")
})
}

func TestBlockExpressionExec(t *testing.T) {
Convey("Should exec a block expression", t, func() {
s := expressions.NewBodyExpression(1)

Expand All @@ -91,9 +83,7 @@ func TestBlockExpressionExec(t *testing.T) {
So(value, ShouldNotBeNil)
So(value, ShouldEqual, "value")
})
}

func TestBlockExpressionExecNonFound(t *testing.T) {
Convey("Should not found a missing statement", t, func() {
s := expressions.NewBodyExpression(1)

Expand All @@ -117,9 +107,7 @@ func TestBlockExpressionExecNonFound(t *testing.T) {
So(err, ShouldHaveSameTypeAs, core.ErrNotFound)
So(value, ShouldHaveSameTypeAs, values.None)
})
}

func TestBlockExpressionExecNilExpression(t *testing.T) {
Convey("Should not exec a nil block expression", t, func() {
s := expressions.NewBodyExpression(1)

Expand All @@ -139,4 +127,21 @@ func TestBlockExpressionExecNilExpression(t *testing.T) {
So(err, ShouldBeNil)
So(value, ShouldHaveSameTypeAs, values.None)
})

Convey("Should stop an execution when context is cancelled", t, func() {
s := expressions.NewBodyExpression(1)
sourceMap := core.NewSourceMap("test", 1, 1)
exp, _ := expressions.NewVariableExpression(sourceMap, "test")
s.Add(exp)

rootScope, _ := core.NewRootScope()
scope := rootScope.Fork()
scope.SetVariable("test", values.NewString("value"))

ctx, cancel := context.WithCancel(context.Background())
cancel()

_, err := s.Exec(ctx, scope)
So(err, ShouldEqual, core.ErrTerminated)
})
}
Loading