mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
refactor(runner): collapse WebDriver forks behind ActionSource/ExtractorSource
This commit is contained in:
1 parent
0436c6094e
commit
1a17734ce0
2 files changed
+84
-23
No files matched your search
+12
-23
@@ -41,6 +41,10 @@ type Summary struct {
|
|||||||
EndTime time.Time
|
EndTime time.Time
|
||||||
Steps int
|
Steps int
|
||||||
Violations []ViolationRecord
|
Violations []ViolationRecord
|
||||||
|
// UnsupportedVerbs lists verbs the picker requested that the platform
|
||||||
|
// could not dispatch, deduped, so the report can flag a spec exercising
|
||||||
|
// gestures this target does not support.
|
||||||
|
UnsupportedVerbs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ViolationRecord struct {
|
type ViolationRecord struct {
|
||||||
@@ -65,6 +69,11 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
// before the deadline is set so the settle time does not eat the run.
|
// before the deadline is set so the settle time does not eat the run.
|
||||||
waitForForeground(ctx, options, logger)
|
waitForForeground(ctx, options, logger)
|
||||||
|
|
||||||
|
// Pick the action and extractor sources once from the driver's
|
||||||
|
// capabilities so the step loop runs one uniform path with no per-step
|
||||||
|
// driver type assertion.
|
||||||
|
actionSource, extractorSource := pickSources(options)
|
||||||
|
|
||||||
summary := Summary{StartTime: time.Now()}
|
summary := Summary{StartTime: time.Now()}
|
||||||
deadline := summary.StartTime.Add(options.Duration)
|
deadline := summary.StartTime.Add(options.Duration)
|
||||||
stepIndex := 0
|
stepIndex := 0
|
||||||
@@ -116,9 +125,8 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
var v8Overrides map[int]json.RawMessage
|
var v8Overrides map[int]json.RawMessage
|
||||||
if web, ok := options.Driver.(driver.WebDriver); ok {
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
overrides, err := web.EvaluateExtractors(gctx)
|
overrides, err := extractorSource.ExtractorOverrides(gctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn("v8 extractor evaluation failed", "step", si, "err", err)
|
logger.Warn("v8 extractor evaluation failed", "step", si, "err", err)
|
||||||
return nil
|
return nil
|
||||||
@@ -126,7 +134,6 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
v8Overrides = overrides
|
v8Overrides = overrides
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
|
||||||
// All goroutines write to local variables and return nil, so the Wait
|
// All goroutines write to local variables and return nil, so the Wait
|
||||||
// error is always nil; ignored intentionally.
|
// error is always nil; ignored intentionally.
|
||||||
_ = g.Wait()
|
_ = g.Wait()
|
||||||
@@ -197,13 +204,7 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
}
|
}
|
||||||
logger.Info("step", "index", stepIndex, "screen", screen, "nodes", treeSize)
|
logger.Info("step", "index", stepIndex, "screen", screen, "nodes", treeSize)
|
||||||
|
|
||||||
var nextAction verifier.Action
|
nextAction, nextErr := actionSource.NextAction(ctx)
|
||||||
var nextErr error
|
|
||||||
if web, ok := options.Driver.(driver.WebDriver); ok {
|
|
||||||
nextAction, nextErr = nextActionFromV8(ctx, web)
|
|
||||||
} else {
|
|
||||||
nextAction, nextErr = options.Verifier.NextAction()
|
|
||||||
}
|
|
||||||
var traceAction *trace.Action
|
var traceAction *trace.Action
|
||||||
if nextErr == nil {
|
if nextErr == nil {
|
||||||
traceAction = traceActionFor(nextAction, tree)
|
traceAction = traceActionFor(nextAction, tree)
|
||||||
@@ -298,6 +299,7 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
summary.UnsupportedVerbs = options.Verifier.UnsupportedVerbs()
|
||||||
summary.EndTime = time.Now()
|
summary.EndTime = time.Now()
|
||||||
return summary, nil
|
return summary, nil
|
||||||
}
|
}
|
||||||
@@ -770,19 +772,6 @@ func captureMetrics(ctx context.Context, options Options, logger *slog.Logger, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nextActionFromV8 invokes the V8-side action generator and decodes the
|
|
||||||
// resulting JSON into a verifier.Action. ErrNoAction is returned when the
|
|
||||||
// generator declined to act this tick.
|
|
||||||
func nextActionFromV8(ctx context.Context, web driver.WebDriver) (verifier.Action, error) {
|
|
||||||
raw, err := web.NextActionFromV8(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return verifier.Action{}, fmt.Errorf("v8 next action: %w", err)
|
|
||||||
}
|
|
||||||
// Both engines emit the unified flat camelCase wire contract; one decoder
|
|
||||||
// reads it. A null payload means the generator declined to act this tick.
|
|
||||||
return verifier.DecodeAction(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// collectWitnesses gathers the violation witness for each newly-violated
|
// collectWitnesses gathers the violation witness for each newly-violated
|
||||||
// property, logs its cause, and returns them keyed by property name for the
|
// property, logs its cause, and returns them keyed by property name for the
|
||||||
// trace. Properties without a captured witness are skipped.
|
// trace. Properties without a captured witness are skipped.
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/priyanshujain/sanderling/internal/driver"
|
||||||
|
"github.com/priyanshujain/sanderling/internal/verifier"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ActionSource resolves the next action for a step. Both runtimes (the goja
|
||||||
|
// picker and the web/V8 picker) implement it so the runner loop has one path
|
||||||
|
// and no per-step driver type assertion. NextAction returns verifier.ErrNoAction
|
||||||
|
// when the picker declined to act this tick.
|
||||||
|
type ActionSource interface {
|
||||||
|
NextAction(ctx context.Context) (verifier.Action, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractorSource yields per-step extractor overrides the runner applies after
|
||||||
|
// PushSnapshot. The mobile path has none (returns nil); the web path returns the
|
||||||
|
// values its extractors computed in V8 against the real DOM.
|
||||||
|
type ExtractorSource interface {
|
||||||
|
ExtractorOverrides(ctx context.Context) (map[int]json.RawMessage, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// gojaSource drives both action selection and (trivially) extractor overrides
|
||||||
|
// for the mobile path, where the goja-bundled picker runs in-process and no V8
|
||||||
|
// extractor values exist.
|
||||||
|
type gojaSource struct {
|
||||||
|
verifier *verifier.Verifier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s gojaSource) NextAction(context.Context) (verifier.Action, error) {
|
||||||
|
return s.verifier.NextAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gojaSource) ExtractorOverrides(context.Context) (map[int]json.RawMessage, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// webSource adapts the chrome driver's V8 picker: it evaluates the bundled
|
||||||
|
// __sanderlingNextAction__ / __sanderlingExtractors__ globals and decodes their
|
||||||
|
// JSON into the unified action and override shapes.
|
||||||
|
type webSource struct {
|
||||||
|
web driver.WebDriver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s webSource) NextAction(ctx context.Context) (verifier.Action, error) {
|
||||||
|
raw, err := s.web.NextActionFromV8(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return verifier.Action{}, fmt.Errorf("v8 next action: %w", err)
|
||||||
|
}
|
||||||
|
// Both engines emit the unified flat camelCase wire contract; one decoder
|
||||||
|
// reads it. A null payload means the generator declined to act this tick.
|
||||||
|
return verifier.DecodeAction(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s webSource) ExtractorOverrides(ctx context.Context) (map[int]json.RawMessage, error) {
|
||||||
|
return s.web.EvaluateExtractors(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pickSources selects the runtime's action and extractor sources ONCE at setup
|
||||||
|
// from the driver's capabilities, so the step loop never type-asserts.
|
||||||
|
func pickSources(options Options) (ActionSource, ExtractorSource) {
|
||||||
|
if web, ok := options.Driver.(driver.WebDriver); ok {
|
||||||
|
source := webSource{web: web}
|
||||||
|
return source, source
|
||||||
|
}
|
||||||
|
source := gojaSource{verifier: options.Verifier}
|
||||||
|
return source, source
|
||||||
|
}
|
||||||
Reference in new issue
Block a user