Skip to content

Commit

Permalink
Update tests to work with new message formats.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmalloc committed Nov 1, 2024
1 parent 85a20c8 commit cb4644c
Show file tree
Hide file tree
Showing 24 changed files with 195 additions and 142 deletions.
12 changes: 8 additions & 4 deletions config/staticconfig/analyze_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,22 @@ func TestAnalyzer(t *testing.T) {
}

// Render the details of the application.
details := config.RenderDetails(app)
err := config.Validate(app)
desc := config.Description(
app,
config.WithValidationResult(err),
)

// Remove the random portion of the temporary directory name
// so that the test output is deterministic.
rel, _ := filepath.Rel(cwd, dir)
details = strings.ReplaceAll(
details,
desc = strings.ReplaceAll(
desc,
"/"+rel+".",
".",
)

if _, err := io.WriteString(out, details); err != nil {
if _, err := io.WriteString(out, desc); err != nil {
return err
}
}
Expand Down
7 changes: 5 additions & 2 deletions config/staticconfig/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package staticconfig
import (
"go/types"

"github.com/dogmatiq/dogma"
"github.com/dogmatiq/enginekit/config"
"github.com/dogmatiq/enginekit/config/internal/configbuilder"
)
Expand All @@ -24,7 +25,9 @@ func analyzeApplicationType(ctx *context, t types.Type) {
ctx.Analysis.Applications = append(ctx.Analysis.Applications, app)
}

func analyzeApplicationConfigurerCall(ctx *configurerCallContext[*configbuilder.ApplicationBuilder]) {
func analyzeApplicationConfigurerCall(
ctx *configurerCallContext[*config.Application, dogma.Application, *configbuilder.ApplicationBuilder],
) {
switch ctx.Method.Name() {
case "RegisterAggregate":
analyzeHandler(ctx, ctx.Builder.Aggregate, nil)
Expand All @@ -35,6 +38,6 @@ func analyzeApplicationConfigurerCall(ctx *configurerCallContext[*configbuilder.
case "RegisterProjection":
analyzeHandler(ctx, ctx.Builder.Projection, analyzeProjectionConfigurerCall)
default:
ctx.Builder.UpdateFidelity(config.Incomplete)
ctx.Builder.Partial()

Check warning on line 41 in config/staticconfig/application.go

View check run for this annotation

Codecov / codecov/patch

config/staticconfig/application.go#L40-L41

Added lines #L40 - L41 were not covered by tests
}
}
131 changes: 77 additions & 54 deletions config/staticconfig/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ import (
"golang.org/x/tools/go/ssa"
)

type entityContext[T configbuilder.EntityBuilder] struct {
type entityContext[
T config.Entity,
E any,
B configbuilder.EntityBuilder[T, E],
] struct {
*context

EntityType types.Type
Builder T
Builder B
ConfigureMethod *ssa.Function
FunctionUnderAnalysis *ssa.Function
ConfigurerParamIndices []int
}

func (c *entityContext[T]) IsConfigurer(v ssa.Value) bool {
func (c *entityContext[T, E, B]) IsConfigurer(v ssa.Value) bool {
for _, i := range c.ConfigurerParamIndices {
if v == c.FunctionUnderAnalysis.Params[i] {
return true
Expand All @@ -29,57 +33,77 @@ func (c *entityContext[T]) IsConfigurer(v ssa.Value) bool {
return false
}

type configurerCallContext[T configbuilder.EntityBuilder] struct {
*entityContext[T]
type configurerCallContext[
T config.Entity,
E any,
B configbuilder.EntityBuilder[T, E],
] struct {
*entityContext[T, E, B]
*ssa.CallCommon

Instruction ssa.CallInstruction
Fidelity config.Fidelity
Instruction ssa.CallInstruction
IsSpeculative bool
}

// configurerCallAnalyzer is a function that analyzes a call to a method on an
// entity's configurer.
type configurerCallAnalyzer[T configbuilder.EntityBuilder] func(*configurerCallContext[T])
type configurerCallAnalyzer[
T config.Entity,
E any,
B configbuilder.EntityBuilder[T, E],
] func(*configurerCallContext[T, E, B])

// analyzeEntity analyzes the Configure() method of the type t, which must be a
// Dogma application or handler.
//
// It calls the analyze function for each call to a method on the configurer,
// other than Identity() which is handled the same in all cases.
func analyzeEntity[T configbuilder.EntityBuilder](
func analyzeEntity[
T config.Entity,
E any,
B configbuilder.EntityBuilder[T, E],
](
ctx *context,
t types.Type,
builder T,
analyze configurerCallAnalyzer[T],
builder B,
analyze configurerCallAnalyzer[T, E, B],
) {
builder.SetSourceTypeName(typename.OfStatic(t))
builder.TypeName(typename.OfStatic(t))
configure := ctx.LookupMethod(t, "Configure")

ectx := &entityContext[T, E, B]{
context: ctx,
EntityType: t,
Builder: builder,
ConfigureMethod: configure,
FunctionUnderAnalysis: configure,
ConfigurerParamIndices: []int{1},
}

fn := func(ctx *configurerCallContext[T, E, B]) {
switch ctx.Method.Name() {
case "Identity":
analyzeIdentity(ctx)
default:
analyze(ctx)
}
}

analyzeConfigurerCallsInFunc(
&entityContext[T]{
context: ctx,
EntityType: t,
Builder: builder,
ConfigureMethod: configure,
FunctionUnderAnalysis: configure,
ConfigurerParamIndices: []int{1},
},
func(ctx *configurerCallContext[T]) {
switch ctx.Method.Name() {
case "Identity":
analyzeIdentity(ctx)
default:
analyze(ctx)
}
},
ectx,
fn,
)
}

// analyzeConfigurerCallsInFunc analyzes calls to methods on the configurer in
// the function under analysis.
func analyzeConfigurerCallsInFunc[T configbuilder.EntityBuilder](
ctx *entityContext[T],
analyze configurerCallAnalyzer[T],
func analyzeConfigurerCallsInFunc[
T config.Entity,
E any,
B configbuilder.EntityBuilder[T, E],
](
ctx *entityContext[T, E, B],
analyze configurerCallAnalyzer[T, E, B],
) {
for b := range ssax.WalkFunc(ctx.FunctionUnderAnalysis) {
for _, inst := range b.Instrs {
Expand All @@ -92,27 +116,24 @@ func analyzeConfigurerCallsInFunc[T configbuilder.EntityBuilder](

// analyzeConfigurerCallsInInstruction analyzes calls to methods on the
// configurer in the given instruction.
func analyzeConfigurerCallsInInstruction[T configbuilder.EntityBuilder](
ctx *entityContext[T],
func analyzeConfigurerCallsInInstruction[
T config.Entity,
E any,
B configbuilder.EntityBuilder[T, E],
](
ctx *entityContext[T, E, B],
inst ssa.CallInstruction,
analyze configurerCallAnalyzer[T],
analyze configurerCallAnalyzer[T, E, B],
) {
com := inst.Common()

if com.IsInvoke() && ctx.IsConfigurer(com.Value) {
// We've found a direct call to a method on the configurer.
var f config.Fidelity
if !ssax.IsUnconditional(inst.Block()) {
f |= config.Speculative
}

analyze(&configurerCallContext[T]{
analyze(&configurerCallContext[T, E, B]{
entityContext: ctx,
CallCommon: com,
Instruction: inst,
Fidelity: f,
IsSpeculative: !ssax.IsUnconditional(inst.Block()),
})

return
}

Expand Down Expand Up @@ -143,12 +164,12 @@ func analyzeConfigurerCallsInInstruction[T configbuilder.EntityBuilder](
// some other un-analyzable function.
fn := com.StaticCallee()
if fn == nil {
ctx.Builder.UpdateFidelity(config.Incomplete)
ctx.Builder.Partial()
return
}

analyzeConfigurerCallsInFunc(
&entityContext[T]{
&entityContext[T, E, B]{
context: ctx.context,
EntityType: ctx.EntityType,
Builder: ctx.Builder,
Expand All @@ -160,24 +181,26 @@ func analyzeConfigurerCallsInInstruction[T configbuilder.EntityBuilder](
)
}

func analyzeIdentity[T configbuilder.EntityBuilder](
ctx *configurerCallContext[T],
func analyzeIdentity[
T config.Entity,
E any,
B configbuilder.EntityBuilder[T, E],
](
ctx *configurerCallContext[T, E, B],
) {
ctx.
Builder.
Identity(func(b *configbuilder.IdentityBuilder) {
b.UpdateFidelity(ctx.Fidelity)
if ctx.IsSpeculative {
b.Speculative()
}

if name, ok := ssax.AsString(ctx.Args[0]).TryGet(); ok {
b.SetName(name)
} else {
b.UpdateFidelity(config.Incomplete)
b.Name(name)
}

if key, ok := ssax.AsString(ctx.Args[1]).TryGet(); ok {
b.SetKey(key)
} else {
b.UpdateFidelity(config.Incomplete)
b.Key(key)
}
})
}
50 changes: 26 additions & 24 deletions config/staticconfig/handler.go
Original file line number Diff line number Diff line change
@@ -1,69 +1,71 @@
package staticconfig

import (
"github.com/dogmatiq/dogma"
"github.com/dogmatiq/enginekit/config"
"github.com/dogmatiq/enginekit/config/internal/configbuilder"
"github.com/dogmatiq/enginekit/config/staticconfig/internal/ssax"
)

func analyzeHandler[T configbuilder.HandlerBuilder](
ctx *configurerCallContext[*configbuilder.ApplicationBuilder],
build func(func(T)),
analyze configurerCallAnalyzer[T],
func analyzeHandler[
T config.Handler,
H any,
B configbuilder.HandlerBuilder[T, H],
](
ctx *configurerCallContext[*config.Application, dogma.Application, *configbuilder.ApplicationBuilder],
build func(func(B)),
analyze configurerCallAnalyzer[T, H, B],
) {
build(func(b T) {
b.UpdateFidelity(ctx.Fidelity)
build(func(b B) {
if ctx.IsSpeculative {
b.Speculative()
}

Check warning on line 22 in config/staticconfig/handler.go

View check run for this annotation

Codecov / codecov/patch

config/staticconfig/handler.go#L21-L22

Added lines #L21 - L22 were not covered by tests

t := ssax.ConcreteType(ctx.Args[0])

if !t.IsPresent() {
b.UpdateFidelity(config.Incomplete)
b.Partial()
return
}

analyzeEntity(
ctx.context,
t.Get(),
b,
func(ctx *configurerCallContext[T]) {
func(ctx *configurerCallContext[T, H, B]) {
switch ctx.Method.Name() {
case "Routes":
analyzeRoutes(ctx)

case "Disable":
// TODO(jmalloc): f is lost in this case, so any handler
// that is _sometimes_ disabled will appear as always
// disabled, which is a bit non-sensical.
//
// It probably needs similar treatment to
// https://github.com/dogmatiq/enginekit/issues/55.
ctx.Builder.SetDisabled(true)
ctx.Builder.Disabled(
func(b *configbuilder.FlagBuilder[config.Disabled]) {
if ctx.IsSpeculative {
b.Speculative()
}

Check warning on line 45 in config/staticconfig/handler.go

View check run for this annotation

Codecov / codecov/patch

config/staticconfig/handler.go#L44-L45

Added lines #L44 - L45 were not covered by tests
b.Value(true)
},
)

default:
if analyze == nil {
ctx.Builder.UpdateFidelity(config.Incomplete)
ctx.Builder.Partial()
} else {
analyze(ctx)
}

Check warning on line 55 in config/staticconfig/handler.go

View check run for this annotation

Codecov / codecov/patch

config/staticconfig/handler.go#L50-L55

Added lines #L50 - L55 were not covered by tests
}
},
)

// If the handler wasn't disabled, and the configuration is NOT
// incomplete, we know that the handler is enabled.
if !b.IsDisabled().IsPresent() && b.Fidelity()&config.Incomplete == 0 {
b.SetDisabled(false)
}
})
}

func analyzeProjectionConfigurerCall(
ctx *configurerCallContext[*configbuilder.ProjectionBuilder],
ctx *configurerCallContext[*config.Projection, dogma.ProjectionMessageHandler, *configbuilder.ProjectionBuilder],
) {
switch ctx.Method.Name() {
case "DeliveryPolicy":
panic("not implemented") // TODO
default:
ctx.Builder.UpdateFidelity(config.Incomplete)
ctx.Builder.Partial()

Check warning on line 69 in config/staticconfig/handler.go

View check run for this annotation

Codecov / codecov/patch

config/staticconfig/handler.go#L64-L69

Added lines #L64 - L69 were not covered by tests
}
}
Loading

0 comments on commit cb4644c

Please sign in to comment.