Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
cookie_guard_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
33
38#include <gtest/gtest.h>
39#include <cookie_guard.H>
40#include <tpl_graph.H>
41
42using namespace Aleph;
43
47
48class CookieGuardTest : public testing::Test
49{
50protected:
52 Node *n1, *n2, *n3;
53 Arc *a1, *a2;
54
55 void SetUp() override
56 {
57 n1 = g.insert_node(1);
58 n2 = g.insert_node(2);
59 n3 = g.insert_node(3);
60 a1 = g.insert_arc(n1, n2, 10);
61 a2 = g.insert_arc(n2, n3, 20);
62 }
63
65 {
66 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
67 if (NODE_COOKIE(it.get_curr()) != nullptr)
68 return false;
69 return true;
70 }
71
73 {
74 for (auto it = g.get_arc_it(); it.has_curr(); it.next_ne())
75 if (ARC_COOKIE(it.get_curr()) != nullptr)
76 return false;
77 return true;
78 }
79};
80
81// =============================================================================
82// Cookie_Guard Tests
83// =============================================================================
84
86{
87 // Set some cookies
88 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
89 NODE_COOKIE(n2) = reinterpret_cast<void*>(0x2);
90 ARC_COOKIE(a1) = reinterpret_cast<void*>(0x3);
91
92 EXPECT_FALSE(all_node_cookies_null());
93 EXPECT_FALSE(all_arc_cookies_null());
94
95 {
97 // Cookies should still be set inside the block
98 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
99 }
100
101 // After guard destructor, all cookies should be null
102 EXPECT_TRUE(all_node_cookies_null());
103 EXPECT_TRUE(all_arc_cookies_null());
104}
105
107{
108 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
109 ARC_COOKIE(a1) = reinterpret_cast<void*>(0x2);
110
111 {
112 Cookie_Guard<Graph> guard(g, true, false); // only clear nodes
113 }
114
115 EXPECT_TRUE(all_node_cookies_null());
116 EXPECT_FALSE(all_arc_cookies_null()); // arcs should still have cookies
117 EXPECT_EQ(ARC_COOKIE(a1), reinterpret_cast<void*>(0x2));
118}
119
121{
122 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
123
124 {
126 guard.release(); // Don't clean on destruction
127 }
128
129 EXPECT_FALSE(all_node_cookies_null());
130 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
131}
132
134{
135 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
136
138 EXPECT_FALSE(all_node_cookies_null());
139
140 guard.clear_now();
141 EXPECT_TRUE(all_node_cookies_null());
142
143 // Set again - guard should not clear again (already inactive)
144 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x2);
145 // guard goes out of scope but won't clear
146}
147
149{
150 static int delete_count = 0;
151 delete_count = 0;
152
153 // Allocate some data
154 NODE_COOKIE(n1) = new int(100);
155 NODE_COOKIE(n2) = new int(200);
156
157 {
159 [](Node* p) {
160 delete static_cast<int*>(NODE_COOKIE(p));
161 delete_count++;
162 },
163 nullptr);
164 }
165
166 EXPECT_EQ(delete_count, 3); // n1, n2, n3 (n3 has nullptr but deleter still called)
167 EXPECT_TRUE(all_node_cookies_null());
168}
169
171{
172 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
173 NODE_COOKIE(n2) = reinterpret_cast<void*>(0x2);
174
175 try
176 {
178 throw std::runtime_error("test exception");
179 }
180 catch (...)
181 {
182 // Exception caught
183 }
184
185 // Despite exception, cookies should be cleaned
186 EXPECT_TRUE(all_node_cookies_null());
187}
188
190{
191 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
192
195
196 // guard1 is now inactive, guard2 is active
197 // Only guard2's destructor should clear cookies
198}
199
200// =============================================================================
201// Cookie_Saver Tests
202// =============================================================================
203
205{
206 // Set original cookies
207 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
208 NODE_COOKIE(n2) = reinterpret_cast<void*>(0x2);
209 ARC_COOKIE(a1) = reinterpret_cast<void*>(0x3);
210
211 {
213
214 // Modify cookies
215 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xA);
216 NODE_COOKIE(n2) = reinterpret_cast<void*>(0xB);
217 ARC_COOKIE(a1) = reinterpret_cast<void*>(0xC);
218
219 // Verify modification
220 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0xA));
221 }
222
223 // After saver destructor, original cookies should be restored
224 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
225 EXPECT_EQ(NODE_COOKIE(n2), reinterpret_cast<void*>(0x2));
226 EXPECT_EQ(ARC_COOKIE(a1), reinterpret_cast<void*>(0x3));
227}
228
230{
231 // Start with null cookies
232 EXPECT_TRUE(all_node_cookies_null());
233
234 {
236
237 // Use cookies
238 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
239 }
240
241 // Should be restored to null
242 EXPECT_TRUE(all_node_cookies_null());
243}
244
246{
247 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
248
249 {
251 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xA);
252 saver.discard(); // Don't restore
253 }
254
255 // New value should be kept
256 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0xA));
257}
258
260{
261 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
262
264 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xA);
265 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0xA));
266
267 saver.restore_now();
268 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
269
270 // Modify again - saver should not restore again
271 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xB);
272 // saver goes out of scope but won't restore
273}
274
276{
277 static int cleanup_count = 0;
278 cleanup_count = 0;
279
280 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
281
282 {
284 [](Node* node) {
286 // Free the allocated memory if it's a valid heap pointer
287 // (not the sentinel value 0x1 or nullptr)
288 void* cookie = NODE_COOKIE(node);
289 if (cookie != nullptr && cookie != reinterpret_cast<void*>(0x1))
290 delete static_cast<int*>(cookie);
291 },
292 nullptr);
293
294 // Allocate temporary data
295 NODE_COOKIE(n1) = new int(100);
296 NODE_COOKIE(n2) = new int(200);
297 }
298
299 // Cleanup should have been called
301 // Original should be restored
302 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
303}
304
306{
307 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
308
309 try
310 {
312 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xA);
313 throw std::runtime_error("test exception");
314 }
315 catch (...)
316 {
317 // Exception caught
318 }
319
320 // Despite exception, original cookies should be restored
321 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
322}
323
325{
326 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
327 NODE_COOKIE(n2) = reinterpret_cast<void*>(0x2);
328
330
331 auto & saved = saver.get_saved_node_cookies();
332 EXPECT_EQ(saved.size(), 3); // 3 nodes
333
334 // Find n1's saved cookie
335 auto it = std::find_if(saved.begin(), saved.end(),
336 [this](const auto & p) { return p.first == n1; });
337 ASSERT_NE(it, saved.end());
338 EXPECT_EQ(it->second, reinterpret_cast<void*>(0x1));
339}
340
341// =============================================================================
342// Convenience Function Tests
343// =============================================================================
344
346{
347 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
348
349 int result = with_clean_cookies(g, [&]() {
350 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
351 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xA);
352 return 42;
353 });
354
355 EXPECT_EQ(result, 42);
356 EXPECT_TRUE(all_node_cookies_null());
357}
358
360{
361 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
362
363 int result = with_saved_cookies(g, [&]() {
364 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xA);
365 return 42;
366 });
367
368 EXPECT_EQ(result, 42);
369 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1)); // restored
370}
371
373{
374 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1);
375
376 {
378 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xA);
379
380 {
382 NODE_COOKIE(n1) = reinterpret_cast<void*>(0xB);
383 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0xB));
384 }
385
386 // Inner restored to 0xA
387 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0xA));
388 }
389
390 // Outer restored to 0x1
391 EXPECT_EQ(NODE_COOKIE(n1), reinterpret_cast<void*>(0x1));
392}
393
394// ============================================================================
395// Scope_Guard Tests
396// ============================================================================
397
399{
400 bool cleanup_called = false;
401
402 {
403 Scope_Guard guard(g, [&cleanup_called](const Graph &) {
404 cleanup_called = true;
405 });
406 EXPECT_TRUE(guard.is_active());
408 }
409
411}
412
414{
415 bool cleanup_called = false;
416
417 {
418 Scope_Guard guard(g, [&cleanup_called](const Graph &) {
419 cleanup_called = true;
420 });
421 guard.release();
422 EXPECT_FALSE(guard.is_active());
423 }
424
426}
427
429{
430 int cleanup_count = 0;
431
432 {
433 Scope_Guard guard(g, [&cleanup_count](const Graph &) {
435 });
436
437 guard.cleanup_now();
439 EXPECT_FALSE(guard.is_active());
440
441 // Second call should not execute cleanup
442 guard.cleanup_now();
444 }
445
446 // Destructor should not call cleanup again
448}
449
451{
452 bool cleanup_called = false;
453
454 {
455 Scope_Guard guard1(g, [&cleanup_called](const Graph &) {
456 cleanup_called = true;
457 });
458
459 Scope_Guard guard2(std::move(guard1));
460 EXPECT_FALSE(guard1.is_active());
461 EXPECT_TRUE(guard2.is_active());
462 }
463
464 // Only guard2's destructor should have called cleanup
466}
467
469{
470 bool cleanup_called = false;
471
472 try
473 {
474 Scope_Guard guard(g, [&cleanup_called](const Graph &) {
475 cleanup_called = true;
476 });
477 throw std::runtime_error("test");
478 }
479 catch (...)
480 {
481 // Exception caught
482 }
483
485}
486
488{
489 // Simulate real usage: set cookies, use guard to clean them
490 NODE_COOKIE(n1) = reinterpret_cast<void*>(0x1234);
491 NODE_COOKIE(n2) = reinterpret_cast<void*>(0x5678);
492
493 {
494 Scope_Guard guard(g, [](const Graph & graph) {
495 for (Node_Iterator<Graph> it(graph); it.has_curr(); it.next_ne())
496 NODE_COOKIE(it.get_curr()) = nullptr;
497 });
498
499 // Cookies are set during scope
500 EXPECT_NE(NODE_COOKIE(n1), nullptr);
501 EXPECT_NE(NODE_COOKIE(n2), nullptr);
502 }
503
504 // After scope, cookies should be cleaned
505 EXPECT_EQ(NODE_COOKIE(n1), nullptr);
506 EXPECT_EQ(NODE_COOKIE(n2), nullptr);
507}
508
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
RAII guard that clears graph cookies on destruction.
RAII guard that saves and restores graph cookies.
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
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
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1206
Generic RAII scope guard for cleanup operations on graphs.
void SetUp() override
auto get_arc_it() const noexcept
Obtains an iterator to the arc of graph.
Definition graph-dry.H:2802
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2780
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
RAII guards for graph node/arc cookies.
Graph::Arc Arc
TEST_F(CookieGuardTest, BasicCleanup)
Graph::Node Node
#define ARC_COOKIE(p)
Return the arc cookie
auto with_clean_cookies(GT &g, Func &&func) -> decltype(func())
Convenience function to run an algorithm with automatic cookie cleanup.
auto with_saved_cookies(GT &g, Func &&func) -> decltype(func())
Convenience function to run an algorithm preserving existing cookies.
#define NODE_COOKIE(p)
Return the node cookie
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Generic graph and digraph implementations.