From 92054a9871f66ea70ddf6b1ea45707ed2bf8dcbf Mon Sep 17 00:00:00 2001 From: plastikfan Date: Mon, 1 Jul 2024 13:09:01 +0100 Subject: [PATCH] test(kernel): add universal navigator tests (#62) --- builders.go | 7 ++- director-resume_test.go | 2 +- driver.go | 2 +- extent.go | 12 +++++ internal/kernel/builder.go | 3 +- internal/kernel/guardian.go | 14 ++++-- internal/kernel/kernel-defs.go | 4 ++ internal/kernel/kernel-support_test.go | 64 ++++++++++++++++++++++++ internal/kernel/mediator.go | 26 ++++------ internal/kernel/navigation-controller.go | 14 ++++-- internal/kernel/navigator-agent.go | 4 +- internal/kernel/navigator-factory.go | 8 +-- internal/kernel/navigator-hades.go | 12 ++++- internal/kernel/navigator.go | 26 ++++++++-- internal/resume/controller.go | 29 ++++++----- internal/resume/resume-defs.go | 8 +-- internal/resume/strategy-fastward.go | 8 ++- internal/resume/strategy-spawn.go | 10 +++- internal/services/broker.go | 4 +- internal/types/definitions.go | 29 +++++++++-- session.go | 28 ++++++++--- synchronise.go | 24 +++++---- 22 files changed, 253 insertions(+), 85 deletions(-) diff --git a/builders.go b/builders.go index 89546ba..ec40330 100644 --- a/builders.go +++ b/builders.go @@ -1,7 +1,6 @@ package tv import ( - "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/internal/kernel" "github.com/snivilised/traverse/internal/types" "github.com/snivilised/traverse/measure" @@ -10,7 +9,7 @@ import ( type buildArtefacts struct { o *pref.Options - nav core.Navigator + nav types.KernelController plugins []types.Plugin ext extent } @@ -77,7 +76,7 @@ func (bs *Builders) buildAll() (*buildArtefacts, error) { if bindErr := p.Init(); bindErr != nil { return &buildArtefacts{ o: o, - nav: artefacts.Navigator, + nav: artefacts.Controller, plugins: plugins, ext: ext, }, bindErr @@ -86,7 +85,7 @@ func (bs *Builders) buildAll() (*buildArtefacts, error) { return &buildArtefacts{ o: o, - nav: artefacts.Navigator, + nav: artefacts.Controller, plugins: plugins, ext: ext, }, nil diff --git a/director-resume_test.go b/director-resume_test.go index 508b57d..3802036 100644 --- a/director-resume_test.go +++ b/director-resume_test.go @@ -88,7 +88,7 @@ var _ = Describe("Director(Resume)", Ordered, func() { Context("features", func() { Context("Run", func() { When("filter", func() { - It("๐Ÿงช should: register ok", func(specCtx SpecContext) { + FIt("๐Ÿงช should: register ok", func(specCtx SpecContext) { defer leaktest.Check(GinkgoT())() ctx, cancel := context.WithCancel(specCtx) diff --git a/driver.go b/driver.go index d956f55..ed8fa62 100644 --- a/driver.go +++ b/driver.go @@ -22,7 +22,7 @@ func (d *driver) init() { _ = m.Data // now invoke session.finish }, - Matcher: services.TopicTraverseResult, + Matcher: services.TopicNavigationComplete, }) } diff --git a/extent.go b/extent.go index 7d52b7b..157f47d 100644 --- a/extent.go +++ b/extent.go @@ -16,6 +16,7 @@ type extent interface { options(...pref.Option) (*pref.Options, error) navFS() fs.FS resFS() fs.FS + complete() bool } type fileSystems struct { @@ -56,6 +57,10 @@ func (ex *primeExtent) options(settings ...pref.Option) (*pref.Options, error) { return pref.Get(settings...) } +func (ex *primeExtent) complete() bool { + return true +} + type resumeExtent struct { baseExtent w *pref.Was @@ -88,3 +93,10 @@ func (ex *resumeExtent) options(settings ...pref.Option) (*pref.Options, error) // return loaded.O, err } + +func (ex *resumeExtent) complete() bool { + // "NOT-IMPL: resumeExtent.complete -> the strategy knows this" + // ===> send to plugin? + // + return true +} diff --git a/internal/kernel/builder.go b/internal/kernel/builder.go index 0efa47d..69b293a 100644 --- a/internal/kernel/builder.go +++ b/internal/kernel/builder.go @@ -1,14 +1,13 @@ package kernel import ( - "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/internal/types" "github.com/snivilised/traverse/pref" ) type ( Artefacts struct { - Navigator core.Navigator + Controller types.KernelController Mediator types.Mediator Facilities types.Facilities Resources *types.Resources diff --git a/internal/kernel/guardian.go b/internal/kernel/guardian.go index ea903bc..4a9db48 100644 --- a/internal/kernel/guardian.go +++ b/internal/kernel/guardian.go @@ -16,17 +16,21 @@ type ( invocationIt = collections.Iterator[types.Link] ) +type owned struct { + mums measure.Mutables +} + // anchor is a specialised link that should always be the // last in the chain and contains the original client's handler. type anchor struct { target core.Client - mums measure.Mutables + owned owned } func (t *anchor) Next(node *core.Node) (bool, error) { if metric := lo.Ternary(node.IsFolder(), - t.mums[enums.MetricNoFoldersInvoked], - t.mums[enums.MetricNoFilesInvoked], + t.owned.mums[enums.MetricNoFoldersInvoked], + t.owned.mums[enums.MetricNoFilesInvoked], ); metric != nil { metric.Tick() } @@ -67,7 +71,9 @@ func newGuardian(callback core.Client, stack := collections.NewStack[types.Link]() stack.Push(&anchor{ target: callback, - mums: mums, + owned: owned{ + mums: mums, + }, }) return &guardian{ diff --git a/internal/kernel/kernel-defs.go b/internal/kernel/kernel-defs.go index f2c9021..b04072d 100644 --- a/internal/kernel/kernel-defs.go +++ b/internal/kernel/kernel-defs.go @@ -12,6 +12,8 @@ import ( type ( // NavigatorImpl NavigatorImpl interface { + Starting(session types.Session) + // Top Top(ctx context.Context, ns *navigationStatic, @@ -24,6 +26,8 @@ type ( ns *navigationStatic, current *core.Node, ) (bool, error) + + Result(ctx context.Context, err error) *types.KernelResult } // NavigatorDriver diff --git a/internal/kernel/kernel-support_test.go b/internal/kernel/kernel-support_test.go index a217728..b06998b 100644 --- a/internal/kernel/kernel-support_test.go +++ b/internal/kernel/kernel-support_test.go @@ -1,6 +1,70 @@ package kernel_test +import ( + "io/fs" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok + "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/cycle" + "github.com/snivilised/traverse/enums" + "github.com/snivilised/traverse/internal/helpers" +) + const ( RootPath = "traversal-root-path" RestorePath = "/from-restore-path" ) + +type recordingMap map[string]int +type recordingScopeMap map[string]enums.FilterScope +type recordingOrderMap map[string]int + +type directoryQuantities struct { + files uint + folders uint + children map[string]int +} + +type naviTE struct { + message string + should string + relative string + once bool + visit bool + caseSensitive bool + subscription enums.Subscription + callback core.Client + mandatory []string + prohibited []string + expectedNoOf directoryQuantities +} + +func begin(em string) cycle.BeginHandler { + return func(root string) { + GinkgoWriter.Printf( + "---> %v [traverse-navigator-test:BEGIN], root: '%v'\n", em, root, + ) + } +} + +func universalCallback(name string) core.Client { + return func(node *core.Node) error { + depth := node.Extension.Depth + GinkgoWriter.Printf( + "---> ๐ŸŒŠ UNIVERSAL//%v-CALLBACK: (depth:%v) '%v'\n", name, depth, node.Path, + ) + Expect(node.Extension).NotTo(BeNil(), helpers.Reason(node.Path)) + return nil + } +} + +func subscribes(subscription enums.Subscription, de fs.DirEntry) bool { + isAnySubscription := (subscription == enums.SubscribeUniversal) + + files := (subscription == enums.SubscribeFiles) && (!de.IsDir()) + folders := ((subscription == enums.SubscribeFolders) || + subscription == enums.SubscribeFoldersWithFiles) && (de.IsDir()) + + return isAnySubscription || files || folders +} diff --git a/internal/kernel/mediator.go b/internal/kernel/mediator.go index 42819db..1b708af 100644 --- a/internal/kernel/mediator.go +++ b/internal/kernel/mediator.go @@ -13,10 +13,6 @@ import ( // mediator controls traversal events, sends notifications and emits life-cycle events -type owned struct { - mums measure.Mutables -} - type mediator struct { root string impl NavigatorImpl @@ -25,7 +21,6 @@ type mediator struct { pad *scratchPad // gets created just before nav begins o *pref.Options resources *types.Resources - owned owned // there should be a registration phase; but doing so mean that // these entities should already exist, which is counter productive. // possibly use dependency inject where entities declare their @@ -48,24 +43,19 @@ func newMediator(using *pref.Using, sealer types.GuardianSealer, res *types.Resources, ) *mediator { - mums := res.Supervisor.Many( - enums.MetricNoFilesInvoked, - enums.MetricNoFoldersInvoked, - ) - return &mediator{ - root: using.Root, - impl: impl, - client: newGuardian(using.Handler, sealer, mums), + root: using.Root, + impl: impl, + client: newGuardian(using.Handler, sealer, res.Supervisor.Many( + enums.MetricNoFilesInvoked, + enums.MetricNoFoldersInvoked, + )), frame: &navigationFrame{ periscope: level.New(), }, pad: newScratch(o), o: o, resources: res, - owned: owned{ - mums: mums, - }, } } @@ -77,6 +67,10 @@ func (m *mediator) Unwind(role enums.Role) error { return m.client.Unwind(role) } +func (m *mediator) Starting(session types.Session) { + m.impl.Starting(session) +} + func (m *mediator) Navigate(ctx context.Context) (core.TraverseResult, error) { // could we pass in the invokable client to Top so the navigators can invoke // as required. diff --git a/internal/kernel/navigation-controller.go b/internal/kernel/navigation-controller.go index 84cbba7..9d4349f 100644 --- a/internal/kernel/navigation-controller.go +++ b/internal/kernel/navigation-controller.go @@ -8,15 +8,23 @@ import ( ) type NavigationController struct { - mediator *mediator + Mediator *mediator +} + +func (nc *NavigationController) Result(ctx context.Context, err error) *types.KernelResult { + return nc.Mediator.impl.Result(ctx, err) +} + +func (nc *NavigationController) Starting(session types.Session) { + nc.Mediator.Starting(session) } func (nc *NavigationController) Navigate(ctx context.Context) (core.TraverseResult, error) { - return nc.mediator.Navigate(ctx) + return nc.Mediator.Navigate(ctx) } func (nc *NavigationController) Impl() NavigatorImpl { - return nc.mediator.impl + return nc.Mediator.impl } func (nc *NavigationController) Register(types.Plugin) error { diff --git a/internal/kernel/navigator-agent.go b/internal/kernel/navigator-agent.go index ecbb39a..c6f018a 100644 --- a/internal/kernel/navigator-agent.go +++ b/internal/kernel/navigator-agent.go @@ -46,9 +46,7 @@ func top(ctx context.Context, }, ) - return &types.KernelResult{ - Err: err, - }, nil + return ns.mediator.impl.Result(ctx, err), err } const ( diff --git a/internal/kernel/navigator-factory.go b/internal/kernel/navigator-factory.go index ba7c5e5..a06a1b3 100644 --- a/internal/kernel/navigator-factory.go +++ b/internal/kernel/navigator-factory.go @@ -19,9 +19,9 @@ func New(using *pref.Using, o *pref.Options, controller := newController(using, o, impl, sealer, res) return &Artefacts{ - Navigator: controller, - Mediator: controller.mediator, - Resources: res, + Controller: controller, + Mediator: controller.Mediator, + Resources: res, } } @@ -32,7 +32,7 @@ func newController(using *pref.Using, res *types.Resources, ) *NavigationController { return &NavigationController{ - mediator: newMediator(using, o, impl, sealer, res), + Mediator: newMediator(using, o, impl, sealer, res), } } diff --git a/internal/kernel/navigator-hades.go b/internal/kernel/navigator-hades.go index e13f850..6ce7abc 100644 --- a/internal/kernel/navigator-hades.go +++ b/internal/kernel/navigator-hades.go @@ -4,10 +4,11 @@ import ( "context" "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/internal/types" "github.com/snivilised/traverse/measure" ) -func HadesNav(err error) core.Navigator { +func HadesNav(err error) types.KernelController { return &navigatorHades{ err: err, } @@ -29,6 +30,15 @@ type navigatorHades struct { err error } +func (n *navigatorHades) Result(_ context.Context, err error) *types.KernelResult { + return &types.KernelResult{ + Err: err, + } +} + +func (n *navigatorHades) Starting(types.Session) { +} + func (n *navigatorHades) Navigate(_ context.Context) (core.TraverseResult, error) { return &hadesResult{ err: n.err, diff --git a/internal/kernel/navigator.go b/internal/kernel/navigator.go index d1afe7e..f61e860 100644 --- a/internal/kernel/navigator.go +++ b/internal/kernel/navigator.go @@ -4,14 +4,16 @@ import ( "context" "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/types" "github.com/snivilised/traverse/pref" ) type navigator struct { - o *pref.Options - using *pref.Using - res *types.Resources + o *pref.Options + using *pref.Using + res *types.Resources + session types.Session } /* @@ -43,12 +45,16 @@ func (n *navigator) ascend(navi *navigationInfo, permit bool) { _, _ = navi, permit } +func (n *navigator) Starting(session types.Session) { + n.session = session +} + func (n *navigator) Top(ctx context.Context, ns *navigationStatic, ) (*types.KernelResult, error) { _, _ = ctx, ns - return &types.KernelResult{}, nil + return n.Result(ctx, nil), nil } func (n *navigator) Travel(context.Context, @@ -57,3 +63,15 @@ func (n *navigator) Travel(context.Context, ) (bool, error) { return continueTraversal, nil } + +func (n *navigator) Result(ctx context.Context, err error) *types.KernelResult { + res := &types.KernelResult{ + Session: n.session, + Err: err, + Complete: n.session.IsComplete(), + } + + _ = services.Broker.Emit(ctx, services.TopicNavigationComplete, res) + + return res +} diff --git a/internal/resume/controller.go b/internal/resume/controller.go index 984dbdd..33f0f06 100644 --- a/internal/resume/controller.go +++ b/internal/resume/controller.go @@ -3,7 +3,6 @@ package resume import ( "context" - "github.com/pkg/errors" "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/i18n" @@ -13,12 +12,20 @@ import ( ) type Controller struct { - controller core.Navigator + kc types.KernelController was *pref.Was strategy resumeStrategy facilities types.Facilities } +func (c *Controller) Starting(session types.Session) { + c.kc.Starting(session) +} + +func (c *Controller) Result(ctx context.Context, err error) *types.KernelResult { + return c.kc.Result(ctx, err) +} + func NewController(was *pref.Was, artefacts *kernel.Artefacts) *kernel.Artefacts { // The Navigator on the incoming artefacts is the core navigator. It is // decorated here for resume. The strategy only needs access to the core navigator. @@ -29,13 +36,13 @@ func NewController(was *pref.Was, artefacts *kernel.Artefacts) *kernel.Artefacts err error ) - if strategy, err = newStrategy(was, artefacts.Navigator); err != nil { + if strategy, err = newStrategy(was, artefacts.Controller); err != nil { return artefacts } return &kernel.Artefacts{ - Navigator: &Controller{ - controller: artefacts.Navigator, + Controller: &Controller{ + kc: artefacts.Controller, was: was, strategy: strategy, facilities: artefacts.Facilities, @@ -44,8 +51,8 @@ func NewController(was *pref.Was, artefacts *kernel.Artefacts) *kernel.Artefacts } } -func newStrategy(was *pref.Was, nav core.Navigator) (strategy resumeStrategy, err error) { - driver, ok := nav.(kernel.NavigatorDriver) +func newStrategy(was *pref.Was, kc types.KernelController) (strategy resumeStrategy, err error) { + driver, ok := kc.(kernel.NavigatorDriver) if !ok { return nil, i18n.ErrInternalFailedToGetNavigatorDriver @@ -53,7 +60,7 @@ func newStrategy(was *pref.Was, nav core.Navigator) (strategy resumeStrategy, er base := baseStrategy{ o: was.O, - nav: nav, + kc: kc, impl: driver.Impl(), } @@ -72,8 +79,6 @@ func newStrategy(was *pref.Was, nav core.Navigator) (strategy resumeStrategy, er return strategy, nil } -func (c *Controller) Navigate(_ context.Context) (core.TraverseResult, error) { - return &types.KernelResult{ - Err: errors.Wrap(core.ErrNotImpl, "resume.Controller.Navigate"), - }, nil +func (c *Controller) Navigate(ctx context.Context) (core.TraverseResult, error) { + return c.strategy.resume(ctx) } diff --git a/internal/resume/resume-defs.go b/internal/resume/resume-defs.go index b588b25..5f9fd52 100644 --- a/internal/resume/resume-defs.go +++ b/internal/resume/resume-defs.go @@ -1,7 +1,8 @@ package resume import ( - "github.com/snivilised/traverse/core" + "context" + "github.com/snivilised/traverse/internal/kernel" "github.com/snivilised/traverse/internal/types" "github.com/snivilised/traverse/pref" @@ -19,12 +20,13 @@ type resumeStrategy interface { init() attach() detach() - resume() (*types.KernelResult, error) + resume(context.Context) (*types.KernelResult, error) + complete() bool finish() error } type baseStrategy struct { o *pref.Options - nav core.Navigator + kc types.KernelController impl kernel.NavigatorImpl } diff --git a/internal/resume/strategy-fastward.go b/internal/resume/strategy-fastward.go index 0b663dd..800d1ce 100644 --- a/internal/resume/strategy-fastward.go +++ b/internal/resume/strategy-fastward.go @@ -1,6 +1,8 @@ package resume import ( + "context" + "github.com/pkg/errors" "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/internal/types" @@ -38,10 +40,14 @@ func (s *fastwardStrategy) detach() { } -func (s *fastwardStrategy) resume() (*types.KernelResult, error) { +func (s *fastwardStrategy) resume(context.Context) (*types.KernelResult, error) { return &types.KernelResult{}, nil } +func (s *fastwardStrategy) complete() bool { + return true +} + func (s *fastwardStrategy) finish() error { return nil } diff --git a/internal/resume/strategy-spawn.go b/internal/resume/strategy-spawn.go index 5f988a8..0e76967 100644 --- a/internal/resume/strategy-spawn.go +++ b/internal/resume/strategy-spawn.go @@ -1,6 +1,8 @@ package resume import ( + "context" + "github.com/snivilised/traverse/internal/types" ) @@ -20,10 +22,14 @@ func (s *spawnStrategy) detach() { } -func (s *spawnStrategy) resume() (*types.KernelResult, error) { - return &types.KernelResult{}, nil +func (s *spawnStrategy) resume(ctx context.Context) (*types.KernelResult, error) { + return s.impl.Result(ctx, nil), nil } func (s *spawnStrategy) finish() error { return nil } + +func (s *spawnStrategy) complete() bool { + panic("NOT-IMPL:spawnStrategy.complete") +} diff --git a/internal/services/broker.go b/internal/services/broker.go index 476a001..445175b 100644 --- a/internal/services/broker.go +++ b/internal/services/broker.go @@ -6,10 +6,10 @@ const ( format = "%03d" TopicInitPlugins = "topic:init.plugins" TopicInterceptNavigator = "topic:intercept.navigator" + TopicNavigationComplete = "topic:navigation.complete" TopicOptionsAnnounce = "topic:options.announce" TopicOptionsBefore = "topic:options.before" TopicOptionsComplete = "topic:options.complete" - TopicTraverseResult = "topic:traverse.result" ) var ( @@ -17,10 +17,10 @@ var ( topics = []string{ TopicInitPlugins, TopicInterceptNavigator, + TopicNavigationComplete, TopicOptionsAnnounce, TopicOptionsBefore, TopicOptionsComplete, - TopicTraverseResult, } ) diff --git a/internal/types/definitions.go b/internal/types/definitions.go index 780e553..9ab8e6a 100644 --- a/internal/types/definitions.go +++ b/internal/types/definitions.go @@ -3,6 +3,7 @@ package types import ( "context" "io/fs" + "time" "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/enums" @@ -13,6 +14,18 @@ import ( // package types defines internal types type ( + Completion interface { + IsComplete() bool + } + + // Session represents a traversal session and keeps tracks of + // timing. + Session interface { + Completion + StartedAt() time.Time + Elapsed() time.Duration + } + // Link represents a single decorator in the chain Link interface { // Next invokes this decorator which returns true if @@ -76,21 +89,29 @@ type ( Metrics() *measure.Supervisor } - // TraverseController - TraverseController interface { + // KernelController + KernelController interface { core.Navigator + Starting(Session) + Result(ctx context.Context, err error) *KernelResult } ) type KernelResult struct { + Session Session Reporter measure.Reporter Err error + Complete bool +} + +func (r *KernelResult) IsComplete() bool { + return r.Complete } -func (r KernelResult) Metrics() measure.Reporter { +func (r *KernelResult) Metrics() measure.Reporter { return r.Reporter } -func (r KernelResult) Error() error { +func (r *KernelResult) Error() error { return r.Err } diff --git a/session.go b/session.go index 30ccdfe..b29a464 100644 --- a/session.go +++ b/session.go @@ -8,13 +8,6 @@ import ( "github.com/snivilised/traverse/internal/types" ) -// Session represents a traversal session and keeps tracks of -// timing. -type Session interface { - StartedAt() time.Time - Elapsed() time.Duration -} - type session struct { sync synchroniser started time.Time @@ -24,9 +17,24 @@ type session struct { func (s *session) start() { s.started = time.Now() + s.sync.Starting(s) } -func (s *session) finish(_ core.TraverseResult) { +/* +func (s *session) finish(result *TraverseResult, _ error) { + s.duration = time.Since(s.startAt) + + if result != nil { + result.Session = s + } +} +*/ + +func (s *session) finish(result core.TraverseResult) { + _ = result + // if result != nil { + // result.Session + // } // I wonder if the traverse result should become available // as a result of a message sent of the bus. Any component // needing access to the result should handle the message. This @@ -35,6 +43,10 @@ func (s *session) finish(_ core.TraverseResult) { s.duration = time.Since(s.started) } +func (s *session) IsComplete() bool { + return s.sync.IsComplete() +} + func (s *session) StartedAt() time.Time { return s.started } diff --git a/synchronise.go b/synchronise.go index 57153c1..5a5740f 100644 --- a/synchronise.go +++ b/synchronise.go @@ -13,10 +13,12 @@ import ( type synchroniser interface { core.Navigator + Starting(types.Session) + IsComplete() bool } type trunk struct { - nav core.Navigator + nav types.KernelController o *pref.Options ext extent err error @@ -29,6 +31,14 @@ func (t trunk) extent() extent { return t.ext } +func (t trunk) IsComplete() bool { + return t.ext.complete() +} + +func (t trunk) Starting(session types.Session) { + t.nav.Starting(session) +} + type concurrent struct { trunk wg boost.WaitGroup @@ -41,9 +51,7 @@ func (c *concurrent) Navigate(ctx context.Context) (core.TraverseResult, error) defer c.close() if c.err != nil { - return types.KernelResult{ - Err: c.err, - }, c.err + return c.nav.Result(ctx, c.err), c.err } c.decorator = func(node *core.Node) error { @@ -82,9 +90,7 @@ func (c *concurrent) Navigate(ctx context.Context) (core.TraverseResult, error) if c.err != nil { err := errors.Wrapf(c.err, i18n.ErrWorkerPoolCreationFailed.Error()) - return types.KernelResult{ - Err: err, - }, err + return c.nav.Result(ctx, err), err } c.open(ctx) @@ -107,9 +113,7 @@ type sequential struct { func (s *sequential) Navigate(ctx context.Context) (core.TraverseResult, error) { if s.err != nil { - return types.KernelResult{ - Err: s.err, - }, s.err + return s.nav.Result(ctx, s.err), s.err } return s.nav.Navigate(ctx)