diff --git a/src/read_graphviz_new.cpp b/src/read_graphviz_new.cpp index fdc6e95f4..99fc81bc4 100644 --- a/src/read_graphviz_new.cpp +++ b/src/read_graphviz_new.cpp @@ -53,6 +53,7 @@ namespace boost namespace read_graphviz_detail { + static const long max_subgraph_nesting_level = 255; struct token { enum token_type @@ -207,7 +208,7 @@ namespace read_graphviz_detail tokenizer(const std::string& str) : begin(str.begin()), end(str.end()) { - std::string end_of_token = "(?=(?:\\W))"; + // std::string end_of_token = "(?=(?:\\W))"; // SEHE: unused? std::string whitespace = "(?:\\s+)"; std::string slash_slash_comment = "(?://.*?$)"; std::string slash_star_comment = "(?:/\\*.*?\\*/)"; @@ -527,6 +528,7 @@ namespace read_graphviz_detail std::map< subgraph_name, subgraph_info > subgraphs; std::string current_subgraph_name; int sgcounter; // Counter for anonymous subgraphs + long sgnesting_level; std::set< std::pair< node_name, node_name > > existing_edges; // Used for checking in strict graphs @@ -538,7 +540,7 @@ namespace read_graphviz_detail subgraph_member_list& current_members() { return current().members; } parser(const std::string& gr, parser_result& result) - : the_tokenizer(gr), lookahead(), r(result), sgcounter(0) + : the_tokenizer(gr), lookahead(), r(result), sgcounter(0), sgnesting_level(0) { current_subgraph_name = "___root___"; current() = subgraph_info(); // Initialize root graph @@ -773,10 +775,18 @@ namespace read_graphviz_detail bool is_anonymous = true; if (first_token.type == token::kw_subgraph) { - if (peek().type == token::identifier) + switch (peek().type) { + case token::identifier: name = get().normalized_value; is_anonymous = false; + break; + case token::left_brace: + is_anonymous = true; + break; + default: + error("Subgraph reference needs a name"); + break; } } if (is_anonymous) @@ -790,25 +800,30 @@ namespace read_graphviz_detail = current(); // Initialize properties and defaults subgraphs[name].members.clear(); // Except member list } - if (first_token.type == token::kw_subgraph - && peek().type != token::left_brace) + if (!is_anonymous && peek().type != token::left_brace) { - if (is_anonymous) - error("Subgraph reference needs a name"); return name; } subgraph_name old_sg = current_subgraph_name; + if (++sgnesting_level > max_subgraph_nesting_level) + { + error("Exceeded maximum subgraph nesting level"); + } current_subgraph_name = name; - if (peek().type == token::left_brace) - get(); - else - error("Wanted left brace to start subgraph"); + if (first_token.type != token::left_brace) + { + if (peek().type == token::left_brace) + get(); + else + error("Wanted left brace to start subgraph"); + } parse_stmt_list(); if (peek().type == token::right_brace) get(); else error("Wanted right brace to end subgraph"); current_subgraph_name = old_sg; + sgnesting_level -= 1; return name; } diff --git a/test/graphviz_test.cpp b/test/graphviz_test.cpp index 7769256ba..c29b05f70 100644 --- a/test/graphviz_test.cpp +++ b/test/graphviz_test.cpp @@ -1,151 +1,192 @@ // Copyright 2004-5 Trustees of Indiana University +// // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // -// graphviz_test.cpp - Test cases for the Boost.Spirit implementation of a -// Graphviz DOT Language reader. +// graphviz_test.cpp - Test cases for the Graphviz DOT Language reader // // Author: Ronald Garcia #define BOOST_GRAPHVIZ_USE_ISTREAM -#include #include #include +#include #include #include -#include -#include -#include #include -#include -#include +#include #include -#include -#include -#include #include -template -class close_to { -public: - explicit close_to(T f) - : f_(f) { } +typedef std::string node_t; +typedef std::pair< node_t, node_t > edge_t; - bool operator()(T l, T r) const { - return std::abs(l - r) <= - (std::max)(f_ * (std::max)(std::abs(l), std::abs(r)), T()); - } +typedef float Mass; +typedef double Weight; +typedef std::map< node_t, Mass > expected_masses_t; +typedef std::map< edge_t, Weight > expected_weights_t; +#define MAP_MASSES boost::assign::list_of< std::pair< node_t, Mass > > +#define MAP_WEIGHTS boost::assign::list_of< std::pair< edge_t, Weight > > -private: - T f_; +struct Fixture +{ + std::string graphviz_text; + size_t correct_num_vertices; + expected_masses_t masses; + expected_weights_t weights; }; -using namespace std; -using namespace boost; +namespace Samples +{ +namespace Directed +{ + static Fixture const basic { + "digraph { a node [mass = 7.7] c e [mass = 6.66] }", + 3, + MAP_MASSES("a", 0.0f)("c", 7.7f)("e", 6.66f), + expected_weights_t(), + }; + + static Fixture const basic_aliased { + "digraph { a node [mass = 7.7] \"a\" e [mass = 6.66] }", + 2, + MAP_MASSES("a", 0.0f)("e", 6.66f), + expected_weights_t(), + }; + + static Fixture const full { + "digraph { a -> b eDge [weight = 7.7] " + "c -> d e-> f [weight = 6.66] " + "d ->e->a [weight=.5]}", + 6, + expected_masses_t(), + MAP_WEIGHTS(edge_t("a", "b"), 0.0)(edge_t("c", "d"), 7.7)( + edge_t("e", "f"), 6.66)(edge_t("d", "e"), 0.5)( + edge_t("e", "a"), 0.5), + }; +} -using namespace boost::assign; +namespace Undirected +{ + static Fixture const basic { + "graph { a nodE [mass = 7.7] c e [mass =\\\n6.66] }", + 3, + MAP_MASSES("a", 0.0f)("c", 7.7f)("e", 6.66f), + expected_weights_t(), + }; + + static Fixture const full { + "graph { a -- b eDge [weight = 7.7] " + "c -- d e -- f [weight = 6.66] }", + 6, + expected_masses_t(), + MAP_WEIGHTS(edge_t("a", "b"), 0.0)(edge_t("c", "d"), 7.7)( + edge_t("e", "f"), 6.66), + }; +} -typedef std::string node_t; -typedef std::pair< node_t, node_t > edge_t; +Fixture const all_directed[] { + Directed::basic, + Directed::basic_aliased, + Directed::full, +}; +Fixture const all_undirected[] { + Undirected::basic, + Undirected::full, +}; +} // Samples -typedef std::map< node_t, float > mass_map_t; -typedef std::map< edge_t, double > weight_map_t; +namespace ComparisonDriver +{ +template < class T > class close_to +{ +public: + explicit close_to(T f) : f_(f) { assert(f >= 0); } -template < typename graph_t, typename NameMap, typename MassMap, - typename WeightMap > -bool test_graph(std::istream& dotfile, graph_t& graph, - std::size_t correct_num_vertices, mass_map_t const& masses, - weight_map_t const& weights, std::string const& node_id, - std::string const& g_name, NameMap name, MassMap mass, WeightMap weight); + bool operator()(T l, T r) const + { + using std::abs; + return abs(l - r) <= f_ * (std::max)(abs(l), abs(r)); + } -template < typename graph_t > -bool test_graph(std::istream& dotfile, std::size_t correct_num_vertices, - mass_map_t const& masses, weight_map_t const& weights, - std::string const& node_id = "node_id", - std::string const& g_name = std::string()) -{ - graph_t g; - return test_graph(dotfile, g, correct_num_vertices, masses, weights, - node_id, g_name, get(vertex_name, g), get(vertex_color, g), - get(edge_weight, g)); -} +private: + T f_; +}; template < typename graph_t, typename NameMap, typename MassMap, typename WeightMap > -bool test_graph(std::istream& dotfile, graph_t& graph, - std::size_t correct_num_vertices, mass_map_t const& masses, - weight_map_t const& weights, std::string const& node_id, - std::string const& g_name, NameMap name, MassMap mass, WeightMap weight) +bool test_graph(std::string const& text, graph_t& graph, + std::size_t correct_num_vertices, expected_masses_t const& expected_masses, + expected_weights_t const& expected_weights, std::string const& node_id, + std::string const& g_name, NameMap name_map, MassMap mass_map, + WeightMap weight_map) { - // Construct a graph and set up the dynamic_property_maps. - dynamic_properties dp(ignore_other_properties); - dp.property(node_id, name); - dp.property("mass", mass); - dp.property("weight", weight); + boost::dynamic_properties dp(boost::ignore_other_properties); + dp.property(node_id, name_map); + dp.property("mass", mass_map); + dp.property("weight", weight_map); boost::ref_property_map< graph_t*, std::string > gname( - get_property(graph, graph_name)); + get_property(graph, boost::graph_name)); dp.property("name", gname); bool result = true; #ifdef BOOST_GRAPHVIZ_USE_ISTREAM - if (read_graphviz(dotfile, graph, dp, node_id)) - { + std::istringstream is(text); + if (read_graphviz(is, graph, dp, node_id)) #else - std::string data; - dotfile >> std::noskipws; - std::copy(std::istream_iterator< char >(dotfile), - std::istream_iterator< char >(), std::back_inserter(data)); - if (read_graphviz(data.begin(), data.end(), graph, dp, node_id)) - { + if (read_graphviz(text.begin(), text.end(), graph, dp, node_id)) #endif + { // check correct vertex count BOOST_TEST_EQ(num_vertices(graph), correct_num_vertices); // check masses - if (!masses.empty()) + if (!expected_masses.empty()) { // assume that all the masses have been set // for each vertex: - typename graph_traits< graph_t >::vertex_iterator i, j; + typename boost::graph_traits< graph_t >::vertex_iterator i, j; for (boost::tie(i, j) = vertices(graph); i != j; ++i) { - // - get its name - std::string node_name = get(name, *i); - // - get its mass - float node_mass = get(mass, *i); - BOOST_TEST(masses.find(node_name) != masses.end()); - float ref_mass = masses.find(node_name)->second; + std::string node_name = get(name_map, *i); + Mass node_mass = get(mass_map, *i); + BOOST_TEST( + expected_masses.find(node_name) != expected_masses.end()); + Mass ref_mass = expected_masses.find(node_name)->second; // - compare the mass to the result in the table - BOOST_TEST_WITH(node_mass, ref_mass, close_to(0.01f)); + BOOST_TEST_WITH(node_mass, ref_mass, close_to< Mass >(0.0001f)); } } // check weights - if (!weights.empty()) + if (!expected_weights.empty()) { // assume that all weights have been set /// for each edge: - typename graph_traits< graph_t >::edge_iterator i, j; + typename boost::graph_traits< graph_t >::edge_iterator i, j; for (boost::tie(i, j) = edges(graph); i != j; ++i) { // - get its name - std::pair< std::string, std::string > edge_name = make_pair( - get(name, source(*i, graph)), get(name, target(*i, graph))); + std::pair< std::string, std::string > edge_name + = make_pair(get(name_map, source(*i, graph)), + get(name_map, target(*i, graph))); // - get its weight - double edge_weight = get(weight, *i); - BOOST_TEST(weights.find(edge_name) != weights.end()); - double ref_weight = weights.find(edge_name)->second; - // - compare the weight to teh result in the table - BOOST_TEST_WITH(edge_weight, ref_weight, close_to(0.01)); + Weight edge_weight = get(weight_map, *i); + BOOST_TEST( + expected_weights.find(edge_name) != expected_weights.end()); + Weight ref_weight = expected_weights.find(edge_name)->second; + // - compare the weight to the result in the table + BOOST_TEST_WITH( + edge_weight, ref_weight, close_to< Weight >(0.000001)); } } if (!g_name.empty()) { - std::string parsed_name = get_property(graph, graph_name); + std::string parsed_name = get_property(graph, boost::graph_name); BOOST_TEST(parsed_name == g_name); } } @@ -158,262 +199,288 @@ bool test_graph(std::istream& dotfile, graph_t& graph, return result; } -// int test_main(int, char*[]) { +template < typename graph_t, typename NameMap, typename MassMap, + typename WeightMap > +bool test_graph(Fixture const& sample, graph_t& g, std::string const& node_id, + std::string const& g_name, NameMap name_map, MassMap mass_map, + WeightMap weight_map) +{ + return test_graph(sample.graphviz_text, g, sample.correct_num_vertices, + sample.masses, sample.weights, node_id, g_name, name_map, mass_map, + weight_map); +} -typedef istringstream gs_t; +template < typename graph_t > +bool test_graph(std::string const& dottext, std::size_t correct_num_vertices, + expected_masses_t const& masses, expected_weights_t const& weights, + std::string const& node_id = "node_id", std::string const& g_name = "") +{ + graph_t g; + return test_graph(dottext, g, correct_num_vertices, masses, weights, + node_id, g_name, get(boost::vertex_name, g), + get(boost::vertex_color, g), get(boost::edge_weight, g)); +} -typedef property< vertex_name_t, std::string, - property< vertex_color_t, float > > +template < typename graph_t > +bool test_graph(Fixture const& sample, std::string const& node_id = "node_id", + std::string const& g_name = "") +{ + graph_t g; + return test_graph(sample.graphviz_text, g, sample.correct_num_vertices, + sample.masses, sample.weights, node_id, g_name, // + get(boost::vertex_name, g), // + get(boost::vertex_color, g), // + get(boost::edge_weight, g) // + ); +} +} + +namespace Models +{ +typedef boost::property< boost::vertex_name_t, std::string, + boost::property< boost::vertex_color_t, Mass > > vertex_p; -typedef property< edge_weight_t, double > edge_p; -typedef property< graph_name_t, std::string > graph_p; +typedef boost::property< boost::edge_weight_t, Weight > edge_p; +typedef boost::property< boost::graph_name_t, std::string > graph_p; -struct vertex_p_bundled +typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, + vertex_p, edge_p, graph_p > + Graph; +typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, + vertex_p, edge_p, graph_p > + DiGraph; + +typedef boost::adjacency_list< boost::setS, boost::vecS, boost::directedS, + vertex_p, edge_p, graph_p > + DiGraphNoParallel; + +struct VertexBundle { std::string name; - float color; + Mass mass; }; -struct edge_p_bundled +struct EdgeBundle { - double weight; + Weight weight; }; +typedef boost::compressed_sparse_row_graph< boost::directedS, VertexBundle, + EdgeBundle, graph_p > + CSRBundledGraph; +typedef boost::compressed_sparse_row_graph< boost::directedS, + boost::no_property, boost::no_property, graph_p > + CSRGraph; +} // Models + +// SEHE I intended for this to be able to pass __FUNCTION__ for reporting +// failures, but there seems to be no way to achieve this with lightweight_test +#define TEST_GRAPH(Model, ...) \ + BOOST_TEST((ComparisonDriver::test_graph< Model >(__VA_ARGS__))); + // Basic directed graph tests void test_basic_directed_graph_1() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 7.7f)("e", 6.66f); - gs_t gs("digraph { a node [mass = 7.7] c e [mass = 6.66] }"); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST((test_graph< graph_t >(gs, 3, masses, weight_map_t()))); + TEST_GRAPH(Models::DiGraph, Samples::Directed::basic); } void test_basic_directed_graph_2() { - mass_map_t masses; - insert(masses)("a", 0.0f)("e", 6.66f); - gs_t gs("digraph { a node [mass = 7.7] \"a\" e [mass = 6.66] }"); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST((test_graph< graph_t >(gs, 2, masses, weight_map_t()))); + TEST_GRAPH(Models::DiGraph, Samples::Directed::basic_aliased); } void test_basic_directed_graph_3() { - weight_map_t weights; - insert(weights)(make_pair("a", "b"), 0.0)(make_pair("c", "d"), 7.7)( - make_pair("e", "f"), 6.66)(make_pair("d", "e"), 0.5)( - make_pair("e", "a"), 0.5); - gs_t gs("digraph { a -> b eDge [weight = 7.7] " - "c -> d e-> f [weight = 6.66] " - "d ->e->a [weight=.5]}"); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST((test_graph< graph_t >(gs, 6, mass_map_t(), weights))); + TEST_GRAPH(Models::DiGraph, Samples::Directed::full); } // undirected graph with alternate node_id property name void test_undirected_graph_alternate_node_id() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 7.7f)("e", 6.66f); - gs_t gs("graph { a node [mass = 7.7] c e [mass = 6.66] }"); - typedef adjacency_list< vecS, vecS, undirectedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST( - (test_graph< graph_t >(gs, 3, masses, weight_map_t(), "nodenames"))); + TEST_GRAPH(Models::Graph, Samples::Undirected::basic, "nodenames"); } // Basic undirected graph tests void test_basic_undirected_graph_1() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 7.7f)("e", 6.66f); - gs_t gs("graph { a node [mass = 7.7] c e [mass =\\\n6.66] }"); - typedef adjacency_list< vecS, vecS, undirectedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST((test_graph< graph_t >(gs, 3, masses, weight_map_t()))); + TEST_GRAPH(Models::Graph, Samples::Undirected::basic); } void test_basic_undirected_graph_2() { - weight_map_t weights; - insert(weights)(make_pair("a", "b"), 0.0)(make_pair("c", "d"), 7.7)( - make_pair("e", "f"), 6.66); - gs_t gs("graph { a -- b eDge [weight = 7.7] " - "c -- d e -- f [weight = 6.66] }"); - typedef adjacency_list< vecS, vecS, undirectedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST((test_graph< graph_t >(gs, 6, mass_map_t(), weights))); + TEST_GRAPH(Models::Graph, Samples::Undirected::full); } // Mismatch directed graph test void test_mismatch_directed_graph() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 7.7f)("e", 6.66f); - gs_t gs("graph { a nodE [mass = 7.7] c e [mass = 6.66] }"); - try - { - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, - graph_p > - graph_t; - test_graph< graph_t >(gs, 3, masses, weight_map_t()); - BOOST_ERROR("Failed to throw boost::undirected_graph_error."); - } - catch (boost::undirected_graph_error&) - { - } - catch (boost::directed_graph_error&) - { - BOOST_ERROR("Threw boost::directed_graph_error, should have thrown " - "boost::undirected_graph_error."); - } + BOOST_TEST_THROWS(TEST_GRAPH(Models::DiGraph, Samples::Undirected::basic), + boost::undirected_graph_error); } // Mismatch undirected graph test void test_mismatch_undirected_graph() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 7.7f)("e", 6.66f); - gs_t gs("digraph { a node [mass = 7.7] c e [mass = 6.66] }"); - try - { - typedef adjacency_list< vecS, vecS, undirectedS, vertex_p, edge_p, - graph_p > - graph_t; - test_graph< graph_t >(gs, 3, masses, weight_map_t()); - BOOST_ERROR("Failed to throw boost::directed_graph_error."); - } - catch (boost::directed_graph_error&) - { - } + BOOST_TEST_THROWS(TEST_GRAPH(Models::Graph, Samples::Directed::basic), + boost::directed_graph_error); } // Complain about parallel edges -void test_complain_about_parallel_edges() -{ - weight_map_t weights; - insert(weights)(make_pair("a", "b"), 7.7); - gs_t gs("diGraph { a -> b [weight = 7.7] a -> b [weight = 7.7] }"); - try - { - typedef adjacency_list< setS, vecS, directedS, vertex_p, edge_p, - graph_p > - graph_t; - test_graph< graph_t >(gs, 2, mass_map_t(), weights); - BOOST_ERROR("Failed to throw boost::bad_parallel_edge."); - } - catch (boost::bad_parallel_edge&) - { - } -} - -// Handle parallel edges gracefully -void test_handle_parallel_edges_gracefully() +void test_parallel_edges() { - weight_map_t weights; - insert(weights)(make_pair("a", "b"), 7.7); - gs_t gs("digraph { a -> b [weight = 7.7] a -> b [weight = 7.7] }"); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST((test_graph< graph_t >(gs, 2, mass_map_t(), weights))); + Fixture parallel { + "diGraph { a -> b [weight = 7.7] a -> b [weight = 7.7] }", + 2, + expected_masses_t(), + MAP_WEIGHTS(edge_t("a", "b"), 7.7), + }; + TEST_GRAPH(Models::DiGraph, parallel); + BOOST_TEST_THROWS(TEST_GRAPH(Models::DiGraphNoParallel, parallel), + boost::bad_parallel_edge); } // Graph Property Test 1 void test_graph_property_test_1() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 0.0f)("e", 6.66f); - gs_t gs("digraph { graph [name=\"foo \\\"escaped\\\"\"] a c e [mass = " - "6.66] }"); - std::string graph_name("foo \"escaped\""); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST( - (test_graph< graph_t >(gs, 3, masses, weight_map_t(), "", graph_name))); + Fixture named { + "digraph { graph [name=\"foo \\\"escaped\\\"\"] a c e [mass = 6.66] " + "}", + 3, + MAP_MASSES("a", 0.0f)("c", 0.0f)("e", 6.66f), + expected_weights_t(), + }; + TEST_GRAPH(Models::DiGraph, named, "", "foo \"escaped\""); } // Graph Property Test 2 void test_graph_property_test_2() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 0.0f)("e", 6.66f); - gs_t gs("digraph { name=\"fo\"+ \"\\\no\" a c e [mass = 6.66] }"); - std::string graph_name("foo"); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST( - (test_graph< graph_t >(gs, 3, masses, weight_map_t(), "", graph_name))); + Fixture named { + "digraph { name=\"fo\"+ \"\\\no\" a c e [mass = 6.66] }", + 3, + MAP_MASSES("a", 0.0f)("c", 0.0f)("e", 6.66f), + expected_weights_t(), + }; + TEST_GRAPH(Models::DiGraph, named, "", "foo"); // SEHE why not "foo\no"? } // Graph Property Test 3 (HTML) void test_graph_property_test_3() { - mass_map_t masses; - insert(masses)("a", 0.0f)("c", 0.0f)("e", 6.66f); std::string graph_name = "foo]]>bar\n
\nbaz"; - gs_t gs("digraph { name=" + graph_name + " a c e [mass = 6.66] }"); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST( - (test_graph< graph_t >(gs, 3, masses, weight_map_t(), "", graph_name))); + Fixture html_named { + "digraph { name=" + graph_name + " a c e [mass = 6.66] }", + 3, + MAP_MASSES("a", 0.0f)("c", 0.0f)("e", 6.66f), + expected_weights_t(), + }; + TEST_GRAPH(Models::DiGraph, html_named, "", graph_name); } // Comments embedded in strings void test_comments_embedded_in_strings() { - gs_t gs("digraph { " - "a0 [ label = \"//depot/path/to/file_14#4\" ];" - "a1 [ label = \"//depot/path/to/file_29#9\" ];" - "a0 -> a1 [ color=gray ];" - "}"); - typedef adjacency_list< vecS, vecS, directedS, vertex_p, edge_p, graph_p > - graph_t; - BOOST_TEST((test_graph< graph_t >(gs, 2, mass_map_t(), weight_map_t()))); + std::string gv("digraph { " + "a0 [ label = \"//depot/path/to/file_14#4\" ];" + "a1 [ label = \"//depot/path/to/file_29#9\" ];" + "a0 -> a1 [ color=gray ];" + "}"); + TEST_GRAPH( + Models::DiGraph, gv, 2, expected_masses_t(), expected_weights_t()); } -#if 0 // Currently broken - void test_basic_csr_directed_graph() { - weight_map_t weights; - insert( weights )(make_pair("a","b"),0.0) - (make_pair("c","d"),7.7)(make_pair("e","f"),6.66) - (make_pair("d","e"),0.5)(make_pair("e","a"),0.5); - gs_t gs("digraph { a -> b eDge [weight = 7.7] " - "c -> d e-> f [weight = 6.66] " - "d ->e->a [weight=.5]}"); - typedef compressed_sparse_row_graph graph_t; - BOOST_TEST((test_graph(gs,6,mass_map_t(),weights,"node_id","",&vertex_p_bundled::name,&vertex_p_bundled::color,&edge_p_bundled::weight))); - } +void test_basic_csr_directed_graph() +{ + auto sample = Samples::Directed::full; + + typedef Models::CSRBundledGraph graph_t; + graph_t g; +#ifdef UNSTABLE_PROPERTY_MAPS_FIXED // https://github.com/boostorg/graph/issues/373 + BOOST_TEST((test_graph(gs, g, 6, mass_map_t(), weights, "node_id", "", + get(&vertex_p_bundled::name, g), // Warning, currently broken + get(&vertex_p_bundled::color, g), // Warning, currently broken + get(&edge_p_bundled::weight, g)) // Warning, currently broken + )); +#else + typedef graph_t::vertex_descriptor V; + typedef graph_t::edge_descriptor E; + TEST_GRAPH(graph_t, sample, g, "node_id", "", // + boost::make_function_property_map< V >( + [&g](V v) -> std::string& { return g[v].name; }), + boost::make_function_property_map< V >( + [&g](V v) -> Mass& { return g[v].mass; }), + boost::make_function_property_map< E >( + [&g](E e) -> Weight& { return g[e].weight; }) // + ); #endif +} void test_basic_csr_directed_graph_ext_props() { - weight_map_t weights; - insert(weights)(make_pair("a", "b"), 0.0)(make_pair("c", "d"), 7.7)( - make_pair("e", "f"), 6.66)(make_pair("d", "e"), 0.5)( - make_pair("e", "a"), 0.5); - gs_t gs("digraph { a -> b eDge [weight = 7.7] " - "c -> d e-> f [weight = 6.66] " - "d ->e->a [weight=.5]}"); - typedef compressed_sparse_row_graph< directedS, no_property, no_property, - graph_p > - graph_t; - graph_t g; - vector_property_map< std::string, - property_map< graph_t, vertex_index_t >::const_type > - vertex_name(get(vertex_index, g)); - vector_property_map< float, - property_map< graph_t, vertex_index_t >::const_type > - vertex_color(get(vertex_index, g)); - vector_property_map< double, - property_map< graph_t, edge_index_t >::const_type > - edge_weight(get(edge_index, g)); - BOOST_TEST((test_graph(gs, g, 6, mass_map_t(), weights, "node_id", "", - vertex_name, vertex_color, edge_weight))); + auto sample = Samples::Directed::full; + using Models::CSRGraph; + CSRGraph g; + boost::property_map< CSRGraph, boost::vertex_index_t >::const_type vidx + = get(boost::vertex_index, g); + boost::property_map< CSRGraph, boost::edge_index_t >::const_type eidx + = get(boost::edge_index, g); + + boost::vector_property_map< std::string, + boost::property_map< CSRGraph, boost::vertex_index_t >::const_type > + vertex_name(vidx); + boost::vector_property_map< Mass, + boost::property_map< CSRGraph, boost::vertex_index_t >::const_type > + vertex_mass(vidx); + boost::vector_property_map< Weight, + boost::property_map< CSRGraph, boost::edge_index_t >::const_type > + edge_weight(eidx); + + TEST_GRAPH(CSRGraph, sample, g, "node_id", "", vertex_name, vertex_mass, + edge_weight); +} + +void test_subgraphs() +{ + // on the BGL side, the new parser doesn't support subgraphs + // however, the docs promise to support reading them on the input side as + // "syntactic sugar". + for (auto gv : { + Fixture { "digraph {}" }, + Fixture { "digraph { 1 -> {} }", 1 }, + Fixture { "digraph { 1 -> {2} }", 2 }, + Fixture { "digraph { 1; { 2; 3; } }", 3 }, + Fixture { "digraph { { 2; 3; } 1; }", 3 }, + Fixture { "digraph { 1; subgraph { 2; 3; } }", 3 }, + Fixture { "digraph { 1 -> subgraph { 2; 3; } }", 3 }, + Fixture { "digraph { 1 -> subgraph hello { 2; 3; } }", 3 }, + Fixture { "digraph { 1 -> subgraph clust_Hello { 2; 3; } }", 3 }, + Fixture { "digraph { 1 -> subgraph \"hello\" { 2; 3; } }", 3 }, + Fixture { + "digraph { {2} -> subgraph \"hello\" {{{{ 2; 3; }}}} }", 2 }, + }) + { + TEST_GRAPH(Models::DiGraph, gv); + } +} + +void test_subgraph_nesting_limit() // issue #364 +{ + auto independent_nests = [=](unsigned level) + { + auto sg = std::string(level, '{') + " 2; 3; " + std::string(level, '}'); + ComparisonDriver::test_graph< Models::DiGraph >( + { "digraph{1->" + sg + "}", 3 }); + ComparisonDriver::test_graph< Models::DiGraph >( + { "digraph{1->" + sg + ";4->" + sg + "}", 4 }); + }; + + constexpr unsigned limit = 255; + independent_nests(1); + independent_nests(limit / 2); + independent_nests(limit - 1); + independent_nests(limit); // edge-case + BOOST_TEST_THROWS(independent_nests(limit + 1), boost::bad_graphviz_syntax); } int main() @@ -426,12 +493,14 @@ int main() test_basic_undirected_graph_2(); test_mismatch_directed_graph(); test_mismatch_undirected_graph(); - test_complain_about_parallel_edges(); - test_handle_parallel_edges_gracefully(); + test_parallel_edges(); test_graph_property_test_1(); test_graph_property_test_2(); test_graph_property_test_3(); test_comments_embedded_in_strings(); test_basic_csr_directed_graph_ext_props(); + test_basic_csr_directed_graph(); + test_subgraphs(); + test_subgraph_nesting_limit(); return boost::report_errors(); }