Console Library
8.0.0
A header-only library that makes C++ simple
Toggle main menu visibility
Loading...
Searching...
No Matches
test.h
Go to the documentation of this file.
1
9
10
/*
11
Copyright (c) 2026 MrXie1109
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
#pragma once
33
#include <cstring>
34
#include <iomanip>
35
#include <iostream>
36
#include <string>
37
38
#include "
../core/csexc.h
"
39
#include "
../io/colorful.h
"
40
#include "
../text/fmt.h
"
41
#include "
../text/repr.h
"
42
#include "
./time.h
"
43
44
namespace
console
{
45
#ifndef CONSOLE_NO_TEST
46
52
inline
int
&
passed_count
() {
53
static
int
count = 0;
54
return
count;
55
}
56
62
inline
int
&
failed_count
() {
63
static
int
count = 0;
64
return
count;
65
}
66
71
inline
void
pre_summary
() {
72
struct
Guard {
73
Guard() { std::cout <<
"=== Start Test ===\n"
<< std::endl; }
74
75
~Guard() {
76
int
passed =
passed_count
();
77
int
failed =
failed_count
();
78
int
total = passed + failed;
79
std::cout <<
"=== Test Summary ==="
<< std::endl;
80
std::cout <<
"Passed: "
<< passed << std::endl;
81
std::cout <<
"Failed: "
<< failed << std::endl;
82
std::cout <<
"Total: "
<< total << std::endl;
83
if
(total > 0)
84
std::cout
85
<<
"Rate: "
<< std::fixed << std::setprecision(1)
86
<< (
static_cast<
double
>
(passed) / total * 100) <<
"%"
87
<< std::endl;
88
else
89
std::cout <<
"Rate: N/A"
<< std::endl;
90
}
91
};
92
static
Guard guard;
93
}
94
108
#define TEST(test_name) \
109
static void console_test_##test_name(); \
110
static int console_result_##test_name = [] { \
111
using namespace ::console; \
112
pre_summary(); \
113
try { \
114
console_test_##test_name(); \
115
ColorGuard cg(color::Green); \
116
std::cout << "[PASS] " #test_name "\n\n"; \
117
++passed_count(); \
118
return 0; \
119
} catch (const ::console::AssertError &e) { \
120
ColorGuard cg(color::Red); \
121
std::cout << console::format( \
122
"[FAIL] {}\n Assert Failed ({}:{}) - {}\n\n", \
123
#test_name, \
124
__FILE__, \
125
__LINE__, \
126
e.what()); \
127
++failed_count(); \
128
return 1; \
129
} catch (const std::system_error &e) { \
130
ColorGuard cg(color::Red); \
131
std::cout << console::format( \
132
"[FAIL] {}\n Threw Exception ({}:{})\n\twhat(): " \
133
"{}\n\tcode(): {}\n\n", \
134
#test_name, \
135
__FILE__, \
136
__LINE__, \
137
e.what(), \
138
e.code()); \
139
++failed_count(); \
140
return 1; \
141
} catch (const std::exception &e) { \
142
ColorGuard cg(color::Red); \
143
std::cout << console::format( \
144
"[FAIL] {}\n Threw Exception ({}:{})\n\twhat(): {}\n\n", \
145
#test_name, \
146
__FILE__, \
147
__LINE__, \
148
e.what()); \
149
++failed_count(); \
150
return 1; \
151
} catch (...) { \
152
ColorGuard cg(color::Red); \
153
std::cout << console::format( \
154
"[FAIL] {}\n Threw Exception ({}:{})\n\n", \
155
#test_name, \
156
__FILE__, \
157
__LINE__); \
158
++failed_count(); \
159
return 1; \
160
} \
161
}(); \
162
static void console_test_##test_name()
163
164
#define EXPR_WARP(expression) "`" #expression "`"
165
171
#define ASSERT_TRUE(condition) \
172
do { \
173
if (!(condition)) throw console::AssertError(#condition); \
174
} while (false)
175
181
#define ASSERT_FALSE(condition) \
182
do { \
183
if (condition) throw console::AssertError("!(" #condition ")"); \
184
} while (false)
185
193
#define ASSERT_EQ(expected, actual) \
194
do { \
195
if ((expected) != (actual)) \
196
throw console::AssertError( \
197
console::format("{} == {}\n\texpected: {}\n\tactual: {}", \
198
EXPR_WARP(expected), \
199
EXPR_WARP(actual), \
200
expected, \
201
actual)); \
202
} while (false)
203
210
#define ASSERT_NE(expected, actual) \
211
do { \
212
if ((expected) == (actual)) \
213
throw console::AssertError( \
214
console::format("{} != {}\n\texpected: {}\n\tactual: {}", \
215
EXPR_WARP(expected), \
216
EXPR_WARP(actual), \
217
expected, \
218
actual)); \
219
} while (false)
220
227
#define ASSERT_LT(a, b) \
228
do { \
229
if ((a) >= (b)) \
230
throw console::AssertError( \
231
console::format("{} < {}\n\tleft: {}\n\tright: {}", \
232
EXPR_WARP(a), \
233
EXPR_WARP(b), \
234
a, \
235
b)); \
236
} while (false)
237
244
#define ASSERT_GT(a, b) \
245
do { \
246
if ((a) <= (b)) \
247
throw console::AssertError( \
248
console::format("{} > {}\n\tleft: {}\n\tright: {}", \
249
EXPR_WARP(a), \
250
EXPR_WARP(b), \
251
a, \
252
b)); \
253
} while (false)
254
261
#define ASSERT_LE(a, b) \
262
do { \
263
if ((a) > (b)) \
264
throw console::AssertError( \
265
console::format("{} <= {}\n\tleft: {}\n\tright: {}", \
266
EXPR_WARP(a), \
267
EXPR_WARP(b), \
268
a, \
269
b)); \
270
} while (false)
271
278
#define ASSERT_GE(a, b) \
279
do { \
280
if ((a) < (b)) \
281
throw console::AssertError( \
282
console::format("{} >= {}\n\tleft: {}\n\tright: {}", \
283
EXPR_WARP(a), \
284
EXPR_WARP(b), \
285
a, \
286
b)); \
287
} while (false)
288
296
#define ASSERT_NEAR(expected, actual, eps) \
297
do { \
298
auto _e = (expected); \
299
auto _a = (actual); \
300
if ((_e - _a) > (eps) || (_a - _e) > (eps)) \
301
throw console::AssertError(console::format( \
302
"{} \u2248 {} [\u00B1{}]\n\texpected: " \
303
"{:.20f}\n\tactual: {:.20f}\n\tepsilon: {:.20f}", \
304
EXPR_WARP(expected), \
305
EXPR_WARP(actual), \
306
eps, \
307
_e, \
308
_a, \
309
eps)); \
310
} while (false)
311
317
#define ASSERT_NULL(ptr) \
318
do { \
319
if ((ptr) != nullptr) \
320
throw console::AssertError( \
321
console::format("{} == nullptr\n\tptr: {:#x}", \
322
EXPR_WARP(ptr), \
323
reinterpret_cast<uintptr_t>(ptr))); \
324
} while (false)
325
331
#define ASSERT_NOTNULL(ptr) \
332
do { \
333
if ((ptr) == nullptr) \
334
throw console::AssertError( \
335
EXPR_WARP(ptr) " == nullptr\n\tptr: nullptr"); \
336
} while (false)
337
341
inline
std::string
ptr_to_str
(
const
char
*s) {
342
if
(s ==
nullptr
)
return
"nullptr"
;
343
return
"\""
+ std::string(s) +
"\""
;
344
}
345
352
#define ASSERT_STREQ(expected, actual) \
353
do { \
354
const char *_e = (expected); \
355
const char *_a = (actual); \
356
if (_e == nullptr || _a == nullptr) { \
357
if (_e != _a) \
358
throw console::AssertError(console::format( \
359
"{} == {}\n\texpected: {}\n\tactual: {}", \
360
EXPR_WARP(expected), \
361
EXPR_WARP(actual), \
362
console::ptr_to_str(_e), \
363
console::ptr_to_str(_a))); \
364
} else if (std::strcmp(_e, _a) != 0) { \
365
throw console::AssertError( \
366
console::format("{} == {}\n\texpected: {}\n\tactual: {}", \
367
EXPR_WARP(expected), \
368
EXPR_WARP(actual), \
369
console::ptr_to_str(_e), \
370
console::ptr_to_str(_a))); \
371
} \
372
} while (false)
373
380
#define ASSERT_STRNE(expected, actual) \
381
do { \
382
const char *_e = (expected); \
383
const char *_a = (actual); \
384
if (_e == nullptr || _a == nullptr) { \
385
if (_e != _a) \
386
throw console::AssertError(console::format( \
387
"{} != {}\n\texpected: {}\n\tactual: {}", \
388
EXPR_WARP(expected), \
389
EXPR_WARP(actual), \
390
console::ptr_to_str(_e), \
391
console::ptr_to_str(_a))); \
392
} else if (std::strcmp(_e, _a) == 0) { \
393
throw console::AssertError( \
394
console::format("{} != {}\n\texpected: {}\n\tactual: {}", \
395
EXPR_WARP(expected), \
396
EXPR_WARP(actual), \
397
console::ptr_to_str(_e), \
398
console::ptr_to_str(_a))); \
399
} \
400
} while (false)
401
408
#define ASSERT_STRCASEEQ(a, b) \
409
do { \
410
const char *_a = (a); \
411
const char *_b = (b); \
412
if (_a == nullptr || _b == nullptr) { \
413
if (_a != _b) \
414
throw console::AssertError(console::format( \
415
"{} == {} [case-insensitive]\n\texpected: {}\n\t" \
416
"actual: {}", \
417
EXPR_WARP(a), \
418
EXPR_WARP(b), \
419
console::ptr_to_str(_a), \
420
console::ptr_to_str(_b))); \
421
} else { \
422
bool _eq = true; \
423
const char *_pa = _a; \
424
const char *_pb = _b; \
425
while (*_pa != '\0' && *_pb != '\0') { \
426
if (static_cast<char>(std::tolower(*_pa)) \
427
!= static_cast<char>(std::tolower(*_pb))) { \
428
_eq = false; \
429
break; \
430
} \
431
++_pa; \
432
++_pb; \
433
} \
434
if (*_pa != *_pb) _eq = false; \
435
if (!_eq) { \
436
throw console::AssertError(console::format( \
437
"{} == {} [case-insensitive]\n\texpected: {}\n\t" \
438
"actual: {}", \
439
EXPR_WARP(a), \
440
EXPR_WARP(b), \
441
console::ptr_to_str(_a), \
442
console::ptr_to_str(_b))); \
443
} \
444
} \
445
} while (false)
446
453
#define ASSERT_STRCONTAINS(str, substr) \
454
do { \
455
const char *_str = (str); \
456
const char *_substr = (substr); \
457
if (_str == nullptr || _substr == nullptr \
458
|| std::strstr(_str, _substr) == nullptr) { \
459
throw console::AssertError( \
460
console::format("{} contains {}\n\tstr: {}\n\tsubstr: {}", \
461
EXPR_WARP(str), \
462
EXPR_WARP(substr), \
463
console::ptr_to_str(_str), \
464
console::ptr_to_str(_substr))); \
465
} \
466
} while (false)
467
473
#define ASSERT_STRNOTCONTAINS(str, substr) \
474
do { \
475
const char *_str = (str); \
476
const char *_substr = (substr); \
477
if (_str == nullptr || _substr == nullptr \
478
|| std::strstr(_str, _substr) != nullptr) { \
479
throw console::AssertError( \
480
console::format("{} does not contain {}\n\tstr: {}\n\t" \
481
"substr: {}", \
482
EXPR_WARP(str), \
483
EXPR_WARP(substr), \
484
console::ptr_to_str(_str), \
485
console::ptr_to_str(_substr))); \
486
} \
487
} while (false)
488
495
#define ASSERT_THROWS(expression, exception_type) \
496
do { \
497
try { \
498
expression; \
499
throw console::AssertError( \
500
EXPR_WARP(expression) " should throw " #exception_type); \
501
} catch (const exception_type &) { \
502
} catch (...) { \
503
throw console::AssertError( \
504
EXPR_WARP(expression) " should throw " #exception_type); \
505
} \
506
} while (false)
507
513
#define ASSERT_NO_THROW(expression) \
514
do { \
515
try { \
516
expression; \
517
} catch (...) { \
518
throw console::AssertError( \
519
EXPR_WARP(expression) " should not throw"); \
520
} \
521
} while (false)
522
528
#define ASSERT_THROWS_ANY(expression) \
529
do { \
530
try { \
531
expression; \
532
} catch (...) { \
533
break; \
534
} \
535
throw console::AssertError(EXPR_WARP(expression) " should throw"); \
536
} while (false)
537
544
#define ASSERT_CONTAINS(container, element) \
545
do { \
546
const auto &_c = (container); \
547
const auto &_e = (element); \
548
bool _found = false; \
549
for (const auto &_item : _c) \
550
if (_item == _e) { \
551
_found = true; \
552
break; \
553
} \
554
if (!_found) \
555
throw console::AssertError( \
556
EXPR_WARP(container) " contains " EXPR_WARP(element)); \
557
} while (false)
558
565
#define ASSERT_SIZE_EQ(container, n) \
566
do { \
567
if ((container).size() != static_cast<std::size_t>(n)) \
568
throw console::AssertError( \
569
console::format("{}.size() == {}\n\tsize: {}\n\texpected: {}", \
570
EXPR_WARP(container), \
571
EXPR_WARP(n), \
572
(container).size(), \
573
static_cast<std::size_t>(n))); \
574
} while (false)
575
581
#define ASSERT_EMPTY(container) \
582
do { \
583
if (!(container).empty()) \
584
throw console::AssertError( \
585
console::format("{} is empty\n\tsize: {}", \
586
EXPR_WARP(container), \
587
(container).size())); \
588
} while (false)
589
595
#define ASSERT_NOT_EMPTY(container) \
596
do { \
597
if ((container).empty()) \
598
throw console::AssertError( \
599
EXPR_WARP(container) " is not empty\n\tsize: 0"); \
600
} while (false)
601
608
#define ASSERT_TIMEOUT(expr, ms) \
609
do { \
610
auto _duration = timer([&] { expr; }); \
611
auto _limit = milliseconds(ms); \
612
if (_duration > _limit) \
613
throw console::AssertError( \
614
console::format("{} timed out after {}\n\telapsed: " \
615
"{}\n\tlimit: {}", \
616
EXPR_WARP(expr), \
617
_limit, \
618
_duration, \
619
_limit)); \
620
} while (false)
621
629
#define ASSERT_PERFORMANCE(expr, ops, ms) \
630
do { \
631
auto _duration = timer([&] { \
632
for (auto i = 0ULL; i < ops; ++i) expr; \
633
}); \
634
auto _limit = milliseconds(ms); \
635
if (_duration > _limit) \
636
throw console::AssertError(console::format( \
637
"{} times {} timed out after {}\n\telapsed: {}\n\t" \
638
"limit: {}\n\tops: {}", \
639
EXPR_WARP(ops), \
640
EXPR_WARP(expr), \
641
_limit, \
642
_duration, \
643
_limit, \
644
ops)); \
645
} while (false)
646
648
#define TEST_MAIN \
649
int main() {}
650
651
#endif
652
}
colorful.h
提供控制台颜色和样式修饰的 ANSI 转义序列常量。
csexc.h
定义 console 库使用的自定义异常类层次结构。
fmt.h
轻量级格式化字符串头文件库。
console
本库所有组件所在的顶层命名空间。
console::pre_summary
void pre_summary()
构造守卫对象,程序结束时输出摘要。
Definition
test.h:71
console::failed_count
int & failed_count()
获取失败的测试用例数量。
Definition
test.h:62
console::passed_count
int & passed_count()
获取通过的测试用例数量。
Definition
test.h:52
console::ptr_to_str
std::string ptr_to_str(const char *s)
辅助函数:安全地将 C 字符串转换为显示字符串。
Definition
test.h:341
repr.h
提供类似 Python 的 repr() 函数,用于生成对象的可读字符串表示。
time.h
提供时间度量、计时、休眠和日期时间格式化功能。
include
util
test.h
Generated on
for Console Library by
1.18.0