valFuzz 1.0
Loading...
Searching...
No Matches
valfuzz.cpp
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2024 Giovanni Santini
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 */
26
27#include "valfuzz/valfuzz.hpp"
28
29namespace valfuzz
30{
31
32std::deque<test_pair> &get_tests()
33{
34 static std::deque<test_pair> registered_tests = {};
35 return registered_tests;
36}
37
38std::deque<fuzz_pair> &get_fuzzs()
39{
40 static std::deque<fuzz_pair> registered_fuzzs = {};
41 return registered_fuzzs;
42}
43
44std::function<void()> &get_function_execute_before()
45{
46 static std::function<void()> function_execute_before = []() {};
47 return function_execute_before;
48}
49
50std::function<void()> &get_function_execute_after()
51{
52 static std::function<void()> function_execute_after = []() {};
53 return function_execute_after;
54}
55
56std::mutex &get_tests_mutex()
57{
58 constinit static std::mutex tests_mutex;
59 return tests_mutex;
60}
61
62std::mutex &get_stream_mutex()
63{
64 constinit static std::mutex stream_mutex;
65 return stream_mutex;
66}
67
68long unsigned int get_num_tests()
69{
70 auto &tests = get_tests();
71 return tests.size();
72}
73
74std::atomic<bool> &get_is_threaded()
75{
76 constinit static std::atomic<bool> is_threaded = true;
77 return is_threaded;
78}
79
80std::vector<std::thread> &get_thread_pool()
81{
82 constinit static std::vector<std::thread> thread_pool;
83 return thread_pool;
84}
85
86std::atomic<long unsigned int> &get_max_num_threads()
87{
88 constinit static std::atomic<long unsigned int> max_num_threads = 4;
89 return max_num_threads;
90}
91
92std::atomic<bool> &get_verbose()
93{
94 constinit static std::atomic<bool> verbose = false;
95 return verbose;
96}
97
98std::atomic<bool> &get_header()
99{
100 constinit static std::atomic<bool> header = true;
101 return header;
102}
103
104std::atomic<bool> &get_do_fuzzing()
105{
106 constinit static std::atomic<bool> do_fuzzing = false;
107 return do_fuzzing;
108}
109
110std::optional<std::string> &get_test_one()
111{
112 constinit static std::optional<std::string> test_one = std::nullopt;
113 return test_one;
114}
115
116std::optional<std::string> &get_fuzz_one()
117{
118 constinit static std::optional<std::string> fuzz_one = std::nullopt;
119 return fuzz_one;
120}
121
122long unsigned int get_num_fuzz_tests()
123{
124 auto &fuzzs = get_fuzzs();
125 return fuzzs.size();
126}
127
128void set_multithreaded(bool is_threaded)
129{
130 auto &is_threaded_ref = get_is_threaded();
131 is_threaded_ref = is_threaded;
132}
133
134void set_max_num_threads(long unsigned int max_num_threads)
135{
136 auto &max_num_threads_ref = get_max_num_threads();
137 max_num_threads_ref = max_num_threads;
138}
139
140void set_verbose(bool verbose)
141{
142 auto &verbose_ref = get_verbose();
143 verbose_ref = verbose;
144}
145
146void set_header(bool header)
147{
148 auto &header_ref = get_header();
149 header_ref = header;
150}
151
152void set_function_execute_before(std::function<void()> f)
153{
154 auto &function_execute_before = get_function_execute_before();
155 function_execute_before = f;
156}
157
158void set_function_execute_after(std::function<void()> f)
159{
160 auto &function_execute_after = get_function_execute_after();
161 function_execute_after = f;
162}
163
164void set_do_fuzzing(bool do_fuzzing)
165{
166 auto &do_fuzzing_ref = get_do_fuzzing();
167 do_fuzzing_ref = do_fuzzing;
168}
169
170void set_test_one(const std::string &test_one)
171{
172 auto &test_one_ref = get_test_one();
173 test_one_ref = test_one;
174}
175
176void set_fuzz_one(const std::string &fuzz_one)
177{
178 auto &fuzz_one_ref = get_fuzz_one();
179 fuzz_one_ref = fuzz_one;
180}
181
182void add_test(const std::string &name, test_function test)
183{
184 auto &tests = get_tests();
185 std::lock_guard<std::mutex> lock(get_tests_mutex());
186 tests.push_back({name, test});
187}
188
189void add_fuzz_test(const std::string &name, fuzz_function fuzz)
190{
191 auto &fuzzs = get_fuzzs();
192 std::lock_guard<std::mutex> lock(get_tests_mutex());
193 fuzzs.push_back({name, fuzz});
194}
195
196std::optional<test_pair> pop_test_or_null()
197{
198 auto &tests = get_tests();
199 auto &tests_mutex = get_tests_mutex();
200
201 std::lock_guard<std::mutex> lock(tests_mutex);
202 if (tests.empty())
203 {
204 return std::nullopt;
205 }
206 test_pair test = tests.front();
207 tests.pop_front();
208 return test;
209}
210
211void run_one_test(const std::string &name)
212{
213 auto &tests = get_tests();
214 bool found = false;
215 for (auto &test : tests)
216 {
217 if (test.first == name)
218 {
219 {
220 std::lock_guard<std::mutex> lock(get_stream_mutex());
221 std::print("Running test: {}\n", test.first);
222 }
223 found = true;
224 test.second(test.first);
225 break;
226 }
227 }
228 if (!found)
229 {
230 std::lock_guard<std::mutex> lock(get_stream_mutex());
231 std::print("Test \"{}\" not found\n", name);
232 std::exit(1);
233 }
234}
235
237{
238 auto test = std::optional<test_pair>{};
239 while ((test = pop_test_or_null()).has_value())
240 {
241 // run task
242 if (get_verbose())
243 {
244 std::lock_guard<std::mutex> lock(get_stream_mutex());
245 std::print("Running test: \"{}\"\n", test.value().first);
246 }
247 test.value().second(test.value().first);
248 }
249}
250
252{
253 if (get_is_threaded())
254 {
255 auto &thread_pool = get_thread_pool();
256 // spawn threads
257 for (long unsigned int i = 0;
258 i < get_max_num_threads() && i < get_num_tests(); i++)
259 {
260 thread_pool.push_back(std::thread(_run_tests));
261 }
262 for (auto &thread : get_thread_pool())
263 {
264 thread.join();
265 }
266 }
267 else
268 {
269 _run_tests();
270 }
271}
272
273std::atomic<long unsigned int> &get_iterations()
274{
275 constinit static std::atomic<long unsigned int> iterations = 0;
276 return iterations;
277}
278
280{
281 auto &iterations = get_iterations();
282 iterations++;
283}
284
285std::optional<fuzz_pair> pop_fuzz_or_null()
286{
287 auto &fuzzs = get_fuzzs();
288 auto &fuzzs_mutex = get_tests_mutex();
289
290 std::lock_guard<std::mutex> lock(fuzzs_mutex);
291 if (fuzzs.empty())
292 {
293 return std::nullopt;
294 }
295 test_pair test = fuzzs.front();
296 fuzzs.pop_front();
297 return test;
298}
299
300void run_one_fuzz(const std::string &name)
301{
302 auto &fuzzs = get_fuzzs();
303 std::deque<fuzz_pair> the_fuzz;
304 std::for_each(fuzzs.begin(), fuzzs.end(),
305 [&the_fuzz, &name](fuzz_pair &fuzz)
306 {
307 if (fuzz.first == name)
308 {
309 for (long unsigned int i = 0;
310 i < get_max_num_threads(); i++)
311 {
312 the_fuzz.push_back(fuzz);
313 }
314 }
315 });
316 if (the_fuzz.empty())
317 {
318 std::lock_guard<std::mutex> lock(get_stream_mutex());
319 std::print("Fuzz test \"{}\" not found\n", name);
320 std::exit(1);
321 }
322 fuzzs = the_fuzz;
323 {
324 std::lock_guard<std::mutex> lock(get_stream_mutex());
325 std::print("Running fuzz test: {}\n", name);
326 }
327}
328
330{
331 auto fuzz = std::optional<fuzz_pair>{};
332 while ((fuzz = pop_fuzz_or_null()).has_value())
333 {
334 if (get_verbose())
335 {
336 std::lock_guard<std::mutex> lock(get_stream_mutex());
337 std::print("Running fuzz: \"{}\"\n", fuzz.value().first);
338 }
339 fuzz.value().second(fuzz.value().first);
340
342 long unsigned int iterations = get_iterations();
343 if (iterations % 1000000 == 0)
344 {
345 std::lock_guard<std::mutex> lock(get_stream_mutex());
346 std::print("Iterations: {}\n", iterations);
347 }
348
349 add_fuzz_test(fuzz.value().first, fuzz.value().second);
350 }
351}
352
354{
355 if (get_is_threaded())
356 {
357 auto &thread_pool = get_thread_pool();
358 // spawn threads
359 for (long unsigned int i = 0;
360 i < get_max_num_threads() && i < get_num_fuzz_tests(); i++)
361 {
362 thread_pool.push_back(std::thread(_run_fuzz_tests));
363 }
364 for (auto &thread : get_thread_pool())
365 {
366 thread.join();
367 }
368 }
369 else
370 {
372 }
373}
374
375void parse_args(int argc, char *argv[])
376{
377 for (int i = 1; i < argc; i++)
378 {
379 if (std::string(argv[i]) == "--test")
380 {
381 if (i + 1 < argc)
382 {
383 set_test_one(argv[i + 1]);
384 i++;
385 }
386 }
387 else if (std::string(argv[i]) == "--fuzz")
388 {
389 set_do_fuzzing(true);
390 }
391 else if (std::string(argv[i]) == "--fuzz-one")
392 {
393 if (i + 1 < argc)
394 {
395 set_do_fuzzing(true);
396 set_fuzz_one(argv[i + 1]);
397 i++;
398 }
399 }
400 else if (std::string(argv[i]) == "--no-multithread")
401 {
402 set_multithreaded(false);
403 }
404 else if (std::string(argv[i]) == "--verbose")
405 {
406 set_verbose(true);
407 }
408 else if (std::string(argv[i]) == "--max-threads")
409 {
410 if (i + 1 < argc)
411 {
412 set_max_num_threads(std::stoul(argv[i + 1]));
413 i++;
414 }
415 }
416 else if (std::string(argv[i]) == "--no-header")
417 {
418 set_header(false);
419 }
420 else if (std::string(argv[i]) == "--help")
421 {
422 std::print("Usage: valfuzz [options]\n");
423 std::print("Options:\n");
424 std::print(" --test <name>: run a specific test\n");
425 std::print(" --fuzz: run fuzz tests\n");
426 std::print(" --fuzz-one <name>: run a specific fuzz test\n");
427 std::print(" --no-multithread: run tests in a single thread\n");
428 std::print(" --verbose: print test names\n");
429 std::print(
430 " --max-threads <num>: set the maximum number of threads\n");
431 std::print(" --no-header: do not print the header at the start\n");
432 std::print(" --help: print this help message\n");
433 std::exit(0);
434 }
435 else
436 {
437 std::print("Unknown option: {}\n", argv[i]);
438 std::exit(1);
439 }
440 }
441}
442
443char valfuzz_banner[] = " _ _____ \n"
444 " __ ____ _| | ___| _ ________\n"
445 " \\ \\ / / _` | | |_ | | | |_ /_ /\n"
446 " \\ V / (_| | | _|| |_| |/ / / / \n"
447 " \\_/ \\__,_|_|_| \\__,_/___/___|\n"
448 " \n"
449 "A modern testing & fuzzing library for C++\n";
450
452{
453 bool verbose = get_verbose();
454 bool is_threaded = get_is_threaded();
455 long unsigned int max_num_threads = get_max_num_threads();
456 std::lock_guard<std::mutex> lock(get_stream_mutex());
457 std::print("{}", valfuzz_banner);
458 std::print("Settings:\n");
459 std::print(" - Multithreaded: {}\n", is_threaded);
460 std::print(" - Max threads: {}\n", max_num_threads);
461 std::print(" - Verbose: {}\n", verbose);
462 std::print("\n");
463}
464
465template <> int get_random<int>()
466{
467 return std::rand();
468}
469
470template <> float get_random<float>()
471{
472 return static_cast<float>(std::rand());
473}
474
475template <> double get_random<double>()
476{
477 double dot =
478 static_cast<double>(std::rand()) / static_cast<double>(RAND_MAX);
479 return static_cast<double>(std::rand()) + dot;
480}
481
482template <> char get_random<char>()
483{
484 return static_cast<char>(std::rand() % 256);
485}
486
487template <> bool get_random<bool>()
488{
489 return static_cast<bool>(std::rand() % 2);
490}
491
492template <> std::string get_random<std::string>()
493{
494 int len = std::rand() % MAX_RANDOM_STRING_LEN;
495 std::string random_string = "";
496 for (int i = 0; i < len; i++)
497 {
498 random_string += get_random<char>();
499 }
500 return random_string;
501}
502
503} // namespace valfuzz
504
505int main(int argc, char **argv)
506{
507 valfuzz::parse_args(argc, argv);
510
511 unsigned int seed = (unsigned int) std::time(nullptr);
512 std::srand(seed);
513
515
517 {
518 if (valfuzz::get_test_one().has_value())
519 {
521 }
522 else
523 {
524 {
525 std::lock_guard<std::mutex> lock(valfuzz::get_stream_mutex());
526 std::print("Seed: {}\n", seed);
527 std::print("Running {} tests...\n", valfuzz::get_num_tests());
528 }
530 }
531 }
532 else
533 {
534 if (valfuzz::get_fuzz_one().has_value())
535 {
537 }
538 else
539 {
540 std::lock_guard<std::mutex> lock(valfuzz::get_stream_mutex());
541 std::print("Seed: {}\n", seed);
542 std::print("Running {} fuzz tests...\n",
544 }
546 }
547
549 std::print("Done\n");
550
551 return 0;
552}
std::pair< std::string, fuzz_function > fuzz_pair
Definition valfuzz.hpp:242
std::atomic< bool > & get_header()
Definition valfuzz.cpp:98
void run_fuzz_tests()
Definition valfuzz.cpp:353
long unsigned int get_num_fuzz_tests()
Definition valfuzz.cpp:122
std::function< void()> & get_function_execute_after()
Definition valfuzz.cpp:50
std::atomic< long unsigned int > & get_iterations()
Definition valfuzz.cpp:273
std::deque< fuzz_pair > & get_fuzzs()
Definition valfuzz.cpp:38
std::atomic< long unsigned int > & get_max_num_threads()
Definition valfuzz.cpp:86
void set_function_execute_before(std::function< void()> f)
Definition valfuzz.cpp:152
void _run_tests()
Definition valfuzz.cpp:236
std::deque< test_pair > & get_tests()
Definition valfuzz.cpp:32
long unsigned int get_num_tests()
Definition valfuzz.cpp:68
void _run_fuzz_tests()
Definition valfuzz.cpp:329
std::vector< std::thread > & get_thread_pool()
Definition valfuzz.cpp:80
void run_tests()
Definition valfuzz.cpp:251
std::mutex & get_tests_mutex()
Definition valfuzz.cpp:56
std::atomic< bool > & get_verbose()
Definition valfuzz.cpp:92
void run_one_fuzz(const std::string &name)
Definition valfuzz.cpp:300
void run_one_test(const std::string &name)
Definition valfuzz.cpp:211
void increment_iterations()
Definition valfuzz.cpp:279
std::atomic< bool > & get_do_fuzzing()
Definition valfuzz.cpp:104
char valfuzz_banner[]
Definition valfuzz.cpp:443
void print_header()
Definition valfuzz.cpp:451
void set_function_execute_after(std::function< void()> f)
Definition valfuzz.cpp:158
void add_fuzz_test(const std::string &name, fuzz_function test)
Definition valfuzz.cpp:189
void set_do_fuzzing(bool do_fuzzing)
Definition valfuzz.cpp:164
void set_fuzz_one(const std::string &fuzz_one)
Definition valfuzz.cpp:176
std::optional< std::string > & get_fuzz_one()
Definition valfuzz.cpp:116
void set_test_one(const std::string &test_one)
Definition valfuzz.cpp:170
std::pair< std::string, test_function > test_pair
Definition valfuzz.hpp:168
void set_multithreaded(bool is_threaded)
Definition valfuzz.cpp:128
std::atomic< bool > & get_is_threaded()
Definition valfuzz.cpp:74
std::function< void(std::string)> test_function
Definition valfuzz.hpp:167
void set_header(bool header)
Definition valfuzz.cpp:146
void set_max_num_threads(long unsigned int max_num_threads)
Definition valfuzz.cpp:134
std::function< void()> & get_function_execute_before()
Definition valfuzz.cpp:44
std::optional< std::string > & get_test_one()
Definition valfuzz.cpp:110
void add_test(const std::string &name, test_function test)
Definition valfuzz.cpp:182
void parse_args(int argc, char *argv[])
Definition valfuzz.cpp:375
std::optional< fuzz_pair > pop_fuzz_or_null()
Definition valfuzz.cpp:285
void set_verbose(bool verbose)
Definition valfuzz.cpp:140
std::function< void(std::string)> fuzz_function
Definition valfuzz.hpp:241
std::mutex & get_stream_mutex()
Definition valfuzz.cpp:62
std::optional< test_pair > pop_test_or_null()
Definition valfuzz.cpp:196
int main(int argc, char **argv)
Definition valfuzz.cpp:505
#define MAX_RANDOM_STRING_LEN
Definition valfuzz.hpp:223