Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
xml_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
36# include <gtest/gtest.h>
37
38# include <cstdio>
39# include <filesystem>
40# include <random>
41# include <stdexcept>
42# include <string>
43
44# include <ahSort.H>
45# include <xml_graph.H>
46
47using namespace Aleph;
48
49namespace
50{
52
53 struct NotAGraph
54 {
55 using Node = int;
56 };
57
58 template <class T>
59 concept can_xml_graph = requires { typename Xml_Graph<T>; };
60
61 static_assert(can_xml_graph<G>);
63
64 // Stateful writer: counts its calls so the tests can see whose copy ran.
65 struct NodeWriter
66 {
67 int calls = 0;
68
69 void operator()(G &, G::Node * p, DynArray<Attr> & attrs)
70 {
71 ++calls;
72 attrs.append(Attr{"info", std::to_string(p->get_info())});
73 }
74 };
75
76 struct NodeReader
77 {
78 void operator()(G &, G::Node * p, DynArray<Attr> & attrs)
79 {
80 for (size_t i = 0; i < attrs.size(); ++i)
81 if (attrs(i).name == "info")
82 p->get_info() = std::stoi(attrs(i).value);
83 }
84 };
85
86 struct ArcWriter
87 {
88 void operator()(G &, G::Arc * a, DynArray<Attr> & attrs)
89 {
90 attrs.append(Attr{"w", std::to_string(a->get_info())});
91 }
92 };
93
94 struct ArcReader
95 {
96 void operator()(G &, G::Arc * a, DynArray<Attr> & attrs)
97 {
98 for (size_t i = 0; i < attrs.size(); ++i)
99 if (attrs(i).name == "w")
100 a->get_info() = std::stoi(attrs(i).value);
101 }
102 };
103
105
106 // Order-independent description of a graph: node infos and
107 // "src info -> tgt info : weight" for every arc.
109 {
111 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
112 items.append("n" + std::to_string(it.get_curr()->get_info()));
113 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
114 {
115 auto * a = it.get_curr();
116 items.append(std::to_string(g.get_src_node(a)->get_info()) + "->" +
117 std::to_string(g.get_tgt_node(a)->get_info()) + ":" +
118 std::to_string(a->get_info()));
119 }
120 return sort(items);
121 }
122
124 {
125 G g;
126 G::Node * n[5];
127 for (int i = 0; i < 5; ++i)
128 n[i] = g.insert_node(10 * (i + 1));
129 g.insert_arc(n[0], n[1], 1);
130 g.insert_arc(n[1], n[2], 2);
131 g.insert_arc(n[2], n[3], 3);
132 g.insert_arc(n[3], n[4], 4);
133 g.insert_arc(n[4], n[0], 5);
134 g.insert_arc(n[0], n[2], 6);
135 return g;
136 }
137
138 // ctest runs each case in its own process, possibly in parallel: every
139 // case writes to files named after itself plus a random token.
140 class XmlGraphTest : public ::testing::Test
141 {
142 std::string prefix;
143
144 protected:
145 void SetUp() override
146 {
147 prefix = std::string("aleph_xml_graph_test_") +
148 ::testing::UnitTest::GetInstance()->current_test_info()->name() +
149 "_" + std::to_string(std::random_device{}()) + "_";
150 }
151
152 std::string file(const std::string & name) const
153 {
154 return (std::filesystem::temp_directory_path() / (prefix + name)).string();
155 }
156
157 void TearDown() override
158 {
159 for (const char * name : {"owned.xml", "shared.xml", "copy.xml", "names.xml"})
160 std::remove(file(name).c_str());
161 }
162 };
163} // namespace
164
165// The default constructor used to keep references to temporary functors,
166// which dangled as soon as it returned.
168{
169 G g = sample_graph();
170 XG xml;
171 xml(g, file("owned.xml"));
172 G h = xml(file("owned.xml"));
173
174 EXPECT_EQ(h.get_num_nodes(), g.get_num_nodes());
175 EXPECT_EQ(h.get_num_arcs(), g.get_num_arcs());
177}
178
179// Functors passed by lvalue are shared: their state is visible to the caller,
180// also through a copy of the reader/writer.
182{
183 G g = sample_graph();
184 NodeReader nr;
185 ArcReader ar;
186 NodeWriter nw;
187 ArcWriter aw;
188 XG shared(nr, ar, nw, aw);
189 shared(g, file("shared.xml"));
190 EXPECT_EQ(nw.calls, 5);
191
192 XG copy = shared;
193 G h = copy(file("shared.xml"));
194 copy(h, file("copy.xml"));
195 EXPECT_EQ(nw.calls, 10);
197}
198
199// A copy of an owning reader/writer owns its own functors.
201{
202 G g = sample_graph();
203 auto * original = new XG;
204 XG copy = *original;
205 delete original;
206
207 copy(g, file("owned.xml"));
208 G h = copy(file("owned.xml"));
210}
211
212namespace
213{
214 // Movable but not copyable, e.g. because it owns a resource.
215 struct NonCopyableWriter
216 {
217 int calls = 0;
218 NonCopyableWriter() = default;
219 NonCopyableWriter(const NonCopyableWriter &) = delete;
220 NonCopyableWriter(NonCopyableWriter &&) = default;
221
222 void operator()(G &, G::Node * p, DynArray<Attr> & attrs)
223 {
224 ++calls;
225 attrs.append(Attr{"info", std::to_string(p->get_info())});
226 }
227 };
228
230} // namespace
231
232// Copying an Xml_Graph used to require every functor type to be
233// copy-constructible, even in shared mode, where no functor is ever actually
234// copied: std::optional<F>'s copy constructor is deleted whenever F is not
235// copy-constructible, regardless of whether the specific optional holds a
236// value. Sharing (not owning) a non-copy-constructible functor must still
237// allow the reader/writer itself to be copied.
239{
240 G g = sample_graph();
243 NonCopyableWriter nw;
245
246 XGNonCopyable shared(nr, ar, nw, aw);
247 XGNonCopyable copy = shared; // must not try to copy nw
248
249 copy(g, file("owned.xml"));
250 EXPECT_EQ(nw.calls, 5);
251}
252
253// Copying an Xml_Graph that *owns* a non-copy-constructible functor cannot
254// duplicate that functor, so it must fail loudly (a runtime error, per
255// CLAUDE.md), not silently share it or refuse to compile for callers who
256// never attempt this copy.
258{
259 // `XGNonCopyable owner(A(), B(), C(), D());` would parse as a function
260 // declaration (most vexing parse); `=` forces expression context.
261 XGNonCopyable owner =
263 EXPECT_THROW((XGNonCopyable(owner)), std::runtime_error);
264}
265
267{
268 G g = sample_graph();
269 XG xml;
270 xml.set_graph_name("network");
271 xml.set_node_name("vertex");
272 xml.set_arc_name("edge");
273 EXPECT_EQ(xml.get_node_name(), "vertex");
274 xml(g, file("names.xml"));
275
276 G h = xml(file("names.xml"));
278
279 // With the default names nothing in the file is recognized.
280 XG defaults;
281 G empty = defaults(file("names.xml"));
282 EXPECT_EQ(empty.get_num_nodes(), 0u);
283}
High-level sorting functions for Aleph containers.
WeightedDigraph::Node Node
long double h
Definition btreepic.C:154
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size() const noexcept
Return the current dimension of array.
T & append()
Allocate a new entry to the end of array.
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
Graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:429
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:525
_Graph_Node Node
The graph type.
Definition tpl_graph.H:433
_Graph_Arc 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
Class that writes and reads a graph (in a very elementary way) as XML.
Definition xml_graph.H:126
auto get_arc_it() const noexcept
Obtains an iterator to the arc of graph.
Definition graph-dry.H:2908
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:826
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2886
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
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
DynList< Node * > prefix(Node *root)
Return a list with preorder traversal of a tree.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Definition ahAlgo.H:584
and
Check uniqueness with explicit hash + equality functors.
DynArray< T > sort(const DynArray< T > &a, Cmp &&cmp=Cmp())
Returns a sorted copy of a DynArray.
Definition ahSort.H:234
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:223
ofstream file
Definition writeRb.C:51
XML serialization for graphs.
TEST_F(XmlGraphTest, RoundTripWithOwnedFunctors)