]> granicus.if.org Git - graphviz/commitdiff
add new test_subgraphs test
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Tue, 5 Jul 2022 20:45:20 +0000 (22:45 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Mon, 25 Jul 2022 18:24:49 +0000 (20:24 +0200)
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
tests/test_subgraphs.cpp [new file with mode: 0644]

index fb3492885ad224dac7d4d90a1df3a0fa9622a8fc..0f9c5b029dad810adff8cc5943b9bb912bbc23b1 100644 (file)
@@ -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 (file)
index 0000000..cf10c8b
--- /dev/null
@@ -0,0 +1,87 @@
+#include <string>
+
+#include <catch2/catch.hpp>
+#include <fmt/format.h>
+
+#include <cgraph/cgraph.h>
+#include <gvc/gvc.h>
+
+#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);
+}