Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
io_graph_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
37#include <gtest/gtest.h>
38#include <sstream>
39#include <cstdio>
40#include <algorithm>
41#include <filesystem>
42#include <io_graph.H>
43#include <tpl_graph.H>
44#if defined(_WIN32)
45# include <process.h>
46#else
47# include <unistd.h>
48#endif
49
50using namespace Aleph;
51
52namespace
53{
62long long process_id() noexcept
63{
64#if defined(_WIN32)
65 return static_cast<long long>(_getpid());
66#else
67 return static_cast<long long>(getpid());
68#endif
69}
70} // namespace
71
72//============================================================================
73// Test Fixtures
74//============================================================================
75
76class IOGraphTest : public ::testing::Test
77{
78protected:
81 using Arc = Graph::Arc;
82
83 void SetUp() override
84 {
85 // Create a simple graph:
86 // 0 --1.5--> 1 --2.5--> 2
87 // |
88 // 3.5
89 // v
90 // 3
91
92 n0 = g.insert_node(100);
93 n1 = g.insert_node(200);
94 n2 = g.insert_node(300);
95 n3 = g.insert_node(400);
96
97 g.insert_arc(n0, n1, 1.5);
98 g.insert_arc(n1, n2, 2.5);
99 g.insert_arc(n1, n3, 3.5);
100
101 // Unique per test case and process (see the `process_id()` comment
102 // above for why a plain rand() is not enough).
103 const auto *test_info = ::testing::UnitTest::GetInstance()->current_test_info();
104 std::string base = std::string("aleph_io_graph_test_") + test_info->name() + "_" +
105 std::to_string(process_id());
106 for (auto &ch : base)
107 if (ch == '/' or ch == '\\' or ch == ' ')
108 ch = '_';
109
110 binary_file = (std::filesystem::temp_directory_path() / (base + ".bin")).string();
111 text_file = (std::filesystem::temp_directory_path() / (base + ".txt")).string();
112 }
113
114 void TearDown() override
115 {
116 std::remove(binary_file.c_str());
117 std::remove(text_file.c_str());
118 }
119
120 // Helper to collect node values
121 std::vector<int> get_node_values(Graph & graph)
122 {
123 std::vector<int> values;
124 for (typename Graph::Node_Iterator it(graph); it.has_curr(); it.next())
125 values.push_back(it.get_curr()->get_info());
126 std::sort(values.begin(), values.end());
127 return values;
128 }
129
130 // Helper to collect arc values
131 std::vector<double> get_arc_values(Graph & graph)
132 {
133 std::vector<double> values;
134 for (typename Graph::Arc_Iterator it(graph); it.has_curr(); it.next())
135 values.push_back(it.get_curr()->get_info());
136 std::sort(values.begin(), values.end());
137 return values;
138 }
139
141 Node *n0, *n1, *n2, *n3;
142 std::string binary_file;
143 std::string text_file;
144};
145
146//============================================================================
147// Constructor Tests
148//============================================================================
149
155
161
163{
165
166 EXPECT_FALSE(io.is_verbose());
167
168 io.set_verbose(true);
169 EXPECT_TRUE(io.is_verbose());
170
171 io.set_verbose(false);
172 EXPECT_FALSE(io.is_verbose());
173}
174
175//============================================================================
176// Binary Mode Tests
177//============================================================================
178
180{
182
183 // Save
184 {
185 std::ofstream out(binary_file, std::ios::binary);
186 ASSERT_TRUE(out.is_open());
187 io.save(out);
188 }
189
190 // Load into new graph
191 Graph g2;
193
194 {
195 std::ifstream in(binary_file, std::ios::binary);
196 ASSERT_TRUE(in.is_open());
197 io2.load(in);
198 }
199
200 // Verify
201 EXPECT_EQ(g2.get_num_nodes(), 4u);
202 EXPECT_EQ(g2.get_num_arcs(), 3u);
203}
204
206{
208
209 // Save
210 {
211 std::ofstream out(binary_file, std::ios::binary);
212 io.save(out);
213 }
214
215 // Load
216 Graph g2;
218
219 {
220 std::ifstream in(binary_file, std::ios::binary);
221 io2.load(in);
222 }
223
224 EXPECT_EQ(get_node_values(g), get_node_values(g2));
225}
226
228{
230
231 // Save
232 {
233 std::ofstream out(binary_file, std::ios::binary);
234 io.save(out);
235 }
236
237 // Load
238 Graph g2;
240
241 {
242 std::ifstream in(binary_file, std::ios::binary);
243 io2.load(in);
244 }
245
246 EXPECT_EQ(get_arc_values(g), get_arc_values(g2));
247}
248
249//============================================================================
250// Text Mode Tests
251//============================================================================
252
254{
256
257 // Save
258 {
259 std::ofstream out(text_file);
260 ASSERT_TRUE(out.is_open());
261 io.save_in_text_mode(out);
262 }
263
264 // Load into new graph
265 Graph g2;
267
268 {
269 std::ifstream in(text_file);
270 ASSERT_TRUE(in.is_open());
271 io2.load_in_text_mode(in);
272 }
273
274 // Verify
275 EXPECT_EQ(g2.get_num_nodes(), 4u);
276 EXPECT_EQ(g2.get_num_arcs(), 3u);
277}
278
280{
282
283 // Save
284 {
285 std::ofstream out(text_file);
286 io.save_in_text_mode(out);
287 }
288
289 // Load
290 Graph g2;
292
293 {
294 std::ifstream in(text_file);
295 io2.load_in_text_mode(in);
296 }
297
298 EXPECT_EQ(get_node_values(g), get_node_values(g2));
299}
300
302{
304
305 std::ostringstream out;
306 io.save_in_text_mode(out);
307
308 std::string content = out.str();
309
310 // Should start with node count and arc count
311 EXPECT_TRUE(content.find("4") != std::string::npos); // 4 nodes
312 EXPECT_TRUE(content.find("3") != std::string::npos); // 3 arcs
313}
314
315//============================================================================
316// Empty Graph Tests
317//============================================================================
318
320{
323
324 // Save
325 {
326 std::ofstream out(binary_file, std::ios::binary);
327 io.save(out);
328 }
329
330 // Load
331 Graph g2;
333
334 {
335 std::ifstream in(binary_file, std::ios::binary);
336 io2.load(in);
337 }
338
339 EXPECT_EQ(g2.get_num_nodes(), 0u);
340 EXPECT_EQ(g2.get_num_arcs(), 0u);
341}
342
344{
347
348 // Save
349 {
350 std::ofstream out(text_file);
351 io.save_in_text_mode(out);
352 }
353
354 // Load
355 Graph g2;
357
358 {
359 std::ifstream in(text_file);
360 io2.load_in_text_mode(in);
361 }
362
363 EXPECT_EQ(g2.get_num_nodes(), 0u);
364 EXPECT_EQ(g2.get_num_arcs(), 0u);
365}
366
367//============================================================================
368// Single Node Graph Tests
369//============================================================================
370
372{
375
377
378 // Save
379 {
380 std::ofstream out(binary_file, std::ios::binary);
381 io.save(out);
382 }
383
384 // Load
385 Graph g2;
387
388 {
389 std::ifstream in(binary_file, std::ios::binary);
390 io2.load(in);
391 }
392
393 EXPECT_EQ(g2.get_num_nodes(), 1u);
394 EXPECT_EQ(g2.get_num_arcs(), 0u);
395
396 typename Graph::Node_Iterator it(g2);
397 EXPECT_EQ(it.get_curr()->get_info(), 42);
398}
399
400//============================================================================
401// Digraph Tests
402//============================================================================
403
404class IODigraphTest : public ::testing::Test
405{
406protected:
410
411 void SetUp() override
412 {
413 n0 = dg.insert_node(10);
414 n1 = dg.insert_node(20);
415 n2 = dg.insert_node(30);
416
417 dg.insert_arc(n0, n1, 1);
418 dg.insert_arc(n1, n2, 2);
419 dg.insert_arc(n2, n0, 3); // Cycle
420
421 // Unique per test case and process (see the `process_id()` comment
422 // near the top of the file for why a plain rand() is not enough).
423 const auto *test_info = ::testing::UnitTest::GetInstance()->current_test_info();
424 std::string base = std::string("aleph_io_digraph_test_") + test_info->name() + "_" +
425 std::to_string(process_id());
426 for (auto &ch : base)
427 if (ch == '/' or ch == '\\' or ch == ' ')
428 ch = '_';
429
430 binary_file = (std::filesystem::temp_directory_path() / (base + ".bin")).string();
431 text_file = (std::filesystem::temp_directory_path() / (base + ".txt")).string();
432 }
433
434 void TearDown() override
435 {
436 std::remove(binary_file.c_str());
437 std::remove(text_file.c_str());
438 }
439
441 Node *n0, *n1, *n2;
442 std::string binary_file;
443 std::string text_file;
444};
445
447{
449
450 // Save
451 {
452 std::ofstream out(binary_file, std::ios::binary);
453 io.save(out);
454 }
455
456 // Load
457 Digraph dg2;
459
460 {
461 std::ifstream in(binary_file, std::ios::binary);
462 io2.load(in);
463 }
464
465 EXPECT_EQ(dg2.get_num_nodes(), 3u);
466 EXPECT_EQ(dg2.get_num_arcs(), 3u);
467}
468
470{
472
473 // Save
474 {
475 std::ofstream out(text_file);
476 io.save_in_text_mode(out);
477 }
478
479 // Load
480 Digraph dg2;
482
483 {
484 std::ifstream in(text_file);
485 io2.load_in_text_mode(in);
486 }
487
488 EXPECT_EQ(dg2.get_num_nodes(), 3u);
489 EXPECT_EQ(dg2.get_num_arcs(), 3u);
490}
491
492//============================================================================
493// Default Functor Tests
494//============================================================================
495
497{
499 Graph g;
500 auto n = g.insert_node(12345);
501
503
504 std::ostringstream ss;
505 store(ss, g, n);
506
507 EXPECT_TRUE(ss.str().find("12345") != std::string::npos);
508}
509
511{
513 Graph g;
514 auto n1 = g.insert_node(1);
515 auto n2 = g.insert_node(2);
516 auto a = g.insert_arc(n1, n2, 3.14159);
517
519
520 std::ostringstream ss;
521 store(ss, g, a);
522
523 EXPECT_TRUE(ss.str().find("3.14") != std::string::npos);
524}
525
527{
529 Graph g;
530 auto n = g.insert_node(0);
531
533
534 std::istringstream ss("999");
535 load(ss, g, n);
536
537 EXPECT_EQ(n->get_info(), 999);
538}
539
541{
543 Graph g;
544 auto n1 = g.insert_node(1);
545 auto n2 = g.insert_node(2);
546 auto a = g.insert_arc(n1, n2, 0.0);
547
549
550 std::istringstream ss("2.718");
551 load(ss, g, a);
552
553 EXPECT_NEAR(a->get_info(), 2.718, 0.001);
554}
555
556//============================================================================
557// Setter Tests
558//============================================================================
559
561{
564 io.set_load_node(ln); // Should not throw
565 SUCCEED();
566}
567
569{
572 io.set_store_node(sn); // Should not throw
573 SUCCEED();
574}
575
577{
580 io.set_load_arc(la); // Should not throw
581 SUCCEED();
582}
583
585{
588 io.set_store_arc(sa); // Should not throw
589 SUCCEED();
590}
591
592//============================================================================
593// Error Handling Tests
594//============================================================================
595
597{
598 Graph g2;
600
601 std::ifstream bad_file("/nonexistent_file_12345.bin", std::ios::binary);
602
603 // Loading from a bad stream should throw
604 EXPECT_THROW(io.load(bad_file), std::runtime_error);
605}
606
608{
609 Graph g2;
611
612 std::ifstream bad_file("/nonexistent_file_12345.txt");
613
614 EXPECT_THROW(io.load_in_text_mode(bad_file), std::runtime_error);
615}
616
617//============================================================================
618// Round-Trip Tests
619//============================================================================
620
622{
623 // First save
624 {
626 std::ofstream out(binary_file, std::ios::binary);
627 io.save(out);
628 }
629
630 // First load
631 Graph g2;
632 {
634 std::ifstream in(binary_file, std::ios::binary);
635 io.load(in);
636 }
637
638 EXPECT_EQ(g2.get_num_nodes(), 4u);
639
640 // Second save (from loaded graph)
641 std::string second_file = binary_file + ".2";
642 {
644 std::ofstream out(second_file, std::ios::binary);
645 io.save(out);
646 }
647
648 // Second load
649 Graph g3;
650 {
652 std::ifstream in(second_file, std::ios::binary);
653 io.load(in);
654 }
655
656 EXPECT_EQ(g3.get_num_nodes(), 4u);
657 EXPECT_EQ(g3.get_num_arcs(), 3u);
658
659 std::remove(second_file.c_str());
660}
661
663{
664 // First save
665 {
667 std::ofstream out(text_file);
668 io.save_in_text_mode(out);
669 }
670
671 // First load
672 Graph g2;
673 {
675 std::ifstream in(text_file);
676 io.load_in_text_mode(in);
677 }
678
679 EXPECT_EQ(g2.get_num_nodes(), 4u);
680 EXPECT_EQ(g2.get_num_arcs(), 3u);
681}
682
683//============================================================================
684// Main
685//============================================================================
686
687int main(int argc, char **argv)
688{
689 ::testing::InitGoogleTest(&argc, argv);
690 return RUN_ALL_TESTS();
691}
int main()
size_t size_t int32_t * out
Definition ca-c-api.h:120
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3960
typename BaseGraph::Arc Arc
Definition graph-dry.H:3964
typename BaseGraph::Node Node
Definition graph-dry.H:3963
Graph serialization and deserialization class.
Definition io_graph.H:320
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:525
Graph_Arc< double > Arc
The node class type.
Definition tpl_graph.H:434
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:605
std::string text_file
void SetUp() override
Digraph::Arc Arc
Digraph::Node Node
void TearDown() override
std::string binary_file
void SetUp() override
void TearDown() override
std::vector< int > get_node_values(Graph &graph)
std::string binary_file
std::string text_file
std::vector< double > get_arc_values(Graph &graph)
#define TEST(name)
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Graph serialization and deserialization utilities.
TEST_F(IOGraphTest, ConstructFromReference)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Default arc loading functor for binary and text modes.
Definition io_graph.H:258
Default node loading functor for binary and text modes.
Definition io_graph.H:223
Default arc storage functor for binary and text modes.
Definition io_graph.H:188
Default node storage functor for binary and text modes.
Definition io_graph.H:153
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:223
Generic graph and digraph implementations.