From ac38b5d2eeaec1cf31932b9591b15021794633b6 Mon Sep 17 00:00:00 2001 From: Magnus Jacobsson Date: Tue, 5 Jul 2022 22:45:20 +0200 Subject: [PATCH] add new test_subgraphs test Upcoming commits in this series will make changes to gvFreeLayout and we want to ensure that those changes don't introduce any memory leaks when run with ASan, since gvFreeLayout handles cleanup of both subgraphs and the root graph. This test is run in CI with ASan leak detection enabled. Towards https://gitlab.com/graphviz/graphviz/-/issues/1800. --- tests/CMakeLists.txt | 1 + tests/test_subgraphs.cpp | 87 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 tests/test_subgraphs.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb3492885..0f9c5b029 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -73,3 +73,4 @@ CREATE_TEST(GVContext_render_svg) CREATE_TEST(GVLayout_construction) CREATE_TEST(GVLayout_render) CREATE_TEST(simple) +CREATE_TEST(subgraphs) diff --git a/tests/test_subgraphs.cpp b/tests/test_subgraphs.cpp new file mode 100644 index 000000000..cf10c8b60 --- /dev/null +++ b/tests/test_subgraphs.cpp @@ -0,0 +1,87 @@ +#include + +#include +#include + +#include +#include + +#include "svg_analyzer.h" + +TEST_CASE("subgraphs in directed and undirected graphs with different layout " + "engines") { + const auto directed_graph = GENERATE(false, true); + + const std::string graph_type = directed_graph ? "digraph" : "graph"; + const std::string edge_op = directed_graph ? "->" : "--"; + + const auto num_subgraphs = GENERATE(1, 2); + + const std::string dot = + num_subgraphs == 1 + ? fmt::format("{} {{subgraph s0 {{a {} b}}}}", graph_type, edge_op) + : fmt::format( + "{} {{subgraph s0 {{a {} b}}; subgraph s1 {{c {} d }}}}", + graph_type, edge_op, edge_op); + INFO(fmt::format("DOT source: {}", dot)); + + auto g = agmemread(dot.c_str()); + REQUIRE(g != nullptr); + + const std::string engine = GENERATE("dot", // + "neato", // + "fdp", // + "sfdp", // + "circo", // + "twopi", // + "osage", // + "patchwork" // + ); + INFO("Layout engine: " + engine); + + auto gvc = gvContextPlugins(lt_preloaded_symbols, false); + { + const auto rc = gvLayout(gvc, g, engine.c_str()); + REQUIRE(rc == 0); + } + + char *result = nullptr; + unsigned length = 0; + { + const auto rc = gvRenderData(gvc, g, "svg", &result, &length); + REQUIRE(rc == 0); + } + REQUIRE(result != nullptr); + REQUIRE(length > 0); + + SVGAnalyzer svg_analyzer{result}; + + const std::size_t num_nodes = num_subgraphs * 2; + const std::size_t num_edges = engine == "patchwork" ? 0 : num_subgraphs; + const std::size_t num_arrowheads = directed_graph ? num_edges : 0; + + const std::size_t num_svgs = 1; + const std::size_t num_groups = 1 + num_nodes + num_edges; + const std::size_t num_ellipses = engine == "patchwork" ? 0 : num_nodes; + const std::size_t num_polygons = + 1 + (engine == "patchwork" ? num_nodes : 0) + num_arrowheads; + const std::size_t num_paths = num_edges; + const std::size_t num_titles = num_nodes + num_edges; + + CHECK(svg_analyzer.num_svgs() == num_svgs); + CHECK(svg_analyzer.num_groups() == num_groups); + CHECK(svg_analyzer.num_circles() == 0); + CHECK(svg_analyzer.num_ellipses() == num_ellipses); + CHECK(svg_analyzer.num_lines() == 0); + CHECK(svg_analyzer.num_paths() == num_paths); + CHECK(svg_analyzer.num_polygons() == num_polygons); + CHECK(svg_analyzer.num_polylines() == 0); + CHECK(svg_analyzer.num_rects() == 0); + CHECK(svg_analyzer.num_titles() == num_titles); + CHECK(svg_analyzer.num_unknowns() == 0); + + gvFreeRenderData(result); + gvFreeLayout(gvc, g); + agclose(g); + gvFreeContext(gvc); +} -- 2.40.0