How We Solved Large-Scale Call Graph Visualization with AntV G6

Facing performance and layout challenges when visualizing thousands of function call nodes, we iterated through Echarts, Ant Design Charts, and finally adopted AntV G6, achieving smooth interaction, automatic layout, and scalable coverage mapping for our link coverage tool.

转转QA
转转QA
转转QA
How We Solved Large-Scale Call Graph Visualization with AntV G6

Introduction

In daily development testing, code coverage is a core metric for test completeness. The existing line‑level coverage tools are insufficient for our needs; we require a link‑coverage view that shows the call chain of interfaces and the coverage status of involved files.

Requirements from the product side can be summarized as:

Clearly display function call relationships.

Show coverage details on click.

Provide as many features as possible.

Other flexible requirements.

Understanding the Real Requirements

We initially assumed the task was simply drawing a flowchart, but the real needs are threefold:

Present the entire system's function call network, not isolated nodes.

Visually indicate function coverage to quickly locate target functions.

Enable click and hover interactions to display different coverage information.

These translate into three technical challenges:

Handling large‑scale data (performance).

Automatically arranging nodes (layout).

Providing smooth user interaction (UX).

First Attempt: Echarts – A Failure

Tree Diagram

Echarts tree layout only supports hierarchical data, which cannot represent many‑to‑many relationships in our call graph, so this approach was abandoned.

Graph

Using the Graph component required explicit x, y coordinates, which we wanted to avoid. We tried a force‑directed layout.

series: [{
  type: 'graph',
  layout: 'none'
}]

Force layout worked for a few dozen nodes but collapsed into an unreadable mass when the node count reached 500.

Force‑Directed Layout

series: [{
  type: 'graph',
  layout: 'force', // force‑directed layout
  force: { repulsion: 100 } // node repulsion
}]

The result was acceptable for small datasets but unsuitable for large ones.

Second Attempt: Ant Design Charts – Still Stuck

We switched to Ant Design Charts' FlowGraph component, which integrates well with our AntD UI library.

<FlowGraph
  data={data}
  nodeConfig={{...}}
  edgeConfig={{...}}
/>

While the visual appearance improved, two major issues emerged:

With large data, updating a node caused the entire canvas to re‑render, leading to noticeable lag.

The product demanded a minimap and toolbar, which were difficult to embed seamlessly.

Final Solution: AntV G6

After two unsatisfactory trials, we evaluated AntV G6, a dedicated graph visualization engine.

Problem 1 – Performance with 1000+ Nodes

We tested G6 with over 1000 nodes. Using single‑node updates kept the UI responsive, with smooth dragging and zooming.

const graph = new G6.Graph({
  container: 'container',
  layout: {
    type: 'antv-dagre',
    rankdir: 'LR',
    ranksep: 100, // layer spacing
    nodesep: 10   // node spacing
  },
  // other configurations...
});

Problem 2 – Automatic Node Arrangement

Use antv-dagre layout to arrange the call chain from left to right.

Adjust ranksep and nodesep to control spacing.

Problem 3 – Smooth Interaction

Key interactive features implemented:

Node Highlight : Clicking a function highlights the node and shows line‑coverage details in a popup.

// Update node state
graph.updateNodeData([{ id, states: ['selected'] }]);
// Focus node in viewport
graph.focusElement(id);
// Redraw chart
graph.draw();
showDetailPanel(id);

Hovering over a node highlights its call path and displays detailed coverage.

Color Coding : Nodes are colored based on coverage status (yellow for selected, green for 100% covered, blue for partially covered, red for uncovered).

const COVERAGE_COLORS = {
  selected: '#fadb14', // yellow
  full: '#52c41a',      // green
  partial: '#1677ff',   // blue
  none: '#f5222d'       // red
};

const getNodeColor = (status, isSelected, isActive) => {
  let baseColor = COVERAGE_COLORS[status];
  if (isSelected) baseColor = COVERAGE_COLORS.selected;
  const alpha = isActive ? '' : '80';
  return baseColor + alpha;
};

Expand/Collapse : Large sub‑trees can be collapsed to keep the view compact.

const { edges, nodes } = graph.getData();
const nodeList = getDescendants(nodeId);
nodes.forEach(node => {
  if (nodeList.includes(node.id)) {
    onCollapseNode({ nodeId: node.id, type: 'node' });
  }
});
edges.forEach(edge => {
  if (nodeList.includes(edge.target)) {
    onCollapseNode({ nodeId: edge.id, type: 'edge' });
  }
});

const onCollapseNode = ({ type = '', nodeId = '' }) => {
  // toggle visibility of node/edge and its descendants
};

Additional features such as canvas dragging, zooming, minimap, toolbar, and tooltip were added to enhance the user experience.

const graph = new G6.Graph({
  behaviors: [
    'drag-canvas',
    'zoom-canvas',
    { type: 'hover-activate', degree: 1 }
  ],
  plugins: [
    { type: 'minimap' },
    { type: 'toolbar' },
    { type: 'tooltip' }
  ]
});

Summary

Lessons Learned

Conduct thorough multi‑dimensional research before selecting a tool to avoid blind reliance on familiar libraries.

Prioritize performance over feature richness; a smooth UI is essential for complex graph scenarios.

Future Plans

Integrate link‑coverage analysis with daily code changes so that each commit can be correlated with affected call paths, enabling precise risk assessment of code modifications.

Author: Liu Xiaoyu, Test Development Engineer at ZuanZuan
visualizationcoveragegraphantv-g6
转转QA
Written by

转转QA

In the era of knowledge sharing, discover 转转QA from a new perspective.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.