From: Magnus Jacobsson Date: Tue, 23 Aug 2022 09:25:17 +0000 (+0200) Subject: tests: SVGAnalyzer: add support for retrieving the Graphviz edge fillcolor X-Git-Tag: 6.0.1~10^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fc94305254081eab29c6663d74c8112be20757bf;p=graphviz tests: SVGAnalyzer: add support for retrieving the Graphviz edge fillcolor --- diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 55f63ae05..f4a49b059 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -79,6 +79,7 @@ endmacro() CREATE_TEST(AGraph_construction) CREATE_TEST(clusters) CREATE_TEST(edge_color) +CREATE_TEST(edge_fillcolor) CREATE_TEST(edge_penwidth) CREATE_TEST(engines) CREATE_TEST(GVContext_construction) diff --git a/tests/graphviz_edge.cpp b/tests/graphviz_edge.cpp index 88eb1ef7d..03c1a9aa7 100644 --- a/tests/graphviz_edge.cpp +++ b/tests/graphviz_edge.cpp @@ -1,12 +1,25 @@ #include #include "graphviz_edge.h" +#include "svg_element.h" GraphvizEdge::GraphvizEdge(SVG::SVGElement &svg_g_element) : m_edgeop(svg_g_element.graphviz_id), m_svg_g_element(svg_g_element) {} std::string_view GraphvizEdge::edgeop() const { return m_edgeop; } +std::string GraphvizEdge::fillcolor() const { + const auto fill = m_svg_g_element.attribute_from_subtree( + &SVG::SVGAttributes::fill, &SVG::SVGElement::is_closed_shape_element, ""); + const auto fill_opacity = m_svg_g_element.attribute_from_subtree( + &SVG::SVGAttributes::fill_opacity, + &SVG::SVGElement::is_closed_shape_element, 1); + if (fill.empty() && fill_opacity == 1) { + return ""; + } + return SVG::to_dot_color(fill, fill_opacity); +} + const SVG::SVGElement &GraphvizEdge::svg_g_element() const { return m_svg_g_element; } diff --git a/tests/graphviz_edge.h b/tests/graphviz_edge.h index 6840127d7..743fca160 100644 --- a/tests/graphviz_edge.h +++ b/tests/graphviz_edge.h @@ -25,6 +25,9 @@ public: /// Return the 'edgeop' according to the DOT language specification. Note that /// this is not the same as the 'id' attribute of an edge. std::string_view edgeop() const; + /// Return the edge's `fillcolor` attribute in RGB hex format if the opacity + /// is 100 % or in RGBA hex format otherwise. + std::string fillcolor() const; /// Return the edge's `penwidth` attribute double penwidth() const; /// Return a non-mutable reference to the SVG `g` element corresponding to the diff --git a/tests/svg_element.cpp b/tests/svg_element.cpp index b00bcf016..5870b3da1 100644 --- a/tests/svg_element.cpp +++ b/tests/svg_element.cpp @@ -16,6 +16,18 @@ static double px_to_pt(double px) { return px * 3 / 4; } +bool SVG::SVGElement::is_closed_shape_element() const { + switch (type) { + case SVG::SVGElementType::Circle: + case SVG::SVGElementType::Ellipse: + case SVG::SVGElementType::Polygon: + case SVG::SVGElementType::Rect: + return true; + default: + return false; + } +} + bool SVG::SVGElement::is_shape_element() const { switch (type) { case SVG::SVGElementType::Circle: diff --git a/tests/svg_element.h b/tests/svg_element.h index da557aa13..18b83f1e4 100644 --- a/tests/svg_element.h +++ b/tests/svg_element.h @@ -135,6 +135,7 @@ public: /// throw an exception unless the `throw_if_bbox_not_defined` parameter is /// `false`. SVG::SVGRect bbox(bool throw_if_bbox_not_defined = true); + bool is_closed_shape_element() const; bool is_shape_element() const; std::string to_string(std::size_t indent_size) const; diff --git a/tests/test_edge_fillcolor.cpp b/tests/test_edge_fillcolor.cpp new file mode 100644 index 000000000..5a7afd561 --- /dev/null +++ b/tests/test_edge_fillcolor.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include + +#include "svg_analyzer.h" +#include "test_utilities.h" + +TEST_CASE("Edge color", + "Test that the Graphviz `color` attribute is used to set the " + "`stroke` and `stroke-opacity` attributes correctly for edges in the " + "generated SVG") { + + const auto primitive_arrow_shape = + GENERATE(from_range(all_primitive_arrow_shapes)); + INFO(fmt::format("Primitive arrow shape: {}", primitive_arrow_shape)); + + const auto edge_rgb_fillcolor = GENERATE("#000000", "#ffffff", "#5580aa"); + const auto opacity = GENERATE(0, 100, 255); + const auto edge_rgba_fillcolor = + fmt::format("{}{:02x}", edge_rgb_fillcolor, opacity); + INFO(fmt::format("Edge fillcolor: {}", edge_rgba_fillcolor)); + + auto dot = + fmt::format("digraph g1 {{edge [arrowhead={} fillcolor=\"{}\"]; a -> b}}", + primitive_arrow_shape, edge_rgba_fillcolor); + + const auto engine = "dot"; + auto svg_analyzer = SVGAnalyzer::make_from_dot(dot, engine); + + const auto expected_edge_fillcolor = [&]() -> const std::string { + if (primitive_arrow_shapes_without_closed_svg_shape.contains( + primitive_arrow_shape)) { + return ""; + } + if (opacity == 255) { + return edge_rgb_fillcolor; + } + if (opacity == 0) { + return "#00000000"; // transparent/none + } + return edge_rgba_fillcolor; + }(); + + for (const auto &graph : svg_analyzer.graphs()) { + for (const auto &edge : graph.edges()) { + CHECK(edge.fillcolor() == expected_edge_fillcolor); + } + } +} diff --git a/tests/test_utilities.cpp b/tests/test_utilities.cpp index 161531420..da7bb6eb7 100644 --- a/tests/test_utilities.cpp +++ b/tests/test_utilities.cpp @@ -184,5 +184,9 @@ const std::unordered_set all_primitive_arrow_shapes = { "box", "crow", "curve", "diamond", "dot", "icurve", "inv", "none", "normal", "tee", "vee"}; +const std::unordered_set + primitive_arrow_shapes_without_closed_svg_shape = {"curve", "icurve", + "none"}; + const std::unordered_set all_rank_directions = {"TB", "BT", "LR", "RL"}; diff --git a/tests/test_utilities.h b/tests/test_utilities.h index d3f4c7d43..887bb7a4d 100644 --- a/tests/test_utilities.h +++ b/tests/test_utilities.h @@ -29,6 +29,8 @@ extern const std::unordered_set extern const std::unordered_set primitive_polygon_and_polyline_arrow_shapes; extern const std::unordered_set all_primitive_arrow_shapes; +extern const std::unordered_set + primitive_arrow_shapes_without_closed_svg_shape; /// rank directions extern const std::unordered_set all_rank_directions;