diff --git a/d2plugin/plugin_features.go b/d2plugin/plugin_features.go index 20bdb4d95..76e1825b4 100644 --- a/d2plugin/plugin_features.go +++ b/d2plugin/plugin_features.go @@ -55,6 +55,16 @@ func FeatureSupportCheck(info *PluginInfo, g *d2graph.Graph) error { } if _, ok := featureMap[DESCENDANT_EDGES]; !ok { for _, e := range g.Edges { + // descendant edges are ok in sequence diagrams + if e.Src.OuterSequenceDiagram() != nil || e.Dst.OuterSequenceDiagram() != nil { + continue + } + if !e.Src.IsContainer() && !e.Dst.IsContainer() { + continue + } + if e.Src == e.Dst { + return fmt.Errorf(`Connection "%s" is a self loop on a container, but layout engine "%s" does not support this.`, e.AbsID(), info.Name) + } if e.Src.IsDescendantOf(e.Dst) || e.Dst.IsDescendantOf(e.Src) { return fmt.Errorf(`Connection "%s" goes from a container to a descendant, but layout engine "%s" does not support this.`, e.AbsID(), info.Name) } diff --git a/e2etests/e2e_test.go b/e2etests/e2e_test.go index ab4059cd4..0f3ba0ac8 100644 --- a/e2etests/e2e_test.go +++ b/e2etests/e2e_test.go @@ -24,6 +24,7 @@ import ( "oss.terrastruct.com/d2/d2layouts/d2near" "oss.terrastruct.com/d2/d2layouts/d2sequence" "oss.terrastruct.com/d2/d2lib" + "oss.terrastruct.com/d2/d2plugin" "oss.terrastruct.com/d2/d2renderers/d2svg" "oss.terrastruct.com/d2/d2target" "oss.terrastruct.com/d2/lib/log" @@ -73,12 +74,14 @@ a -> c } type testCase struct { - name string - script string - mtexts []*d2target.MText - assertions func(t *testing.T, diagram *d2target.Diagram) - skip bool - expErr string + name string + script string + mtexts []*d2target.MText + assertions func(t *testing.T, diagram *d2target.Diagram) + skip bool + dagreFeatureError string + elkFeatureError string + expErr string } func runa(t *testing.T, tcs []testCase) { @@ -136,16 +139,20 @@ func run(t *testing.T, tc testCase) { for _, layoutName := range layoutsTested { var layout func(context.Context, *d2graph.Graph) error + var plugin d2plugin.Plugin if layoutName == "dagre" { layout = d2dagrelayout.DefaultLayout + plugin = &d2plugin.DagrePlugin } else if layoutName == "elk" { // If measured texts exists, we are specifically exercising text measurements, no need to run on both layouts if tc.mtexts != nil { continue } layout = d2elklayout.DefaultLayout + plugin = &d2plugin.ELKPlugin } - diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{ + + diagram, g, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{ Ruler: ruler, MeasuredTexts: tc.mtexts, ThemeID: 0, @@ -160,6 +167,26 @@ func run(t *testing.T, tc testCase) { assert.Success(t, err) } + pluginInfo, err := plugin.Info(ctx) + assert.Success(t, err) + + err = d2plugin.FeatureSupportCheck(pluginInfo, g) + switch layoutName { + case "dagre": + if tc.dagreFeatureError != "" { + assert.Error(t, err) + assert.ErrorString(t, err, tc.dagreFeatureError) + return + } + case "elk": + if tc.elkFeatureError != "" { + assert.Error(t, err) + assert.ErrorString(t, err, tc.elkFeatureError) + return + } + } + assert.Success(t, err) + if tc.assertions != nil { t.Run("assertions", func(t *testing.T) { tc.assertions(t, diagram) diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index 169d1ad1e..615dff3ec 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -448,6 +448,7 @@ eee.shape: document eee <- aaa.ccc (eee <- aaa.ccc)[0]: '222' `, + dagreFeatureError: `Connection "(aaa.ccc -- aaa)[0]" goes from a container to a descendant, but layout engine "dagre" does not support this.`, }, { name: "chaos2", @@ -1792,6 +1793,7 @@ c: { a } `, + dagreFeatureError: `Object "a" has attribute "width" and/or "height" set, but layout engine "dagre" does not support dimensions set on containers.`, }, { name: "crow_foot_arrowhead", diff --git a/e2etests/todo_test.go b/e2etests/todo_test.go index 5d5953cdd..17903edbd 100644 --- a/e2etests/todo_test.go +++ b/e2etests/todo_test.go @@ -14,12 +14,14 @@ func testTodo(t *testing.T) { container.first -> container.second: 1->2 container -> container.second: c->2 `, + dagreFeatureError: `Connection "(container -> container.second)[0]" goes from a container to a descendant, but layout engine "dagre" does not support this.`, }, { name: "child_parent_edges", script: `a.b -> a a.b -> a.b.c a.b.c.d -> a.b`, + dagreFeatureError: `Connection "(a.b -> a)[0]" goes from a container to a descendant, but layout engine "dagre" does not support this.`, }, { name: "container_label_loop", @@ -27,6 +29,7 @@ a.b.c.d -> a.b`, b -> c } a -> a`, + dagreFeatureError: `Connection "(a -> a)[0]" is a self loop on a container, but layout engine "dagre" does not support this.`, }, { // as nesting gets deeper, the groups advance towards `c` and may overlap its lifeline