From: Magnus Jacobsson Date: Wed, 24 Aug 2022 07:37:07 +0000 (+0200) Subject: tests: add new test_edge_node_overlap_simple test X-Git-Tag: 6.0.1~7^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=929b729915db241c62af20262b566eedd859b332;p=graphviz tests: add new test_edge_node_overlap_simple test 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. --- diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f4a49b059..97001958b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 index 000000000..90a2f9c74 --- /dev/null +++ b/tests/test_edge_node_overlap_simple.cpp @@ -0,0 +1,15 @@ +#include + +#include + +#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 index 000000000..80cf89a21 --- /dev/null +++ b/tests/test_edge_node_overlap_utilities.cpp @@ -0,0 +1,93 @@ +#include +#include +#include + +#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 index 000000000..bfe1f234b --- /dev/null +++ b/tests/test_edge_node_overlap_utilities.h @@ -0,0 +1,14 @@ +#include + +#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);