public static String printLogical(List<PlanFragment> fragments, FunctionAndTypeManager functionAndTypeManager, Session session)
    {
        Map<PlanFragmentId, PlanFragment> fragmentsById = Maps.uniqueIndex(fragments, PlanFragment::getId);
        PlanNodeIdGenerator idGenerator = new PlanNodeIdGenerator();

        StringBuilder output = new StringBuilder();
        output.append("digraph logical_plan {\n");

        for (PlanFragment fragment : fragments) {
            printFragmentNodes(output, fragment, idGenerator, functionAndTypeManager, session);
        }

        for (PlanFragment fragment : fragments) {
            fragment.getRoot().accept(new EdgePrinter(output, fragmentsById, idGenerator), null);
        }

        output.append("}\n");

        return output.toString();
    }
@Test
    public void testPrintLogical()
    {
        String actual = printLogical(
                ImmutableList.of(createTestPlanFragment(0, TEST_TABLE_SCAN_NODE)),
                FUNCTION_AND_TYPE_MANAGER,
                testSessionBuilder().build());
        String expected = join(
                System.lineSeparator(),
                "digraph logical_plan {",
                "subgraph cluster_0 {",
                "label = \"SOURCE\"",
                format("plannode_1[%s];", TEST_TABLE_SCAN_NODE_INNER_OUTPUT),
                "}",
                "}",
                "");
        assertEquals(actual, expected);
    }