From 0b04147448b03f6a5e24880521d0077fdcc574af Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 10 May 2022 19:20:25 -0400 Subject: [PATCH 01/14] refactor(container)!: remove dependency on C graphviz --- container/debug.go | 106 +++++++-------------------- container/internal/graphviz/edge.go | 4 + container/internal/graphviz/graph.go | 41 +++++++++++ container/internal/graphviz/node.go | 17 +++++ 4 files changed, 89 insertions(+), 79 deletions(-) create mode 100644 container/internal/graphviz/edge.go create mode 100644 container/internal/graphviz/graph.go create mode 100644 container/internal/graphviz/node.go diff --git a/container/debug.go b/container/debug.go index 2c9cb8cc11a2..3f65e6959d2c 100644 --- a/container/debug.go +++ b/container/debug.go @@ -4,13 +4,11 @@ import ( "bytes" "fmt" "os" - "path/filepath" "reflect" - "github.com/pkg/errors" - - "github.com/goccy/go-graphviz" "github.com/goccy/go-graphviz/cgraph" + + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) // DebugOption is a functional option for running a container that controls @@ -169,8 +167,7 @@ type debugConfig struct { logBuf *[]string // a log buffer for onError/onSuccess processing // graphing - graphviz *graphviz.Graphviz - graph *cgraph.Graph + graph *graphviz.Graph visualizers []func(string) logVisualizer bool @@ -189,15 +186,8 @@ func (c debugOption) applyConfig(ctr *debugConfig) error { var _ DebugOption = (*debugOption)(nil) func newDebugConfig() (*debugConfig, error) { - g := graphviz.New() - graph, err := g.Graph() - if err != nil { - return nil, errors.Wrap(err, "error initializing graph") - } - return &debugConfig{ - graphviz: g, - graph: graph, + graph: graphviz.NewGraph(), }, nil } @@ -220,11 +210,7 @@ func (c debugConfig) logf(format string, args ...interface{}) { func (c *debugConfig) generateGraph() { buf := &bytes.Buffer{} - err := c.graphviz.Render(c.graph, graphviz.XDOT, buf) - if err != nil { - c.logf("Error rendering DOT graph: %+v", err) - return - } + c.graph.RenderDOT(buf) dot := buf.String() if c.logVisualizer { @@ -234,17 +220,6 @@ func (c *debugConfig) generateGraph() { for _, v := range c.visualizers { v(dot) } - - err = c.graph.Close() - if err != nil { - c.logf("Error closing graph: %+v", err) - return - } - - err = c.graphviz.Close() - if err != nil { - c.logf("Error closing graphviz: %+v", err) - } } func (c *debugConfig) addFuncVisualizer(f func(string)) { @@ -259,46 +234,40 @@ func (c *debugConfig) enableLogVisualizer() { func (c *debugConfig) addFileVisualizer(filename string, format string) { c.visualizers = append(c.visualizers, func(_ string) { - err := c.graphviz.RenderFilename(c.graph, graphviz.Format(format), filename) - if err != nil { - c.logf("Error saving graphviz file %s with format %s: %+v", filename, format, err) - } else { - path, err := filepath.Abs(filename) - if err == nil { - c.logf("Saved graph of container to %s", path) - } - } + panic("TODO") + //err := c.graph.RenderDOT(c.graph, graphviz.Format(format), filename) + //if err != nil { + // c.logf("Error saving graphviz file %s with format %s: %+v", filename, format, err) + //} else { + // path, err := filepath.Abs(filename) + // if err == nil { + // c.logf("Saved graph of container to %s", path) + // } + //} }) } -func (c *debugConfig) locationGraphNode(location Location, key *moduleKey) (*cgraph.Node, error) { +func (c *debugConfig) locationGraphNode(location Location, key *moduleKey) *graphviz.Node { graph := c.moduleSubGraph(key) - node, found, err := c.findOrCreateGraphNode(graph, location.Name()) - if err != nil { - return nil, err - } - + node, found := graph.FindOrCreateNode(location.Name()) if found { - return node, nil + return node } node.SetShape(cgraph.BoxShape) node.SetColor("lightgrey") - return node, nil + return node } -func (c *debugConfig) typeGraphNode(typ reflect.Type) (*cgraph.Node, error) { - node, found, err := c.findOrCreateGraphNode(c.graph, moreUsefulTypeString(typ)) - if err != nil { - return nil, err - } +func (c *debugConfig) typeGraphNode(typ reflect.Type) *graphviz.Node { + node, found := c.graph.FindOrCreateNode(moreUsefulTypeString(typ)) if found { - return node, nil + return node } node.SetColor("lightgrey") - return node, err + return node } // moreUsefulTypeString is more useful than reflect.Type.String() @@ -317,37 +286,16 @@ func moreUsefulTypeString(ty reflect.Type) string { } } -func (c *debugConfig) findOrCreateGraphNode(subGraph *cgraph.Graph, name string) (node *cgraph.Node, found bool, err error) { - node, err = c.graph.Node(name) - if err != nil { - return nil, false, errors.Wrapf(err, "error finding graph node %s", name) - } - - if node != nil { - return node, true, nil - } - - node, err = subGraph.CreateNode(name) - if err != nil { - return nil, false, errors.Wrapf(err, "error creating graph node %s", name) - } - - return node, false, nil -} - -func (c *debugConfig) moduleSubGraph(key *moduleKey) *cgraph.Graph { +func (c *debugConfig) moduleSubGraph(key *moduleKey) *graphviz.Graph { graph := c.graph if key != nil { gname := fmt.Sprintf("cluster_%s", key.name) - graph = c.graph.SubGraph(gname, 1) + graph = c.graph.SubGraph(gname) graph.SetLabel(fmt.Sprintf("Module: %s", key.name)) } return graph } -func (c *debugConfig) addGraphEdge(from *cgraph.Node, to *cgraph.Node) { - _, err := c.graph.CreateEdge("", from, to) - if err != nil { - c.logf("error creating graph edge") - } +func (c *debugConfig) addGraphEdge(from, to *graphviz.Node) { + _ = c.graph.CreateEdge("", from, to) } diff --git a/container/internal/graphviz/edge.go b/container/internal/graphviz/edge.go new file mode 100644 index 000000000000..6830387812c6 --- /dev/null +++ b/container/internal/graphviz/edge.go @@ -0,0 +1,4 @@ +package graphviz + +type Edge struct { +} diff --git a/container/internal/graphviz/graph.go b/container/internal/graphviz/graph.go new file mode 100644 index 000000000000..ce187b260794 --- /dev/null +++ b/container/internal/graphviz/graph.go @@ -0,0 +1,41 @@ +package graphviz + +import "io" + +type Graph struct { + parent *Graph + nodes map[string]*Node + subgraphs map[string]*Graph +} + +func NewGraph() *Graph { + return &Graph{ + nodes: map[string]*Node{}, + } +} + +func (g *Graph) FindOrCreateNode(name string) (node *Node, found bool) { + if node, ok := g.nodes[name]; ok { + return node, true + } + + node = &Node{name: name} + g.nodes[name] = node + return node, false +} + +func (n *Graph) SubGraph(name string) *Graph { + return &Graph{} +} + +func (g *Graph) SetLabel(label string) { + +} + +func (g *Graph) CreateEdge(name string, from, to *Node) *Edge { + return &Edge{} +} + +func (g *Graph) RenderDOT(writer io.Writer) { + +} diff --git a/container/internal/graphviz/node.go b/container/internal/graphviz/node.go new file mode 100644 index 000000000000..8ca34c03855c --- /dev/null +++ b/container/internal/graphviz/node.go @@ -0,0 +1,17 @@ +package graphviz + +import ( + "github.com/goccy/go-graphviz/cgraph" +) + +type Node struct { + name string +} + +func (n *Node) SetShape(shape cgraph.Shape) { + +} + +func (n *Node) SetColor(s string) { + +} From 5f8686f8091048e6a469275768551edf44c4e1dc Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 10 May 2022 19:44:35 -0400 Subject: [PATCH 02/14] WIP --- container/container.go | 72 +++++++----------------- container/container_test.go | 25 ++++++--- container/debug.go | 82 +++++++++++++--------------- container/go.mod | 7 +-- container/go.sum | 40 +++++++++----- container/group.go | 7 +-- container/internal/graphviz/edge.go | 4 -- container/internal/graphviz/graph.go | 41 -------------- container/internal/graphviz/node.go | 17 ------ container/module_dep.go | 6 +- container/one_per_module.go | 7 +-- container/resolver.go | 4 +- container/simple.go | 6 +- container/supply.go | 6 +- container/testdata/example.dot | 45 +++++++++++++++ 15 files changed, 166 insertions(+), 203 deletions(-) delete mode 100644 container/internal/graphviz/edge.go delete mode 100644 container/internal/graphviz/graph.go delete mode 100644 container/internal/graphviz/node.go create mode 100644 container/testdata/example.dot diff --git a/container/container.go b/container/container.go index deb52dc51f8f..9a1f9e07d087 100644 --- a/container/container.go +++ b/container/container.go @@ -5,7 +5,7 @@ import ( "fmt" "reflect" - "github.com/goccy/go-graphviz/cgraph" + "github.com/emicklei/dot" "github.com/pkg/errors" ) @@ -38,10 +38,8 @@ func newContainer(cfg *debugConfig) *container { func (c *container) call(provider *ProviderDescriptor, moduleKey *moduleKey) ([]reflect.Value, error) { loc := provider.Location - graphNode, err := c.locationGraphNode(loc, moduleKey) - if err != nil { - return nil, err - } + graphNode := c.locationGraphNode(loc, moduleKey) + markGraphNodeAsFailed(graphNode) if c.callerMap[loc] { @@ -87,18 +85,14 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { elemType = elemType.Elem() } - var typeGraphNode *cgraph.Node - var err error + var typeGraphNode dot.Node if isAutoGroupType(elemType) { c.logf("Registering resolver for auto-group type %v", elemType) sliceType := reflect.SliceOf(elemType) - typeGraphNode, err = c.typeGraphNode(sliceType) - if err != nil { - return nil, err - } - typeGraphNode.SetComment("auto-group") + typeGraphNode = c.typeGraphNode(sliceType) + typeGraphNode.Attr("comment", "auto-group") r := &groupResolver{ typ: elemType, @@ -112,11 +106,8 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { c.logf("Registering resolver for one-per-module type %v", elemType) mapType := reflect.MapOf(stringType, elemType) - typeGraphNode, err = c.typeGraphNode(mapType) - if err != nil { - return nil, err - } - typeGraphNode.SetComment("one-per-module") + typeGraphNode = c.typeGraphNode(mapType) + typeGraphNode.Attr("comment", "one-per-module") r := &onePerModuleResolver{ typ: elemType, @@ -136,11 +127,7 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { var stringType = reflect.TypeOf("") func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (interface{}, error) { - providerGraphNode, err := c.locationGraphNode(provider.Location, key) - if err != nil { - return nil, err - } - + providerGraphNode := c.locationGraphNode(provider.Location, key) hasModuleKeyParam := false hasOwnModuleKeyParam := false for _, in := range provider.Inputs { @@ -164,11 +151,11 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter return nil, err } - var typeGraphNode *cgraph.Node + var typeGraphNode dot.Node if vr != nil { typeGraphNode = vr.typeGraphNode() } else { - typeGraphNode, err = c.typeGraphNode(typ) + typeGraphNode = c.typeGraphNode(typ) if err != nil { return nil, err } @@ -215,11 +202,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter } else { c.logf("Registering resolver for simple type %v", typ) - typeGraphNode, err := c.typeGraphNode(typ) - if err != nil { - return nil, err - } - + typeGraphNode := c.typeGraphNode(typ) vr = &simpleResolver{ node: sp, typ: typ, @@ -260,11 +243,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter typ, provider.Location, existing.describeLocation()) } - typeGraphNode, err := c.typeGraphNode(typ) - if err != nil { - return reflect.Value{}, err - } - + typeGraphNode := c.typeGraphNode(typ) c.resolvers[typ] = &moduleDepResolver{ typ: typ, idxInValues: i, @@ -282,17 +261,9 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter func (c *container) supply(value reflect.Value, location Location) error { typ := value.Type() - locGrapNode, err := c.locationGraphNode(location, nil) - if err != nil { - return err - } + locGrapNode := c.locationGraphNode(location, nil) markGraphNodeAsUsed(locGrapNode) - - typeGraphNode, err := c.typeGraphNode(typ) - if err != nil { - return err - } - + typeGraphNode := c.typeGraphNode(typ) c.addGraphEdge(locGrapNode, typeGraphNode) if existing, ok := c.resolvers[typ]; ok { @@ -312,10 +283,7 @@ func (c *container) supply(value reflect.Value, location Location) error { func (c *container) resolve(in ProviderInput, moduleKey *moduleKey, caller Location) (reflect.Value, error) { c.resolveStack = append(c.resolveStack, resolveFrame{loc: caller, typ: in.Type}) - typeGraphNode, err := c.typeGraphNode(in.Type) - if err != nil { - return reflect.Value{}, err - } + typeGraphNode := c.typeGraphNode(in.Type) if in.Type == moduleKeyType { if moduleKey == nil { @@ -443,10 +411,10 @@ func (c container) formatResolveStack() string { return buf.String() } -func markGraphNodeAsUsed(node *cgraph.Node) { - node.SetColor("black") +func markGraphNodeAsUsed(node dot.Node) { + node.Attr("color", "black") } -func markGraphNodeAsFailed(node *cgraph.Node) { - node.SetColor("red") +func markGraphNodeAsFailed(node dot.Node) { + node.Attr("color", "red") } diff --git a/container/container_test.go b/container/container_test.go index e393b91e2439..6af7013f457b 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "gotest.tools/v3/golden" "github.com/cosmos/cosmos-sdk/container" ) @@ -81,6 +82,13 @@ func (ModuleB) Provide(dependencies BDependencies) (BProvides, Handler, error) { }, Handler{}, nil } +var scenarioConfig = container.Options( + container.Provide(ProvideMsgClientA), + container.ProvideInModule("runtime", ProvideKVStoreKey), + container.ProvideInModule("a", wrapMethod0(ModuleA{})), + container.ProvideInModule("b", wrapMethod0(ModuleB{})), +) + func TestScenario(t *testing.T) { var ( handlers map[string]Handler @@ -90,12 +98,7 @@ func TestScenario(t *testing.T) { ) require.NoError(t, container.Build( - container.Options( - container.Provide(ProvideMsgClientA), - container.ProvideInModule("runtime", ProvideKVStoreKey), - container.ProvideInModule("a", wrapMethod0(ModuleA{})), - container.ProvideInModule("b", wrapMethod0(ModuleB{})), - ), + scenarioConfig, &handlers, &commands, &a, @@ -554,6 +557,7 @@ func TestLogging(t *testing.T) { require.NoError(t, err) defer os.Remove(graphfile.Name()) + var b KeeperB require.NoError(t, container.BuildDebug( container.DebugOptions( container.Logger(func(s string) { @@ -563,10 +567,11 @@ func TestLogging(t *testing.T) { dotGraph = g }), container.LogVisualizer(), - container.FileVisualizer(graphfile.Name(), "svg"), + container.FileVisualizer(graphfile.Name()), container.StdoutLogger(), ), - container.Options(), + scenarioConfig, + &b, )) require.Contains(t, logOut, "digraph") @@ -578,7 +583,9 @@ func TestLogging(t *testing.T) { graphfileContents, err := os.ReadFile(graphfile.Name()) require.NoError(t, err) - require.Contains(t, string(graphfileContents), "n1; + n2->n5; + n9->n8; + n6->n8; + n6->n14; + n15->n16; + n8->n10; + n8->n11; + n8->n12; + n14->n15; + n14->n12; + n14->n11; + n3->n14; + n5->n6; + n1->n3; + +} From 97633dd8999c7fb405f69c3b494f49d1ae44c03f Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Tue, 10 May 2022 21:57:26 -0400 Subject: [PATCH 03/14] working graphviz --- container/container.go | 21 ++-- container/container_test.go | 22 ++-- container/debug.go | 40 ++++---- container/go.mod | 1 - container/group.go | 7 +- container/internal/graphviz/attrs.go | 41 ++++++++ container/internal/graphviz/edge.go | 16 +++ container/internal/graphviz/graph.go | 144 +++++++++++++++++++++++++++ container/internal/graphviz/node.go | 16 +++ container/module_dep.go | 6 +- container/one_per_module.go | 7 +- container/resolver.go | 4 +- container/simple.go | 6 +- container/supply.go | 6 +- container/testdata/example.dot | 86 ++++++++-------- 15 files changed, 324 insertions(+), 99 deletions(-) create mode 100644 container/internal/graphviz/attrs.go create mode 100644 container/internal/graphviz/edge.go create mode 100644 container/internal/graphviz/graph.go create mode 100644 container/internal/graphviz/node.go diff --git a/container/container.go b/container/container.go index 9a1f9e07d087..6877b869d061 100644 --- a/container/container.go +++ b/container/container.go @@ -5,8 +5,9 @@ import ( "fmt" "reflect" - "github.com/emicklei/dot" "github.com/pkg/errors" + + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) type container struct { @@ -85,14 +86,14 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { elemType = elemType.Elem() } - var typeGraphNode dot.Node + var typeGraphNode *graphviz.Node if isAutoGroupType(elemType) { c.logf("Registering resolver for auto-group type %v", elemType) sliceType := reflect.SliceOf(elemType) typeGraphNode = c.typeGraphNode(sliceType) - typeGraphNode.Attr("comment", "auto-group") + typeGraphNode.SetComment("auto-group") r := &groupResolver{ typ: elemType, @@ -107,7 +108,7 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { mapType := reflect.MapOf(stringType, elemType) typeGraphNode = c.typeGraphNode(mapType) - typeGraphNode.Attr("comment", "one-per-module") + typeGraphNode.SetComment("one-per-module") r := &onePerModuleResolver{ typ: elemType, @@ -151,7 +152,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter return nil, err } - var typeGraphNode dot.Node + var typeGraphNode *graphviz.Node if vr != nil { typeGraphNode = vr.typeGraphNode() } else { @@ -411,10 +412,12 @@ func (c container) formatResolveStack() string { return buf.String() } -func markGraphNodeAsUsed(node dot.Node) { - node.Attr("color", "black") +func markGraphNodeAsUsed(node *graphviz.Node) { + node.SetColor("black") + node.SetPenWidth("1.5") + node.SetAttr("fontcolor", "black") } -func markGraphNodeAsFailed(node dot.Node) { - node.Attr("color", "red") +func markGraphNodeAsFailed(node *graphviz.Node) { + node.SetColor("red") } diff --git a/container/container_test.go b/container/container_test.go index 6af7013f457b..c19f3d6117af 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -574,18 +574,18 @@ func TestLogging(t *testing.T) { &b, )) - require.Contains(t, logOut, "digraph") - require.Contains(t, dotGraph, "digraph") - - outfileContents, err := os.ReadFile(outfile.Name()) - require.NoError(t, err) - require.Contains(t, string(outfileContents), "digraph") - - graphfileContents, err := os.ReadFile(graphfile.Name()) - require.NoError(t, err) - require.Contains(t, string(graphfileContents), "digraph") - golden.Assert(t, dotGraph, "example.dot") + + //require.Contains(t, logOut, "digraph") + //require.Contains(t, dotGraph, "digraph") + // + //outfileContents, err := os.ReadFile(outfile.Name()) + //require.NoError(t, err) + //require.Contains(t, string(outfileContents), "digraph") + // + //graphfileContents, err := os.ReadFile(graphfile.Name()) + //require.NoError(t, err) + //require.Contains(t, string(graphfileContents), "digraph") } func TestConditionalDebugging(t *testing.T) { diff --git a/container/debug.go b/container/debug.go index b86f1c912a37..92aa73c5f2ff 100644 --- a/container/debug.go +++ b/container/debug.go @@ -6,7 +6,7 @@ import ( "path/filepath" "reflect" - "github.com/emicklei/dot" + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) // DebugOption is a functional option for running a container that controls @@ -163,7 +163,7 @@ type debugConfig struct { logBuf *[]string // a log buffer for onError/onSuccess processing // graphing - graph *dot.Graph + graph *graphviz.Graph visualizers []func(string) logVisualizer bool @@ -183,7 +183,7 @@ var _ DebugOption = (*debugOption)(nil) func newDebugConfig() (*debugConfig, error) { return &debugConfig{ - graph: dot.NewGraph(), + graph: graphviz.NewGraph(), }, nil } @@ -240,32 +240,36 @@ func (c *debugConfig) addFileVisualizer(filename string) { }) } -func (c *debugConfig) locationGraphNode(location Location, key *moduleKey) dot.Node { +func (c *debugConfig) locationGraphNode(location Location, key *moduleKey) *graphviz.Node { graph := c.moduleSubGraph(key) name := location.Name() - node, found := graph.FindNodeById(name) + node, found := graph.FindOrCreateNode(name) if found { return node } - node = graph.Node(name) - node.Attr("shape", "box") - node.Attr("color", "lightgrey") + node.SetShape("box") + setUnusedStyle(node.Attributes) return node } -func (c *debugConfig) typeGraphNode(typ reflect.Type) dot.Node { +func (c *debugConfig) typeGraphNode(typ reflect.Type) *graphviz.Node { name := moreUsefulTypeString(typ) - node, found := c.graph.FindNodeById(name) + node, found := c.graph.FindOrCreateNode(name) if found { return node } - node = c.graph.Node(name) - node.Attr("color", "lightgrey") + setUnusedStyle(node.Attributes) return node } +func setUnusedStyle(attr *graphviz.Attributes) { + attr.SetColor("lightgrey") + attr.SetPenWidth("0.5") + attr.SetAttr("fontcolor", "dimgrey") +} + // moreUsefulTypeString is more useful than reflect.Type.String() func moreUsefulTypeString(ty reflect.Type) string { switch ty.Kind() { @@ -282,16 +286,18 @@ func moreUsefulTypeString(ty reflect.Type) string { } } -func (c *debugConfig) moduleSubGraph(key *moduleKey) *dot.Graph { +func (c *debugConfig) moduleSubGraph(key *moduleKey) *graphviz.Graph { graph := c.graph if key != nil { gname := fmt.Sprintf("cluster_%s", key.name) - graph = c.graph.Subgraph(gname) - graph.Label(fmt.Sprintf("Module: %s", key.name)) + graph = c.graph.SubGraph(gname) + graph.SetLabel(fmt.Sprintf("Module: %s", key.name)) + graph.SetPenWidth("0.5") + graph.SetAttr("fontsize", "12.0") } return graph } -func (c *debugConfig) addGraphEdge(from, to dot.Node) { - _ = c.graph.Edge(from, to) +func (c *debugConfig) addGraphEdge(from, to *graphviz.Node) { + _ = c.graph.CreateEdge(from, to) } diff --git a/container/go.mod b/container/go.mod index 50a6742ec902..0ea1af4e8c8a 100644 --- a/container/go.mod +++ b/container/go.mod @@ -3,7 +3,6 @@ module github.com/cosmos/cosmos-sdk/container go 1.18 require ( - github.com/emicklei/dot v0.16.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.1 gotest.tools/v3 v3.2.0 diff --git a/container/group.go b/container/group.go index 2da10c2b1df4..ab29a30b85c4 100644 --- a/container/group.go +++ b/container/group.go @@ -4,8 +4,9 @@ import ( "fmt" "reflect" - "github.com/emicklei/dot" "github.com/pkg/errors" + + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) // AutoGroupType marks a type which automatically gets grouped together. For an AutoGroupType T, @@ -34,7 +35,7 @@ type groupResolver struct { providers []*simpleProvider resolved bool values reflect.Value - graphNode dot.Node + graphNode *graphviz.Node } type sliceGroupResolver struct { @@ -89,6 +90,6 @@ func (g *groupResolver) addNode(n *simpleProvider, i int) error { return nil } -func (g groupResolver) typeGraphNode() dot.Node { +func (g groupResolver) typeGraphNode() *graphviz.Node { return g.graphNode } diff --git a/container/internal/graphviz/attrs.go b/container/internal/graphviz/attrs.go new file mode 100644 index 000000000000..e5fdaa5a9983 --- /dev/null +++ b/container/internal/graphviz/attrs.go @@ -0,0 +1,41 @@ +package graphviz + +import ( + "fmt" + "strings" +) + +type Attributes struct { + attrs map[string]string +} + +func NewAttributes() *Attributes { + return &Attributes{attrs: map[string]string{}} +} + +func (a *Attributes) SetAttr(name, value string) { a.attrs[name] = value } + +func (n *Attributes) SetShape(shape string) { n.SetAttr("shape", shape) } + +func (n *Attributes) SetColor(color string) { n.SetAttr("color", color) } + +func (n *Attributes) SetBgColor(color string) { n.SetAttr("bgcolor", color) } + +func (n *Attributes) SetComment(comment string) { n.SetAttr("comment", comment) } + +func (n *Attributes) SetLabel(label string) { n.SetAttr("label", label) } + +func (n *Attributes) SetPenWidth(w string) { n.SetAttr("penwidth", w) } + +func (n *Attributes) SetFontColor(color string) { n.SetAttr("fontcolor", color) } + +func (a *Attributes) String() string { + if len(a.attrs) == 0 { + return "" + } + var attrStrs []string + for k, v := range a.attrs { + attrStrs = append(attrStrs, fmt.Sprintf("%s=%q", k, v)) + } + return fmt.Sprintf("[%s]", strings.Join(attrStrs, ", ")) +} diff --git a/container/internal/graphviz/edge.go b/container/internal/graphviz/edge.go new file mode 100644 index 000000000000..7845003c6595 --- /dev/null +++ b/container/internal/graphviz/edge.go @@ -0,0 +1,16 @@ +package graphviz + +import ( + "fmt" + io "io" +) + +type Edge struct { + *Attributes + from, to *Node +} + +func (e Edge) render(w io.Writer, indent string) error { + _, err := fmt.Fprintf(w, "%s%q -> %q%s;\n", indent, e.from.name, e.to.name, e.Attributes.String()) + return err +} diff --git a/container/internal/graphviz/graph.go b/container/internal/graphviz/graph.go new file mode 100644 index 000000000000..19938d54001c --- /dev/null +++ b/container/internal/graphviz/graph.go @@ -0,0 +1,144 @@ +package graphviz + +import ( + "bytes" + "fmt" + "io" +) + +type Graph struct { + *Attributes + + // name is the optional name of this graph + name string + + // parent is non-nil if this is a sub-graph + parent *Graph + + // allNodes includes all nodes in the graph and its sub-graphs. + // It is set to the same map in parent and sub-graphs. + allNodes map[string]*Node + + // myNodes are the nodes in this graph (whether it's a root or sub-graph) + myNodes map[string]*Node + + subgraphs map[string]*Graph + + edges []*Edge +} + +func NewGraph() *Graph { + return &Graph{ + Attributes: NewAttributes(), + name: "", + parent: nil, + allNodes: map[string]*Node{}, + myNodes: map[string]*Node{}, + subgraphs: map[string]*Graph{}, + edges: nil, + } +} + +func (g *Graph) FindOrCreateNode(name string) (node *Node, found bool) { + if node, ok := g.allNodes[name]; ok { + return node, true + } + + node = &Node{ + Attributes: NewAttributes(), + name: name, + } + g.allNodes[name] = node + g.myNodes[name] = node + return node, false +} + +func (g *Graph) SubGraph(name string) *Graph { + if sub, ok := g.subgraphs[name]; ok { + return sub + } + + n := &Graph{ + Attributes: NewAttributes(), + name: name, + parent: g, + allNodes: g.allNodes, + myNodes: map[string]*Node{}, + subgraphs: map[string]*Graph{}, + edges: nil, + } + g.subgraphs[name] = n + return n +} + +func (g *Graph) CreateEdge(from, to *Node) *Edge { + edge := &Edge{ + Attributes: NewAttributes(), + from: from, + to: to, + } + g.edges = append(g.edges, edge) + return edge +} + +func (g *Graph) RenderDOT(w io.Writer) error { + return g.render(w, "") +} + +func (g *Graph) render(w io.Writer, indent string) error { + if g.parent == nil { + _, err := fmt.Fprintf(w, "%sdigraph %q {\n", indent, g.name) + if err != nil { + return err + } + } else { + _, err := fmt.Fprintf(w, "%ssubgraph %q {\n", indent, g.name) + if err != nil { + return err + } + } + + { + subIndent := indent + " " + + if attrStr := g.Attributes.String(); attrStr != "" { + _, err := fmt.Fprintf(w, "%sgraph %s;\n", subIndent, attrStr) + if err != nil { + return err + } + } + + for _, subgraph := range g.subgraphs { + err := subgraph.render(w, subIndent+" ") + if err != nil { + return err + } + } + + for _, node := range g.myNodes { + err := node.render(w, subIndent) + if err != nil { + return err + } + } + + for _, edge := range g.edges { + err := edge.render(w, subIndent) + if err != nil { + return err + } + } + } + + _, err := fmt.Fprintf(w, "%s}\n\n", indent) + return err +} + +func (g *Graph) String() string { + buf := &bytes.Buffer{} + err := g.RenderDOT(buf) + if err != nil { + panic(err) + } + return buf.String() +} diff --git a/container/internal/graphviz/node.go b/container/internal/graphviz/node.go new file mode 100644 index 000000000000..72bd247fa5b0 --- /dev/null +++ b/container/internal/graphviz/node.go @@ -0,0 +1,16 @@ +package graphviz + +import ( + "fmt" + io "io" +) + +type Node struct { + *Attributes + name string +} + +func (n Node) render(w io.Writer, indent string) error { + _, err := fmt.Fprintf(w, "%s%q%s;\n", indent, n.name, n.Attributes.String()) + return err +} diff --git a/container/module_dep.go b/container/module_dep.go index 9082832609fe..261d3afed79d 100644 --- a/container/module_dep.go +++ b/container/module_dep.go @@ -3,7 +3,7 @@ package container import ( "reflect" - "github.com/emicklei/dot" + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) type moduleDepProvider struct { @@ -17,7 +17,7 @@ type moduleDepResolver struct { idxInValues int node *moduleDepProvider valueMap map[*moduleKey]reflect.Value - graphNode dot.Node + graphNode *graphviz.Node } func (s moduleDepResolver) describeLocation() string { @@ -52,6 +52,6 @@ func (s moduleDepResolver) addNode(p *simpleProvider, _ int) error { return duplicateDefinitionError(s.typ, p.provider.Location, s.node.provider.Location.String()) } -func (s moduleDepResolver) typeGraphNode() dot.Node { +func (s moduleDepResolver) typeGraphNode() *graphviz.Node { return s.graphNode } diff --git a/container/one_per_module.go b/container/one_per_module.go index 395e5d8ac35d..9c525eed6994 100644 --- a/container/one_per_module.go +++ b/container/one_per_module.go @@ -4,8 +4,9 @@ import ( "fmt" "reflect" - "github.com/emicklei/dot" "github.com/pkg/errors" + + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) // OnePerModuleType marks a type which @@ -33,7 +34,7 @@ type onePerModuleResolver struct { idxMap map[*moduleKey]int resolved bool values reflect.Value - graphNode dot.Node + graphNode *graphviz.Node } type mapOfOnePerModuleResolver struct { @@ -100,6 +101,6 @@ func (o *mapOfOnePerModuleResolver) addNode(s *simpleProvider, _ int) error { return errors.Errorf("%v is a one-per-module type and thus %v can't be used as an output parameter in %s", o.typ, o.mapType, s.provider.Location) } -func (o onePerModuleResolver) typeGraphNode() dot.Node { +func (o onePerModuleResolver) typeGraphNode() *graphviz.Node { return o.graphNode } diff --git a/container/resolver.go b/container/resolver.go index 56d80270af4c..3e573556fab1 100644 --- a/container/resolver.go +++ b/container/resolver.go @@ -3,12 +3,12 @@ package container import ( "reflect" - "github.com/emicklei/dot" + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) type resolver interface { addNode(*simpleProvider, int) error resolve(*container, *moduleKey, Location) (reflect.Value, error) describeLocation() string - typeGraphNode() dot.Node + typeGraphNode() *graphviz.Node } diff --git a/container/simple.go b/container/simple.go index 381d85f470f1..da9d35343e1b 100644 --- a/container/simple.go +++ b/container/simple.go @@ -3,7 +3,7 @@ package container import ( "reflect" - "github.com/emicklei/dot" + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) type simpleProvider struct { @@ -19,7 +19,7 @@ type simpleResolver struct { resolved bool typ reflect.Type value reflect.Value - graphNode dot.Node + graphNode *graphviz.Node } func (s *simpleResolver) describeLocation() string { @@ -62,6 +62,6 @@ func (s simpleResolver) addNode(p *simpleProvider, _ int) error { return duplicateDefinitionError(s.typ, p.provider.Location, s.node.provider.Location.String()) } -func (s simpleResolver) typeGraphNode() dot.Node { +func (s simpleResolver) typeGraphNode() *graphviz.Node { return s.graphNode } diff --git a/container/supply.go b/container/supply.go index 8ff782ee5237..e6a1db0b5026 100644 --- a/container/supply.go +++ b/container/supply.go @@ -3,14 +3,14 @@ package container import ( "reflect" - "github.com/emicklei/dot" + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) type supplyResolver struct { typ reflect.Type value reflect.Value loc Location - graphNode dot.Node + graphNode *graphviz.Node } func (s supplyResolver) describeLocation() string { @@ -26,6 +26,6 @@ func (s supplyResolver) resolve(c *container, _ *moduleKey, caller Location) (re return s.value, nil } -func (s supplyResolver) typeGraphNode() dot.Node { +func (s supplyResolver) typeGraphNode() *graphviz.Node { return s.graphNode } diff --git a/container/testdata/example.dot b/container/testdata/example.dot index c5431f0257c2..5d5c182010cb 100644 --- a/container/testdata/example.dot +++ b/container/testdata/example.dot @@ -1,45 +1,43 @@ -digraph { - subgraph s7 { - label="Module: a"; - n8[color="lightgrey",label="github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide",shape="box"]; - - } - subgraph s13 { - label="Module: b"; - n14[color="black",label="github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide",shape="box"]; - n17[color="black",label="github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey",shape="box"]; - - } - subgraph s4 { - label="Module: runtime"; - n5[color="lightgrey",label="github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey",shape="box"]; - - } - - n12[color="lightgrey",comment="auto-group",label="[]github.com/cosmos/cosmos-sdk/container_test.Command"]; - n2[color="black",label="github.com/cosmos/cosmos-sdk/container.ModuleKey"]; - n9[color="lightgrey",label="github.com/cosmos/cosmos-sdk/container.OwnModuleKey"]; - n6[color="black",label="github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"]; - n10[color="lightgrey",label="github.com/cosmos/cosmos-sdk/container_test.KeeperA"]; - n15[color="black",label="github.com/cosmos/cosmos-sdk/container_test.KeeperB"]; - n3[color="black",label="github.com/cosmos/cosmos-sdk/container_test.MsgClientA"]; - n1[color="black",label="github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA",shape="box"]; - n16[color="black",label="github.com/cosmos/cosmos-sdk/container_test.TestLogging",shape="box"]; - n11[color="lightgrey",comment="one-per-module",label="map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"]; - n2->n1; - n2->n5; - n9->n8; - n6->n8; - n6->n14; - n15->n16; - n8->n10; - n8->n11; - n8->n12; - n14->n15; - n14->n12; - n14->n11; - n3->n14; - n5->n6; - n1->n3; - +digraph "" { + subgraph "cluster_runtime" { + graph [label="Module: runtime", penwidth="0.5", fontsize="12.0"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + } + + subgraph "cluster_a" { + graph [penwidth="0.5", fontsize="12.0", label="Module: a"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[shape="box", color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; + } + + subgraph "cluster_b" { + graph [label="Module: b", penwidth="0.5", fontsize="12.0"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + } + + "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[fontcolor="black", color="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container_test.TestLogging"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[penwidth="1.5", fontcolor="black", color="black"]; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; + "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="one-per-module"]; + "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="auto-group"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"[fontcolor="black", shape="box", color="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"; + "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA" -> "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"; + "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide" -> "github.com/cosmos/cosmos-sdk/container_test.KeeperA"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide" -> "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide" -> "[]github.com/cosmos/cosmos-sdk/container_test.Command"; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "github.com/cosmos/cosmos-sdk/container_test.KeeperB"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "[]github.com/cosmos/cosmos-sdk/container_test.Command"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB" -> "github.com/cosmos/cosmos-sdk/container_test.TestLogging"; } + From 543eb193b592c5bbad6fe52d0a25afaf1e83d674 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 11:22:11 -0400 Subject: [PATCH 04/14] docs --- container/container.go | 2 +- container/debug.go | 4 ++-- container/internal/graphviz/attrs.go | 28 +++++++++++++++++++++------- container/internal/graphviz/docs.go | 4 ++++ container/internal/graphviz/edge.go | 3 ++- container/internal/graphviz/graph.go | 8 ++++++++ container/internal/graphviz/node.go | 3 ++- 7 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 container/internal/graphviz/docs.go diff --git a/container/container.go b/container/container.go index 6877b869d061..365ca792def0 100644 --- a/container/container.go +++ b/container/container.go @@ -415,7 +415,7 @@ func (c container) formatResolveStack() string { func markGraphNodeAsUsed(node *graphviz.Node) { node.SetColor("black") node.SetPenWidth("1.5") - node.SetAttr("fontcolor", "black") + node.SetFontColor("black") } func markGraphNodeAsFailed(node *graphviz.Node) { diff --git a/container/debug.go b/container/debug.go index 92aa73c5f2ff..364ee95c3a37 100644 --- a/container/debug.go +++ b/container/debug.go @@ -267,7 +267,7 @@ func (c *debugConfig) typeGraphNode(typ reflect.Type) *graphviz.Node { func setUnusedStyle(attr *graphviz.Attributes) { attr.SetColor("lightgrey") attr.SetPenWidth("0.5") - attr.SetAttr("fontcolor", "dimgrey") + attr.SetFontColor("dimgrey") } // moreUsefulTypeString is more useful than reflect.Type.String() @@ -293,7 +293,7 @@ func (c *debugConfig) moduleSubGraph(key *moduleKey) *graphviz.Graph { graph = c.graph.SubGraph(gname) graph.SetLabel(fmt.Sprintf("Module: %s", key.name)) graph.SetPenWidth("0.5") - graph.SetAttr("fontsize", "12.0") + graph.SetFontSize("12.0") } return graph } diff --git a/container/internal/graphviz/attrs.go b/container/internal/graphviz/attrs.go index e5fdaa5a9983..5852b54b30e6 100644 --- a/container/internal/graphviz/attrs.go +++ b/container/internal/graphviz/attrs.go @@ -5,30 +5,44 @@ import ( "strings" ) +// Attributes represents a graphviz attributes map. type Attributes struct { attrs map[string]string } +// NewAttributes creates a new Attributes instance. func NewAttributes() *Attributes { return &Attributes{attrs: map[string]string{}} } +// SetAttr sets the graphviz attribute to the provided value. func (a *Attributes) SetAttr(name, value string) { a.attrs[name] = value } -func (n *Attributes) SetShape(shape string) { n.SetAttr("shape", shape) } +// SetShape sets the shape attribute. +func (a *Attributes) SetShape(shape string) { a.SetAttr("shape", shape) } -func (n *Attributes) SetColor(color string) { n.SetAttr("color", color) } +// SetColor sets the color attribute. +func (a *Attributes) SetColor(color string) { a.SetAttr("color", color) } -func (n *Attributes) SetBgColor(color string) { n.SetAttr("bgcolor", color) } +// SetBgColor sets the bgcolor attribute. +func (a *Attributes) SetBgColor(color string) { a.SetAttr("bgcolor", color) } -func (n *Attributes) SetComment(comment string) { n.SetAttr("comment", comment) } +// SetLabel sets the label attribute. +func (a *Attributes) SetLabel(label string) { a.SetAttr("label", label) } -func (n *Attributes) SetLabel(label string) { n.SetAttr("label", label) } +// SetComment sets the comment attribute. +func (a *Attributes) SetComment(comment string) { a.SetAttr("comment", comment) } -func (n *Attributes) SetPenWidth(w string) { n.SetAttr("penwidth", w) } +// SetPenWidth sets the penwidth attribute. +func (a *Attributes) SetPenWidth(w string) { a.SetAttr("penwidth", w) } -func (n *Attributes) SetFontColor(color string) { n.SetAttr("fontcolor", color) } +// SetFontColor sets the fontcolor attribute. +func (a *Attributes) SetFontColor(color string) { a.SetAttr("fontcolor", color) } +// SetFontSize sets the fontsize attribute. +func (a *Attributes) SetFontSize(size string) { a.SetAttr("fontsize", size) } + +// String returns the attributes graphviz string in the format [name = "value", ...]. func (a *Attributes) String() string { if len(a.attrs) == 0 { return "" diff --git a/container/internal/graphviz/docs.go b/container/internal/graphviz/docs.go new file mode 100644 index 000000000000..528fc99dfe49 --- /dev/null +++ b/container/internal/graphviz/docs.go @@ -0,0 +1,4 @@ +// Package graphviz provides some simple types for building graphviz DOT files +// based on their usage for container debugging. It does not attempt to cover +// all of graphviz, just what is needed here. +package graphviz diff --git a/container/internal/graphviz/edge.go b/container/internal/graphviz/edge.go index 7845003c6595..368e5a0d302f 100644 --- a/container/internal/graphviz/edge.go +++ b/container/internal/graphviz/edge.go @@ -2,9 +2,10 @@ package graphviz import ( "fmt" - io "io" + "io" ) +// Edge represents a graphviz edge. type Edge struct { *Attributes from, to *Node diff --git a/container/internal/graphviz/graph.go b/container/internal/graphviz/graph.go index 19938d54001c..cde97a15bef7 100644 --- a/container/internal/graphviz/graph.go +++ b/container/internal/graphviz/graph.go @@ -1,3 +1,4 @@ +// Package graphviz package graphviz import ( @@ -6,6 +7,7 @@ import ( "io" ) +// Graph represents a graphviz digraph. type Graph struct { *Attributes @@ -27,6 +29,7 @@ type Graph struct { edges []*Edge } +// NewGraph creates a new Graph instance. func NewGraph() *Graph { return &Graph{ Attributes: NewAttributes(), @@ -39,6 +42,7 @@ func NewGraph() *Graph { } } +// FindOrCreateNode finds or creates the node with the provided name. func (g *Graph) FindOrCreateNode(name string) (node *Node, found bool) { if node, ok := g.allNodes[name]; ok { return node, true @@ -53,6 +57,7 @@ func (g *Graph) FindOrCreateNode(name string) (node *Node, found bool) { return node, false } +// SubGraph finds or creates the subgraph with the provided name. func (g *Graph) SubGraph(name string) *Graph { if sub, ok := g.subgraphs[name]; ok { return sub @@ -71,6 +76,7 @@ func (g *Graph) SubGraph(name string) *Graph { return n } +// CreateEdge creates a new graphviz edge. func (g *Graph) CreateEdge(from, to *Node) *Edge { edge := &Edge{ Attributes: NewAttributes(), @@ -81,6 +87,7 @@ func (g *Graph) CreateEdge(from, to *Node) *Edge { return edge } +// RenderDOT renders the graph to DOT format. func (g *Graph) RenderDOT(w io.Writer) error { return g.render(w, "") } @@ -134,6 +141,7 @@ func (g *Graph) render(w io.Writer, indent string) error { return err } +// String returns the graph in DOT format. func (g *Graph) String() string { buf := &bytes.Buffer{} err := g.RenderDOT(buf) diff --git a/container/internal/graphviz/node.go b/container/internal/graphviz/node.go index 72bd247fa5b0..d464aad869f8 100644 --- a/container/internal/graphviz/node.go +++ b/container/internal/graphviz/node.go @@ -2,9 +2,10 @@ package graphviz import ( "fmt" - io "io" + "io" ) +// Node represents a graphviz node. type Node struct { *Attributes name string From 29ca9740299a9873e37d80b2766746033ab983b6 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 11:50:48 -0400 Subject: [PATCH 05/14] WIP --- container/README.md | 12 +++++++ container/build.go | 1 + container/container.go | 1 + container/container_test.go | 53 +++++++++++++++++++--------- container/debug.go | 5 ++- container/go.mod | 1 + container/go.sum | 4 +-- container/internal/graphviz/graph.go | 24 +++++++------ container/internal/util/util.go | 26 ++++++++++++++ container/testdata/example.dot | 30 ++++++++-------- container/testdata/example_error.dot | 40 +++++++++++++++++++++ 11 files changed, 153 insertions(+), 44 deletions(-) create mode 100644 container/README.md create mode 100644 container/internal/util/util.go create mode 100644 container/testdata/example_error.dot diff --git a/container/README.md b/container/README.md new file mode 100644 index 000000000000..f273c40c7f7d --- /dev/null +++ b/container/README.md @@ -0,0 +1,12 @@ +# Cosmos SDK Dependency Injection `container` Moduleo + +## Overview + +TODO + +## Usage + +TODO + +## Debugging + diff --git a/container/build.go b/container/build.go index b340b2a6034a..f40864ee211a 100644 --- a/container/build.go +++ b/container/build.go @@ -40,6 +40,7 @@ func build(loc Location, debugOpt DebugOption, option Option, outputs ...interfa err = doBuild(cfg, loc, debugOpt, option, outputs...) if err != nil { + cfg.logf("Error: %v", err) if cfg.onError != nil { err2 := cfg.onError.applyConfig(cfg) if err2 != nil { diff --git a/container/container.go b/container/container.go index 365ca792def0..29645456f271 100644 --- a/container/container.go +++ b/container/container.go @@ -420,4 +420,5 @@ func markGraphNodeAsUsed(node *graphviz.Node) { func markGraphNodeAsFailed(node *graphviz.Node) { node.SetColor("red") + node.SetFontColor("red") } diff --git a/container/container_test.go b/container/container_test.go index c19f3d6117af..071c9b203c41 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -542,7 +542,7 @@ func TestStructArgs(t *testing.T) { )) } -func TestLogging(t *testing.T) { +func TestDebugOptions(t *testing.T) { var logOut string var dotGraph string @@ -557,7 +557,6 @@ func TestLogging(t *testing.T) { require.NoError(t, err) defer os.Remove(graphfile.Name()) - var b KeeperB require.NoError(t, container.BuildDebug( container.DebugOptions( container.Logger(func(s string) { @@ -570,22 +569,44 @@ func TestLogging(t *testing.T) { container.FileVisualizer(graphfile.Name()), container.StdoutLogger(), ), - scenarioConfig, - &b, + container.Options(), )) - golden.Assert(t, dotGraph, "example.dot") - - //require.Contains(t, logOut, "digraph") - //require.Contains(t, dotGraph, "digraph") - // - //outfileContents, err := os.ReadFile(outfile.Name()) - //require.NoError(t, err) - //require.Contains(t, string(outfileContents), "digraph") - // - //graphfileContents, err := os.ReadFile(graphfile.Name()) - //require.NoError(t, err) - //require.Contains(t, string(graphfileContents), "digraph") + require.Contains(t, logOut, "digraph") + require.Contains(t, dotGraph, "digraph") + + outfileContents, err := os.ReadFile(outfile.Name()) + require.NoError(t, err) + require.Contains(t, string(outfileContents), "digraph") + + graphfileContents, err := os.ReadFile(graphfile.Name()) + require.NoError(t, err) + require.Contains(t, string(graphfileContents), "digraph") +} + +func TestGraphAndLogOutput(t *testing.T) { + var logOut, graphOut string + var b KeeperB + debugOpts := container.DebugOptions( + container.Logger(func(s string) { + logOut += s + "\n" + }), + container.Visualizer(func(dotGraph string) { + graphOut = dotGraph + })) + require.NoError(t, container.BuildDebug(debugOpts, scenarioConfig, &b)) + golden.Assert(t, logOut, "example.log") + golden.Assert(t, graphOut, "example.dot") + + logOut = "" + badConfig := container.Options( + container.ProvideInModule("runtime", ProvideKVStoreKey), + container.ProvideInModule("a", wrapMethod0(ModuleA{})), + container.ProvideInModule("b", wrapMethod0(ModuleB{})), + ) + require.Error(t, container.BuildDebug(debugOpts, badConfig, &b)) + golden.Assert(t, logOut, "example_error.log") + golden.Assert(t, graphOut, "example_error.dot") } func TestConditionalDebugging(t *testing.T) { diff --git a/container/debug.go b/container/debug.go index 364ee95c3a37..5e70b8ad3d29 100644 --- a/container/debug.go +++ b/container/debug.go @@ -25,7 +25,10 @@ func StdoutLogger() DebugOption { // Visualizer creates an option which provides a visualizer function which // will receive a rendering of the container in the Graphiz DOT format // whenever the container finishes building or fails due to an error. The -// graph is color-coded to aid debugging. +// graph is color-coded to aid debugging with black representing success, +// red representing an error, and gray representing unused types or functions. +// Graph rendering should be deterministic for a given version of the container +// module and container options so that graphs can be used in tests. func Visualizer(visualizer func(dotGraph string)) DebugOption { return debugOption(func(c *debugConfig) error { c.addFuncVisualizer(visualizer) diff --git a/container/go.mod b/container/go.mod index 0ea1af4e8c8a..156a1f6b8e99 100644 --- a/container/go.mod +++ b/container/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.1 + golang.org/x/exp v0.0.0-20220428152302-39d4317da171 gotest.tools/v3 v3.2.0 ) diff --git a/container/go.sum b/container/go.sum index cc08160e5d0f..15bd79c65eba 100644 --- a/container/go.sum +++ b/container/go.sum @@ -1,8 +1,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/dot v0.16.0 h1:7PseyizTgeQ/aSF1eo4LcEfWlQSlzamFZpzY/nMB9EY= -github.com/emicklei/dot v0.16.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -17,6 +15,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4= +golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/container/internal/graphviz/graph.go b/container/internal/graphviz/graph.go index cde97a15bef7..24869b95307c 100644 --- a/container/internal/graphviz/graph.go +++ b/container/internal/graphviz/graph.go @@ -5,6 +5,8 @@ import ( "bytes" "fmt" "io" + + "github.com/cosmos/cosmos-sdk/container/internal/util" ) // Graph represents a graphviz digraph. @@ -115,18 +117,20 @@ func (g *Graph) render(w io.Writer, indent string) error { } } - for _, subgraph := range g.subgraphs { - err := subgraph.render(w, subIndent+" ") - if err != nil { - return err - } + // we do map iteration in sorted order so that outputs are stable and + // can be used in tests + err := util.IterateMapOrdered(g.subgraphs, func(_ string, subgraph *Graph) error { + return subgraph.render(w, subIndent+" ") + }) + if err != nil { + return err } - for _, node := range g.myNodes { - err := node.render(w, subIndent) - if err != nil { - return err - } + err = util.IterateMapOrdered(g.myNodes, func(_ string, node *Node) error { + return node.render(w, subIndent) + }) + if err != nil { + return err } for _, edge := range g.edges { diff --git a/container/internal/util/util.go b/container/internal/util/util.go new file mode 100644 index 000000000000..3a9a95120da0 --- /dev/null +++ b/container/internal/util/util.go @@ -0,0 +1,26 @@ +package util + +import ( + "golang.org/x/exp/constraints" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" +) + +// IterateMapOrdered iterates over the map with keys sorted in ascending order +// calling forEach for each key-value pair as long as forEach does not return an error. +func IterateMapOrdered[K constraints.Ordered, V any](m map[K]V, forEach func(k K, v V) error) error { + keys := OrderedMapKeys(m) + for _, k := range keys { + if err := forEach(k, m[k]); err != nil { + return err + } + } + return nil +} + +// OrderedMapKeys returns the map keys in ascending order. +func OrderedMapKeys[K constraints.Ordered, V any](m map[K]V) []K { + keys := maps.Keys(m) + slices.Sort(keys) + return keys +} diff --git a/container/testdata/example.dot b/container/testdata/example.dot index 5d5c182010cb..599af7d01130 100644 --- a/container/testdata/example.dot +++ b/container/testdata/example.dot @@ -1,29 +1,29 @@ digraph "" { - subgraph "cluster_runtime" { - graph [label="Module: runtime", penwidth="0.5", fontsize="12.0"]; - "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; - } - subgraph "cluster_a" { - graph [penwidth="0.5", fontsize="12.0", label="Module: a"]; + graph [label="Module: a", penwidth="0.5", fontsize="12.0"]; "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[shape="box", color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; } subgraph "cluster_b" { graph [label="Module: b", penwidth="0.5", fontsize="12.0"]; - "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[color="black", penwidth="1.5", fontcolor="black", shape="box"]; + } + + subgraph "cluster_runtime" { + graph [label="Module: runtime", penwidth="0.5", fontsize="12.0"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; } - "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; - "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[fontcolor="black", color="black", penwidth="1.5"]; - "github.com/cosmos/cosmos-sdk/container_test.TestLogging"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="auto-group"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[penwidth="1.5", fontcolor="black", color="black"]; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="one-per-module"]; - "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="auto-group"]; - "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"[fontcolor="black", shape="box", color="black", penwidth="1.5"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"; "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA" -> "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"; @@ -38,6 +38,6 @@ digraph "" { "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "github.com/cosmos/cosmos-sdk/container_test.KeeperB"; "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "[]github.com/cosmos/cosmos-sdk/container_test.Command"; "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"; - "github.com/cosmos/cosmos-sdk/container_test.KeeperB" -> "github.com/cosmos/cosmos-sdk/container_test.TestLogging"; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB" -> "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"; } diff --git a/container/testdata/example_error.dot b/container/testdata/example_error.dot new file mode 100644 index 000000000000..c52d59143083 --- /dev/null +++ b/container/testdata/example_error.dot @@ -0,0 +1,40 @@ +digraph "" { + subgraph "cluster_a" { + graph [label="Module: a", penwidth="0.5", fontsize="12.0"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[fontcolor="dimgrey", shape="box", color="lightgrey", penwidth="0.5"]; + } + + subgraph "cluster_b" { + graph [fontsize="12.0", label="Module: b", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[shape="box", color="red", penwidth="0.5", fontcolor="red"]; + } + + subgraph "cluster_runtime" { + graph [label="Module: runtime", penwidth="0.5", fontsize="12.0"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + } + + "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="auto-group"]; + "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[fontcolor="black", color="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="red", penwidth="0.5", fontcolor="red"]; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[color="red", penwidth="0.5", fontcolor="red"]; + "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[shape="box", color="red", penwidth="0.5", fontcolor="red"]; + "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="one-per-module"]; + "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide" -> "github.com/cosmos/cosmos-sdk/container_test.KeeperA"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide" -> "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide" -> "[]github.com/cosmos/cosmos-sdk/container_test.Command"; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "github.com/cosmos/cosmos-sdk/container_test.KeeperB"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "[]github.com/cosmos/cosmos-sdk/container_test.Command"; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide" -> "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB" -> "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"; +} + From f0301084f58a75e5c7ad2b0b87818fd464f2c4f5 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:19:23 -0400 Subject: [PATCH 06/14] update docs and graphviz --- container/Makefile | 4 + container/README.md | 19 +++ container/container.go | 2 + container/debug.go | 36 +++-- container/internal/graphviz/attrs.go | 3 + container/internal/graphviz/graph.go | 8 +- container/testdata/example.dot | 16 +-- container/testdata/example.svg | 197 +++++++++++++++++++++++++++ container/testdata/example_error.dot | 20 +-- container/testdata/example_error.svg | 179 ++++++++++++++++++++++++ 10 files changed, 449 insertions(+), 35 deletions(-) create mode 100644 container/Makefile create mode 100644 container/testdata/example.svg create mode 100644 container/testdata/example_error.svg diff --git a/container/Makefile b/container/Makefile new file mode 100644 index 000000000000..8156317101fa --- /dev/null +++ b/container/Makefile @@ -0,0 +1,4 @@ +update-examples: + go test . -test.update-golden + dot -Tsvg testdata/example.dot > testdata/example.svg + dot -Tsvg testdata/example_error.dot > testdata/example_error.svg diff --git a/container/README.md b/container/README.md index f273c40c7f7d..450b6c7d0d58 100644 --- a/container/README.md +++ b/container/README.md @@ -10,3 +10,22 @@ TODO ## Debugging +Issues with resolving dependencies in the container can be done with [logs](./testdata/example.log) +and [Graphviz](https://graphviz.org) renderings of the container tree. By default, whenever there is an error, logs will +be printed to stderr and a rendering of the dependency graph in Graphviz DOT format will be saved to +`debug_container.dot`. + +Here is an example Graphviz rendering of a successful build of a dependency graph: +![Graphviz Example](./testdata/example.svg) + + +Rectangles represent functions, ovals represent types, rounded rectangles represent modules and the single hexagon +represents the function which called `Build`. Black-colored shapes mark functions and types that were called/resolved +without an error. Gray-colored nodes mark functions and types that could have been called/resolved in the container but +were left unused. + + +Here is an example Graphviz rendering of a dependency graph build which failed: +![Graphviz Error Example](./testdata/example_error.svg) + + diff --git a/container/container.go b/container/container.go index 29645456f271..76df8bedfa9c 100644 --- a/container/container.go +++ b/container/container.go @@ -361,6 +361,8 @@ func (c *container) build(loc Location, outputs ...interface{}) error { }, Location: loc, } + callerGraphNode := c.locationGraphNode(loc, nil) + callerGraphNode.SetShape("hexagon") desc, err := expandStructArgsProvider(desc) if err != nil { diff --git a/container/debug.go b/container/debug.go index 5e70b8ad3d29..a923f9aff237 100644 --- a/container/debug.go +++ b/container/debug.go @@ -22,6 +22,13 @@ func StdoutLogger() DebugOption { }) } +// StderrLogger is a debug option which routes logging output to stderro. +func StderrLogger() DebugOption { + return Logger(func(s string) { + _, _ = fmt.Fprintln(os.Stderr, s) + }) +} + // Visualizer creates an option which provides a visualizer function which // will receive a rendering of the container in the Graphiz DOT format // whenever the container finishes building or fails due to an error. The @@ -73,16 +80,15 @@ func Logger(logger func(string)) DebugOption { } const ( - debugContainerSvg = "debug_container.svg" debugContainerDot = "debug_container.dot" ) -// Debug is a default debug option which sends log output to stdout, dumps +// Debug is a default debug option which sends log output to stderr, dumps // the container in the graphviz DOT and SVG formats to debug_container.dot // and debug_container.svg respectively. func Debug() DebugOption { return DebugOptions( - StdoutLogger(), + StderrLogger(), FileVisualizer(debugContainerDot), ) } @@ -128,13 +134,12 @@ func DebugCleanup(cleanup func()) DebugOption { } // AutoDebug does the same thing as Debug when there is an error and deletes -// the debug_container.dot and debug_container.dot if they exist when there -// is no error. This is the default debug mode of Run. +// the debug_container.dot if it exists when there is no error. This is the +// default debug mode of Run. func AutoDebug() DebugOption { return DebugOptions( OnError(Debug()), OnSuccess(DebugCleanup(func() { - deleteIfExists(debugContainerSvg) deleteIfExists(debugContainerDot) })), ) @@ -290,15 +295,20 @@ func moreUsefulTypeString(ty reflect.Type) string { } func (c *debugConfig) moduleSubGraph(key *moduleKey) *graphviz.Graph { - graph := c.graph - if key != nil { + if key == nil { + // return the root graph + return c.graph + } else { gname := fmt.Sprintf("cluster_%s", key.name) - graph = c.graph.SubGraph(gname) - graph.SetLabel(fmt.Sprintf("Module: %s", key.name)) - graph.SetPenWidth("0.5") - graph.SetFontSize("12.0") + graph, found := c.graph.FindOrCreateSubGraph(gname) + if !found { + graph.SetLabel(fmt.Sprintf("Module: %s", key.name)) + graph.SetPenWidth("0.5") + graph.SetFontSize("12.0") + graph.SetStyle("rounded") + } + return graph } - return graph } func (c *debugConfig) addGraphEdge(from, to *graphviz.Node) { diff --git a/container/internal/graphviz/attrs.go b/container/internal/graphviz/attrs.go index 5852b54b30e6..634e717cda3f 100644 --- a/container/internal/graphviz/attrs.go +++ b/container/internal/graphviz/attrs.go @@ -42,6 +42,9 @@ func (a *Attributes) SetFontColor(color string) { a.SetAttr("fontcolor", color) // SetFontSize sets the fontsize attribute. func (a *Attributes) SetFontSize(size string) { a.SetAttr("fontsize", size) } +// SetStyle sets the style attribute. +func (a *Attributes) SetStyle(style string) { a.SetAttr("style", style) } + // String returns the attributes graphviz string in the format [name = "value", ...]. func (a *Attributes) String() string { if len(a.attrs) == 0 { diff --git a/container/internal/graphviz/graph.go b/container/internal/graphviz/graph.go index 24869b95307c..d2ae90545f48 100644 --- a/container/internal/graphviz/graph.go +++ b/container/internal/graphviz/graph.go @@ -59,10 +59,10 @@ func (g *Graph) FindOrCreateNode(name string) (node *Node, found bool) { return node, false } -// SubGraph finds or creates the subgraph with the provided name. -func (g *Graph) SubGraph(name string) *Graph { +// FindOrCreateSubGraph finds or creates the subgraph with the provided name. +func (g *Graph) FindOrCreateSubGraph(name string) (graph *Graph, found bool) { if sub, ok := g.subgraphs[name]; ok { - return sub + return sub, true } n := &Graph{ @@ -75,7 +75,7 @@ func (g *Graph) SubGraph(name string) *Graph { edges: nil, } g.subgraphs[name] = n - return n + return n, false } // CreateEdge creates a new graphviz edge. diff --git a/container/testdata/example.dot b/container/testdata/example.dot index 599af7d01130..a6ea7e343204 100644 --- a/container/testdata/example.dot +++ b/container/testdata/example.dot @@ -1,29 +1,29 @@ digraph "" { subgraph "cluster_a" { - graph [label="Module: a", penwidth="0.5", fontsize="12.0"]; + graph [style="rounded", label="Module: a", penwidth="0.5", fontsize="12.0"]; "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[shape="box", color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; } subgraph "cluster_b" { - graph [label="Module: b", penwidth="0.5", fontsize="12.0"]; - "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[color="black", penwidth="1.5", fontcolor="black", shape="box"]; + graph [label="Module: b", penwidth="0.5", fontsize="12.0", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[penwidth="1.5", fontcolor="black", shape="box", color="black"]; } subgraph "cluster_runtime" { - graph [label="Module: runtime", penwidth="0.5", fontsize="12.0"]; + graph [label="Module: runtime", penwidth="0.5", fontsize="12.0", style="rounded"]; "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; } "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="auto-group"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; - "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[penwidth="1.5", fontcolor="black", color="black"]; "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="black", penwidth="1.5", fontcolor="black"]; "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[color="black", penwidth="1.5", fontcolor="black"]; "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; - "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="one-per-module"]; + "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[shape="hexagon", color="black", penwidth="1.5", fontcolor="black"]; + "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[penwidth="0.5", fontcolor="dimgrey", comment="one-per-module", color="lightgrey"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"; "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA" -> "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"; diff --git a/container/testdata/example.svg b/container/testdata/example.svg new file mode 100644 index 000000000000..a324b637cd01 --- /dev/null +++ b/container/testdata/example.svg @@ -0,0 +1,197 @@ + + + + + + + + +cluster_a + +Module: a + + +cluster_b + +Module: b + + +cluster_runtime + +Module: runtime + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + + + + +[]github.com/cosmos/cosmos-sdk/container_test.Command + +[]github.com/cosmos/cosmos-sdk/container_test.Command + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide->[]github.com/cosmos/cosmos-sdk/container_test.Command + + + + + +github.com/cosmos/cosmos-sdk/container_test.KeeperA + +github.com/cosmos/cosmos-sdk/container_test.KeeperA + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide->github.com/cosmos/cosmos-sdk/container_test.KeeperA + + + + + + +map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + +map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide->map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + + + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide->[]github.com/cosmos/cosmos-sdk/container_test.Command + + + + + +github.com/cosmos/cosmos-sdk/container_test.KeeperB + +github.com/cosmos/cosmos-sdk/container_test.KeeperB + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide->github.com/cosmos/cosmos-sdk/container_test.KeeperB + + + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide->map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + + + + + +github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey + +github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey + + + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey + + + +github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey->github.com/cosmos/cosmos-sdk/container_test.KVStoreKey + + + + + +github.com/cosmos/cosmos-sdk/container.ModuleKey + +github.com/cosmos/cosmos-sdk/container.ModuleKey + + + +github.com/cosmos/cosmos-sdk/container.ModuleKey->github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey + + + + + +github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA + +github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA + + + +github.com/cosmos/cosmos-sdk/container.ModuleKey->github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA + + + + + +github.com/cosmos/cosmos-sdk/container.OwnModuleKey + +github.com/cosmos/cosmos-sdk/container.OwnModuleKey + + + +github.com/cosmos/cosmos-sdk/container.OwnModuleKey->github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + + + + + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey->github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + + + + + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey->github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + + + + + +github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + +github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + + + +github.com/cosmos/cosmos-sdk/container_test.KeeperB->github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + + + + + +github.com/cosmos/cosmos-sdk/container_test.MsgClientA + +github.com/cosmos/cosmos-sdk/container_test.MsgClientA + + + +github.com/cosmos/cosmos-sdk/container_test.MsgClientA->github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + + + + + +github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA->github.com/cosmos/cosmos-sdk/container_test.MsgClientA + + + + + diff --git a/container/testdata/example_error.dot b/container/testdata/example_error.dot index c52d59143083..c41e029b7e21 100644 --- a/container/testdata/example_error.dot +++ b/container/testdata/example_error.dot @@ -1,27 +1,27 @@ digraph "" { subgraph "cluster_a" { - graph [label="Module: a", penwidth="0.5", fontsize="12.0"]; - "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[fontcolor="dimgrey", shape="box", color="lightgrey", penwidth="0.5"]; + graph [label="Module: a", penwidth="0.5", fontsize="12.0", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[shape="box", color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; } subgraph "cluster_b" { - graph [fontsize="12.0", label="Module: b", penwidth="0.5"]; + graph [label="Module: b", penwidth="0.5", fontsize="12.0", style="rounded"]; "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[shape="box", color="red", penwidth="0.5", fontcolor="red"]; } subgraph "cluster_runtime" { - graph [label="Module: runtime", penwidth="0.5", fontsize="12.0"]; - "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + graph [label="Module: runtime", penwidth="0.5", fontsize="12.0", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[fontcolor="black", shape="box", color="black", penwidth="1.5"]; } - "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="auto-group"]; + "[]github.com/cosmos/cosmos-sdk/container_test.Command"[penwidth="0.5", fontcolor="dimgrey", comment="auto-group", color="lightgrey"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", penwidth="1.5", fontcolor="black"]; "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; - "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[fontcolor="black", color="black", penwidth="1.5"]; - "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[color="black", penwidth="1.5", fontcolor="black"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="red", penwidth="0.5", fontcolor="red"]; - "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[color="red", penwidth="0.5", fontcolor="red"]; - "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[shape="box", color="red", penwidth="0.5", fontcolor="red"]; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[penwidth="0.5", fontcolor="red", color="red"]; + "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[color="red", penwidth="0.5", fontcolor="red", shape="hexagon"]; "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="one-per-module"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"; "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"; diff --git a/container/testdata/example_error.svg b/container/testdata/example_error.svg new file mode 100644 index 000000000000..6897a23dc9da --- /dev/null +++ b/container/testdata/example_error.svg @@ -0,0 +1,179 @@ + + + + + + + + +cluster_a + +Module: a + + +cluster_b + +Module: b + + +cluster_runtime + +Module: runtime + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + + + + +[]github.com/cosmos/cosmos-sdk/container_test.Command + +[]github.com/cosmos/cosmos-sdk/container_test.Command + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide->[]github.com/cosmos/cosmos-sdk/container_test.Command + + + + + +github.com/cosmos/cosmos-sdk/container_test.KeeperA + +github.com/cosmos/cosmos-sdk/container_test.KeeperA + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide->github.com/cosmos/cosmos-sdk/container_test.KeeperA + + + + + + +map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + +map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide->map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + + + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide->[]github.com/cosmos/cosmos-sdk/container_test.Command + + + + + +github.com/cosmos/cosmos-sdk/container_test.KeeperB + +github.com/cosmos/cosmos-sdk/container_test.KeeperB + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide->github.com/cosmos/cosmos-sdk/container_test.KeeperB + + + + + +github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide->map[string]github.com/cosmos/cosmos-sdk/container_test.Handler + + + + + +github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey + +github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey + + + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey + + + +github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey->github.com/cosmos/cosmos-sdk/container_test.KVStoreKey + + + + + +github.com/cosmos/cosmos-sdk/container.ModuleKey + +github.com/cosmos/cosmos-sdk/container.ModuleKey + + + +github.com/cosmos/cosmos-sdk/container.ModuleKey->github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey + + + + + +github.com/cosmos/cosmos-sdk/container.OwnModuleKey + +github.com/cosmos/cosmos-sdk/container.OwnModuleKey + + + +github.com/cosmos/cosmos-sdk/container.OwnModuleKey->github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + + + + + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey->github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide + + + + + +github.com/cosmos/cosmos-sdk/container_test.KVStoreKey->github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + + + + + +github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + +github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + + + +github.com/cosmos/cosmos-sdk/container_test.KeeperB->github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + + + + + +github.com/cosmos/cosmos-sdk/container_test.MsgClientA + +github.com/cosmos/cosmos-sdk/container_test.MsgClientA + + + +github.com/cosmos/cosmos-sdk/container_test.MsgClientA->github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + + + + + From f3e21199b89ec9a787d9bf1d880ce01a03298bd5 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:21:50 -0400 Subject: [PATCH 07/14] update docs --- container/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/container/README.md b/container/README.md index 450b6c7d0d58..6874bfc1951f 100644 --- a/container/README.md +++ b/container/README.md @@ -18,14 +18,17 @@ be printed to stderr and a rendering of the dependency graph in Graphviz DOT for Here is an example Graphviz rendering of a successful build of a dependency graph: ![Graphviz Example](./testdata/example.svg) - Rectangles represent functions, ovals represent types, rounded rectangles represent modules and the single hexagon represents the function which called `Build`. Black-colored shapes mark functions and types that were called/resolved without an error. Gray-colored nodes mark functions and types that could have been called/resolved in the container but were left unused. - Here is an example Graphviz rendering of a dependency graph build which failed: ![Graphviz Error Example](./testdata/example_error.svg) +Graphviz DOT files can be converted into SVG's for viewing in a web browser using the `dot` command-line tool, ex: +``` +> dot -Tsvg debug_container.dot > debug_container.svg +``` +Many other tools including some IDEs support working with DOT files. \ No newline at end of file From f91bf8d63cc89762e37659de2cad3c1a237a4271 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:23:15 -0400 Subject: [PATCH 08/14] update docs --- container/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/README.md b/container/README.md index 6874bfc1951f..0fa6aa7cc6c2 100644 --- a/container/README.md +++ b/container/README.md @@ -1,4 +1,4 @@ -# Cosmos SDK Dependency Injection `container` Moduleo +# Cosmos SDK Dependency Injection `container` Module ## Overview From 01e1d446705d6e2bb6b354dcebf989685c5414b5 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:25:11 -0400 Subject: [PATCH 09/14] add log golden files --- container/testdata/.gitignore | 1 + container/testdata/example.log | 33 ++++++++++++++++++++++++++++ container/testdata/example_error.log | 30 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 container/testdata/.gitignore create mode 100644 container/testdata/example.log create mode 100644 container/testdata/example_error.log diff --git a/container/testdata/.gitignore b/container/testdata/.gitignore new file mode 100644 index 000000000000..720f7c3e616e --- /dev/null +++ b/container/testdata/.gitignore @@ -0,0 +1 @@ +!*.log \ No newline at end of file diff --git a/container/testdata/example.log b/container/testdata/example.log new file mode 100644 index 000000000000..56d18a4ff640 --- /dev/null +++ b/container/testdata/example.log @@ -0,0 +1,33 @@ +Initializing logger +Registering providers + Registering module-scoped provider: github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) + Registering resolver for module-scoped type container_test.MsgClientA + Registering module-scoped provider: github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) + Registering resolver for module-scoped type container_test.KVStoreKey + Registering github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide (/Users/arc/cosmos-sdk/container/container_test.go:55) + Registering resolver for simple type container_test.KeeperA + Registering resolver for one-per-module type container_test.Handler + Found resolver for container_test.Handler: *container.onePerModuleResolver + Registering resolver for auto-group type container_test.Command + Found resolver for container_test.Command: *container.groupResolver + Registering github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) + Registering resolver for simple type container_test.KeeperB + Found resolver for container_test.Command: *container.groupResolver + Found resolver for container_test.Handler: *container.onePerModuleResolver +Registering outputs + Registering github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:597) +Building container +Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:597) + Providing container_test.KeeperB from github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) to github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) + Providing container_test.KVStoreKey from github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) to github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) + Providing ModuleKey b + Calling github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) + Providing container_test.MsgClientA from github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) to github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) + Providing ModuleKey b + Calling github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) + Calling github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) +Calling github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:597) +Done building container diff --git a/container/testdata/example_error.log b/container/testdata/example_error.log new file mode 100644 index 000000000000..63454f815a00 --- /dev/null +++ b/container/testdata/example_error.log @@ -0,0 +1,30 @@ +Initializing logger +Registering providers + Registering module-scoped provider: github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) + Registering resolver for module-scoped type container_test.KVStoreKey + Registering github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide (/Users/arc/cosmos-sdk/container/container_test.go:55) + Registering resolver for simple type container_test.KeeperA + Registering resolver for one-per-module type container_test.Handler + Found resolver for container_test.Handler: *container.onePerModuleResolver + Registering resolver for auto-group type container_test.Command + Found resolver for container_test.Command: *container.groupResolver + Registering github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) + Registering resolver for simple type container_test.KeeperB + Found resolver for container_test.Command: *container.groupResolver + Found resolver for container_test.Handler: *container.onePerModuleResolver +Registering outputs + Registering github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:607) +Building container +Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:607) + Providing container_test.KeeperB from github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) to github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput + Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) + Providing container_test.KVStoreKey from github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) to github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide + Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) + Providing ModuleKey b + Calling github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) + Error: can't resolve type container_test.MsgClientA for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75): + while resolving: + container_test.MsgClientA for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) + container_test.KVStoreKey for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) + container_test.KeeperB for github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:607) + From f5e31a66ce76fdb286d0598ab3ac72f1bc5437c3 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:25:48 -0400 Subject: [PATCH 10/14] update makefile --- container/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/Makefile b/container/Makefile index 8156317101fa..a1d3020be707 100644 --- a/container/Makefile +++ b/container/Makefile @@ -1,4 +1,4 @@ -update-examples: +update-graphviz-examples: go test . -test.update-golden dot -Tsvg testdata/example.dot > testdata/example.svg dot -Tsvg testdata/example_error.dot > testdata/example_error.svg From af60b110325202726e4cf82e91fcde6a9262aec1 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:26:20 -0400 Subject: [PATCH 11/14] docs --- container/debug.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/debug.go b/container/debug.go index a923f9aff237..228418a4ac22 100644 --- a/container/debug.go +++ b/container/debug.go @@ -22,7 +22,7 @@ func StdoutLogger() DebugOption { }) } -// StderrLogger is a debug option which routes logging output to stderro. +// StderrLogger is a debug option which routes logging output to stderr. func StderrLogger() DebugOption { return Logger(func(s string) { _, _ = fmt.Fprintln(os.Stderr, s) From 2e97d164e0400bde83ffc29827a0ebd253a03bf5 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:26:55 -0400 Subject: [PATCH 12/14] docs --- container/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/Makefile b/container/Makefile index a1d3020be707..007395d63336 100644 --- a/container/Makefile +++ b/container/Makefile @@ -1,4 +1,4 @@ -update-graphviz-examples: +update-debug-examples: go test . -test.update-golden dot -Tsvg testdata/example.dot > testdata/example.svg dot -Tsvg testdata/example_error.dot > testdata/example_error.svg From 3c5c11bc7cd73b75a46f6a0325026b09df528474 Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Wed, 11 May 2022 13:27:10 -0400 Subject: [PATCH 13/14] docs --- container/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/Makefile b/container/Makefile index 007395d63336..8ac9e88558aa 100644 --- a/container/Makefile +++ b/container/Makefile @@ -1,4 +1,4 @@ -update-debug-examples: +update-testdata-examples: go test . -test.update-golden dot -Tsvg testdata/example.dot > testdata/example.svg dot -Tsvg testdata/example_error.dot > testdata/example_error.svg From db60d5e51d68c72082b3fe8e1a77634b332095fe Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Thu, 12 May 2022 12:13:42 -0400 Subject: [PATCH 14/14] fix/remove non-deterministic tests --- container/README.md | 2 +- container/container_test.go | 8 +------ container/internal/graphviz/attrs.go | 6 +++-- container/testdata/.gitignore | 1 - container/testdata/example.dot | 32 +++++++++++++-------------- container/testdata/example.log | 33 ---------------------------- container/testdata/example_error.dot | 30 ++++++++++++------------- container/testdata/example_error.log | 30 ------------------------- 8 files changed, 37 insertions(+), 105 deletions(-) delete mode 100644 container/testdata/.gitignore delete mode 100644 container/testdata/example.log delete mode 100644 container/testdata/example_error.log diff --git a/container/README.md b/container/README.md index 0fa6aa7cc6c2..7534573a655f 100644 --- a/container/README.md +++ b/container/README.md @@ -10,7 +10,7 @@ TODO ## Debugging -Issues with resolving dependencies in the container can be done with [logs](./testdata/example.log) +Issues with resolving dependencies in the container can be done with logs and [Graphviz](https://graphviz.org) renderings of the container tree. By default, whenever there is an error, logs will be printed to stderr and a rendering of the dependency graph in Graphviz DOT format will be saved to `debug_container.dot`. diff --git a/container/container_test.go b/container/container_test.go index 071c9b203c41..23ce2e9fd841 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -585,27 +585,21 @@ func TestDebugOptions(t *testing.T) { } func TestGraphAndLogOutput(t *testing.T) { - var logOut, graphOut string + var graphOut string var b KeeperB debugOpts := container.DebugOptions( - container.Logger(func(s string) { - logOut += s + "\n" - }), container.Visualizer(func(dotGraph string) { graphOut = dotGraph })) require.NoError(t, container.BuildDebug(debugOpts, scenarioConfig, &b)) - golden.Assert(t, logOut, "example.log") golden.Assert(t, graphOut, "example.dot") - logOut = "" badConfig := container.Options( container.ProvideInModule("runtime", ProvideKVStoreKey), container.ProvideInModule("a", wrapMethod0(ModuleA{})), container.ProvideInModule("b", wrapMethod0(ModuleB{})), ) require.Error(t, container.BuildDebug(debugOpts, badConfig, &b)) - golden.Assert(t, logOut, "example_error.log") golden.Assert(t, graphOut, "example_error.dot") } diff --git a/container/internal/graphviz/attrs.go b/container/internal/graphviz/attrs.go index 634e717cda3f..e9fba23da91e 100644 --- a/container/internal/graphviz/attrs.go +++ b/container/internal/graphviz/attrs.go @@ -3,6 +3,8 @@ package graphviz import ( "fmt" "strings" + + "github.com/cosmos/cosmos-sdk/container/internal/util" ) // Attributes represents a graphviz attributes map. @@ -51,8 +53,8 @@ func (a *Attributes) String() string { return "" } var attrStrs []string - for k, v := range a.attrs { - attrStrs = append(attrStrs, fmt.Sprintf("%s=%q", k, v)) + for _, k := range util.OrderedMapKeys(a.attrs) { + attrStrs = append(attrStrs, fmt.Sprintf("%s=%q", k, a.attrs[k])) } return fmt.Sprintf("[%s]", strings.Join(attrStrs, ", ")) } diff --git a/container/testdata/.gitignore b/container/testdata/.gitignore deleted file mode 100644 index 720f7c3e616e..000000000000 --- a/container/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!*.log \ No newline at end of file diff --git a/container/testdata/example.dot b/container/testdata/example.dot index a6ea7e343204..becdb39af35c 100644 --- a/container/testdata/example.dot +++ b/container/testdata/example.dot @@ -1,29 +1,29 @@ digraph "" { subgraph "cluster_a" { - graph [style="rounded", label="Module: a", penwidth="0.5", fontsize="12.0"]; - "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[shape="box", color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; + graph [fontsize="12.0", label="Module: a", penwidth="0.5", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[color="lightgrey", fontcolor="dimgrey", penwidth="0.5", shape="box"]; } subgraph "cluster_b" { - graph [label="Module: b", penwidth="0.5", fontsize="12.0", style="rounded"]; - "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[penwidth="1.5", fontcolor="black", shape="box", color="black"]; + graph [fontsize="12.0", label="Module: b", penwidth="0.5", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[color="black", fontcolor="black", penwidth="1.5", shape="box"]; } subgraph "cluster_runtime" { - graph [label="Module: runtime", penwidth="0.5", fontsize="12.0", style="rounded"]; - "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; + graph [fontsize="12.0", label="Module: runtime", penwidth="0.5", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[color="black", fontcolor="black", penwidth="1.5", shape="box"]; } - "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="auto-group"]; - "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; - "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[penwidth="1.5", fontcolor="black", color="black"]; - "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; - "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"[shape="box", color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[shape="hexagon", color="black", penwidth="1.5", fontcolor="black"]; - "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[penwidth="0.5", fontcolor="dimgrey", comment="one-per-module", color="lightgrey"]; + "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", comment="auto-group", fontcolor="dimgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", fontcolor="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[color="lightgrey", fontcolor="dimgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[color="black", fontcolor="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[color="lightgrey", fontcolor="dimgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="black", fontcolor="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[color="black", fontcolor="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"[color="black", fontcolor="black", penwidth="1.5", shape="box"]; + "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[color="black", fontcolor="black", penwidth="1.5", shape="hexagon"]; + "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", comment="one-per-module", fontcolor="dimgrey", penwidth="0.5"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA"; "github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA" -> "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"; diff --git a/container/testdata/example.log b/container/testdata/example.log deleted file mode 100644 index 56d18a4ff640..000000000000 --- a/container/testdata/example.log +++ /dev/null @@ -1,33 +0,0 @@ -Initializing logger -Registering providers - Registering module-scoped provider: github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) - Registering resolver for module-scoped type container_test.MsgClientA - Registering module-scoped provider: github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) - Registering resolver for module-scoped type container_test.KVStoreKey - Registering github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide (/Users/arc/cosmos-sdk/container/container_test.go:55) - Registering resolver for simple type container_test.KeeperA - Registering resolver for one-per-module type container_test.Handler - Found resolver for container_test.Handler: *container.onePerModuleResolver - Registering resolver for auto-group type container_test.Command - Found resolver for container_test.Command: *container.groupResolver - Registering github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) - Registering resolver for simple type container_test.KeeperB - Found resolver for container_test.Command: *container.groupResolver - Found resolver for container_test.Handler: *container.onePerModuleResolver -Registering outputs - Registering github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:597) -Building container -Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:597) - Providing container_test.KeeperB from github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) to github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput - Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) - Providing container_test.KVStoreKey from github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) to github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide - Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) - Providing ModuleKey b - Calling github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) - Providing container_test.MsgClientA from github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) to github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide - Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) - Providing ModuleKey b - Calling github.com/cosmos/cosmos-sdk/container_test.ProvideMsgClientA (/Users/arc/cosmos-sdk/container/container_test.go:50) - Calling github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) -Calling github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:597) -Done building container diff --git a/container/testdata/example_error.dot b/container/testdata/example_error.dot index c41e029b7e21..277afae1d49b 100644 --- a/container/testdata/example_error.dot +++ b/container/testdata/example_error.dot @@ -1,28 +1,28 @@ digraph "" { subgraph "cluster_a" { - graph [label="Module: a", penwidth="0.5", fontsize="12.0", style="rounded"]; - "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[shape="box", color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; + graph [fontsize="12.0", label="Module: a", penwidth="0.5", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"[color="lightgrey", fontcolor="dimgrey", penwidth="0.5", shape="box"]; } subgraph "cluster_b" { - graph [label="Module: b", penwidth="0.5", fontsize="12.0", style="rounded"]; - "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[shape="box", color="red", penwidth="0.5", fontcolor="red"]; + graph [fontsize="12.0", label="Module: b", penwidth="0.5", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide"[color="red", fontcolor="red", penwidth="0.5", shape="box"]; } subgraph "cluster_runtime" { - graph [label="Module: runtime", penwidth="0.5", fontsize="12.0", style="rounded"]; - "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[fontcolor="black", shape="box", color="black", penwidth="1.5"]; + graph [fontsize="12.0", label="Module: runtime", penwidth="0.5", style="rounded"]; + "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"[color="black", fontcolor="black", penwidth="1.5", shape="box"]; } - "[]github.com/cosmos/cosmos-sdk/container_test.Command"[penwidth="0.5", fontcolor="dimgrey", comment="auto-group", color="lightgrey"]; - "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey"]; - "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[color="black", penwidth="1.5", fontcolor="black"]; - "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[fontcolor="dimgrey", color="lightgrey", penwidth="0.5"]; - "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="red", penwidth="0.5", fontcolor="red"]; - "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[penwidth="0.5", fontcolor="red", color="red"]; - "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[color="red", penwidth="0.5", fontcolor="red", shape="hexagon"]; - "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", penwidth="0.5", fontcolor="dimgrey", comment="one-per-module"]; + "[]github.com/cosmos/cosmos-sdk/container_test.Command"[color="lightgrey", comment="auto-group", fontcolor="dimgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container.ModuleKey"[color="black", fontcolor="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container.OwnModuleKey"[color="lightgrey", fontcolor="dimgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"[color="black", fontcolor="black", penwidth="1.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperA"[color="lightgrey", fontcolor="dimgrey", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.KeeperB"[color="red", fontcolor="red", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.MsgClientA"[color="red", fontcolor="red", penwidth="0.5"]; + "github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput"[color="red", fontcolor="red", penwidth="0.5", shape="hexagon"]; + "map[string]github.com/cosmos/cosmos-sdk/container_test.Handler"[color="lightgrey", comment="one-per-module", fontcolor="dimgrey", penwidth="0.5"]; "github.com/cosmos/cosmos-sdk/container.ModuleKey" -> "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey"; "github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey"; "github.com/cosmos/cosmos-sdk/container_test.KVStoreKey" -> "github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide"; diff --git a/container/testdata/example_error.log b/container/testdata/example_error.log deleted file mode 100644 index 63454f815a00..000000000000 --- a/container/testdata/example_error.log +++ /dev/null @@ -1,30 +0,0 @@ -Initializing logger -Registering providers - Registering module-scoped provider: github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) - Registering resolver for module-scoped type container_test.KVStoreKey - Registering github.com/cosmos/cosmos-sdk/container_test.ModuleA.Provide (/Users/arc/cosmos-sdk/container/container_test.go:55) - Registering resolver for simple type container_test.KeeperA - Registering resolver for one-per-module type container_test.Handler - Found resolver for container_test.Handler: *container.onePerModuleResolver - Registering resolver for auto-group type container_test.Command - Found resolver for container_test.Command: *container.groupResolver - Registering github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) - Registering resolver for simple type container_test.KeeperB - Found resolver for container_test.Command: *container.groupResolver - Found resolver for container_test.Handler: *container.onePerModuleResolver -Registering outputs - Registering github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:607) -Building container -Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:607) - Providing container_test.KeeperB from github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) to github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput - Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) - Providing container_test.KVStoreKey from github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) to github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide - Resolving dependencies for github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) - Providing ModuleKey b - Calling github.com/cosmos/cosmos-sdk/container_test.ProvideKVStoreKey (/Users/arc/cosmos-sdk/container/container_test.go:46) - Error: can't resolve type container_test.MsgClientA for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75): - while resolving: - container_test.MsgClientA for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) - container_test.KVStoreKey for github.com/cosmos/cosmos-sdk/container_test.ModuleB.Provide (/Users/arc/cosmos-sdk/container/container_test.go:75) - container_test.KeeperB for github.com/cosmos/cosmos-sdk/container_test.TestGraphAndLogOutput (/Users/arc/cosmos-sdk/container/container_test.go:607) -