boolean link(ExecNode<?> from, ExecNode<?> to) {
        TopologyNode fromNode = getOrCreateTopologyNode(from);
        TopologyNode toNode = getOrCreateTopologyNode(to);

        if (canReach(toNode, fromNode)) {
            // invalid edge, as `to` is the predecessor of `from`
            return false;
        } else {
            // link `from` and `to`
            fromNode.outputs.add(toNode);
            toNode.inputs.add(fromNode);
            return true;
        }
    }
@Test
    void testLink() {
        Tuple2<TopologyGraph, TestingBatchExecNode[]> tuple2 = buildTopologyGraph();
        TopologyGraph graph = tuple2.f0;
        TestingBatchExecNode[] nodes = tuple2.f1;

        assertThat(graph.link(nodes[2], nodes[4])).isTrue();
        assertThat(graph.link(nodes[3], nodes[5])).isTrue();
        assertThat(graph.link(nodes[5], nodes[6])).isTrue();
        assertThat(graph.link(nodes[7], nodes[2])).isFalse();
        assertThat(graph.link(nodes[7], nodes[4])).isFalse();
        assertThat(graph.link(nodes[0], nodes[7])).isTrue();
    }