Line data Source code
1 : // Copyright (c) 2019-2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : /** \file
20 : * \brief Verify that the safe_object template works.
21 : *
22 : * This file implements tests for the safe_object template to make sure that
23 : * it works as expected.
24 : */
25 :
26 : // self
27 : //
28 : #include <snapdev/safe_object.h>
29 :
30 : #include "catch_main.h"
31 :
32 :
33 : // snapdev
34 : //
35 : #include <snapdev/not_reached.h>
36 :
37 :
38 : // last include
39 : //
40 : #include <snapdev/poison.h>
41 :
42 :
43 : namespace
44 : {
45 :
46 :
47 : bool g_allocated = false;
48 :
49 59 : bool object_is_allocated()
50 : {
51 59 : return g_allocated;
52 : }
53 :
54 : class test_object
55 : {
56 : public:
57 12 : test_object()
58 : {
59 12 : g_allocated = true;
60 12 : }
61 :
62 12 : ~test_object()
63 : {
64 12 : g_allocated = false;
65 12 : }
66 : };
67 :
68 :
69 : typedef int identifier_t;
70 :
71 : typedef std::map<identifier_t, bool> map_id_t;
72 :
73 : map_id_t g_map_ids = map_id_t();
74 :
75 10 : identifier_t alloc_id()
76 : {
77 : for(;;)
78 : {
79 10 : identifier_t id(rand());
80 10 : if(g_map_ids.find(id) == g_map_ids.end())
81 : {
82 10 : g_map_ids[id] = true;
83 20 : return id;
84 : }
85 0 : }
86 : }
87 :
88 10 : void delete_id(identifier_t id)
89 : {
90 10 : auto it(g_map_ids.find(id));
91 10 : CATCH_REQUIRE(it != g_map_ids.end());
92 10 : g_map_ids.erase(it);
93 10 : }
94 :
95 50 : bool id_is_allocated(identifier_t id)
96 : {
97 50 : return g_map_ids.find(id) != g_map_ids.end();
98 : }
99 :
100 :
101 : } // no name namespace
102 :
103 :
104 4 : CATCH_TEST_CASE("safe_object", "[raii]")
105 : {
106 4 : CATCH_START_SECTION("safe_object: expected usage")
107 : {
108 1 : snapdev::safe_object<test_object *> so;
109 1 : CATCH_REQUIRE_FALSE(object_is_allocated());
110 1 : test_object * obj(new test_object);
111 1 : CATCH_REQUIRE(object_is_allocated());
112 1 : so.make_safe(obj);
113 1 : CATCH_REQUIRE(obj != nullptr); // this is always true because if new fails, it throws bad_alloc
114 1 : CATCH_REQUIRE(object_is_allocated());
115 1 : so.release();
116 1 : CATCH_REQUIRE(object_is_allocated());
117 1 : delete obj;
118 1 : CATCH_REQUIRE_FALSE(object_is_allocated());
119 1 : }
120 4 : CATCH_END_SECTION()
121 :
122 4 : CATCH_START_SECTION("safe_object: with exception")
123 : {
124 : try
125 : {
126 1 : snapdev::safe_object<test_object *> so;
127 1 : CATCH_REQUIRE_FALSE(object_is_allocated());
128 1 : test_object * obj(new test_object);
129 1 : CATCH_REQUIRE(object_is_allocated());
130 1 : so.make_safe(obj);
131 1 : CATCH_REQUIRE(obj != nullptr); // this is always true because if new fails, it throws bad_alloc
132 1 : CATCH_REQUIRE(object_is_allocated());
133 1 : throw std::range_error("test exception");
134 : snapdev::NOT_REACHED();
135 1 : }
136 1 : catch(std::exception const & e)
137 : {
138 1 : CATCH_REQUIRE_FALSE(object_is_allocated());
139 1 : }
140 : }
141 4 : CATCH_END_SECTION()
142 :
143 4 : CATCH_START_SECTION("safe_object: test both ways")
144 : {
145 11 : for(int idx(0); idx < 10; ++idx)
146 : {
147 : try
148 : {
149 10 : snapdev::safe_object<test_object *> so;
150 10 : CATCH_REQUIRE_FALSE(object_is_allocated());
151 10 : test_object * obj(new test_object);
152 10 : CATCH_REQUIRE(object_is_allocated());
153 10 : so.make_safe(obj);
154 10 : CATCH_REQUIRE(obj != nullptr); // this is always true because if new fails, it throws bad_alloc
155 10 : CATCH_REQUIRE(object_is_allocated());
156 10 : if((idx & 1) == 0)
157 : {
158 5 : throw std::range_error("test exception");
159 : snapdev::NOT_REACHED();
160 : }
161 5 : so.release();
162 5 : CATCH_REQUIRE(object_is_allocated());
163 5 : delete obj;
164 5 : CATCH_REQUIRE_FALSE(object_is_allocated());
165 10 : }
166 5 : catch(std::exception const & e)
167 : {
168 : // prevent propagation
169 5 : }
170 10 : CATCH_REQUIRE_FALSE(object_is_allocated());
171 : }
172 : }
173 4 : CATCH_END_SECTION()
174 :
175 4 : CATCH_START_SECTION("safe_object: test with resource identifiers")
176 : {
177 11 : for(int idx(0); idx < 10; ++idx)
178 : {
179 10 : identifier_t id(rand());
180 : try
181 : {
182 10 : snapdev::safe_object<identifier_t, delete_id> so;
183 10 : CATCH_REQUIRE_FALSE(id_is_allocated(id));
184 10 : id = alloc_id();
185 10 : CATCH_REQUIRE(id_is_allocated(id));
186 10 : so.make_safe(id);
187 10 : CATCH_REQUIRE(id_is_allocated(id));
188 10 : if((idx & 1) == 0)
189 : {
190 5 : throw std::range_error("id exception");
191 : snapdev::NOT_REACHED();
192 : }
193 5 : so.release();
194 5 : CATCH_REQUIRE(id_is_allocated(id));
195 5 : delete_id(id);
196 5 : CATCH_REQUIRE_FALSE(id_is_allocated(id));
197 10 : }
198 5 : catch(std::exception const & e)
199 : {
200 : // prevent propagation
201 5 : }
202 10 : CATCH_REQUIRE_FALSE(id_is_allocated(id));
203 : }
204 : }
205 4 : CATCH_END_SECTION()
206 4 : }
207 :
208 :
209 :
210 : // vim: ts=4 sw=4 et
211 :
|