From 7a31a28e243c61c42d344d42ed70361f0349e91b Mon Sep 17 00:00:00 2001 From: Aaron Craelius Date: Thu, 12 May 2022 13:00:55 -0400 Subject: [PATCH] refactor(container)!: remove dependency on C graphviz (#11934) ## Description Closes: #11925 This replace the dependency on https://pkg.go.dev/github.com/goccy/go-graphviz which wraps the whole C Graphviz library and is causing ARM build problems in #11924 and generally probably shouldn't be used because it's a heavyweight dependency just used for debugging. It adds: * a custom `graphviz` package that does just what we need for `container` * updates to graphviz rendering to make it nicer and a README with some examples * golden tests for graphviz and log debugging * a `StderrLogger` `DebugOption` which is now the default for `Debug`/`AutoDebug` --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- container/Makefile | 4 + container/README.md | 34 +++++ container/build.go | 1 + container/container.go | 70 +++------- container/container_test.go | 40 ++++-- container/debug.go | 165 +++++++++------------- container/go.mod | 7 +- container/go.sum | 40 ++++-- container/group.go | 8 +- container/internal/graphviz/attrs.go | 60 ++++++++ container/internal/graphviz/docs.go | 4 + container/internal/graphviz/edge.go | 17 +++ container/internal/graphviz/graph.go | 156 +++++++++++++++++++++ container/internal/graphviz/node.go | 17 +++ container/internal/util/util.go | 26 ++++ container/module_dep.go | 6 +- container/one_per_module.go | 8 +- container/resolver.go | 4 +- container/simple.go | 6 +- container/supply.go | 6 +- container/testdata/example.dot | 43 ++++++ container/testdata/example.svg | 197 +++++++++++++++++++++++++++ container/testdata/example_error.dot | 40 ++++++ container/testdata/example_error.svg | 179 ++++++++++++++++++++++++ 24 files changed, 943 insertions(+), 195 deletions(-) create mode 100644 container/Makefile create mode 100644 container/README.md create mode 100644 container/internal/graphviz/attrs.go create mode 100644 container/internal/graphviz/docs.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 create mode 100644 container/internal/util/util.go create mode 100644 container/testdata/example.dot create mode 100644 container/testdata/example.svg create mode 100644 container/testdata/example_error.dot create mode 100644 container/testdata/example_error.svg diff --git a/container/Makefile b/container/Makefile new file mode 100644 index 000000000000..8ac9e88558aa --- /dev/null +++ b/container/Makefile @@ -0,0 +1,4 @@ +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 diff --git a/container/README.md b/container/README.md new file mode 100644 index 000000000000..7534573a655f --- /dev/null +++ b/container/README.md @@ -0,0 +1,34 @@ +# Cosmos SDK Dependency Injection `container` Module + +## Overview + +TODO + +## Usage + +TODO + +## Debugging + +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`. + +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 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 deb52dc51f8f..76df8bedfa9c 100644 --- a/container/container.go +++ b/container/container.go @@ -5,8 +5,9 @@ import ( "fmt" "reflect" - "github.com/goccy/go-graphviz/cgraph" "github.com/pkg/errors" + + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) type container struct { @@ -38,10 +39,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,17 +86,13 @@ func (c *container) getResolver(typ reflect.Type) (resolver, error) { elemType = elemType.Elem() } - var typeGraphNode *cgraph.Node - var err error + var typeGraphNode *graphviz.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 = c.typeGraphNode(sliceType) typeGraphNode.SetComment("auto-group") r := &groupResolver{ @@ -112,10 +107,7 @@ 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 = c.typeGraphNode(mapType) typeGraphNode.SetComment("one-per-module") r := &onePerModuleResolver{ @@ -136,11 +128,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 +152,11 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter return nil, err } - var typeGraphNode *cgraph.Node + var typeGraphNode *graphviz.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 +203,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 +244,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 +262,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 +284,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 { @@ -392,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 { @@ -443,10 +414,13 @@ func (c container) formatResolveStack() string { return buf.String() } -func markGraphNodeAsUsed(node *cgraph.Node) { +func markGraphNodeAsUsed(node *graphviz.Node) { node.SetColor("black") + node.SetPenWidth("1.5") + node.SetFontColor("black") } -func markGraphNodeAsFailed(node *cgraph.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 e393b91e2439..23ce2e9fd841 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, @@ -539,7 +542,7 @@ func TestStructArgs(t *testing.T) { )) } -func TestLogging(t *testing.T) { +func TestDebugOptions(t *testing.T) { var logOut string var dotGraph string @@ -563,7 +566,7 @@ func TestLogging(t *testing.T) { dotGraph = g }), container.LogVisualizer(), - container.FileVisualizer(graphfile.Name(), "svg"), + container.FileVisualizer(graphfile.Name()), container.StdoutLogger(), ), container.Options(), @@ -578,7 +581,26 @@ func TestLogging(t *testing.T) { graphfileContents, err := os.ReadFile(graphfile.Name()) require.NoError(t, err) - require.Contains(t, string(graphfileContents), " %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..d2ae90545f48 --- /dev/null +++ b/container/internal/graphviz/graph.go @@ -0,0 +1,156 @@ +// Package graphviz +package graphviz + +import ( + "bytes" + "fmt" + "io" + + "github.com/cosmos/cosmos-sdk/container/internal/util" +) + +// Graph represents a graphviz digraph. +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 +} + +// NewGraph creates a new Graph instance. +func NewGraph() *Graph { + return &Graph{ + Attributes: NewAttributes(), + name: "", + parent: nil, + allNodes: map[string]*Node{}, + myNodes: map[string]*Node{}, + subgraphs: map[string]*Graph{}, + edges: nil, + } +} + +// 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 + } + + node = &Node{ + Attributes: NewAttributes(), + name: name, + } + g.allNodes[name] = node + g.myNodes[name] = node + return node, false +} + +// 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, true + } + + 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, false +} + +// CreateEdge creates a new graphviz edge. +func (g *Graph) CreateEdge(from, to *Node) *Edge { + edge := &Edge{ + Attributes: NewAttributes(), + from: from, + to: to, + } + g.edges = append(g.edges, edge) + return edge +} + +// RenderDOT renders the graph to DOT format. +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 + } + } + + // 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 + } + + 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 { + err := edge.render(w, subIndent) + if err != nil { + return err + } + } + } + + _, err := fmt.Fprintf(w, "%s}\n\n", indent) + return err +} + +// String returns the graph in DOT format. +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..d464aad869f8 --- /dev/null +++ b/container/internal/graphviz/node.go @@ -0,0 +1,17 @@ +package graphviz + +import ( + "fmt" + "io" +) + +// Node represents a graphviz node. +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/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/module_dep.go b/container/module_dep.go index 7aecc9c1d118..261d3afed79d 100644 --- a/container/module_dep.go +++ b/container/module_dep.go @@ -3,7 +3,7 @@ package container import ( "reflect" - "github.com/goccy/go-graphviz/cgraph" + "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 *cgraph.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() *cgraph.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 e5cab4fb63f7..9c525eed6994 100644 --- a/container/one_per_module.go +++ b/container/one_per_module.go @@ -4,9 +4,9 @@ import ( "fmt" "reflect" - "github.com/goccy/go-graphviz/cgraph" - "github.com/pkg/errors" + + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) // OnePerModuleType marks a type which @@ -34,7 +34,7 @@ type onePerModuleResolver struct { idxMap map[*moduleKey]int resolved bool values reflect.Value - graphNode *cgraph.Node + graphNode *graphviz.Node } type mapOfOnePerModuleResolver struct { @@ -101,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() *cgraph.Node { +func (o onePerModuleResolver) typeGraphNode() *graphviz.Node { return o.graphNode } diff --git a/container/resolver.go b/container/resolver.go index 7583358e042f..3e573556fab1 100644 --- a/container/resolver.go +++ b/container/resolver.go @@ -3,12 +3,12 @@ package container import ( "reflect" - "github.com/goccy/go-graphviz/cgraph" + "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() *cgraph.Node + typeGraphNode() *graphviz.Node } diff --git a/container/simple.go b/container/simple.go index e62744c6084f..da9d35343e1b 100644 --- a/container/simple.go +++ b/container/simple.go @@ -3,7 +3,7 @@ package container import ( "reflect" - "github.com/goccy/go-graphviz/cgraph" + "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 *cgraph.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() *cgraph.Node { +func (s simpleResolver) typeGraphNode() *graphviz.Node { return s.graphNode } diff --git a/container/supply.go b/container/supply.go index d75ad5980398..e6a1db0b5026 100644 --- a/container/supply.go +++ b/container/supply.go @@ -3,14 +3,14 @@ package container import ( "reflect" - "github.com/goccy/go-graphviz/cgraph" + "github.com/cosmos/cosmos-sdk/container/internal/graphviz" ) type supplyResolver struct { typ reflect.Type value reflect.Value loc Location - graphNode *cgraph.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() *cgraph.Node { +func (s supplyResolver) typeGraphNode() *graphviz.Node { return s.graphNode } diff --git a/container/testdata/example.dot b/container/testdata/example.dot new file mode 100644 index 000000000000..becdb39af35c --- /dev/null +++ b/container/testdata/example.dot @@ -0,0 +1,43 @@ +digraph "" { + subgraph "cluster_a" { + 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 [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 [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", 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"; + "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"; +} + 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 new file mode 100644 index 000000000000..277afae1d49b --- /dev/null +++ b/container/testdata/example_error.dot @@ -0,0 +1,40 @@ +digraph "" { + subgraph "cluster_a" { + 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 [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 [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", 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"; + "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"; +} + 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 + + + + +