mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
This patch adds various support for debugging diagnostic paths and events, intended initially for myself to help with debugging -fanalyzer. It adds the optional ability for a diagnostic_event to supply a description of the predicted state of the program at that point along the diagnostic_path. To isolate the diagnostic subsystem from the analyzer, this representation is currently an xml::document with custom elements. The XML representation is similar to the analyzer's internal state but can be easier to read - for example, rather than storing the contents of memory via byte offsets, it uses fields for structs and element indexes for arrays, recursively. These states are handled by the HTML and SARIF diagnostic sinks. The SARIF sink simply embeds the XML as a string in a property bag of the threadFlowLocation object (SARIF v2.1.0 section 3.38). For HTML output, the "experimental-html" sink gains a new "show-state-diagrams=yes" option i.e.: -fdiagnostics-add-output=experimental-html:show-state-diagrams=yes which converts the state XML into SVG diagrams visualizing the state of memory at each event, inspired by the "ddd" debugger. These can be seen by pressing 'j' and 'k' to single-step forward and backward through events, making it *much* easier to debug -fanalyzer. An example of output can be seen here: https://dmalcolm.fedorapeople.org/gcc/2025-06-23/state-diagram-1.c.html showing an issue in a singly-linked list; there are various other examples in the parent directory. Generating the SVG diagrams requires an invocation of "dot" per event, so it noticeable slows down diagnostic emission, hence the opt-in command-line flag. However, I'm already finding bugs in -fanalyzer with this that I hadn't seen before. Given that the UI is rather clunky and there is lots of room for improvement to the visualizations, for now this feature is marked as being for GCC developers, not end-users. The patch also adds a dot::ast_node class hierarachy to make it easy to create GraphViz dot files with the correct escaping, and adds a C++ wrapper around pex adding some syntactic sugar for invoking subprocesses. gcc/ChangeLog: PR other/116792 * Makefile.in (ANALYZER_OBJS): Add analyzer/ana-state-to-diagnostic-state.o. (OBJS): Move graphviz.o to... (OBJS-libcommon): ...here. Add diagnostic-state-to-dot.o and pex.o. * diagnostic-format-html.cc: Include "diagnostic-state.h" and "graphviz.h". (html_generation_options::html_generation_options): Initialize the new flags. (HTML_SCRIPT): Add function "get_any_state_diagram". Use it when changing current focus id to update the visibility of the pertinent diagram, if any. (print_pre_source): New. (html_builder::maybe_make_state_diagram): New. (html_path_label_writer::html_path_label_writer): Add "path" param. Initialize m_path and m_curr_event_id. (html_path_label_writer::begin_label): Store current event id. (html_path_label_writer::end_label): Attempt to make a state diagram and add it if successful. (html_path_label_writer::get_element_id): New. (html_path_label_writer::m_path): New field. (html_path_label_writer::m_curr_event_id): New field. (html_builder::make_element_for_diagnostic): Pass path to label writer. * diagnostic-format-html.h (html_generation_options::m_show_state_diagrams): New field. (html_generation_options::m_show_state_diagram_xml): New field. (html_generation_options::m_show_state_diagram_dot_src): New field. * diagnostic-format-sarif.cc: Include "xml.h". (populate_thread_flow_location_object): If requested, attempt to generate xml state and add it to the proeprty bag as "gcc/diagnostic_event/xml_state" in xml source form. (sarif_generation_options::sarif_generation_options): Initialize m_xml_state. * diagnostic-format-sarif.h (sarif_generation_options::m_xml_state): New field. * diagnostic-path.cc: Define INCLUDE_MAP. Include "xml.h". (diagnostic_event::maybe_make_xml_state): New. * diagnostic-path.h (class xml::document): New forward decl. (diagnostic_event::maybe_make_xml_state): New vfunc decl. * diagnostic-state-to-dot.cc: New file. * diagnostic-state.h: New file. * digraph.cc: Define INCLUDE_STRING and INCLUDE_VECTOR. * doc/analyzer.texi: Document state diagrams in html output. (__analyzer_dump_dot): New. (__analyzer_dump_xml): New. * doc/invoke.texi (sarif): Add "xml-state" key. (experimental-html): Add keys "show-state-diagrams", "show-state-diagrams-dot-src" and "show-state-diagrams-xml". * graphviz.cc: Define INCLUDE_MAP, INCLUDE_STRING, and INCLUDE_VECTOR. Include "xml.h", "xml-printer.h", "pex.h" and "selftest.h". (graphviz_out::graphviz_out): Extract... (dot::writer::writer): ...this. (graphviz_out::write_indent): Convert to... (dot::writer::write_indent): ...this. (graphviz_out::print): Use get_pp. (graphviz_out::println): Likewise. (graphviz_out::begin_tr): Likewise. (graphviz_out::end_tr): Likewise. (graphviz_out::begin_td): Likewise. (graphviz_out::end_td): Likewise. (graphviz_out::begin_trtd): Likewise. (graphviz_out::end_tdtr): Likewise. (dot::ast_node::dump): New. (dot::id::id): New. (dot::id::print): New. (dot::id::is_identifier_p): New. (dot::kv_pair::print): New. (dot::attr_list::print): New. (dot::stmt_list::print): New. (dot::stmt_list::add_edge): New. (dot::stmt_list::add_attr): New. (dot::graph::print): New. (dot::stmt_with_attr_list::set_label): New. (dot::node_stmt::print): New. (dot::attr_stmt::print): New. (dot::kv_stmt::print): New. (dot::node_id::print): New. (dot::port::print): New. (dot::edge_stmt::print): New. (dot::subgraph::print): New. (dot::make_svg_document_buffer_from_graph): New. (dot::make_svg_from_graph): New. (selftest:test_ids): New. (selftest:test_trivial_graph): New. (selftest:test_layout_example): New. (selftest:graphviz_cc_tests): New. * graphviz.h (xml::node): New forward decl. (class graphviz_out): Split out into... (class dot::writer): ...this new class (struct dot::ast_node): New. (struct dot::id): New. (struct dot::kv_pair): New. (struct dot::attr_list): New. (struct dot::stmt_list): New. (struct dot::graph): New. (struct dot::stmt): New. (struct dot::stmt_with_attr_list): New. (struct dot::node_stmt): New. (struct dot::attr_stmt): New. (struct dot::kv_stmt): New. (enum class dot::compass_pt): New. (struct dot::port): New. (struct dot::node_id): New. (struct dot::edge_stmt): New. (struct dot::subgraph): New. (dot::make_svg_from_graph): New. * opts-diagnostic.cc (sarif_scheme_handler::make_sink): Add "xml-state" flag. (html_scheme_handler::make_sink): Add flags "show-state-diagrams", "show-state-diagram-dot-src", and "show-state-diagram-xml". * pex.cc: New file. * pex.h: New file. * selftest-run-tests.cc (selftest::run_tests): Call graphviz_cc_tests. * selftest.h (selftest::graphviz_cc_tests): New decl. * xml.cc (xml::node_with_children::add_comment): New. (xml::node_with_children::find_child_element): New. (xml::element::get_attr): New. (xml::comment::write_as_xml): New. (selftest::test_printer): Add coverage of find_child_element and get_attr. (selftest::test_comment): New. (selftest::xml_cc_tests): Call test_comment. * xml.h: New forward decls. (xml::node::dyn_cast_text): Use nullptr. (xml::node::dyn_cast_element): New vfunc. (xml::node_with_children::add_comment): New decl. (xml::node_with_children::find_child_element): New decl. (xml::element::dyn_cast_element): New vfunc impl. (xml::element::get_attr): New decl. (struct xml::comment): New xml::node subclass. gcc/analyzer/ChangeLog: PR other/116792 * ana-state-to-diagnostic-state.cc: New file. * ana-state-to-diagnostic-state.h: New file. * checker-event.cc: Include "xml.h". (checker_event::checker_event): Initialize m_path. (checker_event::prepare_for_emission): Store the path pointer into m_path. (checker_event::maybe_make_xml_state): New. (function_entry_event::function_entry_event): Add "state" param and use it to initialize m_state. (superedge_event::get_program_state): New. (call_event::get_program_state): New. (warning_event::get_program_state): New. * checker-event.h (checker_event::get_program_state): New vfunc. (checker_event::maybe_make_xml_state): New decl. (checker_event::m_path): New field. (statement_event::get_program_state): New vfunc impl. (function_entry_event::function_entry_event): Add "state" param. (function_entry_event::get_program_state): New vfunc impl. (function_entry_event::m_state): New field. (state_change_event::get_program_state): New vfunc impl. (superedge_event::get_program_state): New vfunc decl. (warning_event::warning_event): Add "program_state_" param and copy it. (warning_event::get_program_state): New vfunc decl. (warning_event::m_program_state): New field. * checker-path.h (checker_path::checker_path): Add ext_state param. (checker_path::get_ext_state): New accessor. (checker_path::m_ext_state): New field. * common.h: Define INCLUDE_MAP and INCLUDE_STRING. * diagnostic-manager.cc (saved_diagnostic::operator==): Don't deduplicate dump_path_diagnostic instances. (diagnostic_manager::emit_saved_diagnostic): Pass ext_state to checker_path ctor. * engine.cc: (impl_region_model_context::on_state_leak): Pass old and new state to state_machine::on_leak. (exploded_node::on_stmt_pre): Implement __analyzer_dump_xml and __analyzer_dump_dot. * exploded-graph.h (impl_region_model_context::get_state): New. * infinite-recursion.cc (recursive_function_entry_event::recursive_function_entry_event): Add "dst_state" param and pass to function_entry_event ctor. (infinite_recursion_diagnostic::add_function_entry_event): Pass state to event ctor. * kf-analyzer.cc: Include "analyzer/program-state.h" (dump_path_diagnostic::dump_path_diagnostic): Add "state" param. (dump_path_diagnostic::get_final_state): New. (dump_path_diagnostic::m_state): New field. (kf_analyzer_dump_path::impl_call_pre): Pass state to warning. * pending-diagnostic.cc (pending_diagnostic::add_function_entry_event): Pass state to function_entry_event. (pending_diagnostic::add_final_event): Likewise to warning_event. * pending-diagnostic.h (pending_diagnostic::get_final_state): New vfunc decl. * program-state.cc: Include "diagnostic-state.h", "graphviz.h" and "analyzer/ana-state-to-diagnostic-state.h". (program_state::dump_dot): New. * program-state.h: Include "text-art/tree-widget.h" and "analyzer/store.h". (class xml::document): New forward decl. (make_xml): New. (dump_xml_to_pp): New. (dump_xml_to_file): New. (dump_xml): New. (dump_dot): New. * record-layout.cc (record_layout::record_layout): Make param const_tree. * record-layout.h (item::item): Likewise. (item::m_field): Likewise. (record_layout::record_layout): Likewise. (record_layout::begin): New. (record_layout::end): New. * region-model.cc (exposure_through_uninit_copy::complain_about_fully_uninit_item): Use const_tree. (exposure_through_uninit_copy::complain_about_partially_uninit_item): Likewise. * region-model.h (region_model_context::get_state): New vfunc. (noop_region_model_context::get_state): New. (region_model_context_decorator::get_state): New. * sm-fd.cc (fd_leak::fd_leak): Add "final_state" param and capture it if present. (fd_leak::get_final_state): New. (fd_leak::m_final_state): New. (fd_state_machine::on_open): Pass nullptr for new "final_state" param. (fd_state_machine::on_creat): Likewise. (fd_state_machine::on_socket): Likewise. (fd_state_machine::on_accept): Likewise. (fd_state_machine::on_leak): Add state params and pass new state as final state to fd_leak ctor. * sm-file.cc: Include "analyzer/program-state.h". (file_leak::file_leak): Add "final_state" param and capture it if present. (file_leak::get_final_state): New. (file_leak::m_final_state): New. (fileptr_state_machine::on_leak): Add state params and pass new state as final state to fd_leak ctor. * sm-malloc.cc: Include "analyzer/ana-state-to-diagnostic-state.h". (malloc_leak::malloc_leak): Add "final_state" param and use it. (malloc_leak::get_final_state): New vfunc impl. (malloc_leak::m_final_state): New field. (malloc_state_machine::on_leak): Add state params; capture final state. (malloc_state_machine::add_state_to_xml): New. * sm.cc (state_machine::on_leak): Add "old_state" and "new_state" params. Use nullptr. (state_machine::add_state_to_xml): New. (state_machine::add_global_state_to_xml): New. * sm.h (class xml_state): New forward decl. (state_machine::on_leak): Add state params. (state_machine::add_state_to_xml): New vfunc decl. (state_machine::add_global_state_to_xml): New vfunc decl. * store.h (bit_range::operator<): New. * varargs.cc (va_list_leak::va_list_leak): Add final_state param and capture it if non-null. (va_list_leak::get_final_state): New. (va_list_leak::m_final_state): New. (va_list_state_machine::on_leak): Add state params and pass final state to va_list_leak ctor. gcc/testsuite/ChangeLog: PR other/116792 * g++.dg/analyzer/state-diagram.C: New test. * gcc.dg/analyzer/analyzer-decls.h (__analyzer_dump_dot): New decl. (__analyzer_dump_xml): New decl. * gcc.dg/analyzer/state-diagram-1-sarif.py: New test script. * gcc.dg/analyzer/state-diagram-1.c: New test. * gcc.dg/analyzer/state-diagram-2.c: New test. * gcc.dg/analyzer/state-diagram-3.c: New test. * gcc.dg/analyzer/state-diagram-4.c: New test. * gcc.dg/analyzer/state-diagram-5-html.py: New test script. * gcc.dg/analyzer/state-diagram-5-sarif.py: New test script. * gcc.dg/analyzer/state-diagram-5.c: New test. * gcc.dg/plugin/analyzer_cpython_plugin.cc: Define INCLUDE_STRING. * gcc.dg/plugin/analyzer_gil_plugin.cc: Likewise. * gcc.dg/plugin/analyzer_kernel_plugin.cc: Likewise. * gcc.dg/plugin/analyzer_known_fns_plugin.cc: Likewise. * lib/htmltest.py (ns): Add SVG namespace. * lib/sarif.py (get_result_by_index): New. (get_xml_state): New. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
281 lines
7.7 KiB
C++
281 lines
7.7 KiB
C++
/* Template classes for directed graphs.
|
|
Copyright (C) 2019-2025 Free Software Foundation, Inc.
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#define INCLUDE_STRING
|
|
#define INCLUDE_VECTOR
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "diagnostic.h"
|
|
#include "graphviz.h"
|
|
#include "digraph.h"
|
|
#include "shortest-paths.h"
|
|
#include "selftest.h"
|
|
|
|
#if CHECKING_P
|
|
|
|
namespace selftest {
|
|
|
|
/* A family of digraph classes for writing selftests. */
|
|
|
|
struct test_node;
|
|
struct test_edge;
|
|
struct test_graph;
|
|
struct test_dump_args_t {};
|
|
struct test_cluster;
|
|
|
|
struct test_graph_traits
|
|
{
|
|
typedef test_node node_t;
|
|
typedef test_edge edge_t;
|
|
typedef test_graph graph_t;
|
|
typedef test_dump_args_t dump_args_t;
|
|
typedef test_cluster cluster_t;
|
|
};
|
|
|
|
struct test_node : public dnode<test_graph_traits>
|
|
{
|
|
test_node (const char *name, int index) : m_name (name), m_index (index) {}
|
|
void dump_dot (graphviz_out *, const dump_args_t &) const override
|
|
{
|
|
}
|
|
|
|
const char *m_name;
|
|
int m_index;
|
|
};
|
|
|
|
struct test_edge : public dedge<test_graph_traits>
|
|
{
|
|
test_edge (node_t *src, node_t *dest)
|
|
: dedge<test_graph_traits> (src, dest)
|
|
{}
|
|
|
|
void dump_dot (graphviz_out *gv, const dump_args_t &) const override
|
|
{
|
|
gv->println ("%s %s %s%c", m_src->m_name, "->", m_dest->m_name, ';');
|
|
}
|
|
};
|
|
|
|
struct test_graph : public digraph<test_graph_traits>
|
|
{
|
|
test_node *add_test_node (const char *name)
|
|
{
|
|
test_node *result = new test_node (name, m_nodes.length ());
|
|
add_node (result);
|
|
return result;
|
|
}
|
|
|
|
test_edge *add_test_edge (test_node *src, test_node *dst)
|
|
{
|
|
test_edge *result = new test_edge (src, dst);
|
|
add_edge (result);
|
|
return result;
|
|
}
|
|
};
|
|
|
|
struct test_cluster : public cluster<test_graph_traits>
|
|
{
|
|
};
|
|
|
|
struct test_path
|
|
{
|
|
auto_vec<const test_edge *> m_edges;
|
|
};
|
|
|
|
/* Smoketest of digraph dumping. */
|
|
|
|
static void
|
|
test_dump_to_dot ()
|
|
{
|
|
test_graph g;
|
|
test_node *a = g.add_test_node ("a");
|
|
test_node *b = g.add_test_node ("b");
|
|
g.add_test_edge (a, b);
|
|
|
|
pretty_printer pp;
|
|
pp.set_output_stream (nullptr);
|
|
test_dump_args_t dump_args;
|
|
g.dump_dot_to_pp (&pp, NULL, dump_args);
|
|
|
|
ASSERT_STR_CONTAINS (pp_formatted_text (&pp),
|
|
"a -> b;\n");
|
|
}
|
|
|
|
/* Test shortest paths from A in this digraph,
|
|
where edges run top-to-bottom if not otherwise labeled:
|
|
|
|
A
|
|
/ \
|
|
B C-->D
|
|
| |
|
|
E |
|
|
\ /
|
|
F. */
|
|
|
|
static void
|
|
test_shortest_paths ()
|
|
{
|
|
test_graph g;
|
|
test_node *a = g.add_test_node ("a");
|
|
test_node *b = g.add_test_node ("b");
|
|
test_node *c = g.add_test_node ("d");
|
|
test_node *d = g.add_test_node ("d");
|
|
test_node *e = g.add_test_node ("e");
|
|
test_node *f = g.add_test_node ("f");
|
|
|
|
test_edge *ab = g.add_test_edge (a, b);
|
|
test_edge *ac = g.add_test_edge (a, c);
|
|
test_edge *cd = g.add_test_edge (c, d);
|
|
test_edge *be = g.add_test_edge (b, e);
|
|
test_edge *ef = g.add_test_edge (e, f);
|
|
test_edge *cf = g.add_test_edge (c, f);
|
|
|
|
/* Use "A" as the origin; all nodes should be reachable. */
|
|
{
|
|
shortest_paths<test_graph_traits, test_path> sp (g, a,
|
|
SPS_FROM_GIVEN_ORIGIN);
|
|
|
|
test_path path_to_a = sp.get_shortest_path (a);
|
|
ASSERT_EQ (path_to_a.m_edges.length (), 0); /* Trivial path. */
|
|
|
|
test_path path_to_b = sp.get_shortest_path (b);
|
|
ASSERT_EQ (path_to_b.m_edges.length (), 1);
|
|
ASSERT_EQ (path_to_b.m_edges[0], ab);
|
|
|
|
test_path path_to_c = sp.get_shortest_path (c);
|
|
ASSERT_EQ (path_to_c.m_edges.length (), 1);
|
|
ASSERT_EQ (path_to_c.m_edges[0], ac);
|
|
|
|
test_path path_to_d = sp.get_shortest_path (d);
|
|
ASSERT_EQ (path_to_d.m_edges.length (), 2);
|
|
ASSERT_EQ (path_to_d.m_edges[0], ac);
|
|
ASSERT_EQ (path_to_d.m_edges[1], cd);
|
|
|
|
test_path path_to_e = sp.get_shortest_path (e);
|
|
ASSERT_EQ (path_to_e.m_edges.length (), 2);
|
|
ASSERT_EQ (path_to_e.m_edges[0], ab);
|
|
ASSERT_EQ (path_to_e.m_edges[1], be);
|
|
|
|
test_path path_to_f = sp.get_shortest_path (f);
|
|
ASSERT_EQ (path_to_f.m_edges.length (), 2);
|
|
ASSERT_EQ (path_to_f.m_edges[0], ac);
|
|
ASSERT_EQ (path_to_f.m_edges[1], cf);
|
|
}
|
|
|
|
/* Verify that we gracefully handle an origin from which some nodes
|
|
aren't reachable. */
|
|
|
|
/* Use "B" as the origin, so only E and F are reachable. */
|
|
{
|
|
shortest_paths<test_graph_traits, test_path> sp (g, b,
|
|
SPS_FROM_GIVEN_ORIGIN);
|
|
|
|
test_path path_to_a = sp.get_shortest_path (a);
|
|
ASSERT_EQ (path_to_a.m_edges.length (), 0); /* No path. */
|
|
|
|
test_path path_to_b = sp.get_shortest_path (b);
|
|
ASSERT_EQ (path_to_b.m_edges.length (), 0); /* Trivial path. */
|
|
|
|
test_path path_to_c = sp.get_shortest_path (c);
|
|
ASSERT_EQ (path_to_c.m_edges.length (), 0); /* No path. */
|
|
|
|
test_path path_to_d = sp.get_shortest_path (d);
|
|
ASSERT_EQ (path_to_d.m_edges.length (), 0); /* No path. */
|
|
|
|
test_path path_to_e = sp.get_shortest_path (e);
|
|
ASSERT_EQ (path_to_e.m_edges.length (), 1);
|
|
ASSERT_EQ (path_to_e.m_edges[0], be);
|
|
|
|
test_path path_to_f = sp.get_shortest_path (f);
|
|
ASSERT_EQ (path_to_f.m_edges.length (), 2);
|
|
ASSERT_EQ (path_to_f.m_edges[0], be);
|
|
ASSERT_EQ (path_to_f.m_edges[1], ef);
|
|
}
|
|
|
|
/* Use "C" as the origin, so only D and F are reachable. */
|
|
{
|
|
shortest_paths<test_graph_traits, test_path> sp (g, c,
|
|
SPS_FROM_GIVEN_ORIGIN);
|
|
|
|
test_path path_to_a = sp.get_shortest_path (a);
|
|
ASSERT_EQ (path_to_a.m_edges.length (), 0); /* No path. */
|
|
|
|
test_path path_to_b = sp.get_shortest_path (b);
|
|
ASSERT_EQ (path_to_b.m_edges.length (), 0); /* No path. */
|
|
|
|
test_path path_to_c = sp.get_shortest_path (c);
|
|
ASSERT_EQ (path_to_c.m_edges.length (), 0); /* Trivial path. */
|
|
|
|
test_path path_to_d = sp.get_shortest_path (d);
|
|
ASSERT_EQ (path_to_d.m_edges.length (), 1);
|
|
ASSERT_EQ (path_to_d.m_edges[0], cd);
|
|
|
|
test_path path_to_e = sp.get_shortest_path (e);
|
|
ASSERT_EQ (path_to_e.m_edges.length (), 0); /* No path. */
|
|
|
|
test_path path_to_f = sp.get_shortest_path (f);
|
|
ASSERT_EQ (path_to_f.m_edges.length (), 1);
|
|
ASSERT_EQ (path_to_f.m_edges[0], cf);
|
|
}
|
|
|
|
/* Test of SPS_TO_GIVEN_TARGET. Use "F" as the target. */
|
|
{
|
|
shortest_paths<test_graph_traits, test_path> sp (g, f,
|
|
SPS_TO_GIVEN_TARGET);
|
|
|
|
test_path path_to_a = sp.get_shortest_path (a);
|
|
ASSERT_EQ (path_to_a.m_edges.length (), 2);
|
|
ASSERT_EQ (path_to_a.m_edges[0], ac);
|
|
ASSERT_EQ (path_to_a.m_edges[1], cf);
|
|
|
|
test_path path_to_b = sp.get_shortest_path (b);
|
|
ASSERT_EQ (path_to_b.m_edges.length (), 2);
|
|
ASSERT_EQ (path_to_b.m_edges[0], be);
|
|
ASSERT_EQ (path_to_b.m_edges[1], ef);
|
|
|
|
test_path path_to_c = sp.get_shortest_path (c);
|
|
ASSERT_EQ (path_to_c.m_edges.length (), 1);
|
|
ASSERT_EQ (path_to_c.m_edges[0], cf);
|
|
|
|
test_path path_to_d = sp.get_shortest_path (d);
|
|
ASSERT_EQ (path_to_d.m_edges.length (), 0); /* No path. */
|
|
|
|
test_path path_to_e = sp.get_shortest_path (e);
|
|
ASSERT_EQ (path_to_e.m_edges.length (), 1);
|
|
ASSERT_EQ (path_to_e.m_edges[0], ef);
|
|
|
|
test_path path_to_f = sp.get_shortest_path (f);
|
|
ASSERT_EQ (path_to_f.m_edges.length (), 0);
|
|
}
|
|
}
|
|
|
|
/* Run all of the selftests within this file. */
|
|
|
|
void
|
|
digraph_cc_tests ()
|
|
{
|
|
test_dump_to_dot ();
|
|
test_shortest_paths ();
|
|
}
|
|
|
|
} // namespace selftest
|
|
|
|
#endif /* #if CHECKING_P */
|