Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
graph_visualization_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
43#include <gtest/gtest.h>
44#include <sstream>
45#include <string>
46
47#include <tpl_graph.H>
48#include <tpl_graph_utils.H>
49#include <generate_graph.H>
50#include <graph_to_tree.H>
52#include <generate_tree.H>
53#include <tpl_tree_node.H>
54
55using namespace std;
56using namespace testing;
57using namespace Aleph;
58
59// ============================================================================
60// Test Fixtures
61// ============================================================================
62
64class SimpleGraphTest : public Test
65{
66protected:
69 using Arc = Graph::Arc;
70
75
76 void SetUp() override
77 {
78 n1 = g.insert_node(1);
79 n2 = g.insert_node(2);
80 n3 = g.insert_node(3);
81 g.insert_arc(n1, n2, 10);
82 g.insert_arc(n2, n3, 20);
83 g.insert_arc(n1, n3, 30);
84 }
85};
86
88class SimpleDigraphTest : public Test
89{
90protected:
94
99
100 void SetUp() override
101 {
102 n1 = g.insert_node(1);
103 n2 = g.insert_node(2);
104 n3 = g.insert_node(3);
105 g.insert_arc(n1, n2, 10);
106 g.insert_arc(n2, n3, 20);
107 g.insert_arc(n1, n3, 30);
108 }
109};
110
112class TreeGraphTest : public Test
113{
114protected:
118
124
125 void SetUp() override
126 {
127 // Create a simple tree:
128 // root(1)
129 // / |
130 // child1(2) child2(3)
131 // |
132 // grandchild(4)
133 root = tree.insert_node(1);
137
141 }
142};
143
144// ============================================================================
145// generate_graph.H Tests - Graphviz DOT generation
146// ============================================================================
147
149{
150 ostringstream out;
152 string result = out.str();
153
154 // Should contain "graph {" for undirected graphs
155 EXPECT_NE(result.find("graph {"), string::npos);
156 // Should NOT contain "digraph"
157 EXPECT_EQ(result.find("digraph {"), string::npos);
158}
159
161{
162 ostringstream out;
164 string result = out.str();
165
166 // Should contain "digraph {" for directed graphs
167 EXPECT_NE(result.find("digraph {"), string::npos);
168}
169
171{
172 ostringstream out;
174 string result = out.str();
175
176 // Should contain labels for all nodes
177 EXPECT_NE(result.find("label = \"1\""), string::npos);
178 EXPECT_NE(result.find("label = \"2\""), string::npos);
179 EXPECT_NE(result.find("label = \"3\""), string::npos);
180}
181
183{
184 ostringstream out;
186 string result = out.str();
187
188 // Should contain "--" for undirected graph arcs
189 EXPECT_NE(result.find("--"), string::npos);
190}
191
193{
194 ostringstream out;
196 string result = out.str();
197
198 // Should contain "->" for directed graph arcs
199 EXPECT_NE(result.find("->"), string::npos);
200}
201
203{
204 ostringstream out;
206 string result = out.str();
207
208 EXPECT_NE(result.find("rankdir = TB"), string::npos);
209}
210
212{
213 ostringstream out;
215 string result = out.str();
216
217 // Should end with closing brace
218 size_t lastBrace = result.rfind('}');
219 EXPECT_NE(lastBrace, string::npos);
220}
221
222// Test default attribute functors
224{
225 ostringstream out;
226 Dft_Node_Attr<Graph>()(g, n1, out);
227 string result = out.str();
228
229 EXPECT_NE(result.find("label"), string::npos);
230 EXPECT_NE(result.find("1"), string::npos);
231}
232
234{
235 auto arc = g.get_first_arc();
236 ostringstream out;
237 Dft_Arc_Attr<Graph>()(g, arc, out);
238 string result = out.str();
239
240 EXPECT_NE(result.find("label"), string::npos);
241}
242
243// Test Dummy_Attr always returns false
250
251// ============================================================================
252// generate_spanning_tree_picture.H Tests
253// ============================================================================
254
261
263{
264 NODE_COOKIE(n1) = n2; // non-null cookie
266 EXPECT_EQ(shader(n1), "SHADOW-NODE");
267}
268
270{
271 auto arc = g.get_first_arc();
272 ARC_COOKIE(arc) = nullptr;
274 EXPECT_EQ(shader(arc), "ARC");
275}
276
278{
279 auto arc = g.get_first_arc();
280 ARC_COOKIE(arc) = n1; // non-null cookie
282 EXPECT_EQ(shader(arc), "SHADOW-ARC");
283}
284
285// ============================================================================
286// graph_to_tree.H Tests
287// ============================================================================
288
291{
294 {
295 tnode->get_key() = gnode->get_info();
296 }
297};
298
309
311{
314
315 // Root should have children
316 auto* firstChild = treeRoot->get_left_child();
317 ASSERT_NE(firstChild, nullptr);
318
319 // Count children
320 int childCount = 0;
321 for (auto* child = firstChild; child != nullptr;
322 child = child->get_right_sibling())
323 childCount++;
324
325 EXPECT_EQ(childCount, 2); // child1 and child2
326
328}
329
331{
334
335 // Find all node values
336 set<int> values;
338 if (node == nullptr) return;
339 values.insert(node->get_key());
340 for (auto* child = node->get_left_child(); child != nullptr;
341 child = child->get_right_sibling())
342 collectValues(child);
343 };
344
346
347 // Should contain all original values
348 EXPECT_TRUE(values.count(1) > 0);
349 EXPECT_TRUE(values.count(2) > 0);
350 EXPECT_TRUE(values.count(3) > 0);
351 EXPECT_TRUE(values.count(4) > 0);
352 EXPECT_EQ(values.size(), 4u);
353
355}
356
358{
359 // Add an arc to create a cycle
360 tree.insert_arc(grandchild, root, 0);
361
363 EXPECT_THROW(Converter()(tree, root), std::domain_error);
364}
365
366// ============================================================================
367// generate_tree.H Tests (additional to existing tests)
368// ============================================================================
369
371{
374
375 ostringstream out;
377 string result = out.str();
378
379 // Should start with Root
380 EXPECT_EQ(result.find("Root"), 0u);
381
383}
384
386{
389
390 ostringstream out;
392 string result = out.str();
393
394 // Should contain Node entries with Dewey notation
395 EXPECT_NE(result.find("Node "), string::npos);
396
398}
399
400// ============================================================================
401// Integration Tests
402// ============================================================================
403
405{
406 // Convert graph to tree
409
410 // Generate tree output
411 ostringstream out;
413 string result = out.str();
414
415 // Verify output is non-empty and has expected structure
416 EXPECT_FALSE(result.empty());
417 EXPECT_NE(result.find("Root"), string::npos);
418 EXPECT_NE(result.find("\"1\""), string::npos); // Root value
419
421}
422
423// ============================================================================
424// Edge Cases
425// ============================================================================
426
428{
430
431 ostringstream out;
433 string result = out.str();
434
435 // Should still produce valid DOT structure
436 EXPECT_NE(result.find("graph {"), string::npos);
437 EXPECT_NE(result.find("}"), string::npos);
438}
439
441{
443 g.insert_node(42);
444
445 ostringstream out;
447 string result = out.str();
448
449 EXPECT_NE(result.find("42"), string::npos);
450}
451
453{
455 auto* node = g.insert_node(42);
456
457 struct Conv {
458 void operator()(decltype(node) gn, Tree_Node<int>* tn) {
459 tn->get_key() = gn->get_info();
460 }
461 };
462
465
466 ASSERT_NE(treeRoot, nullptr);
467 EXPECT_EQ(treeRoot->get_key(), 42);
468 EXPECT_EQ(treeRoot->get_left_child(), nullptr);
469
471}
472
473// ============================================================================
474// Type Traits Tests
475// ============================================================================
476
478{
479 Tree_Node<int> node;
480 node.get_key() = 123;
481
483 string result = writer(&node);
484
485 EXPECT_EQ(result, "123");
486}
487
488// ============================================================================
489// Additional generate_graph.H Tests
490// ============================================================================
491
492// Test digraph_graphviz() - always outputs digraph format
494{
495 // Custom functors for digraph_graphviz
496 struct NodeAttr {
497 void operator()(const Graph&, Node* n, ostream& out) {
498 out << "label=\"" << n->get_info() << "\"";
499 }
500 };
501 struct ArcAttr {
502 void operator()(const Graph&, Arc* a, ostream& out) {
503 out << "label=\"" << a->get_info() << "\"";
504 }
505 };
506
507 ostringstream out;
509 (g, out);
510 string result = out.str();
511
512 // Should contain "digraph {" even for undirected graph
513 EXPECT_NE(result.find("digraph {"), string::npos);
514 EXPECT_NE(result.find("->"), string::npos);
515}
516
517// Test Generate_Graphviz struct
519{
520 struct WriteNode {
521 string operator()(Node* n) const { return to_string(n->get_info()); }
522 };
523 struct WriteArc {
524 string operator()(Arc* a) const { return to_string(a->get_info()); }
525 };
526
527 ostringstream out;
529 string result = out.str();
530
531 EXPECT_NE(result.find("graph {"), string::npos);
532 EXPECT_NE(result.find("rankdir = TB"), string::npos);
533}
534
535// Test custom rankdir values
537{
538 const vector<string> rankdirs = {"TB", "BT", "LR", "RL"};
539
540 for (const auto& dir : rankdirs) {
541 ostringstream out;
543 string result = out.str();
544 EXPECT_NE(result.find("rankdir = " + dir), string::npos)
545 << "Failed for rankdir: " << dir;
546 }
547}
548
549// Test with string node info
551{
553 SGraph g;
554 auto* a = g.insert_node("Alpha");
555 auto* b = g.insert_node("Beta");
556 g.insert_arc(a, b, 1);
557
558 ostringstream out;
560 string result = out.str();
561
562 EXPECT_NE(result.find("Alpha"), string::npos);
563 EXPECT_NE(result.find("Beta"), string::npos);
564}
565
566// Test with double arc weights
568{
570 DGraph g;
571 auto* a = g.insert_node(1);
572 auto* b = g.insert_node(2);
573 g.insert_arc(a, b, 3.14159);
574
575 ostringstream out;
577 string result = out.str();
578
579 EXPECT_NE(result.find("3.14"), string::npos);
580}
581
582// Test DAG with rank_graphviz
584{
586 DAG g;
587
588 // Create a simple DAG: 1 -> 2 -> 3
589 auto* n1 = g.insert_node(1);
590 auto* n2 = g.insert_node(2);
591 auto* n3 = g.insert_node(3);
592 g.insert_arc(n1, n2, 0);
593 g.insert_arc(n2, n3, 0);
594
595 struct NodeAttr {
596 void operator()(const DAG&, DAG::Node* n, ostream& out) {
597 out << "label=\"" << n->get_info() << "\"";
598 }
599 };
600 struct ArcAttr {
601 void operator()(const DAG&, DAG::Arc*, ostream& out) {
602 out << "";
603 }
604 };
605
606 ostringstream out;
609
610 string result = out.str();
611
612 // Should have multiple ranks
613 EXPECT_GE(numRanks, 1u);
614 // Should contain subgraph declarations
615 EXPECT_NE(result.find("subgraph"), string::npos);
616 EXPECT_NE(result.find("rank"), string::npos);
617}
618
619// ============================================================================
620// Additional generate_tree.H Tests
621// ============================================================================
622
623// Test generate_forest with multiple trees
625{
626 // Create a forest of 3 single-node trees as siblings
630
631 tree1->get_key() = 100;
632 tree2->get_key() = 200;
633 tree3->get_key() = 300;
634
635 // Link as siblings
636 tree1->insert_right_sibling(tree2);
637 tree2->insert_right_sibling(tree3);
638
639 ostringstream out;
641 string result = out.str();
642
643 // Should contain all three roots
644 EXPECT_NE(result.find("100"), string::npos);
645 EXPECT_NE(result.find("200"), string::npos);
646 EXPECT_NE(result.find("300"), string::npos);
647
648 // Should have multiple "Root" entries
649 size_t firstRoot = result.find("Root");
650 size_t secondRoot = result.find("Root", firstRoot + 1);
651 size_t thirdRoot = result.find("Root", secondRoot + 1);
652
653 EXPECT_NE(firstRoot, string::npos);
654 EXPECT_NE(secondRoot, string::npos);
655 EXPECT_NE(thirdRoot, string::npos);
656
657 // Clean up - siblings are not automatically deleted
658 delete tree1;
659 delete tree2;
660 delete tree3;
661}
662
663// Test custom Write functor
665{
666 struct HexWriter {
667 string operator()(Tree_Node<int>* p) {
668 ostringstream ss;
669 ss << "0x" << hex << p->get_key();
670 return ss.str();
671 }
672 };
673
675 root->get_key() = 255;
676
677 ostringstream out;
679 string result = out.str();
680
681 EXPECT_NE(result.find("0xff"), string::npos);
682
683 delete root;
684}
685
686// Test deep tree (many levels)
688{
689 const int DEPTH = 10;
691 root->get_key() = 0;
692
693 Tree_Node<int>* current = root;
694 for (int i = 1; i < DEPTH; ++i) {
695 Tree_Node<int>* child = new Tree_Node<int>;
696 child->get_key() = i;
697 current->insert_leftmost_child(child);
698 current = child;
699 }
700
701 ostringstream out;
703 string result = out.str();
704
705 // Should contain all nodes
706 for (int i = 0; i < DEPTH; ++i) {
707 EXPECT_NE(result.find("\"" + to_string(i) + "\""), string::npos)
708 << "Missing node " << i;
709 }
710
711 // Should have deep Dewey notation like "0.0.0.0..."
712 // The deepest node would have 9 dots in its Dewey number
713 EXPECT_NE(result.find("Node 0.0.0.0"), string::npos);
714
716}
717
718// Test wide tree (many siblings)
720{
721 const int WIDTH = 10;
723 root->get_key() = 0;
724
725 for (int i = 1; i <= WIDTH; ++i) {
726 Tree_Node<int>* child = new Tree_Node<int>;
727 child->get_key() = i;
728 root->insert_rightmost_child(child);
729 }
730
731 ostringstream out;
733 string result = out.str();
734
735 // Should contain all children
736 for (int i = 1; i <= WIDTH; ++i) {
737 EXPECT_NE(result.find("\"" + to_string(i) + "\""), string::npos)
738 << "Missing child " << i;
739 }
740
742}
743
744// ============================================================================
745// graph_to_tree.H Additional Tests
746// ============================================================================
747
748// Test free function graph_to_tree_node (not just the class)
759
760// Test with different starting nodes
762{
763 // Start from child1 instead of root
766
767 ASSERT_NE(treeRoot, nullptr);
768 EXPECT_EQ(treeRoot->get_key(), 2); // child1's value
769
770 // Should have grandchild as child (and root as parent via traversal)
771 auto* firstChild = treeRoot->get_left_child();
772 ASSERT_NE(firstChild, nullptr);
773
775}
776
777// Test converter that transforms data
779{
780 struct DoubleConvert {
782 void operator()(GNode* gnode, Tree_Node<int>* tnode) {
783 tnode->get_key() = gnode->get_info() * 2; // Double the value
784 }
785 };
786
789
790 EXPECT_EQ(treeRoot->get_key(), 2); // 1 * 2 = 2
791
793}
794
795// Test with string keys in tree
797{
798 struct StringConvert {
800 void operator()(GNode* gnode, Tree_Node<string>* tnode) {
801 tnode->get_key() = "Node_" + to_string(gnode->get_info());
802 }
803 };
804
807
808 EXPECT_EQ(treeRoot->get_key(), "Node_1");
809
811}
812
813// ============================================================================
814// Custom Filter Tests
815// ============================================================================
816
817// Custom arc filter that only shows arcs with weight > threshold
818template <typename GT>
820{
821 int threshold = 15;
822
823 bool operator()(typename GT::Arc* a) const {
824 return a->get_info() > threshold;
825 }
826};
827
829{
830 // Our graph has arcs with weights 10, 20, 30
831 // Filter should only show arcs > 15 (so 20 and 30)
832
833 struct NodeAttr {
834 void operator()(const Graph&, Node* n, ostream& out) {
835 out << "label=\"" << n->get_info() << "\"";
836 }
837 };
838 struct ArcAttr {
839 void operator()(const Graph&, Arc* a, ostream& out) {
840 out << "label=\"" << a->get_info() << "\"";
841 }
842 };
843
844 ostringstream out;
846 (g, out, NodeAttr(), ArcAttr(), "LR");
847 string result = out.str();
848
849 // Should contain arcs with weight 20 and 30
850 EXPECT_NE(result.find("20"), string::npos);
851 EXPECT_NE(result.find("30"), string::npos);
852 // Should NOT contain arc with weight 10
853 EXPECT_EQ(result.find("\"10\""), string::npos);
854}
855
856// ============================================================================
857// Stress Tests
858// ============================================================================
859
861{
863 LGraph g;
864
865 const int NUM_NODES = 100;
867
868 // Create nodes
869 for (int i = 0; i < NUM_NODES; ++i) {
870 nodes.push_back(g.insert_node(i));
871 }
872
873 // Create a path
874 for (int i = 0; i < NUM_NODES - 1; ++i) {
875 g.insert_arc(nodes[i], nodes[i + 1], i);
876 }
877
878 ostringstream out;
880 string result = out.str();
881
882 // Should produce valid output
883 EXPECT_NE(result.find("graph {"), string::npos);
884 EXPECT_NE(result.find("}"), string::npos);
885
886 // Should contain first and last nodes
887 EXPECT_NE(result.find("\"0\""), string::npos);
888 EXPECT_NE(result.find("\"99\""), string::npos);
889}
890
892{
894 LGraph tree;
895
896 const int NUM_NODES = 100;
898
899 // Create nodes
900 for (int i = 0; i < NUM_NODES; ++i) {
901 nodes.push_back(tree.insert_node(i));
902 }
903
904 // Create a binary-ish tree structure
905 for (int i = 1; i < NUM_NODES; ++i) {
906 int parent = (i - 1) / 2;
907 tree.insert_arc(nodes[parent], nodes[i], 0);
908 }
909
910 struct Conv {
911 void operator()(LGraph::Node* gn, Tree_Node<int>* tn) {
912 tn->get_key() = gn->get_info();
913 }
914 };
915
918
919 ASSERT_NE(treeRoot, nullptr);
920 EXPECT_EQ(treeRoot->get_key(), 0);
921
922 // Count all nodes
923 int count = 0;
925 if (node == nullptr) return;
926 count++;
927 for (auto* child = node->get_left_child(); child != nullptr;
928 child = child->get_right_sibling())
929 countNodes(child);
930 };
932
933 EXPECT_EQ(count, NUM_NODES);
934
936}
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Arc Arc
Definition graph-dry.H:3852
typename BaseGraph::Node Node
Definition graph-dry.H:3851
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
Functor class to convert a tree graph to Tree_Node structure.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Graph_Arc< int > Arc
The node class type.
Definition tpl_graph.H:433
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Graph class implemented with singly-linked adjacency lists.
Definition tpl_sgraph.H:274
Arc * insert_arc(Node *src, Node *tgt, void *a)
Definition tpl_sgraph.H:497
virtual Node * insert_node(Node *p)
Insertion of a node whose memory has already been allocated.
Definition tpl_sgraph.H:487
Generic m-ary trees.
void insert_leftmost_child(Tree_Node *p) noexcept
Inserts p as the leftmost child of this.
T & get_key() noexcept
Returns a modifiable reference to the node contents.
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
Definition graph-dry.H:595
Simple digraph for testing.
Simple graph for testing.
Tree graph for graph_to_tree conversion testing.
#define TEST(name)
Grafo::Node GNode
Definition floyd.cc:48
Graph visualization and output generation utilities.
Spanning tree visualization with highlighted tree/non-tree edges.
Tree visualization and output generation.
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
Convert spanning tree graphs to Tree_Node structures.
TEST_F(SimpleGraphTest, GenerateGraphvizContainsGraphKeyword)
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
size_t rank_graphviz(const GT &g, std::ostream &out, Node_Attr node_attr=Node_Attr(), Arc_Attr arc_attr=Arc_Attr(), const std::string &rankdir="LR")
Generate Graphviz DOT output with topological ranking.
#define ARC_COOKIE(p)
Return the arc cookie
#define NODE_COOKIE(p)
Return the node cookie
void generate_forest(Node *root, std::ostream &out)
Generate a forest specification for the ntreepic drawing tool.
void generate_tree(Node *root, std::ostream &out, const int &tree_number=0)
Generate a tree specification for the ntreepic drawing tool.
void destroy_tree(Node *root)
Destroys (frees memory) the tree whose root is root.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::string to_string(const time_t t, const std::string &format)
Format a time_t value into a string using format.
Definition ah-date.H:140
DynList< T > maps(const C &c, Op op)
Classic map operation.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
STL namespace.
#define WIDTH(p)
Definition ntreepic.C:361
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Default filter for the graph nodes.
Definition tpl_graph.H:1192
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Functor class for generating Graphviz DOT specifications.
Converter from Graph::Node to int for Tree_Node.
List_Graph< Graph_Node< int >, Graph_Arc< int > >::Node GraphNode
void operator()(GraphNode *gnode, Tree_Node< int > *tnode)
Shading functor for spanning tree arcs.
Shading functor for spanning tree nodes.
bool operator()(typename GT::Arc *a) const
Functor to convert tree node to string for output.
string operator()(Tree_Node< int > *p)
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.
General tree (n-ary tree) node.