]> granicus.if.org Git - graphviz/commitdiff
tests: add new test_edge_node_overlap_simple test
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Wed, 24 Aug 2022 07:37:07 +0000 (09:37 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Tue, 6 Sep 2022 19:30:03 +0000 (21:30 +0200)
Upcoming commit series will add additional such tests so most of the
code is in a separate utilities file and is somewhat more generalized
than what is strictly necessary for this first simple test.

tests/CMakeLists.txt
tests/test_edge_node_overlap_simple.cpp [new file with mode: 0644]
tests/test_edge_node_overlap_utilities.cpp [new file with mode: 0644]
tests/test_edge_node_overlap_utilities.h [new file with mode: 0644]

index f4a49b059405c032b933c5dd83c3050500b300bd..97001958b01cbb862a2bf5f61ae06baa4e000e1c 100644 (file)
@@ -28,6 +28,8 @@ add_library(test_common SHARED
             svg_analyzer_interface.h
             svg_element.cpp
             svg_element.h
+            test_edge_node_overlap_utilities.cpp
+            test_edge_node_overlap_utilities.h
             ../cmd/dot/dot_builtins.cpp
 )
 set_target_properties(test_common PROPERTIES CXX_STANDARD 20)
@@ -86,6 +88,7 @@ CREATE_TEST(GVContext_construction)
 CREATE_TEST(GVContext_render_svg)
 CREATE_TEST(GVLayout_construction)
 CREATE_TEST(GVLayout_render)
+CREATE_TEST(edge_node_overlap_simple)
 CREATE_TEST(neatopack)
 CREATE_TEST(node_color)
 CREATE_TEST(node_fillcolor)
diff --git a/tests/test_edge_node_overlap_simple.cpp b/tests/test_edge_node_overlap_simple.cpp
new file mode 100644 (file)
index 0000000..90a2f9c
--- /dev/null
@@ -0,0 +1,15 @@
+#include <string>
+
+#include <catch2/catch.hpp>
+
+#include "test_edge_node_overlap_utilities.h"
+
+TEST_CASE(
+    "Overlap",
+    "[!shouldfail] An edge connected to a node shall not overlap that node") {
+
+  std::string dot =
+      "digraph {node[shape=polygon penwidth=2 fontname=Courier] a -> b}";
+
+  test_edge_node_overlap(dot);
+}
diff --git a/tests/test_edge_node_overlap_utilities.cpp b/tests/test_edge_node_overlap_utilities.cpp
new file mode 100644 (file)
index 0000000..80cf89a
--- /dev/null
@@ -0,0 +1,93 @@
+#include <catch2/catch.hpp>
+#include <cmath>
+#include <fmt/format.h>
+
+#include "svg_analyzer.h"
+#include "test_edge_node_overlap_utilities.h"
+
+/// check that edges do not overlap nodes
+static bool check_analyzed_svg(SVGAnalyzer &svg_analyzer,
+                               const check_options &check_options) {
+
+  REQUIRE(svg_analyzer.graphs().size() == 1);
+  auto &recreated_graph = svg_analyzer.graphs().back();
+
+  auto &tail_node = recreated_graph.node("a");
+  auto &head_node = recreated_graph.node("b");
+  auto &edge = recreated_graph.edge("a->b");
+
+  auto success = true;
+
+// macro for doing the actual check and continue the execution in the same test
+// case even if the assertion fails, while still capturing the result to be
+// used to decide whether to write SVG files at the end of the test case
+#define DO_CHECK(condition)                                                    \
+  do {                                                                         \
+    CHECK(condition);                                                          \
+    success = success && (condition);                                          \
+  } while (0)
+
+  const auto edge_bbox = edge.outline_bbox();
+
+  // check head node and edge overlap
+  {
+    const auto head_node_bbox = head_node.outline_bbox();
+    const auto overlap_bbox = edge_bbox.intersection(head_node_bbox);
+    INFO("Head node overlap:");
+    INFO(fmt::format("  width:  {:.3f}", overlap_bbox.width));
+    INFO(fmt::format("  height: {:.3f}", overlap_bbox.height));
+    // FIXME: add support for rank direction "LR" and "RL". For now assume
+    // "TB" or "BT" and check only in the vertical direction
+    const auto head_node_edge_overlap = overlap_bbox.height;
+
+    // check maximum head node and edge overlap
+    DO_CHECK(head_node_edge_overlap <=
+             check_options.max_node_edge_overlap +
+                 check_options.svg_rounding_error * 2);
+  }
+
+  // check tail node and edge overlap
+  {
+    const auto tail_node_bbox = tail_node.outline_bbox();
+    const auto overlap_bbox = edge_bbox.intersection(tail_node_bbox);
+    INFO("Tail node overlap:");
+    INFO(fmt::format("  width:  {:.6f}", overlap_bbox.width));
+    INFO(fmt::format("  height: {:.6f}", overlap_bbox.height));
+    // FIXME: add support for rank direction "LR" and "RL". For now assume
+    // "TB" or "BT" and check only in the vertical direction
+    const auto tail_node_edge_overlap = overlap_bbox.height;
+
+    // check maximum tail node and edge overlap
+    DO_CHECK(tail_node_edge_overlap <=
+             check_options.max_node_edge_overlap +
+                 check_options.svg_rounding_error * 2);
+  }
+
+  return success;
+}
+
+void test_edge_node_overlap(const std::string &dot) {
+  auto svg_analyzer = SVGAnalyzer::make_from_dot(dot);
+
+  // The binary search in the bezier_clip function in lib/common/splines.c has a
+  // limit for when to consider the boundary found and to be the point inside
+  // the boundary. It is the maximum distance between two points on a bezier
+  // curve that are on opposite sides of the node boundary (for shape_clip) or
+  // on the opposite sides of the boundary of a virtual circle at a specified
+  // distance from a given point (for arrow_clip). An margin is needed to
+  // account for the error that this limit introduces.
+  const double graphviz_bezier_clip_margin = 0.5;
+  const int graphviz_num_decimals_in_svg = 2;
+  const double graphviz_max_svg_rounding_error =
+      std::pow(10, -graphviz_num_decimals_in_svg) / 2;
+
+  const check_options check_options = {
+      .max_node_edge_overlap = graphviz_bezier_clip_margin,
+      .svg_rounding_error = graphviz_max_svg_rounding_error,
+  };
+
+  const auto success = check_analyzed_svg(svg_analyzer, check_options);
+
+  // FIXME: add writing of SVG files for manual inspection when not success
+  REQUIRE(success);
+}
diff --git a/tests/test_edge_node_overlap_utilities.h b/tests/test_edge_node_overlap_utilities.h
new file mode 100644 (file)
index 0000000..bfe1f23
--- /dev/null
@@ -0,0 +1,14 @@
+#include <string>
+
+#include "svg_analyzer.h"
+
+struct check_options {
+  /// maximum allowed overlap between edge and node
+  double max_node_edge_overlap;
+  /// rounding error caused by limited precision in SVG attribute values
+  double svg_rounding_error;
+};
+
+/// generate an SVG graph from the `dot` source and check that edges don't
+/// overlap nodes
+void test_edge_node_overlap(const std::string &dot);