From: Magnus Jacobsson Date: Wed, 17 Aug 2022 10:59:08 +0000 (+0200) Subject: tests: SvgAnalyzer: add support for retrieving Graphviz components X-Git-Tag: 6.0.1~32^2~11 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=28db466f6c203eff53894eef6690c7b0a1a74735;p=graphviz tests: SvgAnalyzer: add support for retrieving Graphviz components --- diff --git a/tests/svg_analyzer.cpp b/tests/svg_analyzer.cpp index 23ffd9d37..bb352b059 100644 --- a/tests/svg_analyzer.cpp +++ b/tests/svg_analyzer.cpp @@ -1,5 +1,7 @@ #include +#include + #include "svg_analyzer.h" #include "svgpp_context.h" #include "svgpp_document_traverser.h" @@ -13,6 +15,12 @@ SVGAnalyzer::SVGAnalyzer(char *text) throw std::runtime_error{ "Wrong number of elements in process after traversing SVG document"}; } + m_elements_in_process.pop_back(); + retrieve_graphviz_components(); +} + +const std::vector &SVGAnalyzer::graphs() const { + return m_graphs; } void SVGAnalyzer::on_enter_element_svg() { m_num_svgs++; } @@ -130,6 +138,42 @@ void SVGAnalyzer::set_class(std::string_view class_) { current_element().attributes.class_ = class_; } +void SVGAnalyzer::retrieve_graphviz_components() { + retrieve_graphviz_components_impl(m_svg); +} + +void SVGAnalyzer::retrieve_graphviz_components_impl( + SVG::SVGElement &svg_element) { + if (svg_element.type == SVG::SVGElementType::Group) { + // The SVG 'class' attribute determines which type of Graphviz element a 'g' + // element corresponds to + const auto class_ = svg_element.attributes.class_; + if (class_ == "graph") { + m_graphs.emplace_back(svg_element); + } else if (class_ == "node") { + if (m_graphs.empty()) { + throw std::runtime_error{ + fmt::format("No graph to add node {} to", svg_element.graphviz_id)}; + } + m_graphs.back().add_node(svg_element); + } else if (class_ == "edge") { + if (m_graphs.empty()) { + throw std::runtime_error{ + fmt::format("No graph to add edge {} to", svg_element.graphviz_id)}; + } + m_graphs.back().add_edge(svg_element); + } else if (class_ == "cluster") { + // ignore of now + } else { + throw std::runtime_error("Unknown class" + std::string{class_}); + } + } + + for (auto &child : svg_element.children) { + retrieve_graphviz_components_impl(child); + } +} + void SVGAnalyzer::set_cx(double cx) { current_element().attributes.cx = cx; } void SVGAnalyzer::set_cy(double cy) { current_element().attributes.cy = cy; } diff --git a/tests/svg_analyzer.h b/tests/svg_analyzer.h index f4a418dea..033b4ee68 100644 --- a/tests/svg_analyzer.h +++ b/tests/svg_analyzer.h @@ -5,16 +5,20 @@ #include #include +#include "graphviz_graph.h" #include "svg_analyzer_interface.h" #include "svg_element.h" /** - * @brief The SVGAnalyzer class analyzes the contents of an SVG document. + * @brief The SVGAnalyzer class analyzes the contents of an SVG document and + * identifies Graphviz graphs, nodes and edges. */ class SVGAnalyzer : public ISVGAnalyzer { public: SVGAnalyzer(char *text); + /// Return a non-mutable reference to the list of Graphviz graphs + const std::vector &graphs() const; void on_enter_element_svg() override; void on_enter_element_g() override; void on_enter_element_circle() override; @@ -78,6 +82,10 @@ private: /// Get the parent element of the current element being processed by the SVG /// document traverser SVG::SVGElement &parent_element(); + /// Traverses the processed SVG element hierarchy, identifies nodes and edges, + /// creates objects representing them and stores them in lists + void retrieve_graphviz_components(); + void retrieve_graphviz_components_impl(SVG::SVGElement &parent_svg_element); /// A list of pointers to elements currently being processed by the SVG++ /// document traverser, in hierarchical order. The front element is the top @@ -95,6 +103,8 @@ private: std::size_t m_num_rects = 0; std::size_t m_num_texts = 0; std::size_t m_num_titles = 0; + /// A list of Graphviz recreated graphs + std::vector m_graphs; /// The top level SVG `svg` element corresponding to the Graphviz graph SVG::SVGElement m_svg; }; diff --git a/tests/test_svg_analyzer.cpp b/tests/test_svg_analyzer.cpp index 6b1b6b888..6d3e282b9 100644 --- a/tests/test_svg_analyzer.cpp +++ b/tests/test_svg_analyzer.cpp @@ -1,7 +1,9 @@ #include +#include #include #include +#include "graphviz_graph.h" #include "svg_analyzer.h" #include #include @@ -41,7 +43,29 @@ TEST_CASE( const std::size_t expected_num_nodes = 2; const std::size_t expected_num_edges = 1; - { + CHECK(svgAnalyzer.graphs().size() == expected_num_graphs); + for (const auto &graph : svgAnalyzer.graphs()) { + CHECK(graph.svg_g_element().type == SVG::SVGElementType::Group); + CHECK(graph.svg_g_element().attributes.class_ == "graph"); + CHECK(graph.svg_g_element().graphviz_id == "g1"); + + CHECK(graph.nodes().size() == expected_num_nodes); + for (const auto &node_it : graph.nodes() | boost::adaptors::indexed(0)) { + const auto node = node_it.value(); + const auto i = node_it.index(); + CHECK(node.svg_g_element().type == SVG::SVGElementType::Group); + CHECK(node.svg_g_element().attributes.class_ == "node"); + const auto node_id = i == 0 ? "a" : "b"; + CHECK(node.svg_g_element().graphviz_id == node_id); + } + + CHECK(graph.edges().size() == expected_num_edges); + for (const auto &edge : graph.edges()) { + CHECK(edge.svg_g_element().type == SVG::SVGElementType::Group); + CHECK(edge.svg_g_element().attributes.class_ == "edge"); + CHECK(edge.svg_g_element().graphviz_id == "a->b"); + } + const std::size_t expected_num_svgs = expected_num_graphs; const std::size_t expected_num_groups = expected_num_graphs + expected_num_nodes + expected_num_edges;