From 9e72ef1a1f29af6aaf9d0f8512adcba9b1a61dd5 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Wed, 23 Sep 2015 06:20:27 +0000 Subject: [PATCH] Remove pseudo nodes that are made unconnected by filtering. --- render/render.go | 15 +++++++++++ render/render_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/render/render.go b/render/render.go index 52b693e62e..0222c4fcea 100644 --- a/render/render.go +++ b/render/render.go @@ -178,10 +178,12 @@ type Filter struct { // Render implements Renderer func (f Filter) Render(rpt report.Report) RenderableNodes { output := RenderableNodes{} + inDegrees := map[string]int{} for id, node := range f.Renderer.Render(rpt) { if f.FilterFunc(node) { output[id] = node } + inDegrees[id] = 0 } // Deleted nodes also need to be cut as destinations in adjacency lists. @@ -190,11 +192,24 @@ func (f Filter) Render(rpt report.Report) RenderableNodes { for _, dstID := range node.Adjacency { if _, ok := output[dstID]; ok { newAdjacency = newAdjacency.Add(dstID) + inDegrees[dstID]++ } } node.Adjacency = newAdjacency output[id] = node } + + // Remove unconnected pseudo nodes, see #483. + for id, inDegree := range inDegrees { + if inDegree > 0 { + continue + } + node := output[id] + if !node.Pseudo || len(node.Adjacency) > 0 { + continue + } + delete(output, id) + } return output } diff --git a/render/render_test.go b/render/render_test.go index ab30ba032c..3275e1293c 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -212,4 +212,65 @@ func TestFilterRender2(t *testing.T) { } } +func TestFilterUnconnectedPesudoNodes(t *testing.T) { + // Test pseudo nodes that are made unconnected by filtering + // are also removed. + { + nodes := render.RenderableNodes{ + "foo": {ID: "foo", Node: report.MakeNode().WithAdjacent("bar")}, + "bar": {ID: "bar", Node: report.MakeNode().WithAdjacent("baz")}, + "baz": {ID: "baz", Node: report.MakeNode(), Pseudo: true}, + } + renderer := render.Filter{ + FilterFunc: func(node render.RenderableNode) bool { + return true + }, + Renderer: mockRenderer{RenderableNodes: nodes}, + } + want := nodes.Prune() + have := renderer.Render(report.MakeReport()).Prune() + if !reflect.DeepEqual(want, have) { + t.Error(test.Diff(want, have)) + } + } + { + renderer := render.Filter{ + FilterFunc: func(node render.RenderableNode) bool { + return node.ID != "bar" + }, + Renderer: mockRenderer{RenderableNodes: render.RenderableNodes{ + "foo": {ID: "foo", Node: report.MakeNode().WithAdjacent("bar")}, + "bar": {ID: "bar", Node: report.MakeNode().WithAdjacent("baz")}, + "baz": {ID: "baz", Node: report.MakeNode(), Pseudo: true}, + }}, + } + want := render.RenderableNodes{ + "foo": {ID: "foo", Origins: report.IDList{}, Node: report.MakeNode()}, + } + have := renderer.Render(report.MakeReport()).Prune() + if !reflect.DeepEqual(want, have) { + t.Error(test.Diff(want, have)) + } + } + { + renderer := render.Filter{ + FilterFunc: func(node render.RenderableNode) bool { + return node.ID != "bar" + }, + Renderer: mockRenderer{RenderableNodes: render.RenderableNodes{ + "foo": {ID: "foo", Node: report.MakeNode()}, + "bar": {ID: "bar", Node: report.MakeNode().WithAdjacent("foo")}, + "baz": {ID: "baz", Node: report.MakeNode().WithAdjacent("bar"), Pseudo: true}, + }}, + } + want := render.RenderableNodes{ + "foo": {ID: "foo", Origins: report.IDList{}, Node: report.MakeNode()}, + } + have := renderer.Render(report.MakeReport()).Prune() + if !reflect.DeepEqual(want, have) { + t.Error(test.Diff(want, have)) + } + } +} + func newu64(value uint64) *uint64 { return &value }