Line data Source code
1 : // Copyright (c) 2011-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 the unique_number class.
21 : *
22 : * This file implements tests to verify that the unique_number implementation
23 : * works properly even when multiple processes use it simultaneously.
24 : */
25 :
26 : // file being tested
27 : //
28 : #include <snapdev/unique_number.h>
29 :
30 :
31 : // self
32 : //
33 : #include "catch_main.h"
34 :
35 :
36 : // snapdev
37 : //
38 : #include <snapdev/not_used.h>
39 :
40 :
41 :
42 : // C++
43 : //
44 : #include <random>
45 : #include <thread>
46 :
47 :
48 : // last include
49 : //
50 : #include <snapdev/poison.h>
51 :
52 :
53 :
54 : // turn off pedantic for __int128
55 : //
56 : #pragma GCC diagnostic ignored "-Wpedantic"
57 :
58 :
59 : namespace
60 : {
61 :
62 :
63 :
64 :
65 : } // no name namespace
66 :
67 :
68 :
69 2 : CATCH_TEST_CASE("unique_number", "[number][file]")
70 : {
71 2 : CATCH_START_SECTION("unique_number: verify unique number basic counter")
72 : {
73 1 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
74 1 : std::string const filename(path + "/test-1.number");
75 :
76 1 : snapdev::NOT_USED(unlink(filename.c_str()));
77 :
78 101 : for(unsigned int n(1); n <= 100; ++n)
79 : {
80 100 : unsigned __int128 const number(snapdev::unique_number(filename));
81 100 : CATCH_REQUIRE(n == number);
82 : }
83 1 : }
84 2 : CATCH_END_SECTION()
85 :
86 2 : CATCH_START_SECTION("unique_number: verify unique number indexes")
87 : {
88 1 : std::random_device rd;
89 1 : std::mt19937 g(rd());
90 :
91 1 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
92 1 : std::string const filename(path + "/test-2.number");
93 : //std::cerr << "--- file: " << filename << "\n";
94 :
95 1 : snapdev::NOT_USED(unlink(filename.c_str()));
96 :
97 : // 100 indexes repeated 100 times
98 : //
99 : // that way we can shuffle all the indexes in order to count them
100 : // in different order
101 : //
102 1 : std::vector<unsigned int> indexes;
103 101 : for(unsigned int count(1); count <= 100; ++count)
104 : {
105 10100 : for(unsigned int idx(0); idx < 100; ++idx)
106 : {
107 10000 : indexes.push_back(idx);
108 : }
109 : }
110 1 : std::shuffle(indexes.begin(), indexes.end(), g);
111 :
112 3 : std::vector<unsigned int> counters(100);
113 10001 : for(auto idx : indexes)
114 : {
115 10000 : unsigned __int128 const number(snapdev::unique_number(filename, idx));
116 10000 : ++counters[idx];
117 10000 : CATCH_REQUIRE(counters[idx] == number);
118 : }
119 1 : }
120 2 : CATCH_END_SECTION()
121 2 : }
122 :
123 :
124 3 : CATCH_TEST_CASE("unique_number_error", "[number][file][error]")
125 : {
126 3 : CATCH_START_SECTION("unique_number_error: non-empty filename is required")
127 : {
128 5 : CATCH_REQUIRE_THROWS_MATCHES(
129 : snapdev::unique_number(std::string())
130 : , snapdev::path_missing
131 : , Catch::Matchers::ExceptionMessage(
132 : "unique_number_error: a counter filename must be specified when calling snapdev::unique_number."));
133 : }
134 3 : CATCH_END_SECTION()
135 :
136 3 : CATCH_START_SECTION("unique_number_error: index out of range")
137 : {
138 1 : std::string const path(SNAP_CATCH2_NAMESPACE::g_tmp_dir());
139 1 : std::string const filename(path + "/test-3.number");
140 :
141 101 : for(int index(-100); index < 0; ++index)
142 : {
143 100 : CATCH_REQUIRE_THROWS_MATCHES(
144 : snapdev::unique_number(filename, index)
145 : , snapdev::out_of_range
146 : , Catch::Matchers::ExceptionMessage(
147 : "unique_number_error: counter index in unique_number must be defined between 0 and "
148 : + std::to_string(snapdev::COUNTER_MAXIMUM_INDEX - 1)
149 : + " inclusive."));
150 : }
151 :
152 101 : for(int index(snapdev::COUNTER_MAXIMUM_INDEX); index < snapdev::COUNTER_MAXIMUM_INDEX + 100; ++index)
153 : {
154 100 : CATCH_REQUIRE_THROWS_MATCHES(
155 : snapdev::unique_number(filename, index)
156 : , snapdev::out_of_range
157 : , Catch::Matchers::ExceptionMessage(
158 : "unique_number_error: counter index in unique_number must be defined between 0 and "
159 : + std::to_string(snapdev::COUNTER_MAXIMUM_INDEX - 1)
160 : + " inclusive."));
161 : }
162 1 : }
163 3 : CATCH_END_SECTION()
164 :
165 3 : CATCH_START_SECTION("unique_number_error: file cannot be opened")
166 : {
167 7 : CATCH_REQUIRE_THROWS_MATCHES(
168 : snapdev::unique_number("/this/wont/open/since/it/does/not/exist")
169 : , snapdev::io_error
170 : , Catch::Matchers::ExceptionMessage(
171 : "unique_number_error: could not open unique_number file \"/this/wont/open/since/it/does/not/exist\"."));
172 : }
173 3 : CATCH_END_SECTION()
174 3 : }
175 :
176 :
177 :
178 : // vim: ts=4 sw=4 et
|