From fbfe00a55c2371409015824fd8b71a6243b95746 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 | 10 ++-- director.go | 15 ++++-- driver.go | 2 +- extent.go | 23 +++++++-- internal-traverse-defs.go | 22 ++++++-- internal/hiber/hibernate-plugin.go | 4 +- internal/kernel/base-plugin.go | 3 +- internal/kernel/builder.go | 18 ++++--- internal/kernel/guardian.go | 14 ++++-- internal/kernel/kernel-defs.go | 4 ++ internal/kernel/kernel-support_test.go | 64 ++++++++++++++++++++++++ internal/kernel/mediator.go | 30 +++++------ internal/kernel/navigation-controller.go | 14 ++++-- internal/kernel/navigator-agent.go | 4 +- internal/kernel/navigator-factory.go | 24 ++++----- internal/kernel/navigator-hades.go | 12 ++++- internal/kernel/navigator.go | 26 ++++++++-- internal/refine/filter-plugin.go | 4 +- internal/resume/controller.go | 34 +++++++------ internal/resume/resume-defs.go | 8 +-- internal/resume/resume-plugin.go | 9 +++- internal/resume/strategy-fastward.go | 8 ++- internal/resume/strategy-spawn.go | 10 +++- internal/sampling/sampling-plugin.go | 4 +- internal/services/broker.go | 4 +- internal/types/definitions.go | 39 +++++++++++++-- session.go | 28 ++++++++--- synchronise.go | 24 +++++---- 28 files changed, 339 insertions(+), 122 deletions(-) diff --git a/builders.go b/builders.go index 89546ba..4de3512 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 } @@ -60,7 +59,8 @@ func (bs *Builders) buildAll() (*buildArtefacts, error) { // plugins, pluginsErr := bs.plugins.build(o, artefacts.Mediator, - ext.plugin(artefacts.Mediator), + artefacts.Controller, + ext.plugin(artefacts), ) if pluginsErr != nil { @@ -77,7 +77,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 +86,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.go b/director.go index 20ac01b..ba3350f 100644 --- a/director.go +++ b/director.go @@ -19,6 +19,7 @@ type ifActive func(o *pref.Options, mediator types.Mediator) types.Plugin // to activate features according to option selections. other plugins will // be initialised after primary plugins func activated(o *pref.Options, mediator types.Mediator, + kc types.KernelController, others ...types.Plugin, ) (plugins []types.Plugin, err error) { var ( @@ -42,7 +43,7 @@ func activated(o *pref.Options, mediator types.Mediator, } for _, plugin := range plugins { - err = plugin.Register() + err = plugin.Register(kc) if err != nil { return nil, err @@ -88,8 +89,10 @@ func Prime(using *pref.Using, settings ...pref.Option) *Builders { return ext.options(settings...) }), - navigator: kernel.Builder(func(o *pref.Options, res *types.Resources) (*kernel.Artefacts, error) { - return kernel.New(using, o, &kernel.Benign{}, res), nil + navigator: kernel.Builder(func(o *pref.Options, + resources *types.Resources, + ) (*kernel.Artefacts, error) { + return kernel.New(using, o, &kernel.Benign{}, resources), nil }), plugins: features(activated), // swap over features & activated } @@ -132,8 +135,10 @@ func Resume(was *Was, settings ...pref.Option) *Builders { return ext.options(settings...) }), - navigator: kernel.Builder(func(o *pref.Options, res *types.Resources) (*kernel.Artefacts, error) { - artefacts := kernel.New(&was.Using, o, resume.GetSealer(was), res) + navigator: kernel.Builder(func(o *pref.Options, + resources *types.Resources, + ) (*kernel.Artefacts, error) { + artefacts := kernel.New(&was.Using, o, resume.GetSealer(was), resources) return resume.NewController(was, artefacts), nil }), 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..ad9c03d 100644 --- a/extent.go +++ b/extent.go @@ -12,10 +12,11 @@ import ( type extent interface { using() *pref.Using was() *pref.Was - plugin(types.Mediator) types.Plugin + plugin(*kernel.Artefacts) types.Plugin // !!! *kernel.Artefacts options(...pref.Option) (*pref.Options, error) navFS() fs.FS resFS() fs.FS + complete() bool } type fileSystems struct { @@ -48,7 +49,7 @@ func (ex *primeExtent) was() *pref.Was { return nil } -func (ex *primeExtent) plugin(types.Mediator) types.Plugin { +func (ex *primeExtent) plugin(*kernel.Artefacts) types.Plugin { return nil } @@ -56,10 +57,15 @@ 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 loaded *pref.LoadInfo + rp *resume.Plugin } func (ex *resumeExtent) using() *pref.Using { @@ -70,12 +76,15 @@ func (ex *resumeExtent) was() *pref.Was { return ex.w } -func (ex *resumeExtent) plugin(mediator types.Mediator) types.Plugin { - return &resume.Plugin{ +func (ex *resumeExtent) plugin(artefacts *kernel.Artefacts) types.Plugin { + ex.rp = &resume.Plugin{ BasePlugin: kernel.BasePlugin{ - Mediator: mediator, + Mediator: artefacts.Mediator, }, + Complete: artefacts.Completion, } + + return ex.rp } func (ex *resumeExtent) options(settings ...pref.Option) (*pref.Options, error) { @@ -88,3 +97,7 @@ func (ex *resumeExtent) options(settings ...pref.Option) (*pref.Options, error) // return loaded.O, err } + +func (ex *resumeExtent) complete() bool { + return ex.rp.Complete() +} diff --git a/internal-traverse-defs.go b/internal-traverse-defs.go index ac632f7..ca9b32a 100644 --- a/internal-traverse-defs.go +++ b/internal-traverse-defs.go @@ -20,13 +20,25 @@ func (fn optionals) build(ext extent) (*pref.Options, error) { // pluginsBuilder type pluginsBuilder interface { - build(*pref.Options, types.Mediator, ...types.Plugin) ([]types.Plugin, error) + build(*pref.Options, + types.Mediator, + types.KernelController, + ...types.Plugin, + ) ([]types.Plugin, error) } -type features func(*pref.Options, types.Mediator, ...types.Plugin) ([]types.Plugin, error) - -func (fn features) build(o *pref.Options, mediator types.Mediator, others ...types.Plugin) ([]types.Plugin, error) { - return fn(o, mediator, others...) +type features func(*pref.Options, + types.Mediator, + types.KernelController, + ...types.Plugin, +) ([]types.Plugin, error) + +func (fn features) build(o *pref.Options, + mediator types.Mediator, + kc types.KernelController, + others ...types.Plugin, +) ([]types.Plugin, error) { + return fn(o, mediator, kc, others...) } type fsBuilder interface { diff --git a/internal/hiber/hibernate-plugin.go b/internal/hiber/hibernate-plugin.go index 31b7ee4..30e1414 100644 --- a/internal/hiber/hibernate-plugin.go +++ b/internal/hiber/hibernate-plugin.go @@ -28,7 +28,9 @@ func (p *Plugin) Name() string { return "hibernation" } -func (p *Plugin) Register() error { +func (p *Plugin) Register(kc types.KernelController) error { + p.Controller = kc + return nil } diff --git a/internal/kernel/base-plugin.go b/internal/kernel/base-plugin.go index 5f8e178..066d9ec 100644 --- a/internal/kernel/base-plugin.go +++ b/internal/kernel/base-plugin.go @@ -5,5 +5,6 @@ import ( ) type BasePlugin struct { - Mediator types.Mediator + Mediator types.Mediator + Controller types.KernelController } diff --git a/internal/kernel/builder.go b/internal/kernel/builder.go index 0efa47d..ba7b906 100644 --- a/internal/kernel/builder.go +++ b/internal/kernel/builder.go @@ -1,26 +1,32 @@ 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 + Completion types.Completion } NavigatorBuilder interface { - Build(o *pref.Options, res *types.Resources) (*Artefacts, error) + Build(o *pref.Options, + resources *types.Resources, + ) (*Artefacts, error) } - Builder func(o *pref.Options, res *types.Resources) (*Artefacts, error) + Builder func(o *pref.Options, + resources *types.Resources, + ) (*Artefacts, error) ) -func (fn Builder) Build(o *pref.Options, res *types.Resources) (*Artefacts, error) { - return fn(o, res) +func (fn Builder) Build(o *pref.Options, + resources *types.Resources, +) (*Artefacts, error) { + return fn(o, 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..98a9283 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 @@ -46,26 +41,21 @@ func newMediator(using *pref.Using, o *pref.Options, impl NavigatorImpl, sealer types.GuardianSealer, - res *types.Resources, + resources *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, resources.Supervisor.Many( + enums.MetricNoFilesInvoked, + enums.MetricNoFoldersInvoked, + )), frame: &navigationFrame{ periscope: level.New(), }, pad: newScratch(o), o: o, - resources: res, - owned: owned{ - mums: mums, - }, + resources: resources, } } @@ -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..991d042 100644 --- a/internal/kernel/navigator-factory.go +++ b/internal/kernel/navigator-factory.go @@ -13,15 +13,15 @@ func (f *facilities) Inject(pref.ActiveState) {} func New(using *pref.Using, o *pref.Options, sealer types.GuardianSealer, - res *types.Resources, + resources *types.Resources, ) *Artefacts { - impl := newImpl(using, o, res) - controller := newController(using, o, impl, sealer, res) + impl := newImpl(using, o, resources) + controller := newController(using, o, impl, sealer, resources) return &Artefacts{ - Navigator: controller, - Mediator: controller.mediator, - Resources: res, + Controller: controller, + Mediator: controller.Mediator, + Resources: resources, } } @@ -29,21 +29,21 @@ func newController(using *pref.Using, o *pref.Options, impl NavigatorImpl, sealer types.GuardianSealer, - res *types.Resources, + resources *types.Resources, ) *NavigationController { return &NavigationController{ - mediator: newMediator(using, o, impl, sealer, res), + Mediator: newMediator(using, o, impl, sealer, resources), } } func newImpl(using *pref.Using, o *pref.Options, - res *types.Resources, + resources *types.Resources, ) (impl NavigatorImpl) { base := navigator{ - using: using, - o: o, - res: res, + using: using, + o: o, + resources: resources, } switch using.Subscription { 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..14e3a61 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 + resources *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 { + result := &types.KernelResult{ + Session: n.session, + Err: err, + Complete: n.session.IsComplete(), + } + + _ = services.Broker.Emit(ctx, services.TopicNavigationComplete, result) + + return result +} diff --git a/internal/refine/filter-plugin.go b/internal/refine/filter-plugin.go index 621a1e8..9a98fdd 100644 --- a/internal/refine/filter-plugin.go +++ b/internal/refine/filter-plugin.go @@ -28,7 +28,9 @@ func (p *Plugin) Name() string { return "filtering" } -func (p *Plugin) Register() error { +func (p *Plugin) Register(kc types.KernelController) error { + p.Controller = kc + return nil } diff --git a/internal/resume/controller.go b/internal/resume/controller.go index 984dbdd..dccb9ab 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,14 +12,22 @@ 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 + // The Controller on the incoming artefacts is the core navigator. It is // decorated here for resume. The strategy only needs access to the core navigator. // The resume navigator delegates to the strategy. // @@ -29,23 +36,24 @@ 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, }, - Mediator: artefacts.Mediator, + Mediator: artefacts.Mediator, + Completion: strategy.complete, } } -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 +61,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 +80,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/resume-plugin.go b/internal/resume/resume-plugin.go index 96f5ef4..fa897de 100644 --- a/internal/resume/resume-plugin.go +++ b/internal/resume/resume-plugin.go @@ -12,13 +12,16 @@ import ( type Plugin struct { kernel.BasePlugin + Complete types.Completion } func (p *Plugin) Name() string { return "resume" } -func (p *Plugin) Register() error { +func (p *Plugin) Register(kc types.KernelController) error { + p.Controller = kc + return nil } @@ -37,6 +40,10 @@ func (p *Plugin) Init() error { return p.Mediator.Decorate(p) } +func (p *Plugin) IsComplete() bool { + return p.Complete() +} + func GetSealer(was *pref.Was) types.GuardianSealer { if was.Strategy == enums.ResumeStrategyFastward { return &fastwardGuardianSealer{} 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..14e2591 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 { + return true // TODO: tbd... +} diff --git a/internal/sampling/sampling-plugin.go b/internal/sampling/sampling-plugin.go index f43f2a7..f4dbaa6 100644 --- a/internal/sampling/sampling-plugin.go +++ b/internal/sampling/sampling-plugin.go @@ -28,7 +28,9 @@ func (p *Plugin) Name() string { return "sampling" } -func (p *Plugin) Register() error { +func (p *Plugin) Register(kc types.KernelController) error { + p.Controller = kc + return nil } 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..9415cf7 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,22 @@ import ( // package types defines internal types type ( + // ResultCompletion used to determine if the result really represents + // final navigation completion. + ResultCompletion interface { + IsComplete() bool + } + + Completion func() bool + + // Session represents a traversal session and keeps tracks of + // timing. + Session interface { + ResultCompletion + StartedAt() time.Time + Elapsed() time.Duration + } + // Link represents a single decorator in the chain Link interface { // Next invokes this decorator which returns true if @@ -60,7 +77,7 @@ type ( // Plugin used to define interaction with supplementary features Plugin interface { Name() string - Register() error + Register(KernelController) error Init() error } @@ -76,21 +93,33 @@ 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 (fn Completion) IsComplete() bool { + return fn() +} + +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)