Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 1 addition & 2 deletions integration/fsc/pingpong/mock/initiator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/assert"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/id"
view3 "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/view"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
)

Expand All @@ -31,7 +30,7 @@ func (p *Initiator) Call(ctx view.Context) (interface{}, error) {
responder := identityProvider.Identity("responder")
var context view.Context
if p.Mock {
c := &view3.MockContext{Ctx: ctx}
c := &DelegatedContext{Ctx: ctx}
c.RespondToAs(ctx.Initiator(), responder, &Responder{})
context = c
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package view
package mock

import (
"context"
"sync"

"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/comm/session"
view2 "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/view"
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
Expand All @@ -25,61 +26,61 @@ type Responders struct {
Channel *session.LocalBidirectionalChannel
}

type MockContext struct {
type DelegatedContext struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we can move the DelegatedContext into the integration test where it is exclusively used, and even make it private.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it here because it is kind of a mock.

Ctx view.Context
responders []*Responders
}

func (c *MockContext) StartSpanFrom(context.Context, string, ...trace.SpanStartOption) (context.Context, trace.Span) {
func (c *DelegatedContext) StartSpanFrom(context.Context, string, ...trace.SpanStartOption) (context.Context, trace.Span) {
return c.Context(), noop.Span{}
}

func (c *MockContext) StartSpan(string, ...trace.SpanStartOption) trace.Span {
func (c *DelegatedContext) StartSpan(string, ...trace.SpanStartOption) trace.Span {
return noop.Span{}
}

func (c *MockContext) GetService(v interface{}) (interface{}, error) {
func (c *DelegatedContext) GetService(v interface{}) (interface{}, error) {
return c.Ctx.GetService(v)
}

func (c *MockContext) ID() string {
func (c *DelegatedContext) ID() string {
return c.Ctx.ID()
}

func (c *MockContext) RunView(view view.View, opts ...view.RunViewOption) (interface{}, error) {
func (c *DelegatedContext) RunView(view view.View, opts ...view.RunViewOption) (interface{}, error) {
return c.Ctx.RunView(view, opts...)
}

func (c *MockContext) Me() view.Identity {
func (c *DelegatedContext) Me() view.Identity {
return c.Ctx.Me()
}

func (c *MockContext) IsMe(id view.Identity) bool {
func (c *DelegatedContext) IsMe(id view.Identity) bool {
return c.Ctx.IsMe(id)
}

func (c *MockContext) Initiator() view.View {
func (c *DelegatedContext) Initiator() view.View {
return c.Ctx.Initiator()
}

func (c *MockContext) GetSessionByID(id string, party view.Identity) (view.Session, error) {
func (c *DelegatedContext) GetSessionByID(id string, party view.Identity) (view.Session, error) {
// TODO: check among the responders
return c.Ctx.GetSessionByID(id, party)
}

func (c *MockContext) Session() view.Session {
func (c *DelegatedContext) Session() view.Session {
return c.Ctx.Session()
}

func (c *MockContext) Context() context.Context {
func (c *DelegatedContext) Context() context.Context {
return c.Ctx.Context()
}

func (c *MockContext) OnError(callback func()) {
func (c *DelegatedContext) OnError(callback func()) {
c.Ctx.OnError(callback)
}

func (c *MockContext) GetSession(caller view.View, party view.Identity, boundToViews ...view.View) (view.Session, error) {
func (c *DelegatedContext) GetSession(caller view.View, party view.Identity, boundToViews ...view.View) (view.Session, error) {
for _, responder := range c.responders {
if responder.InitiatorView == caller && responder.ResponderID.Equal(party) {
responder.Lock.RLock()
Expand All @@ -103,7 +104,7 @@ func (c *MockContext) GetSession(caller view.View, party view.Identity, boundToV
}
left := biChannel.LeftSession()
right := biChannel.RightSession()
RunView(c, responder.ResponderView, view.AsResponder(right))
view2.RunView(c, responder.ResponderView, view.AsResponder(right))

responder.Channel = biChannel
return left, nil
Expand All @@ -113,30 +114,10 @@ func (c *MockContext) GetSession(caller view.View, party view.Identity, boundToV
return c.Ctx.GetSession(caller, party)
}

func (c *MockContext) RespondToAs(initiator view.View, responder view.Identity, r view.View) {
func (c *DelegatedContext) RespondToAs(initiator view.View, responder view.Identity, r view.View) {
c.responders = append(c.responders, &Responders{
InitiatorView: initiator,
ResponderID: responder,
ResponderView: r,
})
}

// RunView runs passed view within the passed context and using the passed options in a separate goroutine
func RunView(context view.Context, view view.View, opts ...view.RunViewOption) {
defer func() {
if r := recover(); r != nil {
logger.Debugf("panic in RunView: %v", r)
}
}()
go func() {
defer func() {
if r := recover(); r != nil {
logger.Debugf("panic in RunView: %v", r)
}
}()
_, err := context.RunView(view, opts...)
if err != nil {
logger.Errorf("failed to run view: %s", err)
}
}()
}
148 changes: 148 additions & 0 deletions platform/view/services/view/child.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package view

import (
"context"

"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
"go.opentelemetry.io/otel/trace"
)

//go:generate counterfeiter -o mock/parent_context.go -fake-name ParentContext . ParentContext
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line could be removed after generating the fakes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @SaidAltury-ibm , it is still good to have it because one can regenerate the mocks faster when needed.

type ParentContext interface {
DisposableContext
Cleanup()
PutSession(caller view.View, party view.Identity, session view.Session) error
}

// ChildContext is a view context with a parent.
// It allows the developer to override session and initiator.
// It also supports error callbacks.
type ChildContext struct {
Parent ParentContext

session view.Session
initiator view.View
errorCallbackFuncs []func()
}

// NewChildContextFromParent return a new ChildContext from the given parent
func NewChildContextFromParent(parentContext ParentContext) *ChildContext {
return NewChildContext(parentContext, nil, nil, nil)
}

// NewChildContextFromParentAndSession return a new ChildContext from the given parent and session
func NewChildContextFromParentAndSession(parentContext ParentContext, session view.Session) *ChildContext {
return NewChildContext(parentContext, session, nil, nil)
}

// NewChildContextFromParentAndInitiator return a new ChildContext from the given parent and initiator view
func NewChildContextFromParentAndInitiator(parentContext ParentContext, initiator view.View) *ChildContext {
return NewChildContext(parentContext, nil, initiator, nil)
}

// NewChildContext return a new ChildContext from the given arguments
func NewChildContext(parentContext ParentContext, session view.Session, initiator view.View, errorCallbackFuncs ...func()) *ChildContext {
return &ChildContext{Parent: parentContext, session: session, initiator: initiator, errorCallbackFuncs: errorCallbackFuncs}
}

func (w *ChildContext) StartSpanFrom(c context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
return w.Parent.StartSpanFrom(c, name, opts...)
}

func (w *ChildContext) GetService(v interface{}) (interface{}, error) {
return w.Parent.GetService(v)
}

func (w *ChildContext) PutService(v interface{}) error {
mutableContext, ok := w.Parent.(view.MutableContext)
if ok {
return mutableContext.PutService(v)
}
return nil
}

func (w *ChildContext) ID() string {
return w.Parent.ID()
}

func (w *ChildContext) Me() view.Identity {
return w.Parent.Me()
}

func (w *ChildContext) IsMe(id view.Identity) bool {
return w.Parent.IsMe(id)
}

func (w *ChildContext) GetSession(caller view.View, party view.Identity, boundToViews ...view.View) (view.Session, error) {
return w.Parent.GetSession(caller, party, boundToViews...)
}

func (w *ChildContext) GetSessionByID(id string, party view.Identity) (view.Session, error) {
return w.Parent.GetSessionByID(id, party)
}

func (w *ChildContext) Context() context.Context {
return w.Parent.Context()
}

func (w *ChildContext) Session() view.Session {
if w.session == nil {
return w.Parent.Session()
}
return w.session
}

func (w *ChildContext) ResetSessions() error {
mutableContext, ok := w.Parent.(view.MutableContext)
if ok {
return mutableContext.ResetSessions()
}
return nil
}

func (w *ChildContext) Initiator() view.View {
if w.initiator == nil {
return w.Parent.Initiator()
}
return w.initiator
}

func (w *ChildContext) OnError(f func()) {
w.errorCallbackFuncs = append(w.errorCallbackFuncs, f)
}

func (w *ChildContext) RunView(v view.View, opts ...view.RunViewOption) (res interface{}, err error) {
return RunViewNow(w, v, opts...)
}

func (w *ChildContext) Dispose() {
if w.Parent != nil {
w.Parent.Dispose()
}
}

func (w *ChildContext) PutSession(caller view.View, party view.Identity, session view.Session) error {
return w.Parent.PutSession(caller, party, session)
}

func (w *ChildContext) Cleanup() {
logger.Debugf("cleaning up child context [%s][%d]", w.ID(), len(w.errorCallbackFuncs))
for _, callbackFunc := range w.errorCallbackFuncs {
w.safeInvoke(callbackFunc)
}
}

func (w *ChildContext) safeInvoke(f func()) {
defer func() {
if r := recover(); r != nil {
logger.Debugf("function [%s] panicked [%s]", f, r)
}
}()
f()
}
Loading
Loading