mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2025-05-10 09:43:51 +00:00
Compare commits
714 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c216dc94d2 | ||
|
61893a00a4 | ||
|
3af7f2c161 | ||
|
a0de42ebc4 | ||
|
7b752106ac | ||
|
9589519d58 | ||
|
caf7c55785 | ||
|
9e4aed482e | ||
|
65d6316d65 | ||
|
3e3a8cc02f | ||
|
b7e33b08f1 | ||
|
0dbe8ba144 | ||
|
dbc4af819a | ||
|
7dbf5471ce | ||
|
72b35befb2 | ||
|
65ce51aed7 | ||
|
787a34ad7f | ||
|
7a212cfe40 | ||
|
0be0526085 | ||
|
87a5ae64a4 | ||
|
33acccb346 | ||
|
c765584e6b | ||
|
0bda3a7d1a | ||
|
2eaa2ea64f | ||
|
94a4028821 | ||
|
a8d6172250 | ||
|
2f39723d08 | ||
|
a9ba0a4dff | ||
|
37399af996 | ||
|
48084d55f2 | ||
|
5a1ecc3958 | ||
|
85b5cdd78d | ||
|
f2928d7152 | ||
|
ee0bee3907 | ||
|
71ba7e7b1b | ||
|
ebe7efa1cc | ||
|
22d90c29b4 | ||
|
b944f942ee | ||
|
550f728165 | ||
|
a4b2c61a65 | ||
|
5c0135fa5d | ||
|
2b5d1eea8d | ||
|
d274c0abe5 | ||
|
dda2e007a0 | ||
|
321a86d9f2 | ||
|
ada97046a2 | ||
|
6e73a63153 | ||
|
cdc223019a | ||
|
574f5ce93e | ||
|
2996cecee0 | ||
|
32bf5c9c09 | ||
|
735e5930eb | ||
|
748f47b377 | ||
|
4cb8ff9f90 | ||
|
985cd9f6a2 | ||
|
233f0fb1b8 | ||
|
03cf43ebaa | ||
|
3c4b96024f | ||
|
d74e4a7c9c | ||
|
bfa2f735f2 | ||
|
b6ab8435d7 | ||
|
39a64fb4e7 | ||
|
d7c14b6f3a | ||
|
1880693aef | ||
|
dd20342825 | ||
|
a268d65c4f | ||
|
b397c768e4 | ||
|
8e22a7676a | ||
|
8a7c536ad5 | ||
|
8aad481c69 | ||
|
5814e121df | ||
|
7adbccbaf7 | ||
|
eb10c22db1 | ||
|
708f860e3a | ||
|
eb30f15363 | ||
|
4941d5b56b | ||
|
9bbb4741b4 | ||
|
282f2feb77 | ||
|
60a1f00618 | ||
|
9104054ca5 | ||
|
d69f144a99 | ||
|
929dfbd348 | ||
|
3047183fd9 | ||
|
ef5e4044f1 | ||
|
3779800322 | ||
|
986a20fb7d | ||
|
8311e1105f | ||
|
ba6845925d | ||
|
343a0fc073 | ||
|
54f8a4d0f3 | ||
|
9c36aae4b7 | ||
|
b766025a83 | ||
|
9b5f76f833 | ||
|
d647f484a4 | ||
|
8794792baa | ||
|
b85768c1f3 | ||
|
e6d71bd702 | ||
|
258992a160 | ||
|
a7bc00e330 | ||
|
11a40584e9 | ||
|
3e86bdb4d8 | ||
|
c817d65695 | ||
|
51dee793fe | ||
|
457fc4306e | ||
|
4f5b003e76 | ||
|
5421e27106 | ||
|
fe07660f40 | ||
|
da2f9e476e | ||
|
1a7a7ed1c3 | ||
|
413994912d | ||
|
01dcf1d0ad | ||
|
8e378779c2 | ||
|
970b52897c | ||
|
412ba04d19 | ||
|
bfef4b3e9b | ||
|
7bd316f3d0 | ||
|
26208363ee | ||
|
b1b4bb8850 | ||
|
9dd565b6e3 | ||
|
924f214303 | ||
|
5c1a34e766 | ||
|
fa90d06dd5 | ||
|
d869054318 | ||
|
0cc1ca9a8d | ||
|
3701195033 | ||
|
f884a56258 | ||
|
d79633ff52 | ||
|
e0ebc431dc | ||
|
131bc6c674 | ||
|
10d68cff50 | ||
|
996acc5253 | ||
|
7c4799d0cf | ||
|
c239087332 | ||
|
7018e9263d | ||
|
4990b4b4b7 | ||
|
5064373c23 | ||
|
6c93aea59a | ||
|
6553cdedab | ||
|
a61b2427b0 | ||
|
af4ece3d5f | ||
|
e64379c3d7 | ||
|
5053912534 | ||
|
932b1cbc32 | ||
|
de36ea7755 | ||
|
9f8db2c230 | ||
|
3f00e1b321 | ||
|
7ab9c119ef | ||
|
3f2922b3fa | ||
|
509f583dca | ||
|
2d01e71286 | ||
|
e612154694 | ||
|
82fcbe3901 | ||
|
dbd2465b56 | ||
|
ea79494b29 | ||
|
f35aff84c2 | ||
|
7b18ae6f16 | ||
|
a79c56d06b | ||
|
3d6e315a4c | ||
|
4c27f9c6ef | ||
|
d173a37d17 | ||
|
7fd346a2ca | ||
|
c673d502b9 | ||
|
c43c51362a | ||
|
3e86d93d13 | ||
|
f6e4e2d0f3 | ||
|
01a52aa8bd | ||
|
8415bf0823 | ||
|
327ff263f5 | ||
|
61c418048d | ||
|
9720ef8c34 | ||
|
978a4f6345 | ||
|
80fb03628b | ||
|
2480c0342c | ||
|
eb6f610a45 | ||
|
cb74e4191b | ||
|
dfa641ca41 | ||
|
969a9f99d5 | ||
|
c099b42ba3 | ||
|
b8315278cb | ||
|
485f8f2411 | ||
|
953e4f3841 | ||
|
adf65cfe61 | ||
|
12c829f6d3 | ||
|
913314f1b1 | ||
|
ef63f97afe | ||
|
bda74db01d | ||
|
9ff3ff9446 | ||
|
c75d071615 | ||
|
b4989130da | ||
|
4fc0303bda | ||
|
3d9cc51851 | ||
|
f69587656f | ||
|
d5fc340c30 | ||
|
d79a547dc9 | ||
|
bd1da4346a | ||
|
4c2a608a0c | ||
|
ee4eb8deaa | ||
|
7196ac8a07 | ||
|
c88b09bc6b | ||
|
87fab847b8 | ||
|
4e6055f084 | ||
|
975cf0dae5 | ||
|
4854a694cd | ||
|
b1f8e986bf | ||
|
c5ee208775 | ||
|
ddfdacfa49 | ||
|
2514ebc20f | ||
|
4f9c6540b2 | ||
|
21c9a6a1ff | ||
|
7f6d413ddd | ||
|
88277139e7 | ||
|
6cdd3493a1 | ||
|
9c91b6f4a6 | ||
|
cee838e335 | ||
|
d82c82db2c | ||
|
ba638ff38e | ||
|
da0c6579fa | ||
|
52a18c78a5 | ||
|
048edec9ed | ||
|
af56b7ec0b | ||
|
6c3e8482f7 | ||
|
390f2c41f6 | ||
|
aa04feebb4 | ||
|
45f3694f82 | ||
|
c5c54b31e2 | ||
|
69c84c9597 | ||
|
ae63b89cbf | ||
|
ff038f98b7 | ||
|
e00fd06355 | ||
|
521529d24d | ||
|
ed0719f2bc | ||
|
6a848b1a16 | ||
|
c8bcaf8a91 | ||
|
8cd0ed0509 | ||
|
177d8420a1 | ||
|
388a8c007c | ||
|
bdefdce1ae | ||
|
9e4f93d87e | ||
|
0b657d28cf | ||
|
c1a09daf15 | ||
|
8438df4a95 | ||
|
67fd7e3d09 | ||
|
d44031615d | ||
|
98cc1ec344 | ||
|
25b1e0d906 | ||
|
05f9f83240 | ||
|
fb739dbaec | ||
|
50fce538c6 | ||
|
3b6597bba9 | ||
|
f10720ed69 | ||
|
2bc550b2f0 | ||
|
ce36b8a6e5 | ||
|
560854a961 | ||
|
2064462c35 | ||
|
07288888ad | ||
|
34d392cf3d | ||
|
825c3fbbb1 | ||
|
00bdf73ec6 | ||
|
f44ab9b3da | ||
|
a61f2b89be | ||
|
b8bafbc291 | ||
|
548dfff0ae | ||
|
4dd2f3d03d | ||
|
6791a8364d | ||
|
d1a1c8a158 | ||
|
b4d26badf2 | ||
|
c5a0673c93 | ||
|
ad40bd6a00 | ||
|
5c00bbf36b | ||
|
9d6f5372a3 | ||
|
f06fd934f6 | ||
|
80c0cc445e | ||
|
762024b890 | ||
|
82a90a2325 | ||
|
b7cac4f4b8 | ||
|
e323374d2a | ||
|
ffc294d37e | ||
|
fceada9ef4 | ||
|
5f0f73fad9 | ||
|
530d6ee098 | ||
|
420c9759c6 | ||
|
2ce7c22218 | ||
|
4ef9ed80cd | ||
|
44b3fe6277 | ||
|
449801990f | ||
|
af2928d316 | ||
|
d948e38820 | ||
|
65218ce222 | ||
|
55e99c4030 | ||
|
b63d50671d | ||
|
eba980846b | ||
|
374d058de7 | ||
|
31cdcc3c3a | ||
|
ad9f6423e2 | ||
|
cbca63f091 | ||
|
b4748a226c | ||
|
5b943d9bb8 | ||
|
c86f69a105 | ||
|
d39fda0657 | ||
|
37f8dc4382 | ||
|
3a8adda381 | ||
|
8aa38aecaf | ||
|
f1dec77f46 | ||
|
cddaedaff8 | ||
|
cefb5a8822 | ||
|
e426a38c3e | ||
|
f14accb7b6 | ||
|
c5c704cb3b | ||
|
115a786581 | ||
|
5ef4cfd263 | ||
|
03fecb2f78 | ||
|
7fc8682a0a | ||
|
f1431311a4 | ||
|
1d14e051a5 | ||
|
97ae6733ed | ||
|
1d6b22b5f0 | ||
|
1a49076b5b | ||
|
d0e4cb3f07 | ||
|
e2813d9d4d | ||
|
20a7f088ce | ||
|
f63ba7d013 | ||
|
0a629d7391 | ||
|
a609330e4c | ||
|
c029597a5a | ||
|
30b7732565 | ||
|
6650632e7f | ||
|
afe627e7af | ||
|
67f6ff7fa9 | ||
|
c7ed1796a7 | ||
|
44c62d838e | ||
|
6bb580cda8 | ||
|
00a8cb8e5d | ||
|
2e34a39673 | ||
|
01b90829bc | ||
|
e699bd0730 | ||
|
961a9379d5 | ||
|
ec87b04aff | ||
|
aabf752a51 | ||
|
afb0674ccb | ||
|
ee625232a4 | ||
|
52d8dd41f1 | ||
|
be07d2d7a9 | ||
|
0f1b62c2b3 | ||
|
c30906a541 | ||
|
3533503323 | ||
|
82acdca638 | ||
|
a1e56a567b | ||
|
0c17d172a2 | ||
|
5d8e7c761f | ||
|
17fc522b75 | ||
|
f1daa5b88b | ||
|
fe9a1949a6 | ||
|
50cba6db9f | ||
|
18592e7f98 | ||
|
bd9612b81e | ||
|
7ab5fb65b2 | ||
|
8df5fedc35 | ||
|
4a61f68fa4 | ||
|
067890133c | ||
|
d3076f5a70 | ||
|
ed129f057f | ||
|
eab5ea01d7 | ||
|
3e287b3a26 | ||
|
4f33637b43 | ||
|
698a1e51ec | ||
|
27c0e1186c | ||
|
c54c71a3e5 | ||
|
3409c00e6f | ||
|
f8ef5fab64 | ||
|
5b397d455d | ||
|
f977558a28 | ||
|
c2e156e0e0 | ||
|
7aba2938d3 | ||
|
d587548250 | ||
|
21f9c51556 | ||
|
985ceba525 | ||
|
e62a4b02e5 | ||
|
ff34749572 | ||
|
e5804d4a50 | ||
|
3956a2b790 | ||
|
b33aa52dc2 | ||
|
76230db97f | ||
|
a66a013ed7 | ||
|
f4b02dfdc1 | ||
|
4cf218643e | ||
|
8f96b69a25 | ||
|
d262033ded | ||
|
5745eabe69 | ||
|
5f18642271 | ||
|
88a9278872 | ||
|
9bb3ca8169 | ||
|
f2f4728489 | ||
|
d1b616286f | ||
|
df74526f91 | ||
|
9f7ae0737a | ||
|
1ebb8412c5 | ||
|
7b69999c37 | ||
|
c7e959a948 | ||
|
ba5884e779 | ||
|
cdaa5c48db | ||
|
bab5c0e907 | ||
|
016838fd10 | ||
|
75053bf855 | ||
|
ae3a6dd2a9 | ||
|
6d963fbe8d | ||
|
88f6245c84 | ||
|
0e7d2f9f93 | ||
|
4e6ded1f36 | ||
|
d663588491 | ||
|
439caf5b79 | ||
|
c4ba43ca6f | ||
|
20cba2ecd9 | ||
|
0ff2e16d69 | ||
|
51607ec752 | ||
|
7992b14896 | ||
|
7e420aeed3 | ||
|
227d2c2050 | ||
|
93e53c91f7 | ||
|
58cffd3223 | ||
|
8f32271e8c | ||
|
c8c1c3d376 | ||
|
9f512acb42 | ||
|
c0b461a3b7 | ||
|
74fe5a5029 | ||
|
9d0a9d4e23 | ||
|
5758769ad3 | ||
|
e7eadc3605 | ||
|
07c6e58951 | ||
|
87994811a1 | ||
|
42feb7e8be | ||
|
26196b70af | ||
|
93a51979c4 | ||
|
ad7edc7b27 | ||
|
27cd4e6ffe | ||
|
cae5a8be1c | ||
|
8e10d4e8e7 | ||
|
b57f79f438 | ||
|
a9cf097951 | ||
|
5c3624e1af | ||
|
cba9ef8c0b | ||
|
4f8407a3a7 | ||
|
656e936f49 | ||
|
d92c314466 | ||
|
b747fb111d | ||
|
7e0a0b2d0c | ||
|
362d064afa | ||
|
1bd88de2e5 | ||
|
0b541ffebc | ||
|
106be19c3e | ||
|
25d72bf881 | ||
|
9d5b5297cc | ||
|
462884bebb | ||
|
b1cc24b795 | ||
|
f0eb55b327 | ||
|
6dc285b5ca | ||
|
07e614eef7 | ||
|
916b2a8fd3 | ||
|
869f5bb279 | ||
|
3e21338f82 | ||
|
37bb3c6a77 | ||
|
d4ab2fa0e6 | ||
|
72d3f4896a | ||
|
5e6f973b99 | ||
|
7ed77b02ad | ||
|
127a64d5a0 | ||
|
caa31aafda | ||
|
dae318495f | ||
|
305a7abcb9 | ||
|
219d13b718 | ||
|
df20c27696 | ||
|
a5a62768c0 | ||
|
4001637beb | ||
|
47044c05a8 | ||
|
a449d82723 | ||
|
fee8e97b4e | ||
|
72d9ed4056 | ||
|
1be1b3a86d | ||
|
9452c0a4b6 | ||
|
307b729549 | ||
|
696239d6e1 | ||
|
6929d90353 | ||
|
348d032029 | ||
|
d1d3fcdfd5 | ||
|
abf3a67dd0 | ||
|
d87abeecf0 | ||
|
4e28e4f741 | ||
|
80a55cedeb | ||
|
d05c343602 | ||
|
33f67386fe | ||
|
56d8168dc4 | ||
|
5d87cc0558 | ||
|
cb41947eb4 | ||
|
0857eba17b | ||
|
020b0db090 | ||
|
bf0760fde4 | ||
|
bb8e45383e | ||
|
a1df576e4f | ||
|
7fb0254794 | ||
|
c82d1e52cc | ||
|
846151b605 | ||
|
e44e31dd5b | ||
|
f7b9501662 | ||
|
e12fe4cbbb | ||
|
49d2e1f135 | ||
|
8191fd8e6c | ||
|
d73395e1dc | ||
|
64d001162b | ||
|
bb00a23116 | ||
|
63d6e9b91b | ||
|
66eed5681a | ||
|
8ecdb11979 | ||
|
894fcc8e02 | ||
|
7f43f0f3ff | ||
|
87e03dd1ce | ||
|
e5cacb465d | ||
|
ee8371f753 | ||
|
081723f983 | ||
|
b61f36579c | ||
|
33f53aa458 | ||
|
412ab5f063 | ||
|
11e02e901c | ||
|
65a8f4cf44 | ||
|
27d128bbb4 | ||
|
070f9bec58 | ||
|
f817032513 | ||
|
17abe221c0 | ||
|
4a7a81e039 | ||
|
37fd4eb643 | ||
|
865b0e4c03 | ||
|
b324921c1a | ||
|
63f72caf30 | ||
|
99ac17b90a | ||
|
4b0ed9ee88 | ||
|
20056f6cda | ||
|
3b35279b16 | ||
|
27deb44df5 | ||
|
24a3ef949b | ||
|
bc3e098964 | ||
|
c247dcdd7b | ||
|
793ae9855e | ||
|
9fa426d51b | ||
|
cec6288a99 | ||
|
9639578c2a | ||
|
743ecbd365 | ||
|
084c643973 | ||
|
824e7682e4 | ||
|
f9074684dd | ||
|
ddff782133 | ||
|
3051152103 | ||
|
06026bb47d | ||
|
226388ae27 | ||
|
ea7548b4cc | ||
|
c7486ead96 | ||
|
90a291214c | ||
|
c111c42a86 | ||
|
6fb5b63018 | ||
|
ec56dfa35e | ||
|
943cd51b67 | ||
|
301faa074c | ||
|
dc0481e832 | ||
|
4f8fcdbaf7 | ||
|
b80aa7fee3 | ||
|
c384be02c9 | ||
|
d17ac3bb40 | ||
|
c7554ccac2 | ||
|
35ef1c7bae | ||
|
d87d0672a8 | ||
|
3da42fd1e8 | ||
|
503aa61325 | ||
|
e4c276d0c2 | ||
|
e07f7691a8 | ||
|
623ab4a96e | ||
|
e1efa337a2 | ||
|
549cdf2f7d | ||
|
3c522386e9 | ||
|
c202aa9ce9 | ||
|
e3e28c6231 | ||
|
4e05368086 | ||
|
e1afe74fe2 | ||
|
461acb02f5 | ||
|
415edc237c | ||
|
e20ecd2574 | ||
|
ab477b5631 | ||
|
0823d5c7f2 | ||
|
1cc6930363 | ||
|
4297500928 | ||
|
a58f042614 | ||
|
469c6bc2b6 | ||
|
887074efd2 | ||
|
9c2c15ca45 | ||
|
1b3b098329 | ||
|
6f7075e3aa | ||
|
ccbddd8842 | ||
|
879dd261c2 | ||
|
52f5eb5980 | ||
|
ea2f69a0d7 | ||
|
9f2064a8ed | ||
|
e3750d9ddf | ||
|
c1eee3012e | ||
|
6b08babbd2 | ||
|
215b81342e | ||
|
06bfa7e08b | ||
|
3d83cbb872 | ||
|
8a803b30f6 | ||
|
80be649de7 | ||
|
9648f950f5 | ||
|
6b9ffc8bec | ||
|
d903053faf | ||
|
676f1b5a26 | ||
|
b8dec12f15 | ||
|
fc9b223acc | ||
|
ba824089d7 | ||
|
1a2faf09e0 | ||
|
5a43bb8149 | ||
|
0104614656 | ||
|
77a77f6d2d | ||
|
089b9daa1c | ||
|
ba34ea4ee8 | ||
|
2917b8a005 | ||
|
dcf24d45a2 | ||
|
75fdb06696 | ||
|
e00ad37580 | ||
|
5cfb70c2b4 | ||
|
2a70c45697 | ||
|
c58b00580e | ||
|
7c60e69c33 | ||
|
33e94891ee | ||
|
73e0729f63 | ||
|
21c529229c | ||
|
63643e6386 | ||
|
6cc2edce99 | ||
|
d122ff3ca8 | ||
|
14c6d526b4 | ||
|
28e07bca16 | ||
|
faa5f1d802 | ||
|
9d3365df54 | ||
|
6ff84d34d1 | ||
|
b845425cd0 | ||
|
89519c88e2 | ||
|
ff813bf99d | ||
|
cf475bcb50 | ||
|
bc80d7c789 | ||
|
b7566f6961 | ||
|
0542fdb8e4 | ||
|
78c474c744 | ||
|
88411a1f52 | ||
|
ae6cf70bc4 | ||
|
68d1281759 | ||
|
0308d60cb2 | ||
|
59f5fdbb33 | ||
|
13184f5f80 | ||
|
8d9a477edb | ||
|
85b4abbf16 | ||
|
e42a358da8 | ||
|
f008fe4539 | ||
|
ddf41d29ef | ||
|
3f88a46c4a | ||
|
242706ea34 | ||
|
a9f5f8683f | ||
|
60c2213893 | ||
|
eb2d28bca2 | ||
|
86f637a246 | ||
|
2c07ec4600 | ||
|
871d8d67b0 | ||
|
7299713195 | ||
|
96afa7e108 | ||
|
55f57af0b9 | ||
|
6b35cd0116 | ||
|
99f2229e48 | ||
|
b9641048fc | ||
|
e9c6c6e609 | ||
|
40db42108f | ||
|
24bb1387d6 | ||
|
d0bd4afb0b | ||
|
78ea786abd | ||
|
9cac2c9ceb | ||
|
0cff3245df | ||
|
0e3925db3f | ||
|
c9a13d214b | ||
|
0954af2d4c | ||
|
7c1c952f5a | ||
|
a6edfc730a | ||
|
c1264bfedc | ||
|
90a5b6ceb0 | ||
|
eb240ad2e5 | ||
|
88c961f37e | ||
|
b952376968 | ||
|
5dd605d3a2 | ||
|
9c0c98b1ed | ||
|
615867322d | ||
|
02d3cd5909 | ||
|
47e5af15ea | ||
|
a5c239c174 | ||
|
c2afc5ca44 | ||
|
cee062d4c9 | ||
|
b21dc8cbe0 | ||
|
e1133a2dcb | ||
|
e273fec93c | ||
|
95d0b073bd | ||
|
9c7d841b37 | ||
|
f086bf5310 | ||
|
6613d7b7ad | ||
|
6adf130bf3 | ||
|
b6b2eaf5bc | ||
|
eb4b7c70a9 | ||
|
84661ea6ed | ||
|
041122908c | ||
|
401de608df | ||
|
726c64cf10 | ||
|
e1f781a21a | ||
|
72b81badad | ||
|
17428a8fbf | ||
|
6e1879dfae | ||
|
eb1d2e04bc |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/test/www*/dir/*.html text eol=lf
|
||||||
|
/test/www*/dir/*.txt text eol=lf
|
69
.github/workflows/abidiff.yaml
vendored
Normal file
69
.github/workflows/abidiff.yaml
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2025 Andrea Pappacoda <andrea@pappacoda.it>
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
name: abidiff
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: sh
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
abi:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
|
container:
|
||||||
|
image: debian:testing
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies
|
||||||
|
run: apt -y --update install --no-install-recommends
|
||||||
|
abigail-tools
|
||||||
|
ca-certificates
|
||||||
|
g++
|
||||||
|
git
|
||||||
|
libbrotli-dev
|
||||||
|
libssl-dev
|
||||||
|
meson
|
||||||
|
pkg-config
|
||||||
|
python3
|
||||||
|
zlib1g-dev
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: current
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: previous
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Checkout previous
|
||||||
|
working-directory: previous
|
||||||
|
run: |
|
||||||
|
git switch master
|
||||||
|
git describe --tags --abbrev=0 master | xargs git checkout
|
||||||
|
|
||||||
|
- name: Build current
|
||||||
|
working-directory: current
|
||||||
|
run: |
|
||||||
|
meson setup --buildtype=debug -Dcpp-httplib_compile=true build
|
||||||
|
ninja -C build
|
||||||
|
|
||||||
|
- name: Build previous
|
||||||
|
working-directory: previous
|
||||||
|
run: |
|
||||||
|
meson setup --buildtype=debug -Dcpp-httplib_compile=true build
|
||||||
|
ninja -C build
|
||||||
|
|
||||||
|
- name: Run abidiff
|
||||||
|
run: abidiff
|
||||||
|
--headers-dir1 previous/build
|
||||||
|
--headers-dir2 current/build
|
||||||
|
previous/build/libcpp-httplib.so
|
||||||
|
current/build/libcpp-httplib.so
|
32
.github/workflows/cifuzz.yaml
vendored
Normal file
32
.github/workflows/cifuzz.yaml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: CIFuzz
|
||||||
|
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Build Fuzzers
|
||||||
|
id: build
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'cpp-httplib'
|
||||||
|
dry-run: false
|
||||||
|
language: c++
|
||||||
|
- name: Run Fuzzers
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'cpp-httplib'
|
||||||
|
fuzz-seconds: 600
|
||||||
|
dry-run: false
|
||||||
|
language: c++
|
||||||
|
- name: Upload Crash
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: failure() && steps.build.outcome == 'success'
|
||||||
|
with:
|
||||||
|
name: artifacts
|
||||||
|
path: ./out/artifacts
|
172
.github/workflows/test.yaml
vendored
172
.github/workflows/test.yaml
vendored
@ -1,41 +1,153 @@
|
|||||||
name: test
|
name: test
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
gtest_filter:
|
||||||
|
description: 'Google Test filter'
|
||||||
|
test_linux:
|
||||||
|
description: 'Test on Linux'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
test_macos:
|
||||||
|
description: 'Test on MacOS'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
test_windows:
|
||||||
|
description: 'Test on Windows'
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
GTEST_FILTER: ${{ github.event.inputs.gtest_filter || '*' }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
style-check:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||||
|
continue-on-error: true
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: run style check
|
||||||
|
run: |
|
||||||
|
clang-format --version
|
||||||
|
cd test && make style_check
|
||||||
|
|
||||||
|
ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: >
|
||||||
|
(github.event_name == 'push') ||
|
||||||
|
(github.event_name == 'pull_request' &&
|
||||||
|
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
|
||||||
|
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_linux == 'true')
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macOS-latest, ubuntu-latest, windows-latest]
|
config:
|
||||||
|
- arch_flags: -m32
|
||||||
|
arch_suffix: :i386
|
||||||
|
name: (32-bit)
|
||||||
|
- arch_flags:
|
||||||
|
arch_suffix:
|
||||||
|
name: (64-bit)
|
||||||
|
name: ubuntu ${{ matrix.config.name }}
|
||||||
steps:
|
steps:
|
||||||
- name: prepare git for checkout on windows
|
- name: checkout
|
||||||
if: matrix.os == 'windows-latest'
|
uses: actions/checkout@v4
|
||||||
|
- name: install libraries
|
||||||
|
run: |
|
||||||
|
sudo dpkg --add-architecture i386
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libc6-dev${{ matrix.config.arch_suffix }} libstdc++-13-dev${{ matrix.config.arch_suffix }} \
|
||||||
|
libssl-dev${{ matrix.config.arch_suffix }} libcurl4-openssl-dev${{ matrix.config.arch_suffix }} \
|
||||||
|
zlib1g-dev${{ matrix.config.arch_suffix }} libbrotli-dev${{ matrix.config.arch_suffix }} \
|
||||||
|
libzstd-dev${{ matrix.config.arch_suffix }}
|
||||||
|
- name: build and run tests
|
||||||
|
run: cd test && make EXTRA_CXXFLAGS="${{ matrix.config.arch_flags }}"
|
||||||
|
- name: run fuzz test target
|
||||||
|
run: cd test && make EXTRA_CXXFLAGS="${{ matrix.config.arch_flags }}" fuzz_test
|
||||||
|
|
||||||
|
macos:
|
||||||
|
runs-on: macos-latest
|
||||||
|
if: >
|
||||||
|
(github.event_name == 'push') ||
|
||||||
|
(github.event_name == 'pull_request' &&
|
||||||
|
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
|
||||||
|
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_macos == 'true')
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: build and run tests
|
||||||
|
run: cd test && make
|
||||||
|
- name: run fuzz test target
|
||||||
|
run: cd test && make fuzz_test
|
||||||
|
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
if: >
|
||||||
|
(github.event_name == 'push') ||
|
||||||
|
(github.event_name == 'pull_request' &&
|
||||||
|
github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) ||
|
||||||
|
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_windows == 'true')
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
config:
|
||||||
|
- with_ssl: false
|
||||||
|
compiled: false
|
||||||
|
run_tests: true
|
||||||
|
name: without SSL
|
||||||
|
- with_ssl: true
|
||||||
|
compiled: false
|
||||||
|
run_tests: true
|
||||||
|
name: with SSL
|
||||||
|
- with_ssl: false
|
||||||
|
compiled: true
|
||||||
|
run_tests: false
|
||||||
|
name: compiled
|
||||||
|
name: windows ${{ matrix.config.name }}
|
||||||
|
steps:
|
||||||
|
- name: Prepare Git for Checkout on Windows
|
||||||
run: |
|
run: |
|
||||||
git config --global core.autocrlf false
|
git config --global core.autocrlf false
|
||||||
git config --global core.eol lf
|
git config --global core.eol lf
|
||||||
- name: checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: install brotli library on ubuntu
|
- name: Export GitHub Actions cache environment variables
|
||||||
if: matrix.os == 'ubuntu-latest'
|
uses: actions/github-script@v7
|
||||||
run: sudo apt update && sudo apt-get install -y libbrotli-dev
|
with:
|
||||||
- name: install brotli library on macOS
|
script: |
|
||||||
if: matrix.os == 'macOS-latest'
|
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
|
||||||
run: brew install brotli
|
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||||
- name: make
|
- name: Setup msbuild on windows
|
||||||
if: matrix.os != 'windows-latest'
|
uses: microsoft/setup-msbuild@v2
|
||||||
run: cd test && make
|
- name: Install vcpkg dependencies
|
||||||
- name: check fuzz test target
|
run: vcpkg install gtest curl zlib brotli zstd
|
||||||
if: matrix.os == 'ubuntu-latest'
|
- name: Install OpenSSL
|
||||||
run: cd test && make -f Makefile.fuzz_test
|
if: ${{ matrix.config.with_ssl }}
|
||||||
- name: setup msbuild on windows
|
run: choco install openssl
|
||||||
if: matrix.os == 'windows-latest'
|
- name: Configure CMake ${{ matrix.config.name }}
|
||||||
uses: warrenbuckley/Setup-MSBuild@v1
|
run: >
|
||||||
- name: make-windows
|
cmake -B build -S .
|
||||||
if: matrix.os == 'windows-latest'
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
run: |
|
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake
|
||||||
cd test
|
-DHTTPLIB_TEST=ON
|
||||||
msbuild.exe test.sln /verbosity:minimal /t:Build "/p:Configuration=Release;Platform=x64"
|
-DHTTPLIB_COMPILE=${{ matrix.config.compiled && 'ON' || 'OFF' }}
|
||||||
x64\Release\test.exe
|
-DHTTPLIB_REQUIRE_ZLIB=ON
|
||||||
|
-DHTTPLIB_REQUIRE_BROTLI=ON
|
||||||
|
-DHTTPLIB_REQUIRE_ZSTD=ON
|
||||||
|
-DHTTPLIB_REQUIRE_OPENSSL=${{ matrix.config.with_ssl && 'ON' || 'OFF' }}
|
||||||
|
- name: Build ${{ matrix.config.name }}
|
||||||
|
run: cmake --build build --config Release -- /v:m /clp:ShowCommandLine
|
||||||
|
- name: Run tests ${{ matrix.config.name }}
|
||||||
|
if: ${{ matrix.config.run_tests }}
|
||||||
|
run: ctest --output-on-failure --test-dir build -C Release
|
||||||
|
|
||||||
|
env:
|
||||||
|
VCPKG_ROOT: "C:/vcpkg"
|
||||||
|
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
|
||||||
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -9,16 +9,27 @@ example/benchmark
|
|||||||
example/redirect
|
example/redirect
|
||||||
example/sse*
|
example/sse*
|
||||||
example/upload
|
example/upload
|
||||||
|
example/one_time_request
|
||||||
|
example/server_and_client
|
||||||
example/*.pem
|
example/*.pem
|
||||||
|
test/httplib.cc
|
||||||
|
test/httplib.h
|
||||||
test/test
|
test/test
|
||||||
|
test/server_fuzzer
|
||||||
test/test_proxy
|
test/test_proxy
|
||||||
|
test/test_split
|
||||||
test/test.xcodeproj/xcuser*
|
test/test.xcodeproj/xcuser*
|
||||||
test/test.xcodeproj/*/xcuser*
|
test/test.xcodeproj/*/xcuser*
|
||||||
|
test/*.o
|
||||||
test/*.pem
|
test/*.pem
|
||||||
test/*.srl
|
test/*.srl
|
||||||
|
test/_build_*
|
||||||
|
work/
|
||||||
|
benchmark/server*
|
||||||
|
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
build/
|
||||||
Debug
|
Debug
|
||||||
Release
|
Release
|
||||||
*.vcxproj.user
|
*.vcxproj.user
|
||||||
@ -28,5 +39,7 @@ Release
|
|||||||
*.db
|
*.db
|
||||||
ipch
|
ipch
|
||||||
*.dSYM
|
*.dSYM
|
||||||
|
*.pyc
|
||||||
.*
|
.*
|
||||||
|
!/.gitattributes
|
||||||
!/.travis.yml
|
!/.travis.yml
|
||||||
|
191
CMakeLists.txt
191
CMakeLists.txt
@ -3,18 +3,23 @@
|
|||||||
* BUILD_SHARED_LIBS (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
|
* BUILD_SHARED_LIBS (default off) builds as a shared library (if HTTPLIB_COMPILE is ON)
|
||||||
* HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on)
|
* HTTPLIB_USE_OPENSSL_IF_AVAILABLE (default on)
|
||||||
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
|
* HTTPLIB_USE_ZLIB_IF_AVAILABLE (default on)
|
||||||
|
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
|
||||||
|
* HTTPLIB_USE_ZSTD_IF_AVAILABLE (default on)
|
||||||
* HTTPLIB_REQUIRE_OPENSSL (default off)
|
* HTTPLIB_REQUIRE_OPENSSL (default off)
|
||||||
* HTTPLIB_REQUIRE_ZLIB (default off)
|
* HTTPLIB_REQUIRE_ZLIB (default off)
|
||||||
* HTTPLIB_USE_BROTLI_IF_AVAILABLE (default on)
|
|
||||||
* HTTPLIB_REQUIRE_BROTLI (default off)
|
* HTTPLIB_REQUIRE_BROTLI (default off)
|
||||||
|
* HTTPLIB_REQUIRE_ZSTD (default off)
|
||||||
|
* HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN (default on)
|
||||||
* HTTPLIB_COMPILE (default off)
|
* HTTPLIB_COMPILE (default off)
|
||||||
|
* HTTPLIB_INSTALL (default on)
|
||||||
|
* HTTPLIB_TEST (default off)
|
||||||
* BROTLI_USE_STATIC_LIBS - tells Cmake to use the static Brotli libs (only works if you have them installed).
|
* BROTLI_USE_STATIC_LIBS - tells Cmake to use the static Brotli libs (only works if you have them installed).
|
||||||
* OPENSSL_USE_STATIC_LIBS - tells Cmake to use the static OpenSSL libs (only works if you have them installed).
|
* OPENSSL_USE_STATIC_LIBS - tells Cmake to use the static OpenSSL libs (only works if you have them installed).
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
After installation with Cmake, a find_package(httplib) is available.
|
After installation with Cmake, a find_package(httplib COMPONENTS OpenSSL ZLIB Brotli zstd) is available.
|
||||||
This creates a httplib::httplib target (if found).
|
This creates a httplib::httplib target (if found and if listed components are supported).
|
||||||
It can be linked like so:
|
It can be linked like so:
|
||||||
|
|
||||||
target_link_libraries(your_exe httplib::httplib)
|
target_link_libraries(your_exe httplib::httplib)
|
||||||
@ -42,6 +47,8 @@
|
|||||||
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
|
* HTTPLIB_IS_USING_OPENSSL - a bool for if OpenSSL support is enabled.
|
||||||
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
|
* HTTPLIB_IS_USING_ZLIB - a bool for if ZLIB support is enabled.
|
||||||
* HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled.
|
* HTTPLIB_IS_USING_BROTLI - a bool for if Brotli support is enabled.
|
||||||
|
* HTTPLIB_IS_USING_ZSTD - a bool for if ZSTD support is enabled.
|
||||||
|
* HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN - a bool for if support of loading system certs from the Apple Keychain is enabled.
|
||||||
* HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only.
|
* HTTPLIB_IS_COMPILED - a bool for if the library is compiled, or otherwise header-only.
|
||||||
* HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include).
|
* HTTPLIB_INCLUDE_DIR - the root path to httplib's header (e.g. /usr/include).
|
||||||
* HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so).
|
* HTTPLIB_LIBRARY - the full path to the library if compiled (e.g. /usr/lib/libhttplib.so).
|
||||||
@ -55,58 +62,50 @@
|
|||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
FindPython3 requires Cmake v3.12
|
|
||||||
ARCH_INDEPENDENT option of write_basic_package_version_file() requires Cmake v3.14
|
ARCH_INDEPENDENT option of write_basic_package_version_file() requires Cmake v3.14
|
||||||
]]
|
]]
|
||||||
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR)
|
||||||
|
|
||||||
# On systems without Git installed, there were errors since execute_process seemed to not throw an error without it?
|
# Get the CPPHTTPLIB_VERSION value and use it as a version
|
||||||
find_package(Git QUIET)
|
# This gets the string with the CPPHTTPLIB_VERSION value from the header.
|
||||||
if(Git_FOUND)
|
|
||||||
# Gets the latest tag as a string like "v0.6.6"
|
|
||||||
# Can silently fail if git isn't on the system
|
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
|
|
||||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
OUTPUT_VARIABLE _raw_version_string
|
|
||||||
ERROR_VARIABLE _git_tag_error
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# execute_process can fail silenty, so check for an error
|
|
||||||
# if there was an error, just use the user agent as a version
|
|
||||||
if(_git_tag_error OR NOT Git_FOUND)
|
|
||||||
message(WARNING "cpp-httplib failed to find the latest Git tag, falling back to using user agent as the version.")
|
|
||||||
# Get the user agent and use it as a version
|
|
||||||
# This gets the string with the user agent from the header.
|
|
||||||
# This is so the maintainer doesn't actually need to update this manually.
|
# This is so the maintainer doesn't actually need to update this manually.
|
||||||
file(STRINGS httplib.h _raw_version_string REGEX "User\-Agent.*cpp\-httplib/([0-9]+\.?)+")
|
file(STRINGS httplib.h _raw_version_string REGEX "CPPHTTPLIB_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"")
|
||||||
endif()
|
|
||||||
# Needed since git tags have "v" prefixing them.
|
# Extracts just the version string itself from the whole string contained in _raw_version_string
|
||||||
# Also used if the fallback to user agent string is being used.
|
# since _raw_version_string would contain the entire line of code where it found the version string
|
||||||
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
string(REGEX MATCH "([0-9]+\\.?)+" _httplib_version "${_raw_version_string}")
|
||||||
|
|
||||||
project(httplib VERSION ${_httplib_version} LANGUAGES CXX)
|
project(httplib
|
||||||
|
VERSION ${_httplib_version}
|
||||||
|
LANGUAGES CXX
|
||||||
|
DESCRIPTION "A C++ header-only HTTP/HTTPS server and client library."
|
||||||
|
HOMEPAGE_URL "https://github.com/yhirose/cpp-httplib"
|
||||||
|
)
|
||||||
|
|
||||||
# Change as needed to set an OpenSSL minimum version.
|
# Change as needed to set an OpenSSL minimum version.
|
||||||
# This is used in the installed Cmake config file.
|
# This is used in the installed Cmake config file.
|
||||||
set(_HTTPLIB_OPENSSL_MIN_VER "1.1.1")
|
set(_HTTPLIB_OPENSSL_MIN_VER "3.0.0")
|
||||||
|
|
||||||
|
# Lets you disable C++ exception during CMake configure time.
|
||||||
|
# The value is used in the install CMake config file.
|
||||||
|
option(HTTPLIB_NO_EXCEPTIONS "Disable the use of C++ exceptions" OFF)
|
||||||
# Allow for a build to require OpenSSL to pass, instead of just being optional
|
# Allow for a build to require OpenSSL to pass, instead of just being optional
|
||||||
option(HTTPLIB_REQUIRE_OPENSSL "Requires OpenSSL to be found & linked, or fails build." OFF)
|
option(HTTPLIB_REQUIRE_OPENSSL "Requires OpenSSL to be found & linked, or fails build." OFF)
|
||||||
option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build." OFF)
|
option(HTTPLIB_REQUIRE_ZLIB "Requires ZLIB to be found & linked, or fails build." OFF)
|
||||||
# Allow for a build to casually enable OpenSSL/ZLIB support, but silenty continue if not found.
|
# Allow for a build to casually enable OpenSSL/ZLIB support, but silently continue if not found.
|
||||||
# Make these options so their automatic use can be specifically disabled (as needed)
|
# Make these options so their automatic use can be specifically disabled (as needed)
|
||||||
option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
|
option(HTTPLIB_USE_OPENSSL_IF_AVAILABLE "Uses OpenSSL (if available) to enable HTTPS support." ON)
|
||||||
option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable Zlib compression support." ON)
|
option(HTTPLIB_USE_ZLIB_IF_AVAILABLE "Uses ZLIB (if available) to enable Zlib compression support." ON)
|
||||||
# Lets you compile the program as a regular library instead of header-only
|
# Lets you compile the program as a regular library instead of header-only
|
||||||
option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF)
|
option(HTTPLIB_COMPILE "If ON, uses a Python script to split the header into a compilable header & source file (requires Python v3)." OFF)
|
||||||
# Just setting this variable here for people building in-tree
|
# Lets you disable the installation (useful when fetched from another CMake project)
|
||||||
if(HTTPLIB_COMPILE)
|
option(HTTPLIB_INSTALL "Enables the installation target" ON)
|
||||||
set(HTTPLIB_IS_COMPILED TRUE)
|
option(HTTPLIB_TEST "Enables testing and builds tests" OFF)
|
||||||
endif()
|
|
||||||
|
|
||||||
option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF)
|
option(HTTPLIB_REQUIRE_BROTLI "Requires Brotli to be found & linked, or fails build." OFF)
|
||||||
option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON)
|
option(HTTPLIB_USE_BROTLI_IF_AVAILABLE "Uses Brotli (if available) to enable Brotli decompression support." ON)
|
||||||
|
option(HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN "Enable feature to load system certs from the Apple Keychain." ON)
|
||||||
|
option(HTTPLIB_REQUIRE_ZSTD "Requires ZSTD to be found & linked, or fails build." OFF)
|
||||||
|
option(HTTPLIB_USE_ZSTD_IF_AVAILABLE "Uses ZSTD (if available) to enable zstd support." ON)
|
||||||
# Defaults to static library
|
# Defaults to static library
|
||||||
option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF)
|
option(BUILD_SHARED_LIBS "Build the library as a shared library instead of static. Has no effect if using header-only." OFF)
|
||||||
if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
||||||
@ -115,40 +114,72 @@ if (BUILD_SHARED_LIBS AND WIN32 AND HTTPLIB_COMPILE)
|
|||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Set some variables that are used in-tree and while building based on our options
|
||||||
|
set(HTTPLIB_IS_COMPILED ${HTTPLIB_COMPILE})
|
||||||
|
set(HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN ${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN})
|
||||||
|
|
||||||
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
|
# Threads needed for <thread> on some systems, and for <pthread.h> on Linux
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
|
# Since Cmake v3.11, Crypto & SSL became optional when not specified as COMPONENTS.
|
||||||
if(HTTPLIB_REQUIRE_OPENSSL)
|
if(HTTPLIB_REQUIRE_OPENSSL)
|
||||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL REQUIRED)
|
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL REQUIRED)
|
||||||
|
set(HTTPLIB_IS_USING_OPENSSL TRUE)
|
||||||
elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
|
elseif(HTTPLIB_USE_OPENSSL_IF_AVAILABLE)
|
||||||
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL QUIET)
|
find_package(OpenSSL ${_HTTPLIB_OPENSSL_MIN_VER} COMPONENTS Crypto SSL QUIET)
|
||||||
|
# Avoid a rare circumstance of not finding all components but the end-user did their
|
||||||
|
# own call for OpenSSL, which might trick us into thinking we'd otherwise have what we wanted
|
||||||
|
if (TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto)
|
||||||
|
set(HTTPLIB_IS_USING_OPENSSL ${OPENSSL_FOUND})
|
||||||
|
else()
|
||||||
|
set(HTTPLIB_IS_USING_OPENSSL FALSE)
|
||||||
endif()
|
endif()
|
||||||
# Just setting this variable here for people building in-tree
|
|
||||||
if(OPENSSL_FOUND)
|
|
||||||
set(HTTPLIB_IS_USING_OPENSSL TRUE)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HTTPLIB_REQUIRE_ZLIB)
|
if(HTTPLIB_REQUIRE_ZLIB)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
||||||
elseif(HTTPLIB_USE_ZLIB_IF_AVAILABLE)
|
elseif(HTTPLIB_USE_ZLIB_IF_AVAILABLE)
|
||||||
find_package(ZLIB QUIET)
|
find_package(ZLIB QUIET)
|
||||||
endif()
|
|
||||||
# Just setting this variable here for people building in-tree
|
|
||||||
# FindZLIB doesn't have a ZLIB_FOUND variable, so check the target.
|
# FindZLIB doesn't have a ZLIB_FOUND variable, so check the target.
|
||||||
if(TARGET ZLIB::ZLIB)
|
if(TARGET ZLIB::ZLIB)
|
||||||
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
set(HTTPLIB_IS_USING_ZLIB TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Adds our cmake folder to the search path for find_package
|
# Adds our cmake folder to the search path for find_package
|
||||||
|
# This is so we can use our custom FindBrotli.cmake
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
if(HTTPLIB_REQUIRE_BROTLI)
|
if(HTTPLIB_REQUIRE_BROTLI)
|
||||||
find_package(Brotli COMPONENTS encoder decoder common REQUIRED)
|
find_package(Brotli COMPONENTS encoder decoder common REQUIRED)
|
||||||
|
set(HTTPLIB_IS_USING_BROTLI TRUE)
|
||||||
elseif(HTTPLIB_USE_BROTLI_IF_AVAILABLE)
|
elseif(HTTPLIB_USE_BROTLI_IF_AVAILABLE)
|
||||||
find_package(Brotli COMPONENTS encoder decoder common QUIET)
|
find_package(Brotli COMPONENTS encoder decoder common QUIET)
|
||||||
|
set(HTTPLIB_IS_USING_BROTLI ${Brotli_FOUND})
|
||||||
endif()
|
endif()
|
||||||
# Just setting this variable here for people building in-tree
|
|
||||||
if(Brotli_FOUND)
|
if(HTTPLIB_REQUIRE_ZSTD)
|
||||||
set(HTTPLIB_IS_USING_BROTLI TRUE)
|
find_package(zstd)
|
||||||
|
if(NOT zstd_FOUND)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd)
|
||||||
|
add_library(zstd::libzstd ALIAS PkgConfig::zstd)
|
||||||
|
endif()
|
||||||
|
set(HTTPLIB_IS_USING_ZSTD TRUE)
|
||||||
|
elseif(HTTPLIB_USE_ZSTD_IF_AVAILABLE)
|
||||||
|
find_package(zstd QUIET)
|
||||||
|
if(NOT zstd_FOUND)
|
||||||
|
find_package(PkgConfig QUIET)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(zstd QUIET IMPORTED_TARGET libzstd)
|
||||||
|
|
||||||
|
if(TARGET PkgConfig::zstd)
|
||||||
|
add_library(zstd::libzstd ALIAS PkgConfig::zstd)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
# Both find_package and PkgConf set a XXX_FOUND var
|
||||||
|
set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Used for default, common dirs that the end-user can change (if needed)
|
# Used for default, common dirs that the end-user can change (if needed)
|
||||||
@ -176,7 +207,7 @@ if(HTTPLIB_COMPILE)
|
|||||||
ERROR_VARIABLE _httplib_split_error
|
ERROR_VARIABLE _httplib_split_error
|
||||||
)
|
)
|
||||||
if(_httplib_split_error)
|
if(_httplib_split_error)
|
||||||
message(FATAL_ERROR "Failed when trying to split Cpp-httplib with the Python script.\n${_httplib_split_error}")
|
message(FATAL_ERROR "Failed when trying to split cpp-httplib with the Python script.\n${_httplib_split_error}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# split.py puts output in "out"
|
# split.py puts output in "out"
|
||||||
@ -188,6 +219,12 @@ if(HTTPLIB_COMPILE)
|
|||||||
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
|
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
|
||||||
)
|
)
|
||||||
|
set_target_properties(${PROJECT_NAME}
|
||||||
|
PROPERTIES
|
||||||
|
VERSION ${${PROJECT_NAME}_VERSION}
|
||||||
|
SOVERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}"
|
||||||
|
OUTPUT_NAME cpp-httplib
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
# This is for header-only.
|
# This is for header-only.
|
||||||
set(_INTERFACE_OR_PUBLIC INTERFACE)
|
set(_INTERFACE_OR_PUBLIC INTERFACE)
|
||||||
@ -198,22 +235,10 @@ endif()
|
|||||||
# Only useful if building in-tree, versus using it from an installation.
|
# Only useful if building in-tree, versus using it from an installation.
|
||||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||||
|
|
||||||
# Might be missing some, but this list is somewhat comprehensive
|
# Require C++11
|
||||||
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
target_compile_features(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} cxx_std_11)
|
||||||
cxx_std_11
|
|
||||||
cxx_nullptr
|
|
||||||
cxx_lambdas
|
|
||||||
cxx_override
|
|
||||||
cxx_defaulted_functions
|
|
||||||
cxx_attribute_deprecated
|
|
||||||
cxx_auto_type
|
|
||||||
cxx_decltype
|
|
||||||
cxx_deleted_functions
|
|
||||||
cxx_range_for
|
|
||||||
cxx_sizeof_member
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
target_include_directories(${PROJECT_NAME} SYSTEM ${_INTERFACE_OR_PUBLIC}
|
||||||
$<BUILD_INTERFACE:${_httplib_build_includedir}>
|
$<BUILD_INTERFACE:${_httplib_build_includedir}>
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
)
|
)
|
||||||
@ -224,72 +249,65 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
|||||||
# Needed for Windows libs on Mingw, as the pragma comment(lib, "xyz") aren't triggered.
|
# Needed for Windows libs on Mingw, as the pragma comment(lib, "xyz") aren't triggered.
|
||||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||||
$<$<PLATFORM_ID:Windows>:crypt32>
|
$<$<PLATFORM_ID:Windows>:crypt32>
|
||||||
$<$<PLATFORM_ID:Windows>:cryptui>
|
# Needed for API from MacOS Security framework
|
||||||
|
"$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN}>>:-framework CoreFoundation -framework Security>"
|
||||||
# Can't put multiple targets in a single generator expression or it bugs out.
|
# Can't put multiple targets in a single generator expression or it bugs out.
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
|
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::common>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
|
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::encoder>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::decoder>
|
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:Brotli::decoder>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:ZLIB::ZLIB>
|
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:ZLIB::ZLIB>
|
||||||
|
$<$<BOOL:${HTTPLIB_IS_USING_ZSTD}>:zstd::libzstd>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:OpenSSL::SSL>
|
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:OpenSSL::SSL>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:OpenSSL::Crypto>
|
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:OpenSSL::Crypto>
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the definitions to enable optional features
|
# Set the definitions to enable optional features
|
||||||
target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
target_compile_definitions(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC}
|
||||||
|
$<$<BOOL:${HTTPLIB_NO_EXCEPTIONS}>:CPPHTTPLIB_NO_EXCEPTIONS>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:CPPHTTPLIB_BROTLI_SUPPORT>
|
$<$<BOOL:${HTTPLIB_IS_USING_BROTLI}>:CPPHTTPLIB_BROTLI_SUPPORT>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_SUPPORT>
|
$<$<BOOL:${HTTPLIB_IS_USING_ZLIB}>:CPPHTTPLIB_ZLIB_SUPPORT>
|
||||||
|
$<$<BOOL:${HTTPLIB_IS_USING_ZSTD}>:CPPHTTPLIB_ZSTD_SUPPORT>
|
||||||
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
|
$<$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>:CPPHTTPLIB_OPENSSL_SUPPORT>
|
||||||
|
$<$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${HTTPLIB_IS_USING_OPENSSL}>,$<BOOL:${HTTPLIB_IS_USING_CERTS_FROM_MACOSX_KEYCHAIN}>>:CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN>
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cmake's find_package search path is different based on the system
|
# CMake configuration files installation directory
|
||||||
# See https://cmake.org/cmake/help/latest/command/find_package.html for the list
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
|
||||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_PREFIX}/cmake/${PROJECT_NAME}")
|
|
||||||
else()
|
|
||||||
# On Non-Windows, it should be /usr/lib/cmake/<name>/<name>Config.cmake
|
|
||||||
# NOTE: This may or may not work for macOS...
|
|
||||||
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
set(_TARGET_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||||
endif()
|
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
# Configures the meta-file httplibConfig.cmake.in to replace variables with paths/values/etc.
|
# Configures the meta-file httplibConfig.cmake.in to replace variables with paths/values/etc.
|
||||||
configure_package_config_file("${PROJECT_NAME}Config.cmake.in"
|
configure_package_config_file("cmake/${PROJECT_NAME}Config.cmake.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}"
|
INSTALL_DESTINATION "${_TARGET_INSTALL_CMAKEDIR}"
|
||||||
# Passes the includedir install path
|
# Passes the includedir install path
|
||||||
PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR
|
PATH_VARS CMAKE_INSTALL_FULL_INCLUDEDIR
|
||||||
# There aren't any components, so don't use the macro
|
|
||||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(HTTPLIB_COMPILE)
|
if(HTTPLIB_COMPILE)
|
||||||
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
# Example: if you find_package(httplib 0.5.4)
|
# Example: if you find_package(httplib 0.5.4)
|
||||||
# then anything >= 0.5 and <= 1.0 is accepted
|
# then anything >= 0.5.4 and < 0.6 is accepted
|
||||||
COMPATIBILITY SameMajorVersion
|
COMPATIBILITY SameMinorVersion
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
# Example: if you find_package(httplib 0.5.4)
|
# Example: if you find_package(httplib 0.5.4)
|
||||||
# then anything >= 0.5 and <= 1.0 is accepted
|
# then anything >= 0.5.4 and < 0.6 is accepted
|
||||||
COMPATIBILITY SameMajorVersion
|
COMPATIBILITY SameMinorVersion
|
||||||
# Tells Cmake that it's a header-only lib
|
# Tells Cmake that it's a header-only lib
|
||||||
# Mildly useful for end-users :)
|
# Mildly useful for end-users :)
|
||||||
ARCH_INDEPENDENT
|
ARCH_INDEPENDENT
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(HTTPLIB_INSTALL)
|
||||||
# Creates the export httplibTargets.cmake
|
# Creates the export httplibTargets.cmake
|
||||||
# This is strictly what holds compilation requirements
|
# This is strictly what holds compilation requirements
|
||||||
# and linkage information (doesn't find deps though).
|
# and linkage information (doesn't find deps though).
|
||||||
install(TARGETS ${PROJECT_NAME}
|
install(TARGETS ${PROJECT_NAME} EXPORT httplibTargets)
|
||||||
EXPORT httplibTargets
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
install(FILES "${_httplib_build_includedir}/httplib.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
install(FILES "${_httplib_build_includedir}/httplib.h" TYPE INCLUDE)
|
||||||
|
|
||||||
install(FILES
|
install(FILES
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||||
@ -307,3 +325,16 @@ install(EXPORT httplibTargets
|
|||||||
NAMESPACE ${PROJECT_NAME}::
|
NAMESPACE ${PROJECT_NAME}::
|
||||||
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
DESTINATION ${_TARGET_INSTALL_CMAKEDIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Install documentation & license
|
||||||
|
# ex: /usr/share/doc/httplib/README.md and /usr/share/licenses/httplib/LICENSE
|
||||||
|
install(FILES "README.md" DESTINATION "${CMAKE_INSTALL_DOCDIR}")
|
||||||
|
install(FILES "LICENSE" DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/${PROJECT_NAME}")
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(HTTPLIB_TEST)
|
||||||
|
include(CTest)
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
|
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
FROM yhirose4dockerhub/ubuntu-builder AS builder
|
||||||
|
WORKDIR /build
|
||||||
|
COPY httplib.h .
|
||||||
|
COPY docker/main.cc .
|
||||||
|
RUN g++ -std=c++23 -static -o server -O2 -I. main.cc && strip server
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=builder /build/server /server
|
||||||
|
COPY docker/html/index.html /html/index.html
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["/server"]
|
422
README.md
422
README.md
@ -5,18 +5,26 @@ cpp-httplib
|
|||||||
|
|
||||||
A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||||
|
|
||||||
It's extremely easy to setup. Just include **httplib.h** file in your code!
|
It's extremely easy to setup. Just include the **httplib.h** file in your code!
|
||||||
|
|
||||||
NOTE: This is a 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
|
> [!IMPORTANT]
|
||||||
|
> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.
|
||||||
|
|
||||||
Simple examples
|
Simple examples
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
#### Server
|
#### Server (Multi-threaded)
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
|
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
#include "path/to/httplib.h"
|
||||||
|
|
||||||
|
// HTTP
|
||||||
httplib::Server svr;
|
httplib::Server svr;
|
||||||
|
|
||||||
|
// HTTPS
|
||||||
|
httplib::SSLServer svr;
|
||||||
|
|
||||||
svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
|
svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
|
||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
@ -27,18 +35,55 @@ svr.listen("0.0.0.0", 8080);
|
|||||||
#### Client
|
#### Client
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");
|
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
#include "path/to/httplib.h"
|
||||||
|
|
||||||
|
// HTTP
|
||||||
|
httplib::Client cli("http://yhirose.github.io");
|
||||||
|
|
||||||
|
// HTTPS
|
||||||
|
httplib::Client cli("https://yhirose.github.io");
|
||||||
|
|
||||||
auto res = cli.Get("/hi");
|
auto res = cli.Get("/hi");
|
||||||
|
res->status;
|
||||||
res->status; // 200
|
res->body;
|
||||||
res->body; // "Hello World!"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Try out the examples on Repl.it!
|
SSL Support
|
||||||
|
-----------
|
||||||
|
|
||||||
1. Run server at https://repl.it/@yhirose/cpp-httplib-server
|
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||||
2. Run client at https://repl.it/@yhirose/cpp-httplib-client
|
|
||||||
|
> [!NOTE]
|
||||||
|
> cpp-httplib currently supports only version 3.0 or later. Please see [this page](https://www.openssl.org/policies/releasestrat.html) to get more information.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> For macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||||
|
#include "path/to/httplib.h"
|
||||||
|
|
||||||
|
// Server
|
||||||
|
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||||
|
|
||||||
|
// Client
|
||||||
|
httplib::Client cli("https://localhost:1234"); // scheme + host
|
||||||
|
httplib::SSLClient cli("localhost:1234"); // host
|
||||||
|
httplib::SSLClient cli("localhost", 1234); // host, port
|
||||||
|
|
||||||
|
// Use your CA bundle
|
||||||
|
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||||
|
|
||||||
|
// Disable cert verification
|
||||||
|
cli.enable_server_certificate_verification(false);
|
||||||
|
|
||||||
|
// Disable host verification
|
||||||
|
cli.enable_server_hostname_verification(false);
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||||
|
|
||||||
Server
|
Server
|
||||||
------
|
------
|
||||||
@ -56,11 +101,20 @@ int main(void)
|
|||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Match the request path against a regular expression
|
||||||
|
// and extract its captures
|
||||||
svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
|
svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
|
||||||
auto numbers = req.matches[1];
|
auto numbers = req.matches[1];
|
||||||
res.set_content(numbers, "text/plain");
|
res.set_content(numbers, "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Capture the second segment of the request path as "id" path param
|
||||||
|
svr.Get("/users/:id", [&](const Request& req, Response& res) {
|
||||||
|
auto user_id = req.path_params.at("id");
|
||||||
|
res.set_content(user_id, "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extract values from HTTP headers and URL query params
|
||||||
svr.Get("/body-header-param", [](const Request& req, Response& res) {
|
svr.Get("/body-header-param", [](const Request& req, Response& res) {
|
||||||
if (req.has_header("Content-Length")) {
|
if (req.has_header("Content-Length")) {
|
||||||
auto val = req.get_header_value("Content-Length");
|
auto val = req.get_header_value("Content-Length");
|
||||||
@ -71,6 +125,21 @@ int main(void)
|
|||||||
res.set_content(req.body, "text/plain");
|
res.set_content(req.body, "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If the handler takes time to finish, you can also poll the connection state
|
||||||
|
svr.Get("/task", [&](const Request& req, Response& res) {
|
||||||
|
const char * result = nullptr;
|
||||||
|
process.run(); // for example, starting an external process
|
||||||
|
while (result == nullptr) {
|
||||||
|
sleep(1);
|
||||||
|
if (req.is_connection_closed()) {
|
||||||
|
process.kill(); // kill the process
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result = process.stdout(); // != nullptr if the process finishes
|
||||||
|
}
|
||||||
|
res.set_content(result, "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
svr.Get("/stop", [&](const Request& req, Response& res) {
|
svr.Get("/stop", [&](const Request& req, Response& res) {
|
||||||
svr.stop();
|
svr.stop();
|
||||||
});
|
});
|
||||||
@ -120,24 +189,40 @@ svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");
|
|||||||
|
|
||||||
The followings are built-in mappings:
|
The followings are built-in mappings:
|
||||||
|
|
||||||
| Extension | MIME Type |
|
| Extension | MIME Type | Extension | MIME Type |
|
||||||
| :-------- | :--------------------- |
|
| :--------- | :-------------------------- | :--------- | :-------------------------- |
|
||||||
| txt | text/plain |
|
| css | text/css | mpga | audio/mpeg |
|
||||||
| html, htm | text/html |
|
| csv | text/csv | weba | audio/webm |
|
||||||
| css | text/css |
|
| txt | text/plain | wav | audio/wave |
|
||||||
| jpeg, jpg | image/jpg |
|
| vtt | text/vtt | otf | font/otf |
|
||||||
| png | image/png |
|
| html, htm | text/html | ttf | font/ttf |
|
||||||
| gif | image/gif |
|
| apng | image/apng | woff | font/woff |
|
||||||
| svg | image/svg+xml |
|
| avif | image/avif | woff2 | font/woff2 |
|
||||||
| ico | image/x-icon |
|
| bmp | image/bmp | 7z | application/x-7z-compressed |
|
||||||
| json | application/json |
|
| gif | image/gif | atom | application/atom+xml |
|
||||||
| pdf | application/pdf |
|
| png | image/png | pdf | application/pdf |
|
||||||
| js | application/javascript |
|
| svg | image/svg+xml | mjs, js | application/javascript |
|
||||||
| wasm | application/wasm |
|
| webp | image/webp | json | application/json |
|
||||||
| xml | application/xml |
|
| ico | image/x-icon | rss | application/rss+xml |
|
||||||
| xhtml | application/xhtml+xml |
|
| tif | image/tiff | tar | application/x-tar |
|
||||||
|
| tiff | image/tiff | xhtml, xht | application/xhtml+xml |
|
||||||
|
| jpeg, jpg | image/jpeg | xslt | application/xslt+xml |
|
||||||
|
| mp4 | video/mp4 | xml | application/xml |
|
||||||
|
| mpeg | video/mpeg | gz | application/gzip |
|
||||||
|
| webm | video/webm | zip | application/zip |
|
||||||
|
| mp3 | audio/mp3 | wasm | application/wasm |
|
||||||
|
|
||||||
NOTE: These the static file server methods are not thread safe.
|
> [!WARNING]
|
||||||
|
> These static file server methods are not thread-safe.
|
||||||
|
|
||||||
|
### File request handler
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// The handler is called right before the response is sent to a client
|
||||||
|
svr.set_file_request_handler([](const Request &req, Response &res) {
|
||||||
|
...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
@ -158,6 +243,48 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Exception handler
|
||||||
|
The exception handler gets called if a user routing handler throws an error.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {
|
||||||
|
auto fmt = "<h1>Error 500</h1><p>%s</p>";
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ep);
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
snprintf(buf, sizeof(buf), fmt, e.what());
|
||||||
|
} catch (...) { // See the following NOTE
|
||||||
|
snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
|
||||||
|
}
|
||||||
|
res.set_content(buf, "text/html");
|
||||||
|
res.status = StatusCode::InternalServerError_500;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> if you don't provide the `catch (...)` block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!
|
||||||
|
|
||||||
|
### Pre routing handler
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
svr.set_pre_routing_handler([](const auto& req, auto& res) {
|
||||||
|
if (req.path == "/hello") {
|
||||||
|
res.set_content("world", "text/html");
|
||||||
|
return Server::HandlerResponse::Handled;
|
||||||
|
}
|
||||||
|
return Server::HandlerResponse::Unhandled;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Post routing handler
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
svr.set_post_routing_handler([](const auto& req, auto& res) {
|
||||||
|
res.set_header("ADDITIONAL_HEADER", "value");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### 'multipart/form-data' POST data
|
### 'multipart/form-data' POST data
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@ -171,12 +298,13 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Receive content with Content receiver
|
### Receive content with a content receiver
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
svr.Post("/content_receiver",
|
svr.Post("/content_receiver",
|
||||||
[&](const Request &req, Response &res, const ContentReader &content_reader) {
|
[&](const Request &req, Response &res, const ContentReader &content_reader) {
|
||||||
if (req.is_multipart_form_data()) {
|
if (req.is_multipart_form_data()) {
|
||||||
|
// NOTE: `content_reader` is blocking until every form data field is read
|
||||||
MultipartFormDataItems files;
|
MultipartFormDataItems files;
|
||||||
content_reader(
|
content_reader(
|
||||||
[&](const MultipartFormData &file) {
|
[&](const MultipartFormData &file) {
|
||||||
@ -193,12 +321,11 @@ svr.Post("/content_receiver",
|
|||||||
body.append(data, data_length);
|
body.append(data, data_length);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
res.set_content(body, "text/plain");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Send content with Content provider
|
### Send content with the content provider
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const size_t DATA_CHUNK_SIZE = 4;
|
const size_t DATA_CHUNK_SIZE = 4;
|
||||||
@ -209,12 +336,12 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
|||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
data->size(), // Content length
|
data->size(), // Content length
|
||||||
"text/plain", // Content type
|
"text/plain", // Content type
|
||||||
[data](size_t offset, size_t length, DataSink &sink) {
|
[&, data](size_t offset, size_t length, DataSink &sink) {
|
||||||
const auto &d = *data;
|
const auto &d = *data;
|
||||||
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||||
return true; // return 'false' if you want to cancel the process.
|
return true; // return 'false' if you want to cancel the process.
|
||||||
},
|
},
|
||||||
[data] { delete data; });
|
[data](bool success) { delete data; });
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -224,7 +351,7 @@ Without content length:
|
|||||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||||
res.set_content_provider(
|
res.set_content_provider(
|
||||||
"text/plain", // Content type
|
"text/plain", // Content type
|
||||||
[&](size_t offset, size_t length, DataSink &sink) {
|
[&](size_t offset, DataSink &sink) {
|
||||||
if (/* there is still data */) {
|
if (/* there is still data */) {
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
// prepare data...
|
// prepare data...
|
||||||
@ -242,6 +369,7 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
|||||||
```cpp
|
```cpp
|
||||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||||
res.set_chunked_content_provider(
|
res.set_chunked_content_provider(
|
||||||
|
"text/plain",
|
||||||
[](size_t offset, DataSink &sink) {
|
[](size_t offset, DataSink &sink) {
|
||||||
sink.write("123", 3);
|
sink.write("123", 3);
|
||||||
sink.write("345", 3);
|
sink.write("345", 3);
|
||||||
@ -253,21 +381,54 @@ svr.Get("/chunked", [&](const Request& req, Response& res) {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
With trailer:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||||
|
res.set_header("Trailer", "Dummy1, Dummy2");
|
||||||
|
res.set_chunked_content_provider(
|
||||||
|
"text/plain",
|
||||||
|
[](size_t offset, DataSink &sink) {
|
||||||
|
sink.write("123", 3);
|
||||||
|
sink.write("345", 3);
|
||||||
|
sink.write("789", 3);
|
||||||
|
sink.done_with_trailer({
|
||||||
|
{"Dummy1", "DummyVal1"},
|
||||||
|
{"Dummy2", "DummyVal2"}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Send file content
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
svr.Get("/content", [&](const Request &req, Response &res) {
|
||||||
|
res.set_file_content("./path/to/content.html");
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.Get("/content", [&](const Request &req, Response &res) {
|
||||||
|
res.set_file_content("./path/to/content", "text/html");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### 'Expect: 100-continue' handler
|
### 'Expect: 100-continue' handler
|
||||||
|
|
||||||
As default, the server sends `100 Continue` response for `Expect: 100-continue` header.
|
By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// Send a '417 Expectation Failed' response.
|
// Send a '417 Expectation Failed' response.
|
||||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||||
return 417;
|
return StatusCode::ExpectationFailed_417;
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// Send a final status without reading the message body.
|
// Send a final status without reading the message body.
|
||||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||||
return res.status = 401;
|
return res.status = StatusCode::Unauthorized_401;
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -275,6 +436,7 @@ svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
svr.set_keep_alive_max_count(2); // Default is 5
|
svr.set_keep_alive_max_count(2); // Default is 5
|
||||||
|
svr.set_keep_alive_timeout(10); // Default is 5
|
||||||
```
|
```
|
||||||
|
|
||||||
### Timeout
|
### Timeout
|
||||||
@ -285,19 +447,22 @@ svr.set_write_timeout(5, 0); // 5 seconds
|
|||||||
svr.set_idle_interval(0, 100000); // 100 milliseconds
|
svr.set_idle_interval(0, 100000); // 100 milliseconds
|
||||||
```
|
```
|
||||||
|
|
||||||
### Set maximum payload length for reading request body
|
### Set maximum payload length for reading a request body
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
|
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> When the request body content type is 'www-form-urlencoded', the actual payload length shouldn't exceed `CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH`.
|
||||||
|
|
||||||
### Server-Sent Events
|
### Server-Sent Events
|
||||||
|
|
||||||
Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssesvr.cc) and [Client example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssecli.cc).
|
Please see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssesvr.cc) and [Client example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssecli.cc).
|
||||||
|
|
||||||
### Default thread pool support
|
### Default thread pool support
|
||||||
|
|
||||||
`ThreadPool` is used as a **default** task queue, and the default thread count is 8, or `std::thread::hardware_concurrency()`. You can change it with `CPPHTTPLIB_THREAD_POOL_COUNT`.
|
`ThreadPool` is used as the **default** task queue, with a default thread count of 8 or `std::thread::hardware_concurrency() - 1`, whichever is greater. You can change it with `CPPHTTPLIB_THREAD_POOL_COUNT`.
|
||||||
|
|
||||||
If you want to set the thread count at runtime, there is no convenient way... But here is how.
|
If you want to set the thread count at runtime, there is no convenient way... But here is how.
|
||||||
|
|
||||||
@ -305,6 +470,17 @@ If you want to set the thread count at runtime, there is no convenient way... Bu
|
|||||||
svr.new_task_queue = [] { return new ThreadPool(12); };
|
svr.new_task_queue = [] { return new ThreadPool(12); };
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also provide an optional parameter to limit the maximum number
|
||||||
|
of pending requests, i.e. requests `accept()`ed by the listener but
|
||||||
|
still waiting to be serviced by worker threads.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
svr.new_task_queue = [] { return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); };
|
||||||
|
```
|
||||||
|
|
||||||
|
Default limit is 0 (unlimited). Once the limit is reached, the listener
|
||||||
|
will shutdown the client connection.
|
||||||
|
|
||||||
### Override the default thread pool with yours
|
### Override the default thread pool with yours
|
||||||
|
|
||||||
You can supply your own thread pool implementation according to your need.
|
You can supply your own thread pool implementation according to your need.
|
||||||
@ -316,8 +492,10 @@ public:
|
|||||||
pool_.start_with_thread_count(n);
|
pool_.start_with_thread_count(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void enqueue(std::function<void()> fn) override {
|
virtual bool enqueue(std::function<void()> fn) override {
|
||||||
pool_.enqueue(fn);
|
/* Return true if the task was actually enqueued, or false
|
||||||
|
* if the caller must drop the corresponding connection. */
|
||||||
|
return pool_.enqueue(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void shutdown() override {
|
virtual void shutdown() override {
|
||||||
@ -345,17 +523,18 @@ int main(void)
|
|||||||
httplib::Client cli("localhost", 1234);
|
httplib::Client cli("localhost", 1234);
|
||||||
|
|
||||||
if (auto res = cli.Get("/hi")) {
|
if (auto res = cli.Get("/hi")) {
|
||||||
if (res->status == 200) {
|
if (res->status == StatusCode::OK_200) {
|
||||||
std::cout << res->body << std::endl;
|
std::cout << res->body << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto err = res.error();
|
auto err = res.error();
|
||||||
...
|
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: Constructor with scheme-host-port string is now supported!
|
> [!TIP]
|
||||||
|
> Constructor with scheme-host-port string is now supported!
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
httplib::Client cli("localhost");
|
httplib::Client cli("localhost");
|
||||||
@ -363,6 +542,7 @@ httplib::Client cli("localhost:8080");
|
|||||||
httplib::Client cli("http://localhost");
|
httplib::Client cli("http://localhost");
|
||||||
httplib::Client cli("http://localhost:8080");
|
httplib::Client cli("http://localhost:8080");
|
||||||
httplib::Client cli("https://localhost");
|
httplib::Client cli("https://localhost");
|
||||||
|
httplib::SSLClient cli("localhost");
|
||||||
```
|
```
|
||||||
|
|
||||||
### Error code
|
### Error code
|
||||||
@ -382,7 +562,9 @@ enum Error {
|
|||||||
SSLConnection,
|
SSLConnection,
|
||||||
SSLLoadingCerts,
|
SSLLoadingCerts,
|
||||||
SSLServerVerification,
|
SSLServerVerification,
|
||||||
UnsupportedMultipartBoundaryChars
|
UnsupportedMultipartBoundaryChars,
|
||||||
|
Compression,
|
||||||
|
ConnectionTimeout,
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -390,14 +572,18 @@ enum Error {
|
|||||||
|
|
||||||
```c++
|
```c++
|
||||||
httplib::Headers headers = {
|
httplib::Headers headers = {
|
||||||
{ "Accept-Encoding", "gzip, deflate" }
|
{ "Hello", "World!" }
|
||||||
};
|
};
|
||||||
auto res = cli.Get("/hi", headers);
|
auto res = cli.Get("/hi", headers);
|
||||||
```
|
```
|
||||||
or
|
or
|
||||||
```c++
|
```c++
|
||||||
|
auto res = cli.Get("/hi", {{"Hello", "World!"}});
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```c++
|
||||||
cli.set_default_headers({
|
cli.set_default_headers({
|
||||||
{ "Accept-Encoding", "gzip, deflate" }
|
{ "Hello", "World!" }
|
||||||
});
|
});
|
||||||
auto res = cli.Get("/hi");
|
auto res = cli.Get("/hi");
|
||||||
```
|
```
|
||||||
@ -468,9 +654,12 @@ res = cli.Options("/resource/foo");
|
|||||||
cli.set_connection_timeout(0, 300000); // 300 milliseconds
|
cli.set_connection_timeout(0, 300000); // 300 milliseconds
|
||||||
cli.set_read_timeout(5, 0); // 5 seconds
|
cli.set_read_timeout(5, 0); // 5 seconds
|
||||||
cli.set_write_timeout(5, 0); // 5 seconds
|
cli.set_write_timeout(5, 0); // 5 seconds
|
||||||
|
|
||||||
|
// This method works the same as curl's `--max-timeout` option
|
||||||
|
svr.set_max_timeout(5000); // 5 seconds
|
||||||
```
|
```
|
||||||
|
|
||||||
### Receive content with Content receiver
|
### Receive content with a content receiver
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
std::string body;
|
std::string body;
|
||||||
@ -488,7 +677,7 @@ std::string body;
|
|||||||
auto res = cli.Get(
|
auto res = cli.Get(
|
||||||
"/stream", Headers(),
|
"/stream", Headers(),
|
||||||
[&](const Response &response) {
|
[&](const Response &response) {
|
||||||
EXPECT_EQ(200, response.status);
|
EXPECT_EQ(StatusCode::OK_200, response.status);
|
||||||
return true; // return 'false' if you want to cancel the request.
|
return true; // return 'false' if you want to cancel the request.
|
||||||
},
|
},
|
||||||
[&](const char *data, size_t data_length) {
|
[&](const char *data, size_t data_length) {
|
||||||
@ -497,12 +686,12 @@ auto res = cli.Get(
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Send content with Content provider
|
### Send content with a content provider
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::string body = ...;
|
std::string body = ...;
|
||||||
|
|
||||||
auto res = cli_.Post(
|
auto res = cli.Post(
|
||||||
"/stream", body.size(),
|
"/stream", body.size(),
|
||||||
[](size_t offset, size_t length, DataSink &sink) {
|
[](size_t offset, size_t length, DataSink &sink) {
|
||||||
sink.write(body.data() + offset, length);
|
sink.write(body.data() + offset, length);
|
||||||
@ -511,10 +700,25 @@ auto res = cli_.Post(
|
|||||||
"text/plain");
|
"text/plain");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Chunked transfer encoding
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto res = cli.Post(
|
||||||
|
"/stream",
|
||||||
|
[](size_t offset, DataSink &sink) {
|
||||||
|
sink.os << "chunked data 1";
|
||||||
|
sink.os << "chunked data 2";
|
||||||
|
sink.os << "chunked data 3";
|
||||||
|
sink.done();
|
||||||
|
return true; // return 'false' if you want to cancel the request.
|
||||||
|
},
|
||||||
|
"text/plain");
|
||||||
|
```
|
||||||
|
|
||||||
### With Progress Callback
|
### With Progress Callback
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
httplib::Client client(url, port);
|
httplib::Client cli(url, port);
|
||||||
|
|
||||||
// prints: 0 / 000 bytes => 50% complete
|
// prints: 0 / 000 bytes => 50% complete
|
||||||
auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
|
auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
|
||||||
@ -541,7 +745,8 @@ cli.set_digest_auth("user", "pass");
|
|||||||
cli.set_bearer_token_auth("token");
|
cli.set_bearer_token_auth("token");
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: OpenSSL is required for Digest Authentication.
|
> [!NOTE]
|
||||||
|
> OpenSSL is required for Digest Authentication.
|
||||||
|
|
||||||
### Proxy server support
|
### Proxy server support
|
||||||
|
|
||||||
@ -558,7 +763,8 @@ cli.set_proxy_digest_auth("user", "pass");
|
|||||||
cli.set_proxy_bearer_token_auth("pass");
|
cli.set_proxy_bearer_token_auth("pass");
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: OpenSSL is required for Digest Authentication.
|
> [!NOTE]
|
||||||
|
> OpenSSL is required for Digest Authentication.
|
||||||
|
|
||||||
### Range
|
### Range
|
||||||
|
|
||||||
@ -605,35 +811,19 @@ res = cli.Get("/");
|
|||||||
res->status; // 200
|
res->status; // 200
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use a specitic network interface
|
### Use a specific network interface
|
||||||
|
|
||||||
NOTE: This feature is not available on Windows, yet.
|
> [!NOTE]
|
||||||
|
> This feature is not available on Windows, yet.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
cli.set_interface("eth0"); // Interface name, IP address or host name
|
cli.set_interface("eth0"); // Interface name, IP address or host name
|
||||||
```
|
```
|
||||||
|
|
||||||
OpenSSL Support
|
|
||||||
---------------
|
|
||||||
|
|
||||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
|
||||||
|
|
||||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
|
||||||
|
|
||||||
```c++
|
|
||||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
|
||||||
|
|
||||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
|
||||||
|
|
||||||
httplib::SSLClient cli("localhost", 1234); // or `httplib::Client cli("https://localhost:1234");`
|
|
||||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
|
||||||
cli.enable_server_certificate_verification(true);
|
|
||||||
```
|
|
||||||
|
|
||||||
Compression
|
Compression
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The server can applie compression to the following MIME type contents:
|
The server can apply compression to the following MIME type contents:
|
||||||
|
|
||||||
* all text types except text/event-stream
|
* all text types except text/event-stream
|
||||||
* image/svg+xml
|
* image/svg+xml
|
||||||
@ -651,6 +841,21 @@ The server can applie compression to the following MIME type contents:
|
|||||||
Brotli compression is available with `CPPHTTPLIB_BROTLI_SUPPORT`. Necessary libraries should be linked.
|
Brotli compression is available with `CPPHTTPLIB_BROTLI_SUPPORT`. Necessary libraries should be linked.
|
||||||
Please see https://github.com/google/brotli for more detail.
|
Please see https://github.com/google/brotli for more detail.
|
||||||
|
|
||||||
|
### Default `Accept-Encoding` value
|
||||||
|
|
||||||
|
The default `Accept-Encoding` value contains all possible compression types. So, the following two examples are same.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
res = cli.Get("/resource/foo");
|
||||||
|
res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||||
|
```
|
||||||
|
|
||||||
|
If we don't want a response without compression, we have to set `Accept-Encoding` to an empty string. This behavior is similar to curl.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
res = cli.Get("/resource/foo", {{"Accept-Encoding", ""}});
|
||||||
|
```
|
||||||
|
|
||||||
### Compress request body on client
|
### Compress request body on client
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
@ -662,17 +867,72 @@ res = cli.Post("/resource/foo", "...", "text/plain");
|
|||||||
|
|
||||||
```c++
|
```c++
|
||||||
cli.set_decompress(false);
|
cli.set_decompress(false);
|
||||||
res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
res = cli.Get("/resource/foo");
|
||||||
res->body; // Compressed data
|
res->body; // Compressed data
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Unix Domain Socket Support
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Unix Domain Socket support is available on Linux and macOS.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// Server
|
||||||
|
httplib::Server svr;
|
||||||
|
svr.set_address_family(AF_UNIX).listen("./my-socket.sock", 80);
|
||||||
|
|
||||||
|
// Client
|
||||||
|
httplib::Client cli("./my-socket.sock");
|
||||||
|
cli.set_address_family(AF_UNIX);
|
||||||
|
```
|
||||||
|
|
||||||
|
"my-socket.sock" can be a relative path or an absolute path. You application must have the appropriate permissions for the path. You can also use an abstract socket address on Linux. To use an abstract socket address, prepend a null byte ('\x00') to the path.
|
||||||
|
|
||||||
|
|
||||||
Split httplib.h into .h and .cc
|
Split httplib.h into .h and .cc
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ ./split.py -h
|
||||||
|
usage: split.py [-h] [-e EXTENSION] [-o OUT]
|
||||||
|
|
||||||
|
This script splits httplib.h into .h and .cc parts.
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
-e EXTENSION, --extension EXTENSION
|
||||||
|
extension of the implementation file (default: cc)
|
||||||
|
-o OUT, --out OUT where to write the files (default: out)
|
||||||
|
|
||||||
|
$ ./split.py
|
||||||
|
Wrote out/httplib.h and out/httplib.cc
|
||||||
|
```
|
||||||
|
|
||||||
|
Dockerfile for Static HTTP Server
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Dockerfile for static HTTP server is available. Port number of this HTTP server is 80, and it serves static files from `/html` directory in the container.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> python3 split.py
|
> docker build -t cpp-httplib-server .
|
||||||
> ls out
|
...
|
||||||
httplib.h httplib.cc
|
|
||||||
|
> docker run --rm -it -p 8080:80 -v ./docker/html:/html cpp-httplib-server
|
||||||
|
Serving HTTP on 0.0.0.0 port 80 ...
|
||||||
|
192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"
|
||||||
|
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."
|
||||||
|
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
|
||||||
|
```
|
||||||
|
|
||||||
|
From Docker Hub
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> docker run --rm -it -p 8080:80 -v ./docker/html:/html yhirose4dockerhub/cpp-httplib-server
|
||||||
|
Serving HTTP on 0.0.0.0 port 80 ...
|
||||||
|
192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] "GET / HTTP/1.1" 200 599 "-" "curl/8.7.1"
|
||||||
|
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET / HTTP/1.1" 200 599 "-" "Mozilla/5.0 ..."
|
||||||
|
192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] "GET /favicon.ico HTTP/1.1" 404 152 "-" "Mozilla/5.0 ..."
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
@ -697,12 +957,16 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
|||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Cygwin on Windows is not supported.
|
> [!NOTE]
|
||||||
|
> cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Windows 8 or lower, Visual Studio 2015 or lower, and Cygwin and MSYS2 including MinGW are neither supported nor tested.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
MIT license (© 2020 Yuji Hirose)
|
MIT license (© 2025 Yuji Hirose)
|
||||||
|
|
||||||
Special Thanks To
|
Special Thanks To
|
||||||
-----------------
|
-----------------
|
||||||
|
77
benchmark/Makefile
Normal file
77
benchmark/Makefile
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
CXXFLAGS = -std=c++11 -O2 -I..
|
||||||
|
|
||||||
|
CPPHTTPLIB_FLAGS = -DCPPHTTPLIB_THREAD_POOL_COUNT=16
|
||||||
|
|
||||||
|
BENCH = bombardier -c 10 -d 5s localhost:8080
|
||||||
|
MONITOR = ali http://localhost:8080
|
||||||
|
|
||||||
|
# cpp-httplib
|
||||||
|
bench: server
|
||||||
|
@echo "--------------------\n cpp-httplib latest\n--------------------\n"
|
||||||
|
@./server & export PID=$$!; $(BENCH); kill $${PID}
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
monitor: server
|
||||||
|
@./server & export PID=$$!; $(MONITOR); kill $${PID}
|
||||||
|
|
||||||
|
run : server
|
||||||
|
@./server
|
||||||
|
|
||||||
|
server : cpp-httplib/main.cpp ../httplib.h
|
||||||
|
@g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib/main.cpp
|
||||||
|
|
||||||
|
# cpp-httplib v0.19.0
|
||||||
|
bench-v19: server-v19
|
||||||
|
@echo "---------------------\n cpp-httplib v0.19.0\n---------------------\n"
|
||||||
|
@./server-v19 & export PID=$$!; $(BENCH); kill $${PID}
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
monitor-v19: server-v19
|
||||||
|
@./server-v19 & export PID=$$!; $(MONITOR); kill $${PID}
|
||||||
|
|
||||||
|
run-v19 : server-v19
|
||||||
|
@./server-v19
|
||||||
|
|
||||||
|
server-v19 : cpp-httplib-v19/main.cpp cpp-httplib-v19/httplib.h
|
||||||
|
@g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib-v19/main.cpp
|
||||||
|
|
||||||
|
# cpp-httplib v0.18.0
|
||||||
|
bench-v18: server-v18
|
||||||
|
@echo "---------------------\n cpp-httplib v0.18.0\n---------------------\n"
|
||||||
|
@./server-v18 & export PID=$$!; $(BENCH); kill $${PID}
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
monitor-v18: server-v18
|
||||||
|
@./server-v18 & export PID=$$!; $(MONITOR); kill $${PID}
|
||||||
|
|
||||||
|
run-v18 : server-v18
|
||||||
|
@./server-v18
|
||||||
|
|
||||||
|
server-v18 : cpp-httplib-v18/main.cpp cpp-httplib-v18/httplib.h
|
||||||
|
@g++ -o $@ $(CXXFLAGS) $(CPPHTTPLIB_FLAGS) cpp-httplib-v18/main.cpp
|
||||||
|
|
||||||
|
# crow
|
||||||
|
bench-crow: server-crow
|
||||||
|
@echo "-------------\n Crow v1.2.0\n-------------\n"
|
||||||
|
@./server-crow & export PID=$$!; $(BENCH); kill $${PID}
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
monitor-crow: server-crow
|
||||||
|
@./server-crow & export PID=$$!; $(MONITOR); kill $${PID}
|
||||||
|
|
||||||
|
run-crow : server-crow
|
||||||
|
@./server-crow
|
||||||
|
|
||||||
|
server-crow : crow/main.cpp
|
||||||
|
@g++ -o $@ $(CXXFLAGS) crow/main.cpp
|
||||||
|
|
||||||
|
# misc
|
||||||
|
build: server server-v18 server-v19 server-crow
|
||||||
|
|
||||||
|
bench-all: bench-crow bench bench-v19 bench-v18
|
||||||
|
|
||||||
|
issue:
|
||||||
|
bombardier -c 10 -d 30s localhost:8080
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf server*
|
10154
benchmark/cpp-httplib-v18/httplib.h
Normal file
10154
benchmark/cpp-httplib-v18/httplib.h
Normal file
File diff suppressed because it is too large
Load Diff
12
benchmark/cpp-httplib-v18/main.cpp
Normal file
12
benchmark/cpp-httplib-v18/main.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "./httplib.h"
|
||||||
|
using namespace httplib;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Server svr;
|
||||||
|
|
||||||
|
svr.Get("/", [](const Request &, Response &res) {
|
||||||
|
res.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.listen("0.0.0.0", 8080);
|
||||||
|
}
|
10475
benchmark/cpp-httplib-v19/httplib.h
Normal file
10475
benchmark/cpp-httplib-v19/httplib.h
Normal file
File diff suppressed because it is too large
Load Diff
12
benchmark/cpp-httplib-v19/main.cpp
Normal file
12
benchmark/cpp-httplib-v19/main.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "./httplib.h"
|
||||||
|
using namespace httplib;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Server svr;
|
||||||
|
|
||||||
|
svr.Get("/", [](const Request &, Response &res) {
|
||||||
|
res.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.listen("0.0.0.0", 8080);
|
||||||
|
}
|
12
benchmark/cpp-httplib/main.cpp
Normal file
12
benchmark/cpp-httplib/main.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "httplib.h"
|
||||||
|
using namespace httplib;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Server svr;
|
||||||
|
|
||||||
|
svr.Get("/", [](const Request &, Response &res) {
|
||||||
|
res.set_content("Hello World!", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.listen("0.0.0.0", 8080);
|
||||||
|
}
|
14316
benchmark/crow/crow_all.h
Normal file
14316
benchmark/crow/crow_all.h
Normal file
File diff suppressed because it is too large
Load Diff
17
benchmark/crow/main.cpp
Normal file
17
benchmark/crow/main.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "crow_all.h"
|
||||||
|
|
||||||
|
class CustomLogger : public crow::ILogHandler {
|
||||||
|
public:
|
||||||
|
void log(std::string, crow::LogLevel) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
CustomLogger logger;
|
||||||
|
crow::logger::setHandler(&logger);
|
||||||
|
|
||||||
|
crow::SimpleApp app;
|
||||||
|
|
||||||
|
CROW_ROUTE(app, "/")([]() { return "Hello world!"; });
|
||||||
|
|
||||||
|
app.port(8080).multithreaded().run();
|
||||||
|
}
|
2
benchmark/download.sh
Executable file
2
benchmark/download.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
rm -f httplib.h
|
||||||
|
wget https://raw.githubusercontent.com/yhirose/cpp-httplib/v$1/httplib.h
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
# If they asked for a specific version, warn/fail since we don't support it.
|
# If they asked for a specific version, warn/fail since we don't support it.
|
||||||
# TODO: if they start distributing the version somewhere, implement finding it.
|
# TODO: if they start distributing the version somewhere, implement finding it.
|
||||||
# But currently there's a version header that doesn't seem to get installed.
|
# See https://github.com/google/brotli/issues/773#issuecomment-579133187
|
||||||
if(Brotli_FIND_VERSION)
|
if(Brotli_FIND_VERSION)
|
||||||
set(_brotli_version_error_msg "FindBrotli.cmake doesn't have version support!")
|
set(_brotli_version_error_msg "FindBrotli.cmake doesn't have version support!")
|
||||||
# If the package is required, throw a fatal error
|
# If the package is required, throw a fatal error
|
||||||
@ -68,29 +68,24 @@ if(BROTLI_USE_STATIC_LIBS)
|
|||||||
set(_brotli_stat_str "_STATIC")
|
set(_brotli_stat_str "_STATIC")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Lets us know we are using the PkgConfig libraries
|
|
||||||
# Will be set false if any non-pkgconf vars are used
|
|
||||||
set(_brotli_using_pkgconf TRUE)
|
|
||||||
|
|
||||||
# Each string here is "ComponentName;LiteralName" (the semi-colon is a delimiter)
|
# Each string here is "ComponentName;LiteralName" (the semi-colon is a delimiter)
|
||||||
foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
|
foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
|
||||||
# Split the component name and literal library name from the listvar
|
# Split the component name and literal library name from the listvar
|
||||||
list(GET _listvar 0 _component_name)
|
list(GET _listvar 0 _component_name)
|
||||||
list(GET _listvar 1 _libname)
|
list(GET _listvar 1 _libname)
|
||||||
|
|
||||||
if(PKG_CONFIG_FOUND)
|
# NOTE: We can't rely on PkgConf for static libs since the upstream static lib support is broken
|
||||||
|
# See https://github.com/google/brotli/issues/795
|
||||||
|
# TODO: whenever their issue is fixed upstream, remove this "AND NOT BROTLI_USE_STATIC_LIBS" check
|
||||||
|
if(PKG_CONFIG_FOUND AND NOT BROTLI_USE_STATIC_LIBS)
|
||||||
# These need to be GLOBAL for MinGW when making ALIAS libraries against them.
|
# These need to be GLOBAL for MinGW when making ALIAS libraries against them.
|
||||||
if(BROTLI_USE_STATIC_LIBS)
|
# Have to postfix _STATIC on the name to tell PkgConfig to find the static libs.
|
||||||
# Have to use _STATIC to tell PkgConfig to find the static libs.
|
pkg_check_modules(Brotli_${_component_name}${_brotli_stat_str} QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
||||||
pkg_check_modules(Brotli_${_component_name}_STATIC QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
|
||||||
else()
|
|
||||||
pkg_check_modules(Brotli_${_component_name} QUIET GLOBAL IMPORTED_TARGET libbrotli${_libname})
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check if the target was already found by Pkgconf
|
# Check if the target was already found by Pkgconf
|
||||||
if(TARGET PkgConfig::Brotli_${_component_name} OR TARGET PkgConfig::Brotli_${_component_name}_STATIC)
|
if(TARGET PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
|
||||||
# Can't use generators for ALIAS targets, so you get this jank
|
# ALIAS since we don't want the PkgConfig namespace on the Cmake library (for end-users)
|
||||||
add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
|
add_library(Brotli::${_component_name} ALIAS PkgConfig::Brotli_${_component_name}${_brotli_stat_str})
|
||||||
|
|
||||||
# Tells HANDLE_COMPONENTS we found the component
|
# Tells HANDLE_COMPONENTS we found the component
|
||||||
@ -109,23 +104,18 @@ foreach(_listvar "common;common" "decoder;dec" "encoder;enc")
|
|||||||
continue()
|
continue()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Lets us know we aren't using the PkgConfig libraries
|
|
||||||
set(_brotli_using_pkgconf FALSE)
|
|
||||||
if(Brotli_FIND_REQUIRED_${_component_name})
|
if(Brotli_FIND_REQUIRED_${_component_name})
|
||||||
# If it's required, we can set the name used in find_library as a required var for FindPackageHandleStandardArgs
|
# If it's required, we can set the name used in find_library as a required var for FindPackageHandleStandardArgs
|
||||||
list(APPEND _brotli_req_vars "Brotli_${_component_name}")
|
list(APPEND _brotli_req_vars "Brotli_${_component_name}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BROTLI_USE_STATIC_LIBS)
|
|
||||||
list(APPEND _brotli_lib_names
|
|
||||||
"brotli${_libname}-static"
|
|
||||||
"libbrotli${_libname}-static"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
list(APPEND _brotli_lib_names
|
list(APPEND _brotli_lib_names
|
||||||
"brotli${_libname}"
|
"brotli${_libname}"
|
||||||
"libbrotli${_libname}"
|
"libbrotli${_libname}"
|
||||||
)
|
)
|
||||||
|
if(BROTLI_USE_STATIC_LIBS)
|
||||||
|
# Postfix "-static" to the libnames since we're looking for static libs
|
||||||
|
list(TRANSFORM _brotli_lib_names APPEND "-static")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_library(Brotli_${_component_name}
|
find_library(Brotli_${_component_name}
|
||||||
@ -168,7 +158,7 @@ find_package_handle_standard_args(Brotli
|
|||||||
Brotli_FOUND
|
Brotli_FOUND
|
||||||
REQUIRED_VARS
|
REQUIRED_VARS
|
||||||
Brotli_INCLUDE_DIR
|
Brotli_INCLUDE_DIR
|
||||||
${_brotli_required_targets}
|
${_brotli_req_vars}
|
||||||
HANDLE_COMPONENTS
|
HANDLE_COMPONENTS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,19 +12,21 @@ set(HTTPLIB_VERSION @PROJECT_VERSION@)
|
|||||||
include(CMakeFindDependencyMacro)
|
include(CMakeFindDependencyMacro)
|
||||||
|
|
||||||
# We add find_dependency calls here to not make the end-user have to call them.
|
# We add find_dependency calls here to not make the end-user have to call them.
|
||||||
find_dependency(Threads REQUIRED)
|
find_dependency(Threads)
|
||||||
if(@HTTPLIB_IS_USING_OPENSSL@)
|
if(@HTTPLIB_IS_USING_OPENSSL@)
|
||||||
# OpenSSL COMPONENTS were added in Cmake v3.11
|
# OpenSSL COMPONENTS were added in Cmake v3.11
|
||||||
if(CMAKE_VERSION VERSION_LESS "3.11")
|
if(CMAKE_VERSION VERSION_LESS "3.11")
|
||||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ REQUIRED)
|
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@)
|
||||||
else()
|
else()
|
||||||
# Once the COMPONENTS were added, they were made optional when not specified.
|
# Once the COMPONENTS were added, they were made optional when not specified.
|
||||||
# Since we use both, we need to search for both.
|
# Since we use both, we need to search for both.
|
||||||
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL REQUIRED)
|
find_dependency(OpenSSL @_HTTPLIB_OPENSSL_MIN_VER@ COMPONENTS Crypto SSL)
|
||||||
endif()
|
endif()
|
||||||
|
set(httplib_OpenSSL_FOUND ${OpenSSL_FOUND})
|
||||||
endif()
|
endif()
|
||||||
if(@HTTPLIB_IS_USING_ZLIB@)
|
if(@HTTPLIB_IS_USING_ZLIB@)
|
||||||
find_dependency(ZLIB REQUIRED)
|
find_dependency(ZLIB)
|
||||||
|
set(httplib_ZLIB_FOUND ${ZLIB_FOUND})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(@HTTPLIB_IS_USING_BROTLI@)
|
if(@HTTPLIB_IS_USING_BROTLI@)
|
||||||
@ -32,7 +34,31 @@ if(@HTTPLIB_IS_USING_BROTLI@)
|
|||||||
# Note that the FindBrotli.cmake file is installed in the same dir as this file.
|
# Note that the FindBrotli.cmake file is installed in the same dir as this file.
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
||||||
set(BROTLI_USE_STATIC_LIBS @BROTLI_USE_STATIC_LIBS@)
|
set(BROTLI_USE_STATIC_LIBS @BROTLI_USE_STATIC_LIBS@)
|
||||||
find_dependency(Brotli COMPONENTS common encoder decoder REQUIRED)
|
find_dependency(Brotli COMPONENTS common encoder decoder)
|
||||||
|
set(httplib_Brotli_FOUND ${Brotli_FOUND})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(@HTTPLIB_IS_USING_ZSTD@)
|
||||||
|
set(httplib_fd_zstd_quiet_arg)
|
||||||
|
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
|
||||||
|
set(httplib_fd_zstd_quiet_arg QUIET)
|
||||||
|
endif()
|
||||||
|
set(httplib_fd_zstd_required_arg)
|
||||||
|
if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
|
||||||
|
set(httplib_fd_zstd_required_arg REQUIRED)
|
||||||
|
endif()
|
||||||
|
find_package(zstd QUIET)
|
||||||
|
if(NOT zstd_FOUND)
|
||||||
|
find_package(PkgConfig ${httplib_fd_zstd_quiet_arg} ${httplib_fd_zstd_required_arg})
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(zstd ${httplib_fd_zstd_quiet_arg} ${httplib_fd_zstd_required_arg} IMPORTED_TARGET libzstd)
|
||||||
|
|
||||||
|
if(TARGET PkgConfig::zstd)
|
||||||
|
add_library(zstd::libzstd ALIAS PkgConfig::zstd)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(httplib_zstd_FOUND ${zstd_FOUND})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Mildly useful for end-users
|
# Mildly useful for end-users
|
||||||
@ -42,10 +68,14 @@ set_and_check(HTTPLIB_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@")
|
|||||||
# This is helpful if you're using Cmake's pre-compiled header feature
|
# This is helpful if you're using Cmake's pre-compiled header feature
|
||||||
set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h")
|
set_and_check(HTTPLIB_HEADER_PATH "@PACKAGE_CMAKE_INSTALL_FULL_INCLUDEDIR@/httplib.h")
|
||||||
|
|
||||||
# Brings in the target library
|
check_required_components(httplib)
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
|
||||||
|
|
||||||
# Ouputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
|
# Brings in the target library, but only if all required components are found
|
||||||
|
if(NOT DEFINED httplib_FOUND OR httplib_FOUND)
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/httplibTargets.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Outputs a "found httplib /usr/include/httplib.h" message when using find_package(httplib)
|
||||||
include(FindPackageMessage)
|
include(FindPackageMessage)
|
||||||
if(TARGET httplib::httplib)
|
if(TARGET httplib::httplib)
|
||||||
set(HTTPLIB_FOUND TRUE)
|
set(HTTPLIB_FOUND TRUE)
|
7
docker-compose.yml
Normal file
7
docker-compose.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
http:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./docker/html:/html
|
21
docker/html/index.html
Normal file
21
docker/html/index.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Welcome to cpp-httplib!</title>
|
||||||
|
<style>
|
||||||
|
html { color-scheme: light dark; }
|
||||||
|
body { width: 35em; margin: 0 auto;
|
||||||
|
font-family: Tahoma, Verdana, Arial, sans-serif; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Welcome to cpp-httplib!</h1>
|
||||||
|
<p>If you see this page, the cpp-httplib web server is successfully installed and
|
||||||
|
working. Further configuration is required.</p>
|
||||||
|
|
||||||
|
<p>For online documentation and support please refer to
|
||||||
|
<a href="https://github.com/yhirose/cpp-httplib">github.com/yhirose/cpp-httplib</a>.<br/>
|
||||||
|
|
||||||
|
<p><em>Thank you for using cpp-httplib.</em></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
81
docker/main.cc
Normal file
81
docker/main.cc
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// main.cc
|
||||||
|
//
|
||||||
|
// Copyright (c) 2025 Yuji Hirose. All rights reserved.
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <format>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <httplib.h>
|
||||||
|
|
||||||
|
constexpr auto error_html = R"(<html>
|
||||||
|
<head><title>{} {}</title></head>
|
||||||
|
<body>
|
||||||
|
<center><h1>404 Not Found</h1></center>
|
||||||
|
<hr><center>cpp-httplib/{}</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)";
|
||||||
|
|
||||||
|
void sigint_handler(int s) { exit(1); }
|
||||||
|
|
||||||
|
std::string time_local() {
|
||||||
|
auto p = std::chrono::system_clock::now();
|
||||||
|
auto t = std::chrono::system_clock::to_time_t(p);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::put_time(std::localtime(&t), "%d/%b/%Y:%H:%M:%S %z");
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string log(auto &req, auto &res) {
|
||||||
|
auto remote_user = "-"; // TODO:
|
||||||
|
auto request = std::format("{} {} {}", req.method, req.path, req.version);
|
||||||
|
auto body_bytes_sent = res.get_header_value("Content-Length");
|
||||||
|
auto http_referer = "-"; // TODO:
|
||||||
|
auto http_user_agent = req.get_header_value("User-Agent", "-");
|
||||||
|
|
||||||
|
// NOTE: From NGINX default access log format
|
||||||
|
// log_format combined '$remote_addr - $remote_user [$time_local] '
|
||||||
|
// '"$request" $status $body_bytes_sent '
|
||||||
|
// '"$http_referer" "$http_user_agent"';
|
||||||
|
return std::format(R"({} - {} [{}] "{}" {} {} "{}" "{}")", req.remote_addr,
|
||||||
|
remote_user, time_local(), request, res.status,
|
||||||
|
body_bytes_sent, http_referer, http_user_agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char **argv) {
|
||||||
|
signal(SIGINT, sigint_handler);
|
||||||
|
|
||||||
|
auto base_dir = "./html";
|
||||||
|
auto host = "0.0.0.0";
|
||||||
|
auto port = 80;
|
||||||
|
|
||||||
|
httplib::Server svr;
|
||||||
|
|
||||||
|
svr.set_error_handler([](auto & /*req*/, auto &res) {
|
||||||
|
auto body =
|
||||||
|
std::format(error_html, res.status, httplib::status_message(res.status),
|
||||||
|
CPPHTTPLIB_VERSION);
|
||||||
|
|
||||||
|
res.set_content(body, "text/html");
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.set_logger(
|
||||||
|
[](auto &req, auto &res) { std::cout << log(req, res) << std::endl; });
|
||||||
|
|
||||||
|
svr.set_mount_point("/", base_dir);
|
||||||
|
|
||||||
|
std::cout << std::format("Serving HTTP on {0} port {1} ...", host, port)
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
auto ret = svr.listen(host, port);
|
||||||
|
|
||||||
|
return ret ? 0 : 1;
|
||||||
|
}
|
12
example/Dockerfile.hello
Normal file
12
example/Dockerfile.hello
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
FROM alpine as builder
|
||||||
|
WORKDIR /src/example
|
||||||
|
RUN apk add g++ make openssl-dev zlib-dev brotli-dev
|
||||||
|
COPY ./httplib.h /src
|
||||||
|
COPY ./example/hello.cc /src/example
|
||||||
|
COPY ./example/Makefile /src/example
|
||||||
|
RUN make hello
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
RUN apk --no-cache add brotli libstdc++
|
||||||
|
COPY --from=builder /src/example/hello /bin/hello
|
||||||
|
CMD ["/bin/hello"]
|
@ -1,16 +1,24 @@
|
|||||||
|
|
||||||
#CXX = clang++
|
#CXX = clang++
|
||||||
CXXFLAGS = -std=c++11 -I.. -Wall -Wextra -pthread
|
CXXFLAGS = -O2 -std=c++11 -I.. -Wall -Wextra -pthread
|
||||||
|
|
||||||
OPENSSL_DIR = /usr/local/opt/openssl
|
PREFIX ?= $(shell brew --prefix)
|
||||||
|
|
||||||
|
OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
|
ifneq ($(OS), Windows_NT)
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S), Darwin)
|
||||||
|
OPENSSL_SUPPORT += -framework CoreFoundation -framework Security
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
BROTLI_DIR = /usr/local/opt/brotli
|
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||||
# BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon-static -lbrotlienc-static -lbrotlidec-static
|
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||||
|
|
||||||
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark
|
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client
|
||||||
|
|
||||||
server : server.cc ../httplib.h Makefile
|
server : server.cc ../httplib.h Makefile
|
||||||
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||||
@ -42,9 +50,15 @@ ssecli : ssecli.cc ../httplib.h Makefile
|
|||||||
benchmark : benchmark.cc ../httplib.h Makefile
|
benchmark : benchmark.cc ../httplib.h Makefile
|
||||||
$(CXX) -o benchmark $(CXXFLAGS) benchmark.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
$(CXX) -o benchmark $(CXXFLAGS) benchmark.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||||
|
|
||||||
|
one_time_request : one_time_request.cc ../httplib.h Makefile
|
||||||
|
$(CXX) -o one_time_request $(CXXFLAGS) one_time_request.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||||
|
|
||||||
|
server_and_client : server_and_client.cc ../httplib.h Makefile
|
||||||
|
$(CXX) -o server_and_client $(CXXFLAGS) server_and_client.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT)
|
||||||
|
|
||||||
pem:
|
pem:
|
||||||
openssl genrsa 2048 > key.pem
|
openssl genrsa 2048 > key.pem
|
||||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm server client hello simplecli simplesvr upload redirect ssesvr sselci benchmark *.pem
|
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client *.pem
|
||||||
|
@ -26,7 +26,7 @@ int main(void) {
|
|||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
StopWatch sw(to_string(i).c_str());
|
StopWatch sw(to_string(i).c_str());
|
||||||
auto res = cli.Post("/post", body, "application/octet-stream");
|
auto res = cli.Post("/post", body, "application/octet-stream");
|
||||||
assert(res->status == 200);
|
assert(res->status == httplib::StatusCode::OK_200);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -22,34 +22,34 @@
|
|||||||
<ProjectGuid>{6DB1FC63-B153-4279-92B7-D8A11AF285D6}</ProjectGuid>
|
<ProjectGuid>{6DB1FC63-B153-4279-92B7-D8A11AF285D6}</ProjectGuid>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>client</RootNamespace>
|
<RootNamespace>client</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
@ -15,5 +15,5 @@ int main(void) {
|
|||||||
res.set_content("Hello World!", "text/plain");
|
res.set_content("Hello World!", "text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
svr.listen("localhost", 8080);
|
svr.listen("0.0.0.0", 8080);
|
||||||
}
|
}
|
||||||
|
56
example/one_time_request.cc
Normal file
56
example/one_time_request.cc
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#include <httplib.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace httplib;
|
||||||
|
|
||||||
|
const char *HOST = "localhost";
|
||||||
|
const int PORT = 1234;
|
||||||
|
|
||||||
|
void one_time_request_server(const char *label) {
|
||||||
|
std::thread th;
|
||||||
|
Server svr;
|
||||||
|
|
||||||
|
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_content(std::string("Hello from ") + label, "text/plain");
|
||||||
|
|
||||||
|
// Stop server
|
||||||
|
th = std::thread([&]() { svr.stop(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.listen(HOST, PORT);
|
||||||
|
th.join();
|
||||||
|
|
||||||
|
std::cout << label << " ended..." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_request(const char *label) {
|
||||||
|
Client cli(HOST, PORT);
|
||||||
|
|
||||||
|
std::cout << "Send " << label << " request" << std::endl;
|
||||||
|
auto res = cli.Get("/hi");
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
std::cout << res->body << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Request error: " + to_string(res.error()) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
auto th1 = std::thread([&]() { one_time_request_server("Server #1"); });
|
||||||
|
auto th2 = std::thread([&]() { one_time_request_server("Server #2"); });
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
send_request("1st");
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
send_request("2nd");
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
send_request("3rd");
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
th1.join();
|
||||||
|
th2.join();
|
||||||
|
}
|
@ -18,38 +18,41 @@
|
|||||||
<Platform>x64</Platform>
|
<Platform>x64</Platform>
|
||||||
</ProjectConfiguration>
|
</ProjectConfiguration>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="simplesvr.cc" />
|
||||||
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>{864CD288-050A-4C8B-9BEF-3048BD876C5B}</ProjectGuid>
|
<ProjectGuid>{864CD288-050A-4C8B-9BEF-3048BD876C5B}</ProjectGuid>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>sample</RootNamespace>
|
<RootNamespace>sample</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
@ -151,9 +154,6 @@
|
|||||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="server.cc" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
|
90
example/server_and_client.cc
Normal file
90
example/server_and_client.cc
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// server_and_client.cc
|
||||||
|
//
|
||||||
|
// Copyright (c) 2025 Yuji Hirose. All rights reserved.
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <httplib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace httplib;
|
||||||
|
|
||||||
|
std::string dump_headers(const Headers &headers) {
|
||||||
|
std::string s;
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
|
for (auto it = headers.begin(); it != headers.end(); ++it) {
|
||||||
|
const auto &x = *it;
|
||||||
|
snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str());
|
||||||
|
s += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logger(const Request &req, const Response &res) {
|
||||||
|
std::string s;
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
|
||||||
|
s += "================================\n";
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(),
|
||||||
|
req.version.c_str(), req.path.c_str());
|
||||||
|
s += buf;
|
||||||
|
|
||||||
|
std::string query;
|
||||||
|
for (auto it = req.params.begin(); it != req.params.end(); ++it) {
|
||||||
|
const auto &x = *it;
|
||||||
|
snprintf(buf, sizeof(buf), "%c%s=%s",
|
||||||
|
(it == req.params.begin()) ? '?' : '&', x.first.c_str(),
|
||||||
|
x.second.c_str());
|
||||||
|
query += buf;
|
||||||
|
}
|
||||||
|
snprintf(buf, sizeof(buf), "%s\n", query.c_str());
|
||||||
|
s += buf;
|
||||||
|
|
||||||
|
s += dump_headers(req.headers);
|
||||||
|
|
||||||
|
s += "--------------------------------\n";
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str());
|
||||||
|
s += buf;
|
||||||
|
s += dump_headers(res.headers);
|
||||||
|
s += "\n";
|
||||||
|
|
||||||
|
if (!res.body.empty()) { s += res.body; }
|
||||||
|
|
||||||
|
s += "\n";
|
||||||
|
|
||||||
|
std::cout << s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
// Server
|
||||||
|
Server svr;
|
||||||
|
svr.set_logger(logger);
|
||||||
|
|
||||||
|
svr.Post("/post", [&](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_content("POST", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
auto th = std::thread([&]() { svr.listen("localhost", 8080); });
|
||||||
|
|
||||||
|
auto se = detail::scope_exit([&] {
|
||||||
|
svr.stop();
|
||||||
|
th.join();
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.wait_until_ready();
|
||||||
|
|
||||||
|
// Client
|
||||||
|
Client cli{"localhost", 8080};
|
||||||
|
|
||||||
|
std::string body = R"({"hello": "world"})";
|
||||||
|
|
||||||
|
auto res = cli.Post("/post", body, "application/json");
|
||||||
|
std::cout << "--------------------------------" << std::endl;
|
||||||
|
std::cout << to_string(res.error()) << std::endl;
|
||||||
|
}
|
@ -1,10 +1,3 @@
|
|||||||
//
|
|
||||||
// sse.cc
|
|
||||||
//
|
|
||||||
// Copyright (c) 2020 Yuji Hirose. All rights reserved.
|
|
||||||
// MIT License
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@ -19,16 +12,13 @@ using namespace std;
|
|||||||
|
|
||||||
class EventDispatcher {
|
class EventDispatcher {
|
||||||
public:
|
public:
|
||||||
EventDispatcher() {
|
EventDispatcher() {}
|
||||||
id_ = 0;
|
|
||||||
cid_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_event(DataSink *sink) {
|
void wait_event(DataSink *sink) {
|
||||||
unique_lock<mutex> lk(m_);
|
unique_lock<mutex> lk(m_);
|
||||||
int id = id_;
|
int id = id_;
|
||||||
cv_.wait(lk, [&] { return cid_ == id; });
|
cv_.wait(lk, [&] { return cid_ == id; });
|
||||||
if (sink->is_writable()) { sink->write(message_.data(), message_.size()); }
|
sink->write(message_.data(), message_.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_event(const string &message) {
|
void send_event(const string &message) {
|
||||||
@ -41,8 +31,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
mutex m_;
|
mutex m_;
|
||||||
condition_variable cv_;
|
condition_variable cv_;
|
||||||
atomic_int id_;
|
atomic_int id_{0};
|
||||||
atomic_int cid_;
|
atomic_int cid_{-1};
|
||||||
string message_;
|
string message_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
6
example/uploader.sh
Executable file
6
example/uploader.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
for i in {1..1000000}
|
||||||
|
do
|
||||||
|
echo "#### $i ####"
|
||||||
|
curl -X POST -F image_file=@$1 http://localhost:1234/post > /dev/null
|
||||||
|
done
|
116
meson.build
Normal file
116
meson.build
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
project(
|
||||||
|
'cpp-httplib',
|
||||||
|
'cpp',
|
||||||
|
license: 'MIT',
|
||||||
|
default_options: [
|
||||||
|
'cpp_std=c++11',
|
||||||
|
'buildtype=release',
|
||||||
|
'b_ndebug=if-release',
|
||||||
|
'b_lto=true',
|
||||||
|
'warning_level=3'
|
||||||
|
],
|
||||||
|
meson_version: '>=0.62.0'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check just in case downstream decides to edit the source
|
||||||
|
# and add a project version
|
||||||
|
version = meson.project_version()
|
||||||
|
if version == 'undefined'
|
||||||
|
cxx = meson.get_compiler('cpp')
|
||||||
|
version = cxx.get_define('CPPHTTPLIB_VERSION',
|
||||||
|
prefix: '#include <httplib.h>',
|
||||||
|
include_directories: include_directories('.')).strip('"')
|
||||||
|
assert(version != '', 'failed to get version from httplib.h')
|
||||||
|
endif
|
||||||
|
|
||||||
|
deps = [dependency('threads')]
|
||||||
|
args = []
|
||||||
|
|
||||||
|
openssl_dep = dependency('openssl', version: '>=3.0.0', required: get_option('cpp-httplib_openssl'))
|
||||||
|
if openssl_dep.found()
|
||||||
|
deps += openssl_dep
|
||||||
|
args += '-DCPPHTTPLIB_OPENSSL_SUPPORT'
|
||||||
|
if host_machine.system() == 'darwin'
|
||||||
|
macosx_keychain_dep = dependency('appleframeworks', modules: ['CoreFoundation', 'Security'], required: get_option('cpp-httplib_macosx_keychain'))
|
||||||
|
if macosx_keychain_dep.found()
|
||||||
|
deps += macosx_keychain_dep
|
||||||
|
args += '-DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN'
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
zlib_dep = dependency('zlib', required: get_option('cpp-httplib_zlib'))
|
||||||
|
if zlib_dep.found()
|
||||||
|
deps += zlib_dep
|
||||||
|
args += '-DCPPHTTPLIB_ZLIB_SUPPORT'
|
||||||
|
endif
|
||||||
|
|
||||||
|
brotli_deps = [dependency('libbrotlicommon', required: get_option('cpp-httplib_brotli'))]
|
||||||
|
brotli_deps += dependency('libbrotlidec', required: get_option('cpp-httplib_brotli'))
|
||||||
|
brotli_deps += dependency('libbrotlienc', required: get_option('cpp-httplib_brotli'))
|
||||||
|
|
||||||
|
brotli_found_all = true
|
||||||
|
foreach brotli_dep : brotli_deps
|
||||||
|
if not brotli_dep.found()
|
||||||
|
brotli_found_all = false
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
if brotli_found_all
|
||||||
|
deps += brotli_deps
|
||||||
|
args += '-DCPPHTTPLIB_BROTLI_SUPPORT'
|
||||||
|
endif
|
||||||
|
|
||||||
|
cpp_httplib_dep = dependency('', required: false)
|
||||||
|
|
||||||
|
if get_option('cpp-httplib_compile')
|
||||||
|
python3 = find_program('python3')
|
||||||
|
|
||||||
|
httplib_ch = custom_target(
|
||||||
|
'split',
|
||||||
|
input: 'httplib.h',
|
||||||
|
output: ['httplib.cc', 'httplib.h'],
|
||||||
|
command: [python3, files('split.py'), '--out', meson.current_build_dir()],
|
||||||
|
install: true,
|
||||||
|
install_dir: [false, get_option('includedir')]
|
||||||
|
)
|
||||||
|
lib = library(
|
||||||
|
'cpp-httplib',
|
||||||
|
sources: httplib_ch,
|
||||||
|
dependencies: deps,
|
||||||
|
cpp_args: args,
|
||||||
|
version: version,
|
||||||
|
soversion: version.split('.')[0] + '.' + version.split('.')[1],
|
||||||
|
install: true
|
||||||
|
)
|
||||||
|
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, link_with: lib, sources: httplib_ch[1])
|
||||||
|
|
||||||
|
import('pkgconfig').generate(
|
||||||
|
lib,
|
||||||
|
description: 'A C++ HTTP/HTTPS server and client library',
|
||||||
|
extra_cflags: args,
|
||||||
|
url: 'https://github.com/yhirose/cpp-httplib',
|
||||||
|
version: version
|
||||||
|
)
|
||||||
|
else
|
||||||
|
install_headers('httplib.h')
|
||||||
|
cpp_httplib_dep = declare_dependency(compile_args: args, dependencies: deps, include_directories: '.')
|
||||||
|
|
||||||
|
import('pkgconfig').generate(
|
||||||
|
name: 'cpp-httplib',
|
||||||
|
description: 'A C++ HTTP/HTTPS server and client library',
|
||||||
|
install_dir: get_option('datadir')/'pkgconfig',
|
||||||
|
url: 'https://github.com/yhirose/cpp-httplib',
|
||||||
|
version: version
|
||||||
|
)
|
||||||
|
endif
|
||||||
|
|
||||||
|
meson.override_dependency('cpp-httplib', cpp_httplib_dep)
|
||||||
|
|
||||||
|
if get_option('cpp-httplib_test')
|
||||||
|
subdir('test')
|
||||||
|
endif
|
10
meson_options.txt
Normal file
10
meson_options.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
option('cpp-httplib_openssl', type: 'feature', value: 'auto', description: 'Enable OpenSSL support')
|
||||||
|
option('cpp-httplib_zlib', type: 'feature', value: 'auto', description: 'Enable zlib support')
|
||||||
|
option('cpp-httplib_brotli', type: 'feature', value: 'auto', description: 'Enable Brotli support')
|
||||||
|
option('cpp-httplib_macosx_keychain', type: 'feature', value: 'auto', description: 'Enable loading certs from the Keychain on Apple devices')
|
||||||
|
option('cpp-httplib_compile', type: 'boolean', value: false, description: 'Split the header into a compilable header & source file (requires python3)')
|
||||||
|
option('cpp-httplib_test', type: 'boolean', value: false, description: 'Build tests')
|
65
split.py
Normal file → Executable file
65
split.py
Normal file → Executable file
@ -1,32 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""This script splits httplib.h into .h and .cc parts."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
border = '// ----------------------------------------------------------------------------'
|
border = '// ----------------------------------------------------------------------------'
|
||||||
|
|
||||||
PythonVersion = sys.version_info[0];
|
args_parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
args_parser.add_argument(
|
||||||
|
"-e", "--extension", help="extension of the implementation file (default: cc)",
|
||||||
|
default="cc"
|
||||||
|
)
|
||||||
|
args_parser.add_argument(
|
||||||
|
"-o", "--out", help="where to write the files (default: out)", default="out"
|
||||||
|
)
|
||||||
|
args = args_parser.parse_args()
|
||||||
|
|
||||||
with open('httplib.h') as f:
|
cur_dir = os.path.dirname(sys.argv[0])
|
||||||
|
lib_name = 'httplib'
|
||||||
|
header_name = '/' + lib_name + '.h'
|
||||||
|
source_name = '/' + lib_name + '.' + args.extension
|
||||||
|
# get the input file
|
||||||
|
in_file = cur_dir + header_name
|
||||||
|
# get the output file
|
||||||
|
h_out = args.out + header_name
|
||||||
|
cc_out = args.out + source_name
|
||||||
|
|
||||||
|
# if the modification time of the out file is after the in file,
|
||||||
|
# don't split (as it is already finished)
|
||||||
|
do_split = True
|
||||||
|
|
||||||
|
if os.path.exists(h_out):
|
||||||
|
in_time = os.path.getmtime(in_file)
|
||||||
|
out_time = os.path.getmtime(h_out)
|
||||||
|
do_split = in_time > out_time
|
||||||
|
|
||||||
|
if do_split:
|
||||||
|
with open(in_file) as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
inImplementation = False
|
|
||||||
|
|
||||||
if PythonVersion < 3:
|
python_version = sys.version_info[0]
|
||||||
os.makedirs('out')
|
if python_version < 3:
|
||||||
|
os.makedirs(args.out)
|
||||||
else:
|
else:
|
||||||
os.makedirs('out', exist_ok=True)
|
os.makedirs(args.out, exist_ok=True)
|
||||||
|
|
||||||
with open('out/httplib.h', 'w') as fh:
|
in_implementation = False
|
||||||
with open('out/httplib.cc', 'w') as fc:
|
cc_out = args.out + source_name
|
||||||
|
with open(h_out, 'w') as fh, open(cc_out, 'w') as fc:
|
||||||
fc.write('#include "httplib.h"\n')
|
fc.write('#include "httplib.h"\n')
|
||||||
fc.write('namespace httplib {\n')
|
fc.write('namespace httplib {\n')
|
||||||
for line in lines:
|
for line in lines:
|
||||||
isBorderLine = border in line
|
is_border_line = border in line
|
||||||
if isBorderLine:
|
if is_border_line:
|
||||||
inImplementation = not inImplementation
|
in_implementation = not in_implementation
|
||||||
else:
|
elif in_implementation:
|
||||||
if inImplementation:
|
|
||||||
fc.write(line.replace('inline ', ''))
|
fc.write(line.replace('inline ', ''))
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
fh.write(line)
|
fh.write(line)
|
||||||
pass
|
|
||||||
fc.write('} // namespace httplib\n')
|
fc.write('} // namespace httplib\n')
|
||||||
|
|
||||||
|
print("Wrote {} and {}".format(h_out, cc_out))
|
||||||
|
else:
|
||||||
|
print("{} and {} are up to date".format(h_out, cc_out))
|
||||||
|
121
test/CMakeLists.txt
Normal file
121
test/CMakeLists.txt
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
find_package(GTest)
|
||||||
|
|
||||||
|
if(GTest_FOUND)
|
||||||
|
if(NOT TARGET GTest::gtest_main AND TARGET GTest::Main)
|
||||||
|
# CMake <3.20
|
||||||
|
add_library(GTest::gtest_main INTERFACE IMPORTED)
|
||||||
|
target_link_libraries(GTest::gtest_main INTERFACE GTest::Main)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if(POLICY CMP0135)
|
||||||
|
cmake_policy(SET CMP0135 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
set(BUILD_GMOCK OFF)
|
||||||
|
set(INSTALL_GTEST OFF)
|
||||||
|
set(gtest_force_shared_crt ON)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
gtest
|
||||||
|
URL https://github.com/google/googletest/archive/main.tar.gz
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(gtest)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(CURL REQUIRED)
|
||||||
|
|
||||||
|
add_executable(httplib-test test.cc include_httplib.cc $<$<BOOL:${WIN32}>:include_windows_h.cc>)
|
||||||
|
target_compile_options(httplib-test PRIVATE "$<$<CXX_COMPILER_ID:MSVC>:/utf-8;/bigobj>")
|
||||||
|
target_link_libraries(httplib-test PRIVATE httplib GTest::gtest_main CURL::libcurl)
|
||||||
|
gtest_discover_tests(httplib-test)
|
||||||
|
|
||||||
|
file(
|
||||||
|
COPY www www2 www3 ca-bundle.crt image.jpg
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(HTTPLIB_IS_USING_OPENSSL)
|
||||||
|
if (OPENSSL_VERSION VERSION_LESS "3.2.0")
|
||||||
|
set(OPENSSL_X509_FLAG "-x509")
|
||||||
|
else()
|
||||||
|
set(OPENSSL_X509_FLAG "-x509v1")
|
||||||
|
endif()
|
||||||
|
find_program(OPENSSL_COMMAND
|
||||||
|
NAMES openssl
|
||||||
|
PATHS ${OPENSSL_INCLUDE_DIR}/../bin
|
||||||
|
REQUIRED
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||||
|
OUTPUT_FILE key.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem
|
||||||
|
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key.pem
|
||||||
|
OUTPUT_FILE cert.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} req ${OPENSSL_X509_FLAG} -new -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||||
|
OUTPUT_FILE rootCA.key.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} req ${OPENSSL_X509_FLAG} -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.rootCA.conf -key rootCA.key.pem -days 1024
|
||||||
|
OUTPUT_FILE rootCA.cert.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} genrsa 2048
|
||||||
|
OUTPUT_FILE client.key.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client.key.pem
|
||||||
|
COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial
|
||||||
|
OUTPUT_FILE client.cert.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} genrsa -passout pass:test123! 2048
|
||||||
|
OUTPUT_FILE key_encrypted.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key key_encrypted.pem
|
||||||
|
COMMAND ${OPENSSL_COMMAND} x509 -days 3650 -req -signkey key_encrypted.pem
|
||||||
|
OUTPUT_FILE cert_encrypted.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} genrsa -aes256 -passout pass:test012! 2048
|
||||||
|
OUTPUT_FILE client_encrypted.key.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client_encrypted.key.pem -passin pass:test012!
|
||||||
|
COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial
|
||||||
|
OUTPUT_FILE client_encrypted.cert.pem
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND_ERROR_IS_FATAL ANY
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(fuzzing)
|
103
test/Makefile
103
test/Makefile
@ -1,34 +1,105 @@
|
|||||||
#CXX = clang++
|
CXX = clang++
|
||||||
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion #-fsanitize=address
|
CXXFLAGS = -g -std=c++11 -I. -Wall -Wextra -Wtype-limits -Wconversion -Wshadow $(EXTRA_CXXFLAGS) # -fno-exceptions -DCPPHTTPLIB_NO_EXCEPTIONS -fsanitize=address
|
||||||
|
|
||||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
PREFIX ?= $(shell brew --prefix)
|
||||||
|
|
||||||
|
OPENSSL_DIR = $(PREFIX)/opt/openssl@3
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||||
|
|
||||||
|
ifneq ($(OS), Windows_NT)
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S), Darwin)
|
||||||
|
OPENSSL_SUPPORT += -DCPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN -framework CoreFoundation -framework Security
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||||
|
|
||||||
BROTLI_DIR = /usr/local/opt/brotli
|
BROTLI_DIR = $(PREFIX)/opt/brotli
|
||||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
||||||
|
|
||||||
all : test
|
ZSTD_DIR = $(PREFIX)/opt/zstd
|
||||||
|
ZSTD_SUPPORT = -DCPPHTTPLIB_ZSTD_SUPPORT -I$(ZSTD_DIR)/include -L$(ZSTD_DIR)/lib -lzstd
|
||||||
|
|
||||||
|
TEST_ARGS = gtest/src/gtest-all.cc gtest/src/gtest_main.cc -Igtest -Igtest/include $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(ZSTD_SUPPORT) -pthread -lcurl
|
||||||
|
|
||||||
|
# By default, use standalone_fuzz_target_runner.
|
||||||
|
# This runner does no fuzzing, but simply executes the inputs
|
||||||
|
# provided via parameters.
|
||||||
|
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
|
||||||
|
# to link the fuzzer(s) against a real fuzzing engine.
|
||||||
|
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
|
||||||
|
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
|
||||||
|
|
||||||
|
CLANG_FORMAT = clang-format
|
||||||
|
REALPATH = $(shell which grealpath 2>/dev/null || which realpath 2>/dev/null)
|
||||||
|
STYLE_CHECK_FILES = $(filter-out httplib.h httplib.cc, \
|
||||||
|
$(wildcard example/*.h example/*.cc fuzzing/*.h fuzzing/*.cc *.h *.cc ../httplib.h))
|
||||||
|
|
||||||
|
all : test test_split
|
||||||
./test
|
./test
|
||||||
|
|
||||||
proxy : test_proxy
|
proxy : test_proxy
|
||||||
./test_proxy
|
./test_proxy
|
||||||
|
|
||||||
test : test.cc ../httplib.h Makefile cert.pem
|
test : test.cc include_httplib.cc ../httplib.h Makefile cert.pem
|
||||||
$(CXX) -o test $(CXXFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
$(CXX) -o $@ -I.. $(CXXFLAGS) test.cc include_httplib.cc $(TEST_ARGS)
|
||||||
|
@file $@
|
||||||
|
|
||||||
|
# Note: The intention of test_split is to verify that it works to compile and
|
||||||
|
# link the split httplib.h, so there is normally no need to execute it.
|
||||||
|
test_split : test.cc ../httplib.h httplib.cc Makefile cert.pem
|
||||||
|
$(CXX) -o $@ $(CXXFLAGS) test.cc httplib.cc $(TEST_ARGS)
|
||||||
|
|
||||||
|
check_abi:
|
||||||
|
@./check-shared-library-abi-compatibility.sh
|
||||||
|
|
||||||
|
.PHONY: style_check
|
||||||
|
style_check: $(STYLE_CHECK_FILES)
|
||||||
|
@for file in $(STYLE_CHECK_FILES); do \
|
||||||
|
$(CLANG_FORMAT) $$file > $$file.formatted; \
|
||||||
|
if ! diff -u $$file $$file.formatted; then \
|
||||||
|
file2=$$($(REALPATH) --relative-to=.. $$file); \
|
||||||
|
printf "\n%*s\n" 80 | tr ' ' '#'; \
|
||||||
|
printf "##%*s##\n" 76; \
|
||||||
|
printf "## %-70s ##\n" "$$file2 not properly formatted. Please run clang-format."; \
|
||||||
|
printf "##%*s##\n" 76; \
|
||||||
|
printf "%*s\n\n" 80 | tr ' ' '#'; \
|
||||||
|
failed=1; \
|
||||||
|
fi; \
|
||||||
|
rm -f $$file.formatted; \
|
||||||
|
done; \
|
||||||
|
if [ -n "$$failed" ]; then \
|
||||||
|
echo "Style check failed for one or more files. See above for details."; \
|
||||||
|
false; \
|
||||||
|
else \
|
||||||
|
echo "All files are properly formatted."; \
|
||||||
|
fi
|
||||||
|
|
||||||
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
test_proxy : test_proxy.cc ../httplib.h Makefile cert.pem
|
||||||
$(CXX) -o test_proxy $(CXXFLAGS) test_proxy.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) -pthread
|
$(CXX) -o $@ -I.. $(CXXFLAGS) test_proxy.cc $(TEST_ARGS)
|
||||||
|
|
||||||
|
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
||||||
|
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
||||||
|
fuzz_test: server_fuzzer
|
||||||
|
./server_fuzzer fuzzing/corpus/*
|
||||||
|
|
||||||
|
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||||
|
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
|
||||||
|
$(CXX) -o $@ -I.. $(CXXFLAGS) $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||||
|
@file $@
|
||||||
|
|
||||||
|
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
|
||||||
|
# feeds it to server_fuzzer.
|
||||||
|
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
|
||||||
|
$(CXX) -o $@ -I.. $(CXXFLAGS) -c $<
|
||||||
|
|
||||||
|
httplib.cc : ../httplib.h
|
||||||
|
python3 ../split.py -o .
|
||||||
|
|
||||||
cert.pem:
|
cert.pem:
|
||||||
openssl genrsa 2048 > key.pem
|
./gen-certs.sh
|
||||||
openssl req -new -batch -config test.conf -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
|
||||||
openssl genrsa 2048 > rootCA.key.pem
|
|
||||||
openssl req -x509 -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
|
||||||
openssl genrsa 2048 > client.key.pem
|
|
||||||
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
|
|
||||||
#c_rehash .
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f test test_proxy pem *.0 *.1 *.srl
|
rm -rf test test_split test_proxy server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
|
|
||||||
#CXX = clang++
|
|
||||||
CXXFLAGS += -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion
|
|
||||||
|
|
||||||
OPENSSL_DIR = /usr/local/opt/openssl@1.1
|
|
||||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
|
||||||
|
|
||||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
|
||||||
|
|
||||||
BROTLI_DIR = /usr/local/opt/brotli
|
|
||||||
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
|
|
||||||
|
|
||||||
# By default, use standalone_fuzz_target_runner.
|
|
||||||
# This runner does no fuzzing, but simply executes the inputs
|
|
||||||
# provided via parameters.
|
|
||||||
# Run e.g. "make all LIB_FUZZING_ENGINE=/path/to/libFuzzer.a"
|
|
||||||
# to link the fuzzer(s) against a real fuzzing engine.
|
|
||||||
# OSS-Fuzz will define its own value for LIB_FUZZING_ENGINE.
|
|
||||||
LIB_FUZZING_ENGINE ?= standalone_fuzz_target_runner.o
|
|
||||||
|
|
||||||
# Runs server_fuzzer.cc based on value of $(LIB_FUZZING_ENGINE).
|
|
||||||
# Usage: make fuzz_test LIB_FUZZING_ENGINE=/path/to/libFuzzer
|
|
||||||
all fuzz_test: server_fuzzer
|
|
||||||
./server_fuzzer fuzzing/corpus/*
|
|
||||||
|
|
||||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
|
||||||
server_fuzzer : fuzzing/server_fuzzer.cc ../httplib.h standalone_fuzz_target_runner.o
|
|
||||||
$(CXX) $(CXXFLAGS) -o $@ $< $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT) $(BROTLI_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
|
||||||
|
|
||||||
# Standalone fuzz runner, which just reads inputs from fuzzing/corpus/ dir and
|
|
||||||
# feeds it to server_fuzzer.
|
|
||||||
standalone_fuzz_target_runner.o : fuzzing/standalone_fuzz_target_runner.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f server_fuzzer pem *.0 *.o *.1 *.srl *.zip
|
|
10
test/fuzzing/CMakeLists.txt
Normal file
10
test/fuzzing/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
file(GLOB HTTPLIB_CORPUS corpus/*)
|
||||||
|
add_executable(httplib-test-fuzz
|
||||||
|
server_fuzzer.cc
|
||||||
|
standalone_fuzz_target_runner.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(httplib-test-fuzz PRIVATE httplib)
|
||||||
|
add_test(
|
||||||
|
NAME httplib-test-fuzz
|
||||||
|
COMMAND httplib-test-fuzz ${HTTPLIB_CORPUS}
|
||||||
|
)
|
@ -19,7 +19,8 @@ all : server_fuzzer
|
|||||||
|
|
||||||
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
# Fuzz target, so that you can choose which $(LIB_FUZZING_ENGINE) to use.
|
||||||
server_fuzzer : server_fuzzer.cc ../../httplib.h
|
server_fuzzer : server_fuzzer.cc ../../httplib.h
|
||||||
$(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
# $(CXX) $(CXXFLAGS) -o $@ $< -Wl,-Bstatic $(OPENSSL_SUPPORT) -Wl,-Bdynamic -ldl $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $< $(ZLIB_SUPPORT) $(LIB_FUZZING_ENGINE) -pthread
|
||||||
zip -q -r server_fuzzer_seed_corpus.zip corpus
|
zip -q -r server_fuzzer_seed_corpus.zip corpus
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
BIN
test/fuzzing/corpus/3
Normal file
BIN
test/fuzzing/corpus/3
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
19
test/fuzzing/corpus/issue1264
Normal file
19
test/fuzzing/corpus/issue1264
Normal file
File diff suppressed because one or more lines are too long
@ -1,4 +1,5 @@
|
|||||||
#include <memory>
|
#include <cstdint>
|
||||||
|
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
|
|
||||||
class FuzzedStream : public httplib::Stream {
|
class FuzzedStream : public httplib::Stream {
|
||||||
@ -7,12 +8,10 @@ class FuzzedStream : public httplib::Stream {
|
|||||||
: data_(data), size_(size), read_pos_(0) {}
|
: data_(data), size_(size), read_pos_(0) {}
|
||||||
|
|
||||||
ssize_t read(char *ptr, size_t size) override {
|
ssize_t read(char *ptr, size_t size) override {
|
||||||
if (size + read_pos_ > size_) {
|
if (size + read_pos_ > size_) { size = size_ - read_pos_; }
|
||||||
size = size_ - read_pos_;
|
|
||||||
}
|
|
||||||
memcpy(ptr, data_ + read_pos_, size);
|
memcpy(ptr, data_ + read_pos_, size);
|
||||||
read_pos_ += size;
|
read_pos_ += size;
|
||||||
return size;
|
return static_cast<ssize_t>(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t write(const char *ptr, size_t size) override {
|
ssize_t write(const char *ptr, size_t size) override {
|
||||||
@ -20,21 +19,30 @@ class FuzzedStream : public httplib::Stream {
|
|||||||
return static_cast<int>(size);
|
return static_cast<int>(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int write(const char* ptr) { return write(ptr, strlen(ptr)); }
|
ssize_t write(const char *ptr) { return write(ptr, strlen(ptr)); }
|
||||||
|
|
||||||
int write(const std::string& s) { return write(s.data(), s.size()); }
|
ssize_t write(const std::string &s) { return write(s.data(), s.size()); }
|
||||||
|
|
||||||
std::string get_remote_addr() const { return ""; }
|
|
||||||
|
|
||||||
bool is_readable() const override { return true; }
|
bool is_readable() const override { return true; }
|
||||||
|
|
||||||
bool is_writable() const override { return true; }
|
bool wait_readable() const override { return true; }
|
||||||
|
|
||||||
|
bool wait_writable() const override { return true; }
|
||||||
|
|
||||||
void get_remote_ip_and_port(std::string &ip, int &port) const override {
|
void get_remote_ip_and_port(std::string &ip, int &port) const override {
|
||||||
ip = "127.0.0.1";
|
ip = "127.0.0.1";
|
||||||
port = 8080;
|
port = 8080;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void get_local_ip_and_port(std::string &ip, int &port) const override {
|
||||||
|
ip = "127.0.0.1";
|
||||||
|
port = 8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_t socket() const override { return 0; }
|
||||||
|
|
||||||
|
time_t duration() const override { return 0; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint8_t *data_;
|
const uint8_t *data_;
|
||||||
size_t size_;
|
size_t size_;
|
||||||
@ -46,36 +54,40 @@ class FuzzableServer : public httplib::Server {
|
|||||||
public:
|
public:
|
||||||
void ProcessFuzzedRequest(FuzzedStream &stream) {
|
void ProcessFuzzedRequest(FuzzedStream &stream) {
|
||||||
bool connection_close = false;
|
bool connection_close = false;
|
||||||
process_request(stream, /*last_connection=*/false, connection_close,
|
process_request(stream,
|
||||||
nullptr);
|
/*remote_addr=*/"",
|
||||||
|
/*remote_port =*/0,
|
||||||
|
/*local_addr=*/"",
|
||||||
|
/*local_port =*/0,
|
||||||
|
/*last_connection=*/false, connection_close, nullptr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static FuzzableServer g_server;
|
static FuzzableServer g_server;
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
|
extern "C" int LLVMFuzzerInitialize(int * /*argc*/, char *** /*argv*/) {
|
||||||
g_server.Get(R"(.*)",
|
g_server.Get(R"(.*)",
|
||||||
[&](const httplib::Request& req, httplib::Response& res) {
|
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||||
res.set_content("response content", "text/plain");
|
res.set_content("response content", "text/plain");
|
||||||
});
|
});
|
||||||
g_server.Post(R"(.*)",
|
g_server.Post(R"(.*)",
|
||||||
[&](const httplib::Request& req, httplib::Response& res) {
|
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||||
res.set_content("response content", "text/plain");
|
res.set_content("response content", "text/plain");
|
||||||
});
|
});
|
||||||
g_server.Put(R"(.*)",
|
g_server.Put(R"(.*)",
|
||||||
[&](const httplib::Request& req, httplib::Response& res) {
|
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||||
res.set_content("response content", "text/plain");
|
res.set_content("response content", "text/plain");
|
||||||
});
|
});
|
||||||
g_server.Patch(R"(.*)",
|
g_server.Patch(R"(.*)",
|
||||||
[&](const httplib::Request& req, httplib::Response& res) {
|
[&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||||
res.set_content("response content", "text/plain");
|
res.set_content("response content", "text/plain");
|
||||||
});
|
});
|
||||||
g_server.Delete(R"(.*)",
|
g_server.Delete(
|
||||||
[&](const httplib::Request& req, httplib::Response& res) {
|
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||||
res.set_content("response content", "text/plain");
|
res.set_content("response content", "text/plain");
|
||||||
});
|
});
|
||||||
g_server.Options(R"(.*)",
|
g_server.Options(
|
||||||
[&](const httplib::Request& req, httplib::Response& res) {
|
R"(.*)", [&](const httplib::Request & /*req*/, httplib::Response &res) {
|
||||||
res.set_content("response content", "text/plain");
|
res.set_content("response content", "text/plain");
|
||||||
});
|
});
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
// on the test corpus or on a single file,
|
// on the test corpus or on a single file,
|
||||||
// e.g. the one that comes from a bug report.
|
// e.g. the one that comes from a bug report.
|
||||||
|
|
||||||
#include <cassert>
|
#include <cstdint>
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// Forward declare the "fuzz target" interface.
|
// Forward declare the "fuzz target" interface.
|
||||||
// We deliberately keep this inteface simple and header-free.
|
// We deliberately keep this interface simple and header-free.
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
|
||||||
|
|
||||||
// It reads all files passed as parameters and feeds their contents
|
// It reads all files passed as parameters and feeds their contents
|
||||||
@ -20,12 +20,12 @@ int main(int argc, char **argv) {
|
|||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
std::ifstream in(argv[i]);
|
std::ifstream in(argv[i]);
|
||||||
in.seekg(0, in.end);
|
in.seekg(0, in.end);
|
||||||
size_t length = in.tellg();
|
size_t length = static_cast<size_t>(in.tellg());
|
||||||
in.seekg(0, in.beg);
|
in.seekg(0, in.beg);
|
||||||
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
std::cout << "Reading " << length << " bytes from " << argv[i] << std::endl;
|
||||||
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
// Allocate exactly length bytes so that we reliably catch buffer overflows.
|
||||||
std::vector<char> bytes(length);
|
std::vector<char> bytes(length);
|
||||||
in.read(bytes.data(), bytes.size());
|
in.read(bytes.data(), static_cast<std::streamsize>(bytes.size()));
|
||||||
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t *>(bytes.data()),
|
||||||
bytes.size());
|
bytes.size());
|
||||||
std::cout << "Execution successful" << std::endl;
|
std::cout << "Execution successful" << std::endl;
|
||||||
|
18
test/gen-certs.sh
Executable file
18
test/gen-certs.sh
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
if [[ $(openssl version) =~ 3\.[2-9]\.[0-9]+ ]]; then
|
||||||
|
OPENSSL_X509_FLAG='-x509v1'
|
||||||
|
else
|
||||||
|
OPENSSL_X509_FLAG='-x509'
|
||||||
|
fi
|
||||||
|
|
||||||
|
openssl genrsa 2048 > key.pem
|
||||||
|
openssl req -new -batch -config test.conf -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||||
|
openssl req -x509 -config test.conf -key key.pem -sha256 -days 3650 -nodes -out cert2.pem -extensions SAN
|
||||||
|
openssl genrsa 2048 > rootCA.key.pem
|
||||||
|
openssl req $OPENSSL_X509_FLAG -new -batch -config test.rootCA.conf -key rootCA.key.pem -days 1024 > rootCA.cert.pem
|
||||||
|
openssl genrsa 2048 > client.key.pem
|
||||||
|
openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem
|
||||||
|
openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem
|
||||||
|
openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem
|
||||||
|
openssl genrsa -aes256 -passout pass:test012! 2048 > client_encrypted.key.pem
|
||||||
|
openssl req -new -batch -config test.conf -key client_encrypted.key.pem -passin pass:test012! | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client_encrypted.cert.pem
|
File diff suppressed because it is too large
Load Diff
19570
test/gtest/gtest.h
19570
test/gtest/gtest.h
File diff suppressed because it is too large
Load Diff
237
test/gtest/include/gtest/gtest-assertion-result.h
Normal file
237
test/gtest/include/gtest/gtest-assertion-result.h
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Copyright 2005, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This file implements the AssertionResult type.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "gtest/gtest-message.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
|
||||||
|
/* class A needs to have dll-interface to be used by clients of class B */)
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// A class for indicating whether an assertion was successful. When
|
||||||
|
// the assertion wasn't successful, the AssertionResult object
|
||||||
|
// remembers a non-empty message that describes how it failed.
|
||||||
|
//
|
||||||
|
// To create an instance of this class, use one of the factory functions
|
||||||
|
// (AssertionSuccess() and AssertionFailure()).
|
||||||
|
//
|
||||||
|
// This class is useful for two purposes:
|
||||||
|
// 1. Defining predicate functions to be used with Boolean test assertions
|
||||||
|
// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts
|
||||||
|
// 2. Defining predicate-format functions to be
|
||||||
|
// used with predicate assertions (ASSERT_PRED_FORMAT*, etc).
|
||||||
|
//
|
||||||
|
// For example, if you define IsEven predicate:
|
||||||
|
//
|
||||||
|
// testing::AssertionResult IsEven(int n) {
|
||||||
|
// if ((n % 2) == 0)
|
||||||
|
// return testing::AssertionSuccess();
|
||||||
|
// else
|
||||||
|
// return testing::AssertionFailure() << n << " is odd";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5)))
|
||||||
|
// will print the message
|
||||||
|
//
|
||||||
|
// Value of: IsEven(Fib(5))
|
||||||
|
// Actual: false (5 is odd)
|
||||||
|
// Expected: true
|
||||||
|
//
|
||||||
|
// instead of a more opaque
|
||||||
|
//
|
||||||
|
// Value of: IsEven(Fib(5))
|
||||||
|
// Actual: false
|
||||||
|
// Expected: true
|
||||||
|
//
|
||||||
|
// in case IsEven is a simple Boolean predicate.
|
||||||
|
//
|
||||||
|
// If you expect your predicate to be reused and want to support informative
|
||||||
|
// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up
|
||||||
|
// about half as often as positive ones in our tests), supply messages for
|
||||||
|
// both success and failure cases:
|
||||||
|
//
|
||||||
|
// testing::AssertionResult IsEven(int n) {
|
||||||
|
// if ((n % 2) == 0)
|
||||||
|
// return testing::AssertionSuccess() << n << " is even";
|
||||||
|
// else
|
||||||
|
// return testing::AssertionFailure() << n << " is odd";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print
|
||||||
|
//
|
||||||
|
// Value of: IsEven(Fib(6))
|
||||||
|
// Actual: true (8 is even)
|
||||||
|
// Expected: false
|
||||||
|
//
|
||||||
|
// NB: Predicates that support negative Boolean assertions have reduced
|
||||||
|
// performance in positive ones so be careful not to use them in tests
|
||||||
|
// that have lots (tens of thousands) of positive Boolean assertions.
|
||||||
|
//
|
||||||
|
// To use this class with EXPECT_PRED_FORMAT assertions such as:
|
||||||
|
//
|
||||||
|
// // Verifies that Foo() returns an even number.
|
||||||
|
// EXPECT_PRED_FORMAT1(IsEven, Foo());
|
||||||
|
//
|
||||||
|
// you need to define:
|
||||||
|
//
|
||||||
|
// testing::AssertionResult IsEven(const char* expr, int n) {
|
||||||
|
// if ((n % 2) == 0)
|
||||||
|
// return testing::AssertionSuccess();
|
||||||
|
// else
|
||||||
|
// return testing::AssertionFailure()
|
||||||
|
// << "Expected: " << expr << " is even\n Actual: it's " << n;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If Foo() returns 5, you will see the following message:
|
||||||
|
//
|
||||||
|
// Expected: Foo() is even
|
||||||
|
// Actual: it's 5
|
||||||
|
//
|
||||||
|
class GTEST_API_ AssertionResult {
|
||||||
|
public:
|
||||||
|
// Copy constructor.
|
||||||
|
// Used in EXPECT_TRUE/FALSE(assertion_result).
|
||||||
|
AssertionResult(const AssertionResult& other);
|
||||||
|
|
||||||
|
// C4800 is a level 3 warning in Visual Studio 2015 and earlier.
|
||||||
|
// This warning is not emitted in Visual Studio 2017.
|
||||||
|
// This warning is off by default starting in Visual Studio 2019 but can be
|
||||||
|
// enabled with command-line options.
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Used in the EXPECT_TRUE/FALSE(bool_expression).
|
||||||
|
//
|
||||||
|
// T must be contextually convertible to bool.
|
||||||
|
//
|
||||||
|
// The second parameter prevents this overload from being considered if
|
||||||
|
// the argument is implicitly convertible to AssertionResult. In that case
|
||||||
|
// we want AssertionResult's copy constructor to be used.
|
||||||
|
template <typename T>
|
||||||
|
explicit AssertionResult(
|
||||||
|
const T& success,
|
||||||
|
typename std::enable_if<
|
||||||
|
!std::is_convertible<T, AssertionResult>::value>::type*
|
||||||
|
/*enabler*/
|
||||||
|
= nullptr)
|
||||||
|
: success_(success) {}
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Assignment operator.
|
||||||
|
AssertionResult& operator=(AssertionResult other) {
|
||||||
|
swap(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if and only if the assertion succeeded.
|
||||||
|
operator bool() const { return success_; } // NOLINT
|
||||||
|
|
||||||
|
// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
|
||||||
|
AssertionResult operator!() const;
|
||||||
|
|
||||||
|
// Returns the text streamed into this AssertionResult. Test assertions
|
||||||
|
// use it when they fail (i.e., the predicate's outcome doesn't match the
|
||||||
|
// assertion's expectation). When nothing has been streamed into the
|
||||||
|
// object, returns an empty string.
|
||||||
|
const char* message() const {
|
||||||
|
return message_.get() != nullptr ? message_->c_str() : "";
|
||||||
|
}
|
||||||
|
// Deprecated; please use message() instead.
|
||||||
|
const char* failure_message() const { return message(); }
|
||||||
|
|
||||||
|
// Streams a custom failure message into this object.
|
||||||
|
template <typename T>
|
||||||
|
AssertionResult& operator<<(const T& value) {
|
||||||
|
AppendMessage(Message() << value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows streaming basic output manipulators such as endl or flush into
|
||||||
|
// this object.
|
||||||
|
AssertionResult& operator<<(
|
||||||
|
::std::ostream& (*basic_manipulator)(::std::ostream& stream)) {
|
||||||
|
AppendMessage(Message() << basic_manipulator);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Appends the contents of message to message_.
|
||||||
|
void AppendMessage(const Message& a_message) {
|
||||||
|
if (message_.get() == nullptr) message_.reset(new ::std::string);
|
||||||
|
message_->append(a_message.GetString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap the contents of this AssertionResult with other.
|
||||||
|
void swap(AssertionResult& other);
|
||||||
|
|
||||||
|
// Stores result of the assertion predicate.
|
||||||
|
bool success_;
|
||||||
|
// Stores the message describing the condition in case the expectation
|
||||||
|
// construct is not satisfied with the predicate's outcome.
|
||||||
|
// Referenced via a pointer to avoid taking too much stack frame space
|
||||||
|
// with test assertions.
|
||||||
|
std::unique_ptr< ::std::string> message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Makes a successful assertion result.
|
||||||
|
GTEST_API_ AssertionResult AssertionSuccess();
|
||||||
|
|
||||||
|
// Makes a failed assertion result.
|
||||||
|
GTEST_API_ AssertionResult AssertionFailure();
|
||||||
|
|
||||||
|
// Makes a failed assertion result with the given failure message.
|
||||||
|
// Deprecated; use AssertionFailure() << msg.
|
||||||
|
GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_
|
345
test/gtest/include/gtest/gtest-death-test.h
Normal file
345
test/gtest/include/gtest/gtest-death-test.h
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
// Copyright 2005, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This header file defines the public API for death tests. It is
|
||||||
|
// #included by gtest.h so a user doesn't need to include this
|
||||||
|
// directly.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-death-test-internal.h"
|
||||||
|
|
||||||
|
// This flag controls the style of death tests. Valid values are "threadsafe",
|
||||||
|
// meaning that the death test child process will re-execute the test binary
|
||||||
|
// from the start, running only a single death test, or "fast",
|
||||||
|
// meaning that the child process will execute the test logic immediately
|
||||||
|
// after forking.
|
||||||
|
GTEST_DECLARE_string_(death_test_style);
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
#if GTEST_HAS_DEATH_TEST
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Returns a Boolean value indicating whether the caller is currently
|
||||||
|
// executing in the context of the death test child process. Tools such as
|
||||||
|
// Valgrind heap checkers may need this to modify their behavior in death
|
||||||
|
// tests. IMPORTANT: This is an internal utility. Using it may break the
|
||||||
|
// implementation of death tests. User code MUST NOT use it.
|
||||||
|
GTEST_API_ bool InDeathTestChild();
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// The following macros are useful for writing death tests.
|
||||||
|
|
||||||
|
// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
|
||||||
|
// executed:
|
||||||
|
//
|
||||||
|
// 1. It generates a warning if there is more than one active
|
||||||
|
// thread. This is because it's safe to fork() or clone() only
|
||||||
|
// when there is a single thread.
|
||||||
|
//
|
||||||
|
// 2. The parent process clone()s a sub-process and runs the death
|
||||||
|
// test in it; the sub-process exits with code 0 at the end of the
|
||||||
|
// death test, if it hasn't exited already.
|
||||||
|
//
|
||||||
|
// 3. The parent process waits for the sub-process to terminate.
|
||||||
|
//
|
||||||
|
// 4. The parent process checks the exit code and error message of
|
||||||
|
// the sub-process.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
|
||||||
|
// for (int i = 0; i < 5; i++) {
|
||||||
|
// EXPECT_DEATH(server.ProcessRequest(i),
|
||||||
|
// "Invalid request .* in ProcessRequest()")
|
||||||
|
// << "Failed to die on request " << i;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
|
||||||
|
//
|
||||||
|
// bool KilledBySIGHUP(int exit_code) {
|
||||||
|
// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
|
||||||
|
//
|
||||||
|
// The final parameter to each of these macros is a matcher applied to any data
|
||||||
|
// the sub-process wrote to stderr. For compatibility with existing tests, a
|
||||||
|
// bare string is interpreted as a regular expression matcher.
|
||||||
|
//
|
||||||
|
// On the regular expressions used in death tests:
|
||||||
|
//
|
||||||
|
// On POSIX-compliant systems (*nix), we use the <regex.h> library,
|
||||||
|
// which uses the POSIX extended regex syntax.
|
||||||
|
//
|
||||||
|
// On other platforms (e.g. Windows or Mac), we only support a simple regex
|
||||||
|
// syntax implemented as part of Google Test. This limited
|
||||||
|
// implementation should be enough most of the time when writing
|
||||||
|
// death tests; though it lacks many features you can find in PCRE
|
||||||
|
// or POSIX extended regex syntax. For example, we don't support
|
||||||
|
// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
|
||||||
|
// repetition count ("x{5,7}"), among others.
|
||||||
|
//
|
||||||
|
// Below is the syntax that we do support. We chose it to be a
|
||||||
|
// subset of both PCRE and POSIX extended regex, so it's easy to
|
||||||
|
// learn wherever you come from. In the following: 'A' denotes a
|
||||||
|
// literal character, period (.), or a single \\ escape sequence;
|
||||||
|
// 'x' and 'y' denote regular expressions; 'm' and 'n' are for
|
||||||
|
// natural numbers.
|
||||||
|
//
|
||||||
|
// c matches any literal character c
|
||||||
|
// \\d matches any decimal digit
|
||||||
|
// \\D matches any character that's not a decimal digit
|
||||||
|
// \\f matches \f
|
||||||
|
// \\n matches \n
|
||||||
|
// \\r matches \r
|
||||||
|
// \\s matches any ASCII whitespace, including \n
|
||||||
|
// \\S matches any character that's not a whitespace
|
||||||
|
// \\t matches \t
|
||||||
|
// \\v matches \v
|
||||||
|
// \\w matches any letter, _, or decimal digit
|
||||||
|
// \\W matches any character that \\w doesn't match
|
||||||
|
// \\c matches any literal character c, which must be a punctuation
|
||||||
|
// . matches any single character except \n
|
||||||
|
// A? matches 0 or 1 occurrences of A
|
||||||
|
// A* matches 0 or many occurrences of A
|
||||||
|
// A+ matches 1 or many occurrences of A
|
||||||
|
// ^ matches the beginning of a string (not that of each line)
|
||||||
|
// $ matches the end of a string (not that of each line)
|
||||||
|
// xy matches x followed by y
|
||||||
|
//
|
||||||
|
// If you accidentally use PCRE or POSIX extended regex features
|
||||||
|
// not implemented by us, you will get a run-time failure. In that
|
||||||
|
// case, please try to rewrite your regular expression within the
|
||||||
|
// above syntax.
|
||||||
|
//
|
||||||
|
// This implementation is *not* meant to be as highly tuned or robust
|
||||||
|
// as a compiled regex library, but should perform well enough for a
|
||||||
|
// death test, which already incurs significant overhead by launching
|
||||||
|
// a child process.
|
||||||
|
//
|
||||||
|
// Known caveats:
|
||||||
|
//
|
||||||
|
// A "threadsafe" style death test obtains the path to the test
|
||||||
|
// program from argv[0] and re-executes it in the sub-process. For
|
||||||
|
// simplicity, the current implementation doesn't search the PATH
|
||||||
|
// when launching the sub-process. This means that the user must
|
||||||
|
// invoke the test program via a path that contains at least one
|
||||||
|
// path separator (e.g. path/to/foo_test and
|
||||||
|
// /absolute/path/to/bar_test are fine, but foo_test is not). This
|
||||||
|
// is rarely a problem as people usually don't put the test binary
|
||||||
|
// directory in PATH.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Asserts that a given `statement` causes the program to exit, with an
|
||||||
|
// integer exit status that satisfies `predicate`, and emitting error output
|
||||||
|
// that matches `matcher`.
|
||||||
|
#define ASSERT_EXIT(statement, predicate, matcher) \
|
||||||
|
GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_)
|
||||||
|
|
||||||
|
// Like `ASSERT_EXIT`, but continues on to successive tests in the
|
||||||
|
// test suite, if any:
|
||||||
|
#define EXPECT_EXIT(statement, predicate, matcher) \
|
||||||
|
GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_)
|
||||||
|
|
||||||
|
// Asserts that a given `statement` causes the program to exit, either by
|
||||||
|
// explicitly exiting with a nonzero exit code or being killed by a
|
||||||
|
// signal, and emitting error output that matches `matcher`.
|
||||||
|
#define ASSERT_DEATH(statement, matcher) \
|
||||||
|
ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
|
||||||
|
|
||||||
|
// Like `ASSERT_DEATH`, but continues on to successive tests in the
|
||||||
|
// test suite, if any:
|
||||||
|
#define EXPECT_DEATH(statement, matcher) \
|
||||||
|
EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
|
||||||
|
|
||||||
|
// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
|
||||||
|
|
||||||
|
// Tests that an exit code describes a normal exit with a given exit code.
|
||||||
|
class GTEST_API_ ExitedWithCode {
|
||||||
|
public:
|
||||||
|
explicit ExitedWithCode(int exit_code);
|
||||||
|
ExitedWithCode(const ExitedWithCode&) = default;
|
||||||
|
void operator=(const ExitedWithCode& other) = delete;
|
||||||
|
bool operator()(int exit_status) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int exit_code_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
|
||||||
|
// Tests that an exit code describes an exit due to termination by a
|
||||||
|
// given signal.
|
||||||
|
class GTEST_API_ KilledBySignal {
|
||||||
|
public:
|
||||||
|
explicit KilledBySignal(int signum);
|
||||||
|
bool operator()(int exit_status) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int signum_;
|
||||||
|
};
|
||||||
|
#endif // !GTEST_OS_WINDOWS
|
||||||
|
|
||||||
|
// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
|
||||||
|
// The death testing framework causes this to have interesting semantics,
|
||||||
|
// since the sideeffects of the call are only visible in opt mode, and not
|
||||||
|
// in debug mode.
|
||||||
|
//
|
||||||
|
// In practice, this can be used to test functions that utilize the
|
||||||
|
// LOG(DFATAL) macro using the following style:
|
||||||
|
//
|
||||||
|
// int DieInDebugOr12(int* sideeffect) {
|
||||||
|
// if (sideeffect) {
|
||||||
|
// *sideeffect = 12;
|
||||||
|
// }
|
||||||
|
// LOG(DFATAL) << "death";
|
||||||
|
// return 12;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) {
|
||||||
|
// int sideeffect = 0;
|
||||||
|
// // Only asserts in dbg.
|
||||||
|
// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
|
||||||
|
//
|
||||||
|
// #ifdef NDEBUG
|
||||||
|
// // opt-mode has sideeffect visible.
|
||||||
|
// EXPECT_EQ(12, sideeffect);
|
||||||
|
// #else
|
||||||
|
// // dbg-mode no visible sideeffect.
|
||||||
|
// EXPECT_EQ(0, sideeffect);
|
||||||
|
// #endif
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// This will assert that DieInDebugReturn12InOpt() crashes in debug
|
||||||
|
// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
|
||||||
|
// appropriate fallback value (12 in this case) in opt mode. If you
|
||||||
|
// need to test that a function has appropriate side-effects in opt
|
||||||
|
// mode, include assertions against the side-effects. A general
|
||||||
|
// pattern for this is:
|
||||||
|
//
|
||||||
|
// EXPECT_DEBUG_DEATH({
|
||||||
|
// // Side-effects here will have an effect after this statement in
|
||||||
|
// // opt mode, but none in debug mode.
|
||||||
|
// EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
|
||||||
|
// }, "death");
|
||||||
|
//
|
||||||
|
#ifdef NDEBUG
|
||||||
|
|
||||||
|
#define EXPECT_DEBUG_DEATH(statement, regex) \
|
||||||
|
GTEST_EXECUTE_STATEMENT_(statement, regex)
|
||||||
|
|
||||||
|
#define ASSERT_DEBUG_DEATH(statement, regex) \
|
||||||
|
GTEST_EXECUTE_STATEMENT_(statement, regex)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define EXPECT_DEBUG_DEATH(statement, regex) EXPECT_DEATH(statement, regex)
|
||||||
|
|
||||||
|
#define ASSERT_DEBUG_DEATH(statement, regex) ASSERT_DEATH(statement, regex)
|
||||||
|
|
||||||
|
#endif // NDEBUG for EXPECT_DEBUG_DEATH
|
||||||
|
#endif // GTEST_HAS_DEATH_TEST
|
||||||
|
|
||||||
|
// This macro is used for implementing macros such as
|
||||||
|
// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
|
||||||
|
// death tests are not supported. Those macros must compile on such systems
|
||||||
|
// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters
|
||||||
|
// on systems that support death tests. This allows one to write such a macro on
|
||||||
|
// a system that does not support death tests and be sure that it will compile
|
||||||
|
// on a death-test supporting system. It is exposed publicly so that systems
|
||||||
|
// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST
|
||||||
|
// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and
|
||||||
|
// ASSERT_DEATH_IF_SUPPORTED.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// statement - A statement that a macro such as EXPECT_DEATH would test
|
||||||
|
// for program termination. This macro has to make sure this
|
||||||
|
// statement is compiled but not executed, to ensure that
|
||||||
|
// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
|
||||||
|
// parameter if and only if EXPECT_DEATH compiles with it.
|
||||||
|
// regex - A regex that a macro such as EXPECT_DEATH would use to test
|
||||||
|
// the output of statement. This parameter has to be
|
||||||
|
// compiled but not evaluated by this macro, to ensure that
|
||||||
|
// this macro only accepts expressions that a macro such as
|
||||||
|
// EXPECT_DEATH would accept.
|
||||||
|
// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
|
||||||
|
// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
|
||||||
|
// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
|
||||||
|
// compile inside functions where ASSERT_DEATH doesn't
|
||||||
|
// compile.
|
||||||
|
//
|
||||||
|
// The branch that has an always false condition is used to ensure that
|
||||||
|
// statement and regex are compiled (and thus syntactically correct) but
|
||||||
|
// never executed. The unreachable code macro protects the terminator
|
||||||
|
// statement from generating an 'unreachable code' warning in case
|
||||||
|
// statement unconditionally returns or throws. The Message constructor at
|
||||||
|
// the end allows the syntax of streaming additional messages into the
|
||||||
|
// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
|
||||||
|
#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
|
||||||
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
|
if (::testing::internal::AlwaysTrue()) { \
|
||||||
|
GTEST_LOG_(WARNING) << "Death tests are not supported on this platform.\n" \
|
||||||
|
<< "Statement '" #statement "' cannot be verified."; \
|
||||||
|
} else if (::testing::internal::AlwaysFalse()) { \
|
||||||
|
::testing::internal::RE::PartialMatch(".*", (regex)); \
|
||||||
|
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||||
|
terminator; \
|
||||||
|
} else \
|
||||||
|
::testing::Message()
|
||||||
|
|
||||||
|
// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
|
||||||
|
// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
|
||||||
|
// death tests are supported; otherwise they just issue a warning. This is
|
||||||
|
// useful when you are combining death test assertions with normal test
|
||||||
|
// assertions in one test.
|
||||||
|
#if GTEST_HAS_DEATH_TEST
|
||||||
|
#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||||
|
EXPECT_DEATH(statement, regex)
|
||||||
|
#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||||
|
ASSERT_DEATH(statement, regex)
|
||||||
|
#else
|
||||||
|
#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||||
|
GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
|
||||||
|
#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
|
||||||
|
GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
|
956
test/gtest/include/gtest/gtest-matchers.h
Normal file
956
test/gtest/include/gtest/gtest-matchers.h
Normal file
@ -0,0 +1,956 @@
|
|||||||
|
// Copyright 2007, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This file implements just enough of the matcher interface to allow
|
||||||
|
// EXPECT_DEATH and friends to accept a matcher argument.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "gtest/gtest-printers.h"
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
// MSVC warning C5046 is new as of VS2017 version 15.8.
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER >= 1915
|
||||||
|
#define GTEST_MAYBE_5046_ 5046
|
||||||
|
#else
|
||||||
|
#define GTEST_MAYBE_5046_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(
|
||||||
|
4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by
|
||||||
|
clients of class B */
|
||||||
|
/* Symbol involving type with internal linkage not defined */)
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// To implement a matcher Foo for type T, define:
|
||||||
|
// 1. a class FooMatcherMatcher that implements the matcher interface:
|
||||||
|
// using is_gtest_matcher = void;
|
||||||
|
// bool MatchAndExplain(const T&, std::ostream*);
|
||||||
|
// (MatchResultListener* can also be used instead of std::ostream*)
|
||||||
|
// void DescribeTo(std::ostream*);
|
||||||
|
// void DescribeNegationTo(std::ostream*);
|
||||||
|
//
|
||||||
|
// 2. a factory function that creates a Matcher<T> object from a
|
||||||
|
// FooMatcherMatcher.
|
||||||
|
|
||||||
|
class MatchResultListener {
|
||||||
|
public:
|
||||||
|
// Creates a listener object with the given underlying ostream. The
|
||||||
|
// listener does not own the ostream, and does not dereference it
|
||||||
|
// in the constructor or destructor.
|
||||||
|
explicit MatchResultListener(::std::ostream* os) : stream_(os) {}
|
||||||
|
virtual ~MatchResultListener() = 0; // Makes this class abstract.
|
||||||
|
|
||||||
|
// Streams x to the underlying ostream; does nothing if the ostream
|
||||||
|
// is NULL.
|
||||||
|
template <typename T>
|
||||||
|
MatchResultListener& operator<<(const T& x) {
|
||||||
|
if (stream_ != nullptr) *stream_ << x;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the underlying ostream.
|
||||||
|
::std::ostream* stream() { return stream_; }
|
||||||
|
|
||||||
|
// Returns true if and only if the listener is interested in an explanation
|
||||||
|
// of the match result. A matcher's MatchAndExplain() method can use
|
||||||
|
// this information to avoid generating the explanation when no one
|
||||||
|
// intends to hear it.
|
||||||
|
bool IsInterested() const { return stream_ != nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
::std::ostream* const stream_;
|
||||||
|
|
||||||
|
MatchResultListener(const MatchResultListener&) = delete;
|
||||||
|
MatchResultListener& operator=(const MatchResultListener&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline MatchResultListener::~MatchResultListener() {}
|
||||||
|
|
||||||
|
// An instance of a subclass of this knows how to describe itself as a
|
||||||
|
// matcher.
|
||||||
|
class GTEST_API_ MatcherDescriberInterface {
|
||||||
|
public:
|
||||||
|
virtual ~MatcherDescriberInterface() {}
|
||||||
|
|
||||||
|
// Describes this matcher to an ostream. The function should print
|
||||||
|
// a verb phrase that describes the property a value matching this
|
||||||
|
// matcher should have. The subject of the verb phrase is the value
|
||||||
|
// being matched. For example, the DescribeTo() method of the Gt(7)
|
||||||
|
// matcher prints "is greater than 7".
|
||||||
|
virtual void DescribeTo(::std::ostream* os) const = 0;
|
||||||
|
|
||||||
|
// Describes the negation of this matcher to an ostream. For
|
||||||
|
// example, if the description of this matcher is "is greater than
|
||||||
|
// 7", the negated description could be "is not greater than 7".
|
||||||
|
// You are not required to override this when implementing
|
||||||
|
// MatcherInterface, but it is highly advised so that your matcher
|
||||||
|
// can produce good error messages.
|
||||||
|
virtual void DescribeNegationTo(::std::ostream* os) const {
|
||||||
|
*os << "not (";
|
||||||
|
DescribeTo(os);
|
||||||
|
*os << ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The implementation of a matcher.
|
||||||
|
template <typename T>
|
||||||
|
class MatcherInterface : public MatcherDescriberInterface {
|
||||||
|
public:
|
||||||
|
// Returns true if and only if the matcher matches x; also explains the
|
||||||
|
// match result to 'listener' if necessary (see the next paragraph), in
|
||||||
|
// the form of a non-restrictive relative clause ("which ...",
|
||||||
|
// "whose ...", etc) that describes x. For example, the
|
||||||
|
// MatchAndExplain() method of the Pointee(...) matcher should
|
||||||
|
// generate an explanation like "which points to ...".
|
||||||
|
//
|
||||||
|
// Implementations of MatchAndExplain() should add an explanation of
|
||||||
|
// the match result *if and only if* they can provide additional
|
||||||
|
// information that's not already present (or not obvious) in the
|
||||||
|
// print-out of x and the matcher's description. Whether the match
|
||||||
|
// succeeds is not a factor in deciding whether an explanation is
|
||||||
|
// needed, as sometimes the caller needs to print a failure message
|
||||||
|
// when the match succeeds (e.g. when the matcher is used inside
|
||||||
|
// Not()).
|
||||||
|
//
|
||||||
|
// For example, a "has at least 10 elements" matcher should explain
|
||||||
|
// what the actual element count is, regardless of the match result,
|
||||||
|
// as it is useful information to the reader; on the other hand, an
|
||||||
|
// "is empty" matcher probably only needs to explain what the actual
|
||||||
|
// size is when the match fails, as it's redundant to say that the
|
||||||
|
// size is 0 when the value is already known to be empty.
|
||||||
|
//
|
||||||
|
// You should override this method when defining a new matcher.
|
||||||
|
//
|
||||||
|
// It's the responsibility of the caller (Google Test) to guarantee
|
||||||
|
// that 'listener' is not NULL. This helps to simplify a matcher's
|
||||||
|
// implementation when it doesn't care about the performance, as it
|
||||||
|
// can talk to 'listener' without checking its validity first.
|
||||||
|
// However, in order to implement dummy listeners efficiently,
|
||||||
|
// listener->stream() may be NULL.
|
||||||
|
virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
|
||||||
|
|
||||||
|
// Inherits these methods from MatcherDescriberInterface:
|
||||||
|
// virtual void DescribeTo(::std::ostream* os) const = 0;
|
||||||
|
// virtual void DescribeNegationTo(::std::ostream* os) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
struct AnyEq {
|
||||||
|
template <typename A, typename B>
|
||||||
|
bool operator()(const A& a, const B& b) const {
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AnyNe {
|
||||||
|
template <typename A, typename B>
|
||||||
|
bool operator()(const A& a, const B& b) const {
|
||||||
|
return a != b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AnyLt {
|
||||||
|
template <typename A, typename B>
|
||||||
|
bool operator()(const A& a, const B& b) const {
|
||||||
|
return a < b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AnyGt {
|
||||||
|
template <typename A, typename B>
|
||||||
|
bool operator()(const A& a, const B& b) const {
|
||||||
|
return a > b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AnyLe {
|
||||||
|
template <typename A, typename B>
|
||||||
|
bool operator()(const A& a, const B& b) const {
|
||||||
|
return a <= b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct AnyGe {
|
||||||
|
template <typename A, typename B>
|
||||||
|
bool operator()(const A& a, const B& b) const {
|
||||||
|
return a >= b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A match result listener that ignores the explanation.
|
||||||
|
class DummyMatchResultListener : public MatchResultListener {
|
||||||
|
public:
|
||||||
|
DummyMatchResultListener() : MatchResultListener(nullptr) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DummyMatchResultListener(const DummyMatchResultListener&) = delete;
|
||||||
|
DummyMatchResultListener& operator=(const DummyMatchResultListener&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A match result listener that forwards the explanation to a given
|
||||||
|
// ostream. The difference between this and MatchResultListener is
|
||||||
|
// that the former is concrete.
|
||||||
|
class StreamMatchResultListener : public MatchResultListener {
|
||||||
|
public:
|
||||||
|
explicit StreamMatchResultListener(::std::ostream* os)
|
||||||
|
: MatchResultListener(os) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
StreamMatchResultListener(const StreamMatchResultListener&) = delete;
|
||||||
|
StreamMatchResultListener& operator=(const StreamMatchResultListener&) =
|
||||||
|
delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SharedPayloadBase {
|
||||||
|
std::atomic<int> ref{1};
|
||||||
|
void Ref() { ref.fetch_add(1, std::memory_order_relaxed); }
|
||||||
|
bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct SharedPayload : SharedPayloadBase {
|
||||||
|
explicit SharedPayload(const T& v) : value(v) {}
|
||||||
|
explicit SharedPayload(T&& v) : value(std::move(v)) {}
|
||||||
|
|
||||||
|
static void Destroy(SharedPayloadBase* shared) {
|
||||||
|
delete static_cast<SharedPayload*>(shared);
|
||||||
|
}
|
||||||
|
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An internal class for implementing Matcher<T>, which will derive
|
||||||
|
// from it. We put functionalities common to all Matcher<T>
|
||||||
|
// specializations here to avoid code duplication.
|
||||||
|
template <typename T>
|
||||||
|
class MatcherBase : private MatcherDescriberInterface {
|
||||||
|
public:
|
||||||
|
// Returns true if and only if the matcher matches x; also explains the
|
||||||
|
// match result to 'listener'.
|
||||||
|
bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
|
||||||
|
GTEST_CHECK_(vtable_ != nullptr);
|
||||||
|
return vtable_->match_and_explain(*this, x, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if and only if this matcher matches x.
|
||||||
|
bool Matches(const T& x) const {
|
||||||
|
DummyMatchResultListener dummy;
|
||||||
|
return MatchAndExplain(x, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes this matcher to an ostream.
|
||||||
|
void DescribeTo(::std::ostream* os) const final {
|
||||||
|
GTEST_CHECK_(vtable_ != nullptr);
|
||||||
|
vtable_->describe(*this, os, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes the negation of this matcher to an ostream.
|
||||||
|
void DescribeNegationTo(::std::ostream* os) const final {
|
||||||
|
GTEST_CHECK_(vtable_ != nullptr);
|
||||||
|
vtable_->describe(*this, os, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explains why x matches, or doesn't match, the matcher.
|
||||||
|
void ExplainMatchResultTo(const T& x, ::std::ostream* os) const {
|
||||||
|
StreamMatchResultListener listener(os);
|
||||||
|
MatchAndExplain(x, &listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the describer for this matcher object; retains ownership
|
||||||
|
// of the describer, which is only guaranteed to be alive when
|
||||||
|
// this matcher object is alive.
|
||||||
|
const MatcherDescriberInterface* GetDescriber() const {
|
||||||
|
if (vtable_ == nullptr) return nullptr;
|
||||||
|
return vtable_->get_describer(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
MatcherBase() : vtable_(nullptr), buffer_() {}
|
||||||
|
|
||||||
|
// Constructs a matcher from its implementation.
|
||||||
|
template <typename U>
|
||||||
|
explicit MatcherBase(const MatcherInterface<U>* impl)
|
||||||
|
: vtable_(nullptr), buffer_() {
|
||||||
|
Init(impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
MatcherBase(M&& m) : vtable_(nullptr), buffer_() { // NOLINT
|
||||||
|
Init(std::forward<M>(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
MatcherBase(const MatcherBase& other)
|
||||||
|
: vtable_(other.vtable_), buffer_(other.buffer_) {
|
||||||
|
if (IsShared()) buffer_.shared->Ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
MatcherBase& operator=(const MatcherBase& other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
Destroy();
|
||||||
|
vtable_ = other.vtable_;
|
||||||
|
buffer_ = other.buffer_;
|
||||||
|
if (IsShared()) buffer_.shared->Ref();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatcherBase(MatcherBase&& other)
|
||||||
|
: vtable_(other.vtable_), buffer_(other.buffer_) {
|
||||||
|
other.vtable_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatcherBase& operator=(MatcherBase&& other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
Destroy();
|
||||||
|
vtable_ = other.vtable_;
|
||||||
|
buffer_ = other.buffer_;
|
||||||
|
other.vtable_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MatcherBase() override { Destroy(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct VTable {
|
||||||
|
bool (*match_and_explain)(const MatcherBase&, const T&,
|
||||||
|
MatchResultListener*);
|
||||||
|
void (*describe)(const MatcherBase&, std::ostream*, bool negation);
|
||||||
|
// Returns the captured object if it implements the interface, otherwise
|
||||||
|
// returns the MatcherBase itself.
|
||||||
|
const MatcherDescriberInterface* (*get_describer)(const MatcherBase&);
|
||||||
|
// Called on shared instances when the reference count reaches 0.
|
||||||
|
void (*shared_destroy)(SharedPayloadBase*);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsShared() const {
|
||||||
|
return vtable_ != nullptr && vtable_->shared_destroy != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the implementation uses a listener, call that.
|
||||||
|
template <typename P>
|
||||||
|
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
|
||||||
|
MatchResultListener* listener)
|
||||||
|
-> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) {
|
||||||
|
return P::Get(m).MatchAndExplain(value, listener->stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
|
||||||
|
MatchResultListener* listener)
|
||||||
|
-> decltype(P::Get(m).MatchAndExplain(value, listener)) {
|
||||||
|
return P::Get(m).MatchAndExplain(value, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
static void DescribeImpl(const MatcherBase& m, std::ostream* os,
|
||||||
|
bool negation) {
|
||||||
|
if (negation) {
|
||||||
|
P::Get(m).DescribeNegationTo(os);
|
||||||
|
} else {
|
||||||
|
P::Get(m).DescribeTo(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
static const MatcherDescriberInterface* GetDescriberImpl(
|
||||||
|
const MatcherBase& m) {
|
||||||
|
// If the impl is a MatcherDescriberInterface, then return it.
|
||||||
|
// Otherwise use MatcherBase itself.
|
||||||
|
// This allows us to implement the GetDescriber() function without support
|
||||||
|
// from the impl, but some users really want to get their impl back when
|
||||||
|
// they call GetDescriber().
|
||||||
|
// We use std::get on a tuple as a workaround of not having `if constexpr`.
|
||||||
|
return std::get<(
|
||||||
|
std::is_convertible<decltype(&P::Get(m)),
|
||||||
|
const MatcherDescriberInterface*>::value
|
||||||
|
? 1
|
||||||
|
: 0)>(std::make_tuple(&m, &P::Get(m)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
const VTable* GetVTable() {
|
||||||
|
static constexpr VTable kVTable = {&MatchAndExplainImpl<P>,
|
||||||
|
&DescribeImpl<P>, &GetDescriberImpl<P>,
|
||||||
|
P::shared_destroy};
|
||||||
|
return &kVTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
union Buffer {
|
||||||
|
// Add some types to give Buffer some common alignment/size use cases.
|
||||||
|
void* ptr;
|
||||||
|
double d;
|
||||||
|
int64_t i;
|
||||||
|
// And add one for the out-of-line cases.
|
||||||
|
SharedPayloadBase* shared;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Destroy() {
|
||||||
|
if (IsShared() && buffer_.shared->Unref()) {
|
||||||
|
vtable_->shared_destroy(buffer_.shared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
static constexpr bool IsInlined() {
|
||||||
|
return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) &&
|
||||||
|
std::is_trivially_copy_constructible<M>::value &&
|
||||||
|
std::is_trivially_destructible<M>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename M, bool = MatcherBase::IsInlined<M>()>
|
||||||
|
struct ValuePolicy {
|
||||||
|
static const M& Get(const MatcherBase& m) {
|
||||||
|
// When inlined along with Init, need to be explicit to avoid violating
|
||||||
|
// strict aliasing rules.
|
||||||
|
const M* ptr =
|
||||||
|
static_cast<const M*>(static_cast<const void*>(&m.buffer_));
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
static void Init(MatcherBase& m, M impl) {
|
||||||
|
::new (static_cast<void*>(&m.buffer_)) M(impl);
|
||||||
|
}
|
||||||
|
static constexpr auto shared_destroy = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
struct ValuePolicy<M, false> {
|
||||||
|
using Shared = SharedPayload<M>;
|
||||||
|
static const M& Get(const MatcherBase& m) {
|
||||||
|
return static_cast<Shared*>(m.buffer_.shared)->value;
|
||||||
|
}
|
||||||
|
template <typename Arg>
|
||||||
|
static void Init(MatcherBase& m, Arg&& arg) {
|
||||||
|
m.buffer_.shared = new Shared(std::forward<Arg>(arg));
|
||||||
|
}
|
||||||
|
static constexpr auto shared_destroy = &Shared::Destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename U, bool B>
|
||||||
|
struct ValuePolicy<const MatcherInterface<U>*, B> {
|
||||||
|
using M = const MatcherInterface<U>;
|
||||||
|
using Shared = SharedPayload<std::unique_ptr<M>>;
|
||||||
|
static const M& Get(const MatcherBase& m) {
|
||||||
|
return *static_cast<Shared*>(m.buffer_.shared)->value;
|
||||||
|
}
|
||||||
|
static void Init(MatcherBase& m, M* impl) {
|
||||||
|
m.buffer_.shared = new Shared(std::unique_ptr<M>(impl));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto shared_destroy = &Shared::Destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
void Init(M&& m) {
|
||||||
|
using MM = typename std::decay<M>::type;
|
||||||
|
using Policy = ValuePolicy<MM>;
|
||||||
|
vtable_ = GetVTable<Policy>();
|
||||||
|
Policy::Init(*this, std::forward<M>(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
const VTable* vtable_;
|
||||||
|
Buffer buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
|
||||||
|
// object that can check whether a value of type T matches. The
|
||||||
|
// implementation of Matcher<T> is just a std::shared_ptr to const
|
||||||
|
// MatcherInterface<T>. Don't inherit from Matcher!
|
||||||
|
template <typename T>
|
||||||
|
class Matcher : public internal::MatcherBase<T> {
|
||||||
|
public:
|
||||||
|
// Constructs a null matcher. Needed for storing Matcher objects in STL
|
||||||
|
// containers. A default-constructed matcher is not yet initialized. You
|
||||||
|
// cannot use it until a valid value has been assigned to it.
|
||||||
|
explicit Matcher() {} // NOLINT
|
||||||
|
|
||||||
|
// Constructs a matcher from its implementation.
|
||||||
|
explicit Matcher(const MatcherInterface<const T&>* impl)
|
||||||
|
: internal::MatcherBase<T>(impl) {}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
explicit Matcher(
|
||||||
|
const MatcherInterface<U>* impl,
|
||||||
|
typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
|
||||||
|
nullptr)
|
||||||
|
: internal::MatcherBase<T>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {} // NOLINT
|
||||||
|
|
||||||
|
// Implicit constructor here allows people to write
|
||||||
|
// EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
|
||||||
|
Matcher(T value); // NOLINT
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following two specializations allow the user to write str
|
||||||
|
// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string
|
||||||
|
// matcher is expected.
|
||||||
|
template <>
|
||||||
|
class GTEST_API_ Matcher<const std::string&>
|
||||||
|
: public internal::MatcherBase<const std::string&> {
|
||||||
|
public:
|
||||||
|
Matcher() {}
|
||||||
|
|
||||||
|
explicit Matcher(const MatcherInterface<const std::string&>* impl)
|
||||||
|
: internal::MatcherBase<const std::string&>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<const std::string&>(std::forward<M>(m)) {}
|
||||||
|
|
||||||
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
|
// str is a std::string object.
|
||||||
|
Matcher(const std::string& s); // NOLINT
|
||||||
|
|
||||||
|
// Allows the user to write "foo" instead of Eq("foo") sometimes.
|
||||||
|
Matcher(const char* s); // NOLINT
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class GTEST_API_ Matcher<std::string>
|
||||||
|
: public internal::MatcherBase<std::string> {
|
||||||
|
public:
|
||||||
|
Matcher() {}
|
||||||
|
|
||||||
|
explicit Matcher(const MatcherInterface<const std::string&>* impl)
|
||||||
|
: internal::MatcherBase<std::string>(impl) {}
|
||||||
|
explicit Matcher(const MatcherInterface<std::string>* impl)
|
||||||
|
: internal::MatcherBase<std::string>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<std::string>(std::forward<M>(m)) {}
|
||||||
|
|
||||||
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
|
// str is a string object.
|
||||||
|
Matcher(const std::string& s); // NOLINT
|
||||||
|
|
||||||
|
// Allows the user to write "foo" instead of Eq("foo") sometimes.
|
||||||
|
Matcher(const char* s); // NOLINT
|
||||||
|
};
|
||||||
|
|
||||||
|
#if GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
// The following two specializations allow the user to write str
|
||||||
|
// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view
|
||||||
|
// matcher is expected.
|
||||||
|
template <>
|
||||||
|
class GTEST_API_ Matcher<const internal::StringView&>
|
||||||
|
: public internal::MatcherBase<const internal::StringView&> {
|
||||||
|
public:
|
||||||
|
Matcher() {}
|
||||||
|
|
||||||
|
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
|
||||||
|
: internal::MatcherBase<const internal::StringView&>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
|
// str is a std::string object.
|
||||||
|
Matcher(const std::string& s); // NOLINT
|
||||||
|
|
||||||
|
// Allows the user to write "foo" instead of Eq("foo") sometimes.
|
||||||
|
Matcher(const char* s); // NOLINT
|
||||||
|
|
||||||
|
// Allows the user to pass absl::string_views or std::string_views directly.
|
||||||
|
Matcher(internal::StringView s); // NOLINT
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class GTEST_API_ Matcher<internal::StringView>
|
||||||
|
: public internal::MatcherBase<internal::StringView> {
|
||||||
|
public:
|
||||||
|
Matcher() {}
|
||||||
|
|
||||||
|
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
|
||||||
|
: internal::MatcherBase<internal::StringView>(impl) {}
|
||||||
|
explicit Matcher(const MatcherInterface<internal::StringView>* impl)
|
||||||
|
: internal::MatcherBase<internal::StringView>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {}
|
||||||
|
|
||||||
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
|
// str is a std::string object.
|
||||||
|
Matcher(const std::string& s); // NOLINT
|
||||||
|
|
||||||
|
// Allows the user to write "foo" instead of Eq("foo") sometimes.
|
||||||
|
Matcher(const char* s); // NOLINT
|
||||||
|
|
||||||
|
// Allows the user to pass absl::string_views or std::string_views directly.
|
||||||
|
Matcher(internal::StringView s); // NOLINT
|
||||||
|
};
|
||||||
|
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
|
||||||
|
// Prints a matcher in a human-readable format.
|
||||||
|
template <typename T>
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Matcher<T>& matcher) {
|
||||||
|
matcher.DescribeTo(&os);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The PolymorphicMatcher class template makes it easy to implement a
|
||||||
|
// polymorphic matcher (i.e. a matcher that can match values of more
|
||||||
|
// than one type, e.g. Eq(n) and NotNull()).
|
||||||
|
//
|
||||||
|
// To define a polymorphic matcher, a user should provide an Impl
|
||||||
|
// class that has a DescribeTo() method and a DescribeNegationTo()
|
||||||
|
// method, and define a member function (or member function template)
|
||||||
|
//
|
||||||
|
// bool MatchAndExplain(const Value& value,
|
||||||
|
// MatchResultListener* listener) const;
|
||||||
|
//
|
||||||
|
// See the definition of NotNull() for a complete example.
|
||||||
|
template <class Impl>
|
||||||
|
class PolymorphicMatcher {
|
||||||
|
public:
|
||||||
|
explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {}
|
||||||
|
|
||||||
|
// Returns a mutable reference to the underlying matcher
|
||||||
|
// implementation object.
|
||||||
|
Impl& mutable_impl() { return impl_; }
|
||||||
|
|
||||||
|
// Returns an immutable reference to the underlying matcher
|
||||||
|
// implementation object.
|
||||||
|
const Impl& impl() const { return impl_; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
operator Matcher<T>() const {
|
||||||
|
return Matcher<T>(new MonomorphicImpl<const T&>(impl_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
class MonomorphicImpl : public MatcherInterface<T> {
|
||||||
|
public:
|
||||||
|
explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
|
||||||
|
|
||||||
|
void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); }
|
||||||
|
|
||||||
|
void DescribeNegationTo(::std::ostream* os) const override {
|
||||||
|
impl_.DescribeNegationTo(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MatchAndExplain(T x, MatchResultListener* listener) const override {
|
||||||
|
return impl_.MatchAndExplain(x, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Impl impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
Impl impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates a matcher from its implementation.
|
||||||
|
// DEPRECATED: Especially in the generic code, prefer:
|
||||||
|
// Matcher<T>(new MyMatcherImpl<const T&>(...));
|
||||||
|
//
|
||||||
|
// MakeMatcher may create a Matcher that accepts its argument by value, which
|
||||||
|
// leads to unnecessary copies & lack of support for non-copyable types.
|
||||||
|
template <typename T>
|
||||||
|
inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
|
||||||
|
return Matcher<T>(impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a polymorphic matcher from its implementation. This is
|
||||||
|
// easier to use than the PolymorphicMatcher<Impl> constructor as it
|
||||||
|
// doesn't require you to explicitly write the template argument, e.g.
|
||||||
|
//
|
||||||
|
// MakePolymorphicMatcher(foo);
|
||||||
|
// vs
|
||||||
|
// PolymorphicMatcher<TypeOfFoo>(foo);
|
||||||
|
template <class Impl>
|
||||||
|
inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
|
||||||
|
return PolymorphicMatcher<Impl>(impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
// Implements a matcher that compares a given value with a
|
||||||
|
// pre-supplied value using one of the ==, <=, <, etc, operators. The
|
||||||
|
// two values being compared don't have to have the same type.
|
||||||
|
//
|
||||||
|
// The matcher defined here is polymorphic (for example, Eq(5) can be
|
||||||
|
// used to match an int, a short, a double, etc). Therefore we use
|
||||||
|
// a template type conversion operator in the implementation.
|
||||||
|
//
|
||||||
|
// The following template definition assumes that the Rhs parameter is
|
||||||
|
// a "bare" type (i.e. neither 'const T' nor 'T&').
|
||||||
|
template <typename D, typename Rhs, typename Op>
|
||||||
|
class ComparisonBase {
|
||||||
|
public:
|
||||||
|
explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
|
||||||
|
|
||||||
|
using is_gtest_matcher = void;
|
||||||
|
|
||||||
|
template <typename Lhs>
|
||||||
|
bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
|
||||||
|
return Op()(lhs, Unwrap(rhs_));
|
||||||
|
}
|
||||||
|
void DescribeTo(std::ostream* os) const {
|
||||||
|
*os << D::Desc() << " ";
|
||||||
|
UniversalPrint(Unwrap(rhs_), os);
|
||||||
|
}
|
||||||
|
void DescribeNegationTo(std::ostream* os) const {
|
||||||
|
*os << D::NegatedDesc() << " ";
|
||||||
|
UniversalPrint(Unwrap(rhs_), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
static const T& Unwrap(const T& v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
static const T& Unwrap(std::reference_wrapper<T> v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rhs rhs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Rhs>
|
||||||
|
class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
|
||||||
|
public:
|
||||||
|
explicit EqMatcher(const Rhs& rhs)
|
||||||
|
: ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) {}
|
||||||
|
static const char* Desc() { return "is equal to"; }
|
||||||
|
static const char* NegatedDesc() { return "isn't equal to"; }
|
||||||
|
};
|
||||||
|
template <typename Rhs>
|
||||||
|
class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
|
||||||
|
public:
|
||||||
|
explicit NeMatcher(const Rhs& rhs)
|
||||||
|
: ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) {}
|
||||||
|
static const char* Desc() { return "isn't equal to"; }
|
||||||
|
static const char* NegatedDesc() { return "is equal to"; }
|
||||||
|
};
|
||||||
|
template <typename Rhs>
|
||||||
|
class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
|
||||||
|
public:
|
||||||
|
explicit LtMatcher(const Rhs& rhs)
|
||||||
|
: ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) {}
|
||||||
|
static const char* Desc() { return "is <"; }
|
||||||
|
static const char* NegatedDesc() { return "isn't <"; }
|
||||||
|
};
|
||||||
|
template <typename Rhs>
|
||||||
|
class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
|
||||||
|
public:
|
||||||
|
explicit GtMatcher(const Rhs& rhs)
|
||||||
|
: ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) {}
|
||||||
|
static const char* Desc() { return "is >"; }
|
||||||
|
static const char* NegatedDesc() { return "isn't >"; }
|
||||||
|
};
|
||||||
|
template <typename Rhs>
|
||||||
|
class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
|
||||||
|
public:
|
||||||
|
explicit LeMatcher(const Rhs& rhs)
|
||||||
|
: ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) {}
|
||||||
|
static const char* Desc() { return "is <="; }
|
||||||
|
static const char* NegatedDesc() { return "isn't <="; }
|
||||||
|
};
|
||||||
|
template <typename Rhs>
|
||||||
|
class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
|
||||||
|
public:
|
||||||
|
explicit GeMatcher(const Rhs& rhs)
|
||||||
|
: ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) {}
|
||||||
|
static const char* Desc() { return "is >="; }
|
||||||
|
static const char* NegatedDesc() { return "isn't >="; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if<
|
||||||
|
std::is_constructible<std::string, T>::value>::type>
|
||||||
|
using StringLike = T;
|
||||||
|
|
||||||
|
// Implements polymorphic matchers MatchesRegex(regex) and
|
||||||
|
// ContainsRegex(regex), which can be used as a Matcher<T> as long as
|
||||||
|
// T can be converted to a string.
|
||||||
|
class MatchesRegexMatcher {
|
||||||
|
public:
|
||||||
|
MatchesRegexMatcher(const RE* regex, bool full_match)
|
||||||
|
: regex_(regex), full_match_(full_match) {}
|
||||||
|
|
||||||
|
#if GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
bool MatchAndExplain(const internal::StringView& s,
|
||||||
|
MatchResultListener* listener) const {
|
||||||
|
return MatchAndExplain(std::string(s), listener);
|
||||||
|
}
|
||||||
|
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
|
||||||
|
// Accepts pointer types, particularly:
|
||||||
|
// const char*
|
||||||
|
// char*
|
||||||
|
// const wchar_t*
|
||||||
|
// wchar_t*
|
||||||
|
template <typename CharType>
|
||||||
|
bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
|
||||||
|
return s != nullptr && MatchAndExplain(std::string(s), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches anything that can convert to std::string.
|
||||||
|
//
|
||||||
|
// This is a template, not just a plain function with const std::string&,
|
||||||
|
// because absl::string_view has some interfering non-explicit constructors.
|
||||||
|
template <class MatcheeStringType>
|
||||||
|
bool MatchAndExplain(const MatcheeStringType& s,
|
||||||
|
MatchResultListener* /* listener */) const {
|
||||||
|
const std::string& s2(s);
|
||||||
|
return full_match_ ? RE::FullMatch(s2, *regex_)
|
||||||
|
: RE::PartialMatch(s2, *regex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DescribeTo(::std::ostream* os) const {
|
||||||
|
*os << (full_match_ ? "matches" : "contains") << " regular expression ";
|
||||||
|
UniversalPrinter<std::string>::Print(regex_->pattern(), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DescribeNegationTo(::std::ostream* os) const {
|
||||||
|
*os << "doesn't " << (full_match_ ? "match" : "contain")
|
||||||
|
<< " regular expression ";
|
||||||
|
UniversalPrinter<std::string>::Print(regex_->pattern(), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::shared_ptr<const RE> regex_;
|
||||||
|
const bool full_match_;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Matches a string that fully matches regular expression 'regex'.
|
||||||
|
// The matcher takes ownership of 'regex'.
|
||||||
|
inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
|
||||||
|
const internal::RE* regex) {
|
||||||
|
return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
|
||||||
|
}
|
||||||
|
template <typename T = std::string>
|
||||||
|
PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
|
||||||
|
const internal::StringLike<T>& regex) {
|
||||||
|
return MatchesRegex(new internal::RE(std::string(regex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches a string that contains regular expression 'regex'.
|
||||||
|
// The matcher takes ownership of 'regex'.
|
||||||
|
inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
|
||||||
|
const internal::RE* regex) {
|
||||||
|
return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
|
||||||
|
}
|
||||||
|
template <typename T = std::string>
|
||||||
|
PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
|
||||||
|
const internal::StringLike<T>& regex) {
|
||||||
|
return ContainsRegex(new internal::RE(std::string(regex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a polymorphic matcher that matches anything equal to x.
|
||||||
|
// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
|
||||||
|
// wouldn't compile.
|
||||||
|
template <typename T>
|
||||||
|
inline internal::EqMatcher<T> Eq(T x) {
|
||||||
|
return internal::EqMatcher<T>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a Matcher<T> from a 'value' of type T. The constructed
|
||||||
|
// matcher matches any value that's equal to 'value'.
|
||||||
|
template <typename T>
|
||||||
|
Matcher<T>::Matcher(T value) {
|
||||||
|
*this = Eq(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a monomorphic matcher that matches anything with type Lhs
|
||||||
|
// and equal to rhs. A user may need to use this instead of Eq(...)
|
||||||
|
// in order to resolve an overloading ambiguity.
|
||||||
|
//
|
||||||
|
// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
|
||||||
|
// or Matcher<T>(x), but more readable than the latter.
|
||||||
|
//
|
||||||
|
// We could define similar monomorphic matchers for other comparison
|
||||||
|
// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
|
||||||
|
// it yet as those are used much less than Eq() in practice. A user
|
||||||
|
// can always write Matcher<T>(Lt(5)) to be explicit about the type,
|
||||||
|
// for example.
|
||||||
|
template <typename Lhs, typename Rhs>
|
||||||
|
inline Matcher<Lhs> TypedEq(const Rhs& rhs) {
|
||||||
|
return Eq(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a polymorphic matcher that matches anything >= x.
|
||||||
|
template <typename Rhs>
|
||||||
|
inline internal::GeMatcher<Rhs> Ge(Rhs x) {
|
||||||
|
return internal::GeMatcher<Rhs>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a polymorphic matcher that matches anything > x.
|
||||||
|
template <typename Rhs>
|
||||||
|
inline internal::GtMatcher<Rhs> Gt(Rhs x) {
|
||||||
|
return internal::GtMatcher<Rhs>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a polymorphic matcher that matches anything <= x.
|
||||||
|
template <typename Rhs>
|
||||||
|
inline internal::LeMatcher<Rhs> Le(Rhs x) {
|
||||||
|
return internal::LeMatcher<Rhs>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a polymorphic matcher that matches anything < x.
|
||||||
|
template <typename Rhs>
|
||||||
|
inline internal::LtMatcher<Rhs> Lt(Rhs x) {
|
||||||
|
return internal::LtMatcher<Rhs>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a polymorphic matcher that matches anything != x.
|
||||||
|
template <typename Rhs>
|
||||||
|
inline internal::NeMatcher<Rhs> Ne(Rhs x) {
|
||||||
|
return internal::NeMatcher<Rhs>(x);
|
||||||
|
}
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
218
test/gtest/include/gtest/gtest-message.h
Normal file
218
test/gtest/include/gtest/gtest-message.h
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
// Copyright 2005, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This header file defines the Message class.
|
||||||
|
//
|
||||||
|
// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
|
||||||
|
// leave some internal implementation details in this header file.
|
||||||
|
// They are clearly marked by comments like this:
|
||||||
|
//
|
||||||
|
// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||||
|
//
|
||||||
|
// Such code is NOT meant to be used by a user directly, and is subject
|
||||||
|
// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
|
||||||
|
// program!
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
|
||||||
|
/* class A needs to have dll-interface to be used by clients of class B */)
|
||||||
|
|
||||||
|
// Ensures that there is at least one operator<< in the global namespace.
|
||||||
|
// See Message& operator<<(...) below for why.
|
||||||
|
void operator<<(const testing::internal::Secret&, int);
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// The Message class works like an ostream repeater.
|
||||||
|
//
|
||||||
|
// Typical usage:
|
||||||
|
//
|
||||||
|
// 1. You stream a bunch of values to a Message object.
|
||||||
|
// It will remember the text in a stringstream.
|
||||||
|
// 2. Then you stream the Message object to an ostream.
|
||||||
|
// This causes the text in the Message to be streamed
|
||||||
|
// to the ostream.
|
||||||
|
//
|
||||||
|
// For example;
|
||||||
|
//
|
||||||
|
// testing::Message foo;
|
||||||
|
// foo << 1 << " != " << 2;
|
||||||
|
// std::cout << foo;
|
||||||
|
//
|
||||||
|
// will print "1 != 2".
|
||||||
|
//
|
||||||
|
// Message is not intended to be inherited from. In particular, its
|
||||||
|
// destructor is not virtual.
|
||||||
|
//
|
||||||
|
// Note that stringstream behaves differently in gcc and in MSVC. You
|
||||||
|
// can stream a NULL char pointer to it in the former, but not in the
|
||||||
|
// latter (it causes an access violation if you do). The Message
|
||||||
|
// class hides this difference by treating a NULL char pointer as
|
||||||
|
// "(null)".
|
||||||
|
class GTEST_API_ Message {
|
||||||
|
private:
|
||||||
|
// The type of basic IO manipulators (endl, ends, and flush) for
|
||||||
|
// narrow streams.
|
||||||
|
typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructs an empty Message.
|
||||||
|
Message();
|
||||||
|
|
||||||
|
// Copy constructor.
|
||||||
|
Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT
|
||||||
|
*ss_ << msg.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a Message from a C-string.
|
||||||
|
explicit Message(const char* str) : ss_(new ::std::stringstream) {
|
||||||
|
*ss_ << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streams a non-pointer value to this object.
|
||||||
|
template <typename T>
|
||||||
|
inline Message& operator<<(const T& val) {
|
||||||
|
// Some libraries overload << for STL containers. These
|
||||||
|
// overloads are defined in the global namespace instead of ::std.
|
||||||
|
//
|
||||||
|
// C++'s symbol lookup rule (i.e. Koenig lookup) says that these
|
||||||
|
// overloads are visible in either the std namespace or the global
|
||||||
|
// namespace, but not other namespaces, including the testing
|
||||||
|
// namespace which Google Test's Message class is in.
|
||||||
|
//
|
||||||
|
// To allow STL containers (and other types that has a << operator
|
||||||
|
// defined in the global namespace) to be used in Google Test
|
||||||
|
// assertions, testing::Message must access the custom << operator
|
||||||
|
// from the global namespace. With this using declaration,
|
||||||
|
// overloads of << defined in the global namespace and those
|
||||||
|
// visible via Koenig lookup are both exposed in this function.
|
||||||
|
using ::operator<<;
|
||||||
|
*ss_ << val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streams a pointer value to this object.
|
||||||
|
//
|
||||||
|
// This function is an overload of the previous one. When you
|
||||||
|
// stream a pointer to a Message, this definition will be used as it
|
||||||
|
// is more specialized. (The C++ Standard, section
|
||||||
|
// [temp.func.order].) If you stream a non-pointer, then the
|
||||||
|
// previous definition will be used.
|
||||||
|
//
|
||||||
|
// The reason for this overload is that streaming a NULL pointer to
|
||||||
|
// ostream is undefined behavior. Depending on the compiler, you
|
||||||
|
// may get "0", "(nil)", "(null)", or an access violation. To
|
||||||
|
// ensure consistent result across compilers, we always treat NULL
|
||||||
|
// as "(null)".
|
||||||
|
template <typename T>
|
||||||
|
inline Message& operator<<(T* const& pointer) { // NOLINT
|
||||||
|
if (pointer == nullptr) {
|
||||||
|
*ss_ << "(null)";
|
||||||
|
} else {
|
||||||
|
*ss_ << pointer;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the basic IO manipulators are overloaded for both narrow
|
||||||
|
// and wide streams, we have to provide this specialized definition
|
||||||
|
// of operator <<, even though its body is the same as the
|
||||||
|
// templatized version above. Without this definition, streaming
|
||||||
|
// endl or other basic IO manipulators to Message will confuse the
|
||||||
|
// compiler.
|
||||||
|
Message& operator<<(BasicNarrowIoManip val) {
|
||||||
|
*ss_ << val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instead of 1/0, we want to see true/false for bool values.
|
||||||
|
Message& operator<<(bool b) { return *this << (b ? "true" : "false"); }
|
||||||
|
|
||||||
|
// These two overloads allow streaming a wide C string to a Message
|
||||||
|
// using the UTF-8 encoding.
|
||||||
|
Message& operator<<(const wchar_t* wide_c_str);
|
||||||
|
Message& operator<<(wchar_t* wide_c_str);
|
||||||
|
|
||||||
|
#if GTEST_HAS_STD_WSTRING
|
||||||
|
// Converts the given wide string to a narrow string using the UTF-8
|
||||||
|
// encoding, and streams the result to this Message object.
|
||||||
|
Message& operator<<(const ::std::wstring& wstr);
|
||||||
|
#endif // GTEST_HAS_STD_WSTRING
|
||||||
|
|
||||||
|
// Gets the text streamed to this object so far as an std::string.
|
||||||
|
// Each '\0' character in the buffer is replaced with "\\0".
|
||||||
|
//
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||||
|
std::string GetString() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// We'll hold the text streamed to this object here.
|
||||||
|
const std::unique_ptr< ::std::stringstream> ss_;
|
||||||
|
|
||||||
|
// We declare (but don't implement) this to prevent the compiler
|
||||||
|
// from implementing the assignment operator.
|
||||||
|
void operator=(const Message&);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Streams a Message to an ostream.
|
||||||
|
inline std::ostream& operator<<(std::ostream& os, const Message& sb) {
|
||||||
|
return os << sb.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Converts a streamable value to an std::string. A NULL pointer is
|
||||||
|
// converted to "(null)". When the input value is a ::string,
|
||||||
|
// ::std::string, ::wstring, or ::std::wstring object, each NUL
|
||||||
|
// character in it is replaced with "\\0".
|
||||||
|
template <typename T>
|
||||||
|
std::string StreamableToString(const T& streamable) {
|
||||||
|
return (Message() << streamable).GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
|
510
test/gtest/include/gtest/gtest-param-test.h
Normal file
510
test/gtest/include/gtest/gtest-param-test.h
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
// Copyright 2008, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Macros and functions for implementing parameterized tests
|
||||||
|
// in Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
|
||||||
|
|
||||||
|
// Value-parameterized tests allow you to test your code with different
|
||||||
|
// parameters without writing multiple copies of the same test.
|
||||||
|
//
|
||||||
|
// Here is how you use value-parameterized tests:
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// To write value-parameterized tests, first you should define a fixture
|
||||||
|
// class. It is usually derived from testing::TestWithParam<T> (see below for
|
||||||
|
// another inheritance scheme that's sometimes useful in more complicated
|
||||||
|
// class hierarchies), where the type of your parameter values.
|
||||||
|
// TestWithParam<T> is itself derived from testing::Test. T can be any
|
||||||
|
// copyable type. If it's a raw pointer, you are responsible for managing the
|
||||||
|
// lifespan of the pointed values.
|
||||||
|
|
||||||
|
class FooTest : public ::testing::TestWithParam<const char*> {
|
||||||
|
// You can implement all the usual class fixture members here.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Then, use the TEST_P macro to define as many parameterized tests
|
||||||
|
// for this fixture as you want. The _P suffix is for "parameterized"
|
||||||
|
// or "pattern", whichever you prefer to think.
|
||||||
|
|
||||||
|
TEST_P(FooTest, DoesBlah) {
|
||||||
|
// Inside a test, access the test parameter with the GetParam() method
|
||||||
|
// of the TestWithParam<T> class:
|
||||||
|
EXPECT_TRUE(foo.Blah(GetParam()));
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(FooTest, HasBlahBlah) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test
|
||||||
|
// case with any set of parameters you want. Google Test defines a number
|
||||||
|
// of functions for generating test parameters. They return what we call
|
||||||
|
// (surprise!) parameter generators. Here is a summary of them, which
|
||||||
|
// are all in the testing namespace:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Range(begin, end [, step]) - Yields values {begin, begin+step,
|
||||||
|
// begin+step+step, ...}. The values do not
|
||||||
|
// include end. step defaults to 1.
|
||||||
|
// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}.
|
||||||
|
// ValuesIn(container) - Yields values from a C-style array, an STL
|
||||||
|
// ValuesIn(begin,end) container, or an iterator range [begin, end).
|
||||||
|
// Bool() - Yields sequence {false, true}.
|
||||||
|
// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product
|
||||||
|
// for the math savvy) of the values generated
|
||||||
|
// by the N generators.
|
||||||
|
//
|
||||||
|
// For more details, see comments at the definitions of these functions below
|
||||||
|
// in this file.
|
||||||
|
//
|
||||||
|
// The following statement will instantiate tests from the FooTest test suite
|
||||||
|
// each with parameter values "meeny", "miny", and "moe".
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(InstantiationName,
|
||||||
|
FooTest,
|
||||||
|
Values("meeny", "miny", "moe"));
|
||||||
|
|
||||||
|
// To distinguish different instances of the pattern, (yes, you
|
||||||
|
// can instantiate it more than once) the first argument to the
|
||||||
|
// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the
|
||||||
|
// actual test suite name. Remember to pick unique prefixes for different
|
||||||
|
// instantiations. The tests from the instantiation above will have
|
||||||
|
// these names:
|
||||||
|
//
|
||||||
|
// * InstantiationName/FooTest.DoesBlah/0 for "meeny"
|
||||||
|
// * InstantiationName/FooTest.DoesBlah/1 for "miny"
|
||||||
|
// * InstantiationName/FooTest.DoesBlah/2 for "moe"
|
||||||
|
// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
|
||||||
|
// * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
|
||||||
|
// * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
|
||||||
|
//
|
||||||
|
// You can use these names in --gtest_filter.
|
||||||
|
//
|
||||||
|
// This statement will instantiate all tests from FooTest again, each
|
||||||
|
// with parameter values "cat" and "dog":
|
||||||
|
|
||||||
|
const char* pets[] = {"cat", "dog"};
|
||||||
|
INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
|
||||||
|
|
||||||
|
// The tests from the instantiation above will have these names:
|
||||||
|
//
|
||||||
|
// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
|
||||||
|
// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
|
||||||
|
// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
|
||||||
|
// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
|
||||||
|
//
|
||||||
|
// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests
|
||||||
|
// in the given test suite, whether their definitions come before or
|
||||||
|
// AFTER the INSTANTIATE_TEST_SUITE_P statement.
|
||||||
|
//
|
||||||
|
// Please also note that generator expressions (including parameters to the
|
||||||
|
// generators) are evaluated in InitGoogleTest(), after main() has started.
|
||||||
|
// This allows the user on one hand, to adjust generator parameters in order
|
||||||
|
// to dynamically determine a set of tests to run and on the other hand,
|
||||||
|
// give the user a chance to inspect the generated tests with Google Test
|
||||||
|
// reflection API before RUN_ALL_TESTS() is executed.
|
||||||
|
//
|
||||||
|
// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
|
||||||
|
// for more examples.
|
||||||
|
//
|
||||||
|
// In the future, we plan to publish the API for defining new parameter
|
||||||
|
// generators. But for now this interface remains part of the internal
|
||||||
|
// implementation and is subject to change.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// A parameterized test fixture must be derived from testing::Test and from
|
||||||
|
// testing::WithParamInterface<T>, where T is the type of the parameter
|
||||||
|
// values. Inheriting from TestWithParam<T> satisfies that requirement because
|
||||||
|
// TestWithParam<T> inherits from both Test and WithParamInterface. In more
|
||||||
|
// complicated hierarchies, however, it is occasionally useful to inherit
|
||||||
|
// separately from Test and WithParamInterface. For example:
|
||||||
|
|
||||||
|
class BaseTest : public ::testing::Test {
|
||||||
|
// You can inherit all the usual members for a non-parameterized test
|
||||||
|
// fixture here.
|
||||||
|
};
|
||||||
|
|
||||||
|
class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
|
||||||
|
// The usual test fixture members go here too.
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(BaseTest, HasFoo) {
|
||||||
|
// This is an ordinary non-parameterized test.
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(DerivedTest, DoesBlah) {
|
||||||
|
// GetParam works just the same here as if you inherit from TestWithParam.
|
||||||
|
EXPECT_TRUE(foo.Blah(GetParam()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
#include "gtest/internal/gtest-param-util.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// Functions producing parameter generators.
|
||||||
|
//
|
||||||
|
// Google Test uses these generators to produce parameters for value-
|
||||||
|
// parameterized tests. When a parameterized test suite is instantiated
|
||||||
|
// with a particular generator, Google Test creates and runs tests
|
||||||
|
// for each element in the sequence produced by the generator.
|
||||||
|
//
|
||||||
|
// In the following sample, tests from test suite FooTest are instantiated
|
||||||
|
// each three times with parameter values 3, 5, and 8:
|
||||||
|
//
|
||||||
|
// class FooTest : public TestWithParam<int> { ... };
|
||||||
|
//
|
||||||
|
// TEST_P(FooTest, TestThis) {
|
||||||
|
// }
|
||||||
|
// TEST_P(FooTest, TestThat) {
|
||||||
|
// }
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8));
|
||||||
|
//
|
||||||
|
|
||||||
|
// Range() returns generators providing sequences of values in a range.
|
||||||
|
//
|
||||||
|
// Synopsis:
|
||||||
|
// Range(start, end)
|
||||||
|
// - returns a generator producing a sequence of values {start, start+1,
|
||||||
|
// start+2, ..., }.
|
||||||
|
// Range(start, end, step)
|
||||||
|
// - returns a generator producing a sequence of values {start, start+step,
|
||||||
|
// start+step+step, ..., }.
|
||||||
|
// Notes:
|
||||||
|
// * The generated sequences never include end. For example, Range(1, 5)
|
||||||
|
// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
|
||||||
|
// returns a generator producing {1, 3, 5, 7}.
|
||||||
|
// * start and end must have the same type. That type may be any integral or
|
||||||
|
// floating-point type or a user defined type satisfying these conditions:
|
||||||
|
// * It must be assignable (have operator=() defined).
|
||||||
|
// * It must have operator+() (operator+(int-compatible type) for
|
||||||
|
// two-operand version).
|
||||||
|
// * It must have operator<() defined.
|
||||||
|
// Elements in the resulting sequences will also have that type.
|
||||||
|
// * Condition start < end must be satisfied in order for resulting sequences
|
||||||
|
// to contain any elements.
|
||||||
|
//
|
||||||
|
template <typename T, typename IncrementT>
|
||||||
|
internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
|
||||||
|
return internal::ParamGenerator<T>(
|
||||||
|
new internal::RangeGenerator<T, IncrementT>(start, end, step));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
internal::ParamGenerator<T> Range(T start, T end) {
|
||||||
|
return Range(start, end, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValuesIn() function allows generation of tests with parameters coming from
|
||||||
|
// a container.
|
||||||
|
//
|
||||||
|
// Synopsis:
|
||||||
|
// ValuesIn(const T (&array)[N])
|
||||||
|
// - returns a generator producing sequences with elements from
|
||||||
|
// a C-style array.
|
||||||
|
// ValuesIn(const Container& container)
|
||||||
|
// - returns a generator producing sequences with elements from
|
||||||
|
// an STL-style container.
|
||||||
|
// ValuesIn(Iterator begin, Iterator end)
|
||||||
|
// - returns a generator producing sequences with elements from
|
||||||
|
// a range [begin, end) defined by a pair of STL-style iterators. These
|
||||||
|
// iterators can also be plain C pointers.
|
||||||
|
//
|
||||||
|
// Please note that ValuesIn copies the values from the containers
|
||||||
|
// passed in and keeps them to generate tests in RUN_ALL_TESTS().
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// This instantiates tests from test suite StringTest
|
||||||
|
// each with C-string values of "foo", "bar", and "baz":
|
||||||
|
//
|
||||||
|
// const char* strings[] = {"foo", "bar", "baz"};
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings));
|
||||||
|
//
|
||||||
|
// This instantiates tests from test suite StlStringTest
|
||||||
|
// each with STL strings with values "a" and "b":
|
||||||
|
//
|
||||||
|
// ::std::vector< ::std::string> GetParameterStrings() {
|
||||||
|
// ::std::vector< ::std::string> v;
|
||||||
|
// v.push_back("a");
|
||||||
|
// v.push_back("b");
|
||||||
|
// return v;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(CharSequence,
|
||||||
|
// StlStringTest,
|
||||||
|
// ValuesIn(GetParameterStrings()));
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This will also instantiate tests from CharTest
|
||||||
|
// each with parameter values 'a' and 'b':
|
||||||
|
//
|
||||||
|
// ::std::list<char> GetParameterChars() {
|
||||||
|
// ::std::list<char> list;
|
||||||
|
// list.push_back('a');
|
||||||
|
// list.push_back('b');
|
||||||
|
// return list;
|
||||||
|
// }
|
||||||
|
// ::std::list<char> l = GetParameterChars();
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(CharSequence2,
|
||||||
|
// CharTest,
|
||||||
|
// ValuesIn(l.begin(), l.end()));
|
||||||
|
//
|
||||||
|
template <typename ForwardIterator>
|
||||||
|
internal::ParamGenerator<
|
||||||
|
typename std::iterator_traits<ForwardIterator>::value_type>
|
||||||
|
ValuesIn(ForwardIterator begin, ForwardIterator end) {
|
||||||
|
typedef typename std::iterator_traits<ForwardIterator>::value_type ParamType;
|
||||||
|
return internal::ParamGenerator<ParamType>(
|
||||||
|
new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
|
||||||
|
return ValuesIn(array, array + N);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Container>
|
||||||
|
internal::ParamGenerator<typename Container::value_type> ValuesIn(
|
||||||
|
const Container& container) {
|
||||||
|
return ValuesIn(container.begin(), container.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values() allows generating tests from explicitly specified list of
|
||||||
|
// parameters.
|
||||||
|
//
|
||||||
|
// Synopsis:
|
||||||
|
// Values(T v1, T v2, ..., T vN)
|
||||||
|
// - returns a generator producing sequences with elements v1, v2, ..., vN.
|
||||||
|
//
|
||||||
|
// For example, this instantiates tests from test suite BarTest each
|
||||||
|
// with values "one", "two", and "three":
|
||||||
|
//
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(NumSequence,
|
||||||
|
// BarTest,
|
||||||
|
// Values("one", "two", "three"));
|
||||||
|
//
|
||||||
|
// This instantiates tests from test suite BazTest each with values 1, 2, 3.5.
|
||||||
|
// The exact type of values will depend on the type of parameter in BazTest.
|
||||||
|
//
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
|
||||||
|
//
|
||||||
|
//
|
||||||
|
template <typename... T>
|
||||||
|
internal::ValueArray<T...> Values(T... v) {
|
||||||
|
return internal::ValueArray<T...>(std::move(v)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool() allows generating tests with parameters in a set of (false, true).
|
||||||
|
//
|
||||||
|
// Synopsis:
|
||||||
|
// Bool()
|
||||||
|
// - returns a generator producing sequences with elements {false, true}.
|
||||||
|
//
|
||||||
|
// It is useful when testing code that depends on Boolean flags. Combinations
|
||||||
|
// of multiple flags can be tested when several Bool()'s are combined using
|
||||||
|
// Combine() function.
|
||||||
|
//
|
||||||
|
// In the following example all tests in the test suite FlagDependentTest
|
||||||
|
// will be instantiated twice with parameters false and true.
|
||||||
|
//
|
||||||
|
// class FlagDependentTest : public testing::TestWithParam<bool> {
|
||||||
|
// virtual void SetUp() {
|
||||||
|
// external_flag = GetParam();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool());
|
||||||
|
//
|
||||||
|
inline internal::ParamGenerator<bool> Bool() { return Values(false, true); }
|
||||||
|
|
||||||
|
// Combine() allows the user to combine two or more sequences to produce
|
||||||
|
// values of a Cartesian product of those sequences' elements.
|
||||||
|
//
|
||||||
|
// Synopsis:
|
||||||
|
// Combine(gen1, gen2, ..., genN)
|
||||||
|
// - returns a generator producing sequences with elements coming from
|
||||||
|
// the Cartesian product of elements from the sequences generated by
|
||||||
|
// gen1, gen2, ..., genN. The sequence elements will have a type of
|
||||||
|
// std::tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
|
||||||
|
// of elements from sequences produces by gen1, gen2, ..., genN.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// This will instantiate tests in test suite AnimalTest each one with
|
||||||
|
// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
|
||||||
|
// tuple("dog", BLACK), and tuple("dog", WHITE):
|
||||||
|
//
|
||||||
|
// enum Color { BLACK, GRAY, WHITE };
|
||||||
|
// class AnimalTest
|
||||||
|
// : public testing::TestWithParam<std::tuple<const char*, Color> > {...};
|
||||||
|
//
|
||||||
|
// TEST_P(AnimalTest, AnimalLooksNice) {...}
|
||||||
|
//
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest,
|
||||||
|
// Combine(Values("cat", "dog"),
|
||||||
|
// Values(BLACK, WHITE)));
|
||||||
|
//
|
||||||
|
// This will instantiate tests in FlagDependentTest with all variations of two
|
||||||
|
// Boolean flags:
|
||||||
|
//
|
||||||
|
// class FlagDependentTest
|
||||||
|
// : public testing::TestWithParam<std::tuple<bool, bool> > {
|
||||||
|
// virtual void SetUp() {
|
||||||
|
// // Assigns external_flag_1 and external_flag_2 values from the tuple.
|
||||||
|
// std::tie(external_flag_1, external_flag_2) = GetParam();
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// TEST_P(FlagDependentTest, TestFeature1) {
|
||||||
|
// // Test your code using external_flag_1 and external_flag_2 here.
|
||||||
|
// }
|
||||||
|
// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest,
|
||||||
|
// Combine(Bool(), Bool()));
|
||||||
|
//
|
||||||
|
template <typename... Generator>
|
||||||
|
internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) {
|
||||||
|
return internal::CartesianProductHolder<Generator...>(g...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TEST_P(test_suite_name, test_name) \
|
||||||
|
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
|
||||||
|
: public test_suite_name { \
|
||||||
|
public: \
|
||||||
|
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \
|
||||||
|
void TestBody() override; \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
static int AddToRegistry() { \
|
||||||
|
::testing::UnitTest::GetInstance() \
|
||||||
|
->parameterized_test_registry() \
|
||||||
|
.GetTestSuitePatternHolder<test_suite_name>( \
|
||||||
|
GTEST_STRINGIFY_(test_suite_name), \
|
||||||
|
::testing::internal::CodeLocation(__FILE__, __LINE__)) \
|
||||||
|
->AddTestPattern( \
|
||||||
|
GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \
|
||||||
|
new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \
|
||||||
|
test_suite_name, test_name)>(), \
|
||||||
|
::testing::internal::CodeLocation(__FILE__, __LINE__)); \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
|
||||||
|
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
|
||||||
|
(const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \
|
||||||
|
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \
|
||||||
|
const GTEST_TEST_CLASS_NAME_(test_suite_name, \
|
||||||
|
test_name) &) = delete; /* NOLINT */ \
|
||||||
|
}; \
|
||||||
|
int GTEST_TEST_CLASS_NAME_(test_suite_name, \
|
||||||
|
test_name)::gtest_registering_dummy_ = \
|
||||||
|
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \
|
||||||
|
void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
|
||||||
|
|
||||||
|
// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify
|
||||||
|
// generator and an optional function or functor that generates custom test name
|
||||||
|
// suffixes based on the test parameters. Such a function or functor should
|
||||||
|
// accept one argument of type testing::TestParamInfo<class ParamType>, and
|
||||||
|
// return std::string.
|
||||||
|
//
|
||||||
|
// testing::PrintToStringParamName is a builtin test suffix generator that
|
||||||
|
// returns the value of testing::PrintToString(GetParam()).
|
||||||
|
//
|
||||||
|
// Note: test names must be non-empty, unique, and may only contain ASCII
|
||||||
|
// alphanumeric characters or underscore. Because PrintToString adds quotes
|
||||||
|
// to std::string and C strings, it won't work for these types.
|
||||||
|
|
||||||
|
#define GTEST_EXPAND_(arg) arg
|
||||||
|
#define GTEST_GET_FIRST_(first, ...) first
|
||||||
|
#define GTEST_GET_SECOND_(first, second, ...) second
|
||||||
|
|
||||||
|
#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \
|
||||||
|
static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \
|
||||||
|
gtest_##prefix##test_suite_name##_EvalGenerator_() { \
|
||||||
|
return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \
|
||||||
|
} \
|
||||||
|
static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \
|
||||||
|
const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \
|
||||||
|
if (::testing::internal::AlwaysFalse()) { \
|
||||||
|
::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \
|
||||||
|
__VA_ARGS__, \
|
||||||
|
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
|
||||||
|
DUMMY_PARAM_))); \
|
||||||
|
auto t = std::make_tuple(__VA_ARGS__); \
|
||||||
|
static_assert(std::tuple_size<decltype(t)>::value <= 2, \
|
||||||
|
"Too Many Args!"); \
|
||||||
|
} \
|
||||||
|
return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \
|
||||||
|
__VA_ARGS__, \
|
||||||
|
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
|
||||||
|
DUMMY_PARAM_))))(info); \
|
||||||
|
} \
|
||||||
|
static int gtest_##prefix##test_suite_name##_dummy_ \
|
||||||
|
GTEST_ATTRIBUTE_UNUSED_ = \
|
||||||
|
::testing::UnitTest::GetInstance() \
|
||||||
|
->parameterized_test_registry() \
|
||||||
|
.GetTestSuitePatternHolder<test_suite_name>( \
|
||||||
|
GTEST_STRINGIFY_(test_suite_name), \
|
||||||
|
::testing::internal::CodeLocation(__FILE__, __LINE__)) \
|
||||||
|
->AddTestSuiteInstantiation( \
|
||||||
|
GTEST_STRINGIFY_(prefix), \
|
||||||
|
>est_##prefix##test_suite_name##_EvalGenerator_, \
|
||||||
|
>est_##prefix##test_suite_name##_EvalGenerateName_, \
|
||||||
|
__FILE__, __LINE__)
|
||||||
|
|
||||||
|
// Allow Marking a Parameterized test class as not needing to be instantiated.
|
||||||
|
#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \
|
||||||
|
namespace gtest_do_not_use_outside_namespace_scope {} \
|
||||||
|
static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \
|
||||||
|
GTEST_STRINGIFY_(T))
|
||||||
|
|
||||||
|
// Legacy API is deprecated but still available
|
||||||
|
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
#define INSTANTIATE_TEST_CASE_P \
|
||||||
|
static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \
|
||||||
|
""); \
|
||||||
|
INSTANTIATE_TEST_SUITE_P
|
||||||
|
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
|
1048
test/gtest/include/gtest/gtest-printers.h
Normal file
1048
test/gtest/include/gtest/gtest-printers.h
Normal file
File diff suppressed because it is too large
Load Diff
248
test/gtest/include/gtest/gtest-spi.h
Normal file
248
test/gtest/include/gtest/gtest-spi.h
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
// Copyright 2007, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Utilities for testing Google Test itself and code that uses Google Test
|
||||||
|
// (e.g. frameworks built on top of Google Test).
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
|
||||||
|
/* class A needs to have dll-interface to be used by clients of class B */)
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// This helper class can be used to mock out Google Test failure reporting
|
||||||
|
// so that we can test Google Test or code that builds on Google Test.
|
||||||
|
//
|
||||||
|
// An object of this class appends a TestPartResult object to the
|
||||||
|
// TestPartResultArray object given in the constructor whenever a Google Test
|
||||||
|
// failure is reported. It can either intercept only failures that are
|
||||||
|
// generated in the same thread that created this object or it can intercept
|
||||||
|
// all generated failures. The scope of this mock object can be controlled with
|
||||||
|
// the second argument to the two arguments constructor.
|
||||||
|
class GTEST_API_ ScopedFakeTestPartResultReporter
|
||||||
|
: public TestPartResultReporterInterface {
|
||||||
|
public:
|
||||||
|
// The two possible mocking modes of this object.
|
||||||
|
enum InterceptMode {
|
||||||
|
INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures.
|
||||||
|
INTERCEPT_ALL_THREADS // Intercepts all failures.
|
||||||
|
};
|
||||||
|
|
||||||
|
// The c'tor sets this object as the test part result reporter used
|
||||||
|
// by Google Test. The 'result' parameter specifies where to report the
|
||||||
|
// results. This reporter will only catch failures generated in the current
|
||||||
|
// thread. DEPRECATED
|
||||||
|
explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
|
||||||
|
|
||||||
|
// Same as above, but you can choose the interception scope of this object.
|
||||||
|
ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
|
||||||
|
TestPartResultArray* result);
|
||||||
|
|
||||||
|
// The d'tor restores the previous test part result reporter.
|
||||||
|
~ScopedFakeTestPartResultReporter() override;
|
||||||
|
|
||||||
|
// Appends the TestPartResult object to the TestPartResultArray
|
||||||
|
// received in the constructor.
|
||||||
|
//
|
||||||
|
// This method is from the TestPartResultReporterInterface
|
||||||
|
// interface.
|
||||||
|
void ReportTestPartResult(const TestPartResult& result) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
const InterceptMode intercept_mode_;
|
||||||
|
TestPartResultReporterInterface* old_reporter_;
|
||||||
|
TestPartResultArray* const result_;
|
||||||
|
|
||||||
|
ScopedFakeTestPartResultReporter(const ScopedFakeTestPartResultReporter&) =
|
||||||
|
delete;
|
||||||
|
ScopedFakeTestPartResultReporter& operator=(
|
||||||
|
const ScopedFakeTestPartResultReporter&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// A helper class for implementing EXPECT_FATAL_FAILURE() and
|
||||||
|
// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given
|
||||||
|
// TestPartResultArray contains exactly one failure that has the given
|
||||||
|
// type and contains the given substring. If that's not the case, a
|
||||||
|
// non-fatal failure will be generated.
|
||||||
|
class GTEST_API_ SingleFailureChecker {
|
||||||
|
public:
|
||||||
|
// The constructor remembers the arguments.
|
||||||
|
SingleFailureChecker(const TestPartResultArray* results,
|
||||||
|
TestPartResult::Type type, const std::string& substr);
|
||||||
|
~SingleFailureChecker();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const TestPartResultArray* const results_;
|
||||||
|
const TestPartResult::Type type_;
|
||||||
|
const std::string substr_;
|
||||||
|
|
||||||
|
SingleFailureChecker(const SingleFailureChecker&) = delete;
|
||||||
|
SingleFailureChecker& operator=(const SingleFailureChecker&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||||
|
|
||||||
|
// A set of macros for testing Google Test assertions or code that's expected
|
||||||
|
// to generate Google Test fatal failures (e.g. a failure from an ASSERT_EQ, but
|
||||||
|
// not a non-fatal failure, as from EXPECT_EQ). It verifies that the given
|
||||||
|
// statement will cause exactly one fatal Google Test failure with 'substr'
|
||||||
|
// being part of the failure message.
|
||||||
|
//
|
||||||
|
// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
|
||||||
|
// affects and considers failures generated in the current thread and
|
||||||
|
// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
|
||||||
|
//
|
||||||
|
// The verification of the assertion is done correctly even when the statement
|
||||||
|
// throws an exception or aborts the current function.
|
||||||
|
//
|
||||||
|
// Known restrictions:
|
||||||
|
// - 'statement' cannot reference local non-static variables or
|
||||||
|
// non-static members of the current object.
|
||||||
|
// - 'statement' cannot return a value.
|
||||||
|
// - You cannot stream a failure message to this macro.
|
||||||
|
//
|
||||||
|
// Note that even though the implementations of the following two
|
||||||
|
// macros are much alike, we cannot refactor them to use a common
|
||||||
|
// helper macro, due to some peculiarity in how the preprocessor
|
||||||
|
// works. The AcceptsMacroThatExpandsToUnprotectedComma test in
|
||||||
|
// gtest_unittest.cc will fail to compile if we do that.
|
||||||
|
#define EXPECT_FATAL_FAILURE(statement, substr) \
|
||||||
|
do { \
|
||||||
|
class GTestExpectFatalFailureHelper { \
|
||||||
|
public: \
|
||||||
|
static void Execute() { statement; } \
|
||||||
|
}; \
|
||||||
|
::testing::TestPartResultArray gtest_failures; \
|
||||||
|
::testing::internal::SingleFailureChecker gtest_checker( \
|
||||||
|
>est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \
|
||||||
|
{ \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter:: \
|
||||||
|
INTERCEPT_ONLY_CURRENT_THREAD, \
|
||||||
|
>est_failures); \
|
||||||
|
GTestExpectFatalFailureHelper::Execute(); \
|
||||||
|
} \
|
||||||
|
} while (::testing::internal::AlwaysFalse())
|
||||||
|
|
||||||
|
#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
|
||||||
|
do { \
|
||||||
|
class GTestExpectFatalFailureHelper { \
|
||||||
|
public: \
|
||||||
|
static void Execute() { statement; } \
|
||||||
|
}; \
|
||||||
|
::testing::TestPartResultArray gtest_failures; \
|
||||||
|
::testing::internal::SingleFailureChecker gtest_checker( \
|
||||||
|
>est_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \
|
||||||
|
{ \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
|
||||||
|
>est_failures); \
|
||||||
|
GTestExpectFatalFailureHelper::Execute(); \
|
||||||
|
} \
|
||||||
|
} while (::testing::internal::AlwaysFalse())
|
||||||
|
|
||||||
|
// A macro for testing Google Test assertions or code that's expected to
|
||||||
|
// generate Google Test non-fatal failures (e.g. a failure from an EXPECT_EQ,
|
||||||
|
// but not from an ASSERT_EQ). It asserts that the given statement will cause
|
||||||
|
// exactly one non-fatal Google Test failure with 'substr' being part of the
|
||||||
|
// failure message.
|
||||||
|
//
|
||||||
|
// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
|
||||||
|
// affects and considers failures generated in the current thread and
|
||||||
|
// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
|
||||||
|
//
|
||||||
|
// 'statement' is allowed to reference local variables and members of
|
||||||
|
// the current object.
|
||||||
|
//
|
||||||
|
// The verification of the assertion is done correctly even when the statement
|
||||||
|
// throws an exception or aborts the current function.
|
||||||
|
//
|
||||||
|
// Known restrictions:
|
||||||
|
// - You cannot stream a failure message to this macro.
|
||||||
|
//
|
||||||
|
// Note that even though the implementations of the following two
|
||||||
|
// macros are much alike, we cannot refactor them to use a common
|
||||||
|
// helper macro, due to some peculiarity in how the preprocessor
|
||||||
|
// works. If we do that, the code won't compile when the user gives
|
||||||
|
// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
|
||||||
|
// expands to code containing an unprotected comma. The
|
||||||
|
// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
|
||||||
|
// catches that.
|
||||||
|
//
|
||||||
|
// For the same reason, we have to write
|
||||||
|
// if (::testing::internal::AlwaysTrue()) { statement; }
|
||||||
|
// instead of
|
||||||
|
// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
|
||||||
|
// to avoid an MSVC warning on unreachable code.
|
||||||
|
#define EXPECT_NONFATAL_FAILURE(statement, substr) \
|
||||||
|
do { \
|
||||||
|
::testing::TestPartResultArray gtest_failures; \
|
||||||
|
::testing::internal::SingleFailureChecker gtest_checker( \
|
||||||
|
>est_failures, ::testing::TestPartResult::kNonFatalFailure, \
|
||||||
|
(substr)); \
|
||||||
|
{ \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter:: \
|
||||||
|
INTERCEPT_ONLY_CURRENT_THREAD, \
|
||||||
|
>est_failures); \
|
||||||
|
if (::testing::internal::AlwaysTrue()) { \
|
||||||
|
statement; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (::testing::internal::AlwaysFalse())
|
||||||
|
|
||||||
|
#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
|
||||||
|
do { \
|
||||||
|
::testing::TestPartResultArray gtest_failures; \
|
||||||
|
::testing::internal::SingleFailureChecker gtest_checker( \
|
||||||
|
>est_failures, ::testing::TestPartResult::kNonFatalFailure, \
|
||||||
|
(substr)); \
|
||||||
|
{ \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
|
||||||
|
::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
|
||||||
|
>est_failures); \
|
||||||
|
if (::testing::internal::AlwaysTrue()) { \
|
||||||
|
statement; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (::testing::internal::AlwaysFalse())
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
|
190
test/gtest/include/gtest/gtest-test-part.h
Normal file
190
test/gtest/include/gtest/gtest-test-part.h
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// Copyright 2008, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
#include "gtest/internal/gtest-string.h"
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
|
||||||
|
/* class A needs to have dll-interface to be used by clients of class B */)
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// A copyable object representing the result of a test part (i.e. an
|
||||||
|
// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
|
||||||
|
//
|
||||||
|
// Don't inherit from TestPartResult as its destructor is not virtual.
|
||||||
|
class GTEST_API_ TestPartResult {
|
||||||
|
public:
|
||||||
|
// The possible outcomes of a test part (i.e. an assertion or an
|
||||||
|
// explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
|
||||||
|
enum Type {
|
||||||
|
kSuccess, // Succeeded.
|
||||||
|
kNonFatalFailure, // Failed but the test can continue.
|
||||||
|
kFatalFailure, // Failed and the test should be terminated.
|
||||||
|
kSkip // Skipped.
|
||||||
|
};
|
||||||
|
|
||||||
|
// C'tor. TestPartResult does NOT have a default constructor.
|
||||||
|
// Always use this constructor (with parameters) to create a
|
||||||
|
// TestPartResult object.
|
||||||
|
TestPartResult(Type a_type, const char* a_file_name, int a_line_number,
|
||||||
|
const char* a_message)
|
||||||
|
: type_(a_type),
|
||||||
|
file_name_(a_file_name == nullptr ? "" : a_file_name),
|
||||||
|
line_number_(a_line_number),
|
||||||
|
summary_(ExtractSummary(a_message)),
|
||||||
|
message_(a_message) {}
|
||||||
|
|
||||||
|
// Gets the outcome of the test part.
|
||||||
|
Type type() const { return type_; }
|
||||||
|
|
||||||
|
// Gets the name of the source file where the test part took place, or
|
||||||
|
// NULL if it's unknown.
|
||||||
|
const char* file_name() const {
|
||||||
|
return file_name_.empty() ? nullptr : file_name_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the line in the source file where the test part took place,
|
||||||
|
// or -1 if it's unknown.
|
||||||
|
int line_number() const { return line_number_; }
|
||||||
|
|
||||||
|
// Gets the summary of the failure message.
|
||||||
|
const char* summary() const { return summary_.c_str(); }
|
||||||
|
|
||||||
|
// Gets the message associated with the test part.
|
||||||
|
const char* message() const { return message_.c_str(); }
|
||||||
|
|
||||||
|
// Returns true if and only if the test part was skipped.
|
||||||
|
bool skipped() const { return type_ == kSkip; }
|
||||||
|
|
||||||
|
// Returns true if and only if the test part passed.
|
||||||
|
bool passed() const { return type_ == kSuccess; }
|
||||||
|
|
||||||
|
// Returns true if and only if the test part non-fatally failed.
|
||||||
|
bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
|
||||||
|
|
||||||
|
// Returns true if and only if the test part fatally failed.
|
||||||
|
bool fatally_failed() const { return type_ == kFatalFailure; }
|
||||||
|
|
||||||
|
// Returns true if and only if the test part failed.
|
||||||
|
bool failed() const { return fatally_failed() || nonfatally_failed(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type type_;
|
||||||
|
|
||||||
|
// Gets the summary of the failure message by omitting the stack
|
||||||
|
// trace in it.
|
||||||
|
static std::string ExtractSummary(const char* message);
|
||||||
|
|
||||||
|
// The name of the source file where the test part took place, or
|
||||||
|
// "" if the source file is unknown.
|
||||||
|
std::string file_name_;
|
||||||
|
// The line in the source file where the test part took place, or -1
|
||||||
|
// if the line number is unknown.
|
||||||
|
int line_number_;
|
||||||
|
std::string summary_; // The test failure summary.
|
||||||
|
std::string message_; // The test failure message.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prints a TestPartResult object.
|
||||||
|
std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
|
||||||
|
|
||||||
|
// An array of TestPartResult objects.
|
||||||
|
//
|
||||||
|
// Don't inherit from TestPartResultArray as its destructor is not
|
||||||
|
// virtual.
|
||||||
|
class GTEST_API_ TestPartResultArray {
|
||||||
|
public:
|
||||||
|
TestPartResultArray() {}
|
||||||
|
|
||||||
|
// Appends the given TestPartResult to the array.
|
||||||
|
void Append(const TestPartResult& result);
|
||||||
|
|
||||||
|
// Returns the TestPartResult at the given index (0-based).
|
||||||
|
const TestPartResult& GetTestPartResult(int index) const;
|
||||||
|
|
||||||
|
// Returns the number of TestPartResult objects in the array.
|
||||||
|
int size() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<TestPartResult> array_;
|
||||||
|
|
||||||
|
TestPartResultArray(const TestPartResultArray&) = delete;
|
||||||
|
TestPartResultArray& operator=(const TestPartResultArray&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This interface knows how to report a test part result.
|
||||||
|
class GTEST_API_ TestPartResultReporterInterface {
|
||||||
|
public:
|
||||||
|
virtual ~TestPartResultReporterInterface() {}
|
||||||
|
|
||||||
|
virtual void ReportTestPartResult(const TestPartResult& result) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
|
||||||
|
// statement generates new fatal failures. To do so it registers itself as the
|
||||||
|
// current test part result reporter. Besides checking if fatal failures were
|
||||||
|
// reported, it only delegates the reporting to the former result reporter.
|
||||||
|
// The original result reporter is restored in the destructor.
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
|
||||||
|
class GTEST_API_ HasNewFatalFailureHelper
|
||||||
|
: public TestPartResultReporterInterface {
|
||||||
|
public:
|
||||||
|
HasNewFatalFailureHelper();
|
||||||
|
~HasNewFatalFailureHelper() override;
|
||||||
|
void ReportTestPartResult(const TestPartResult& result) override;
|
||||||
|
bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool has_new_fatal_failure_;
|
||||||
|
TestPartResultReporterInterface* original_reporter_;
|
||||||
|
|
||||||
|
HasNewFatalFailureHelper(const HasNewFatalFailureHelper&) = delete;
|
||||||
|
HasNewFatalFailureHelper& operator=(const HasNewFatalFailureHelper&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
|
331
test/gtest/include/gtest/gtest-typed-test.h
Normal file
331
test/gtest/include/gtest/gtest-typed-test.h
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
// Copyright 2008 Google Inc.
|
||||||
|
// All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
|
||||||
|
|
||||||
|
// This header implements typed tests and type-parameterized tests.
|
||||||
|
|
||||||
|
// Typed (aka type-driven) tests repeat the same test for types in a
|
||||||
|
// list. You must know which types you want to test with when writing
|
||||||
|
// typed tests. Here's how you do it:
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// First, define a fixture class template. It should be parameterized
|
||||||
|
// by a type. Remember to derive it from testing::Test.
|
||||||
|
template <typename T>
|
||||||
|
class FooTest : public testing::Test {
|
||||||
|
public:
|
||||||
|
...
|
||||||
|
typedef std::list<T> List;
|
||||||
|
static T shared_;
|
||||||
|
T value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Next, associate a list of types with the test suite, which will be
|
||||||
|
// repeated for each type in the list. The typedef is necessary for
|
||||||
|
// the macro to parse correctly.
|
||||||
|
typedef testing::Types<char, int, unsigned int> MyTypes;
|
||||||
|
TYPED_TEST_SUITE(FooTest, MyTypes);
|
||||||
|
|
||||||
|
// If the type list contains only one type, you can write that type
|
||||||
|
// directly without Types<...>:
|
||||||
|
// TYPED_TEST_SUITE(FooTest, int);
|
||||||
|
|
||||||
|
// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
|
||||||
|
// tests for this test suite as you want.
|
||||||
|
TYPED_TEST(FooTest, DoesBlah) {
|
||||||
|
// Inside a test, refer to the special name TypeParam to get the type
|
||||||
|
// parameter. Since we are inside a derived class template, C++ requires
|
||||||
|
// us to visit the members of FooTest via 'this'.
|
||||||
|
TypeParam n = this->value_;
|
||||||
|
|
||||||
|
// To visit static members of the fixture, add the TestFixture::
|
||||||
|
// prefix.
|
||||||
|
n += TestFixture::shared_;
|
||||||
|
|
||||||
|
// To refer to typedefs in the fixture, add the "typename
|
||||||
|
// TestFixture::" prefix.
|
||||||
|
typename TestFixture::List values;
|
||||||
|
values.push_back(n);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST(FooTest, HasPropertyA) { ... }
|
||||||
|
|
||||||
|
// TYPED_TEST_SUITE takes an optional third argument which allows to specify a
|
||||||
|
// class that generates custom test name suffixes based on the type. This should
|
||||||
|
// be a class which has a static template function GetName(int index) returning
|
||||||
|
// a string for each type. The provided integer index equals the index of the
|
||||||
|
// type in the provided type list. In many cases the index can be ignored.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
// class MyTypeNames {
|
||||||
|
// public:
|
||||||
|
// template <typename T>
|
||||||
|
// static std::string GetName(int) {
|
||||||
|
// if (std::is_same<T, char>()) return "char";
|
||||||
|
// if (std::is_same<T, int>()) return "int";
|
||||||
|
// if (std::is_same<T, unsigned int>()) return "unsignedInt";
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames);
|
||||||
|
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
|
// Type-parameterized tests are abstract test patterns parameterized
|
||||||
|
// by a type. Compared with typed tests, type-parameterized tests
|
||||||
|
// allow you to define the test pattern without knowing what the type
|
||||||
|
// parameters are. The defined pattern can be instantiated with
|
||||||
|
// different types any number of times, in any number of translation
|
||||||
|
// units.
|
||||||
|
//
|
||||||
|
// If you are designing an interface or concept, you can define a
|
||||||
|
// suite of type-parameterized tests to verify properties that any
|
||||||
|
// valid implementation of the interface/concept should have. Then,
|
||||||
|
// each implementation can easily instantiate the test suite to verify
|
||||||
|
// that it conforms to the requirements, without having to write
|
||||||
|
// similar tests repeatedly. Here's an example:
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// First, define a fixture class template. It should be parameterized
|
||||||
|
// by a type. Remember to derive it from testing::Test.
|
||||||
|
template <typename T>
|
||||||
|
class FooTest : public testing::Test {
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
// Next, declare that you will define a type-parameterized test suite
|
||||||
|
// (the _P suffix is for "parameterized" or "pattern", whichever you
|
||||||
|
// prefer):
|
||||||
|
TYPED_TEST_SUITE_P(FooTest);
|
||||||
|
|
||||||
|
// Then, use TYPED_TEST_P() to define as many type-parameterized tests
|
||||||
|
// for this type-parameterized test suite as you want.
|
||||||
|
TYPED_TEST_P(FooTest, DoesBlah) {
|
||||||
|
// Inside a test, refer to TypeParam to get the type parameter.
|
||||||
|
TypeParam n = 0;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPED_TEST_P(FooTest, HasPropertyA) { ... }
|
||||||
|
|
||||||
|
// Now the tricky part: you need to register all test patterns before
|
||||||
|
// you can instantiate them. The first argument of the macro is the
|
||||||
|
// test suite name; the rest are the names of the tests in this test
|
||||||
|
// case.
|
||||||
|
REGISTER_TYPED_TEST_SUITE_P(FooTest,
|
||||||
|
DoesBlah, HasPropertyA);
|
||||||
|
|
||||||
|
// Finally, you are free to instantiate the pattern with the types you
|
||||||
|
// want. If you put the above code in a header file, you can #include
|
||||||
|
// it in multiple C++ source files and instantiate it multiple times.
|
||||||
|
//
|
||||||
|
// To distinguish different instances of the pattern, the first
|
||||||
|
// argument to the INSTANTIATE_* macro is a prefix that will be added
|
||||||
|
// to the actual test suite name. Remember to pick unique prefixes for
|
||||||
|
// different instances.
|
||||||
|
typedef testing::Types<char, int, unsigned int> MyTypes;
|
||||||
|
INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
|
||||||
|
|
||||||
|
// If the type list contains only one type, you can write that type
|
||||||
|
// directly without Types<...>:
|
||||||
|
// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int);
|
||||||
|
//
|
||||||
|
// Similar to the optional argument of TYPED_TEST_SUITE above,
|
||||||
|
// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to
|
||||||
|
// generate custom names.
|
||||||
|
// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames);
|
||||||
|
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
#include "gtest/internal/gtest-type-util.h"
|
||||||
|
|
||||||
|
// Implements typed tests.
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// Expands to the name of the typedef for the type parameters of the
|
||||||
|
// given test suite.
|
||||||
|
#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_
|
||||||
|
|
||||||
|
// Expands to the name of the typedef for the NameGenerator, responsible for
|
||||||
|
// creating the suffixes of the name.
|
||||||
|
#define GTEST_NAME_GENERATOR_(TestSuiteName) \
|
||||||
|
gtest_type_params_##TestSuiteName##_NameGenerator
|
||||||
|
|
||||||
|
#define TYPED_TEST_SUITE(CaseName, Types, ...) \
|
||||||
|
typedef ::testing::internal::GenerateTypeList<Types>::type \
|
||||||
|
GTEST_TYPE_PARAMS_(CaseName); \
|
||||||
|
typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
|
||||||
|
GTEST_NAME_GENERATOR_(CaseName)
|
||||||
|
|
||||||
|
#define TYPED_TEST(CaseName, TestName) \
|
||||||
|
static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \
|
||||||
|
"test-name must not be empty"); \
|
||||||
|
template <typename gtest_TypeParam_> \
|
||||||
|
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
|
||||||
|
: public CaseName<gtest_TypeParam_> { \
|
||||||
|
private: \
|
||||||
|
typedef CaseName<gtest_TypeParam_> TestFixture; \
|
||||||
|
typedef gtest_TypeParam_ TypeParam; \
|
||||||
|
void TestBody() override; \
|
||||||
|
}; \
|
||||||
|
static bool gtest_##CaseName##_##TestName##_registered_ \
|
||||||
|
GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \
|
||||||
|
CaseName, \
|
||||||
|
::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \
|
||||||
|
TestName)>, \
|
||||||
|
GTEST_TYPE_PARAMS_( \
|
||||||
|
CaseName)>::Register("", \
|
||||||
|
::testing::internal::CodeLocation( \
|
||||||
|
__FILE__, __LINE__), \
|
||||||
|
GTEST_STRINGIFY_(CaseName), \
|
||||||
|
GTEST_STRINGIFY_(TestName), 0, \
|
||||||
|
::testing::internal::GenerateNames< \
|
||||||
|
GTEST_NAME_GENERATOR_(CaseName), \
|
||||||
|
GTEST_TYPE_PARAMS_(CaseName)>()); \
|
||||||
|
template <typename gtest_TypeParam_> \
|
||||||
|
void GTEST_TEST_CLASS_NAME_(CaseName, \
|
||||||
|
TestName)<gtest_TypeParam_>::TestBody()
|
||||||
|
|
||||||
|
// Legacy API is deprecated but still available
|
||||||
|
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
#define TYPED_TEST_CASE \
|
||||||
|
static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \
|
||||||
|
TYPED_TEST_SUITE
|
||||||
|
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
|
||||||
|
// Implements type-parameterized tests.
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// Expands to the namespace name that the type-parameterized tests for
|
||||||
|
// the given type-parameterized test suite are defined in. The exact
|
||||||
|
// name of the namespace is subject to change without notice.
|
||||||
|
#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// Expands to the name of the variable used to remember the names of
|
||||||
|
// the defined tests in the given test suite.
|
||||||
|
#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \
|
||||||
|
gtest_typed_test_suite_p_state_##TestSuiteName##_
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
|
||||||
|
//
|
||||||
|
// Expands to the name of the variable used to remember the names of
|
||||||
|
// the registered tests in the given test suite.
|
||||||
|
#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \
|
||||||
|
gtest_registered_test_names_##TestSuiteName##_
|
||||||
|
|
||||||
|
// The variables defined in the type-parameterized test macros are
|
||||||
|
// static as typically these macros are used in a .h file that can be
|
||||||
|
// #included in multiple translation units linked together.
|
||||||
|
#define TYPED_TEST_SUITE_P(SuiteName) \
|
||||||
|
static ::testing::internal::TypedTestSuitePState \
|
||||||
|
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName)
|
||||||
|
|
||||||
|
// Legacy API is deprecated but still available
|
||||||
|
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
#define TYPED_TEST_CASE_P \
|
||||||
|
static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \
|
||||||
|
TYPED_TEST_SUITE_P
|
||||||
|
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
|
||||||
|
#define TYPED_TEST_P(SuiteName, TestName) \
|
||||||
|
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
|
||||||
|
template <typename gtest_TypeParam_> \
|
||||||
|
class TestName : public SuiteName<gtest_TypeParam_> { \
|
||||||
|
private: \
|
||||||
|
typedef SuiteName<gtest_TypeParam_> TestFixture; \
|
||||||
|
typedef gtest_TypeParam_ TypeParam; \
|
||||||
|
void TestBody() override; \
|
||||||
|
}; \
|
||||||
|
static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
|
||||||
|
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
|
||||||
|
__FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \
|
||||||
|
GTEST_STRINGIFY_(TestName)); \
|
||||||
|
} \
|
||||||
|
template <typename gtest_TypeParam_> \
|
||||||
|
void GTEST_SUITE_NAMESPACE_( \
|
||||||
|
SuiteName)::TestName<gtest_TypeParam_>::TestBody()
|
||||||
|
|
||||||
|
// Note: this won't work correctly if the trailing arguments are macros.
|
||||||
|
#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \
|
||||||
|
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
|
||||||
|
typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \
|
||||||
|
} \
|
||||||
|
static const char* const GTEST_REGISTERED_TEST_NAMES_( \
|
||||||
|
SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \
|
||||||
|
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
|
||||||
|
GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
|
||||||
|
|
||||||
|
// Legacy API is deprecated but still available
|
||||||
|
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
#define REGISTER_TYPED_TEST_CASE_P \
|
||||||
|
static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \
|
||||||
|
""); \
|
||||||
|
REGISTER_TYPED_TEST_SUITE_P
|
||||||
|
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
|
||||||
|
#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
|
||||||
|
static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \
|
||||||
|
"test-suit-prefix must not be empty"); \
|
||||||
|
static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \
|
||||||
|
::testing::internal::TypeParameterizedTestSuite< \
|
||||||
|
SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
|
||||||
|
::testing::internal::GenerateTypeList<Types>::type>:: \
|
||||||
|
Register(GTEST_STRINGIFY_(Prefix), \
|
||||||
|
::testing::internal::CodeLocation(__FILE__, __LINE__), \
|
||||||
|
>EST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \
|
||||||
|
GTEST_STRINGIFY_(SuiteName), \
|
||||||
|
GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
|
||||||
|
::testing::internal::GenerateNames< \
|
||||||
|
::testing::internal::NameGeneratorSelector< \
|
||||||
|
__VA_ARGS__>::type, \
|
||||||
|
::testing::internal::GenerateTypeList<Types>::type>())
|
||||||
|
|
||||||
|
// Legacy API is deprecated but still available
|
||||||
|
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
#define INSTANTIATE_TYPED_TEST_CASE_P \
|
||||||
|
static_assert( \
|
||||||
|
::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \
|
||||||
|
INSTANTIATE_TYPED_TEST_SUITE_P
|
||||||
|
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
|
2297
test/gtest/include/gtest/gtest.h
Normal file
2297
test/gtest/include/gtest/gtest.h
Normal file
File diff suppressed because it is too large
Load Diff
279
test/gtest/include/gtest/gtest_pred_impl.h
Normal file
279
test/gtest/include/gtest/gtest_pred_impl.h
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
// Copyright 2006, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Implements a family of generic predicate assertion macros.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
||||||
|
|
||||||
|
#include "gtest/gtest-assertion-result.h"
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// This header implements a family of generic predicate assertion
|
||||||
|
// macros:
|
||||||
|
//
|
||||||
|
// ASSERT_PRED_FORMAT1(pred_format, v1)
|
||||||
|
// ASSERT_PRED_FORMAT2(pred_format, v1, v2)
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// where pred_format is a function or functor that takes n (in the
|
||||||
|
// case of ASSERT_PRED_FORMATn) values and their source expression
|
||||||
|
// text, and returns a testing::AssertionResult. See the definition
|
||||||
|
// of ASSERT_EQ in gtest.h for an example.
|
||||||
|
//
|
||||||
|
// If you don't care about formatting, you can use the more
|
||||||
|
// restrictive version:
|
||||||
|
//
|
||||||
|
// ASSERT_PRED1(pred, v1)
|
||||||
|
// ASSERT_PRED2(pred, v1, v2)
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// where pred is an n-ary function or functor that returns bool,
|
||||||
|
// and the values v1, v2, ..., must support the << operator for
|
||||||
|
// streaming to std::ostream.
|
||||||
|
//
|
||||||
|
// We also define the EXPECT_* variations.
|
||||||
|
//
|
||||||
|
// For now we only support predicates whose arity is at most 5.
|
||||||
|
// Please email googletestframework@googlegroups.com if you need
|
||||||
|
// support for higher arities.
|
||||||
|
|
||||||
|
// GTEST_ASSERT_ is the basic statement to which all of the assertions
|
||||||
|
// in this file reduce. Don't use this in your code.
|
||||||
|
|
||||||
|
#define GTEST_ASSERT_(expression, on_failure) \
|
||||||
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
|
if (const ::testing::AssertionResult gtest_ar = (expression)) \
|
||||||
|
; \
|
||||||
|
else \
|
||||||
|
on_failure(gtest_ar.failure_message())
|
||||||
|
|
||||||
|
// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use
|
||||||
|
// this in your code.
|
||||||
|
template <typename Pred, typename T1>
|
||||||
|
AssertionResult AssertPred1Helper(const char* pred_text, const char* e1,
|
||||||
|
Pred pred, const T1& v1) {
|
||||||
|
if (pred(v1)) return AssertionSuccess();
|
||||||
|
|
||||||
|
return AssertionFailure()
|
||||||
|
<< pred_text << "(" << e1 << ") evaluates to false, where"
|
||||||
|
<< "\n"
|
||||||
|
<< e1 << " evaluates to " << ::testing::PrintToString(v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
|
||||||
|
// Don't use this in your code.
|
||||||
|
#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure) \
|
||||||
|
GTEST_ASSERT_(pred_format(#v1, v1), on_failure)
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use
|
||||||
|
// this in your code.
|
||||||
|
#define GTEST_PRED1_(pred, v1, on_failure) \
|
||||||
|
GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, #v1, pred, v1), on_failure)
|
||||||
|
|
||||||
|
// Unary predicate assertion macros.
|
||||||
|
#define EXPECT_PRED_FORMAT1(pred_format, v1) \
|
||||||
|
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define EXPECT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED_FORMAT1(pred_format, v1) \
|
||||||
|
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
|
||||||
|
|
||||||
|
// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use
|
||||||
|
// this in your code.
|
||||||
|
template <typename Pred, typename T1, typename T2>
|
||||||
|
AssertionResult AssertPred2Helper(const char* pred_text, const char* e1,
|
||||||
|
const char* e2, Pred pred, const T1& v1,
|
||||||
|
const T2& v2) {
|
||||||
|
if (pred(v1, v2)) return AssertionSuccess();
|
||||||
|
|
||||||
|
return AssertionFailure()
|
||||||
|
<< pred_text << "(" << e1 << ", " << e2
|
||||||
|
<< ") evaluates to false, where"
|
||||||
|
<< "\n"
|
||||||
|
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
|
||||||
|
<< e2 << " evaluates to " << ::testing::PrintToString(v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
|
||||||
|
// Don't use this in your code.
|
||||||
|
#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \
|
||||||
|
GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure)
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use
|
||||||
|
// this in your code.
|
||||||
|
#define GTEST_PRED2_(pred, v1, v2, on_failure) \
|
||||||
|
GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, #v1, #v2, pred, v1, v2), \
|
||||||
|
on_failure)
|
||||||
|
|
||||||
|
// Binary predicate assertion macros.
|
||||||
|
#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
|
||||||
|
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define EXPECT_PRED2(pred, v1, v2) \
|
||||||
|
GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
|
||||||
|
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED2(pred, v1, v2) \
|
||||||
|
GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
|
||||||
|
|
||||||
|
// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use
|
||||||
|
// this in your code.
|
||||||
|
template <typename Pred, typename T1, typename T2, typename T3>
|
||||||
|
AssertionResult AssertPred3Helper(const char* pred_text, const char* e1,
|
||||||
|
const char* e2, const char* e3, Pred pred,
|
||||||
|
const T1& v1, const T2& v2, const T3& v3) {
|
||||||
|
if (pred(v1, v2, v3)) return AssertionSuccess();
|
||||||
|
|
||||||
|
return AssertionFailure()
|
||||||
|
<< pred_text << "(" << e1 << ", " << e2 << ", " << e3
|
||||||
|
<< ") evaluates to false, where"
|
||||||
|
<< "\n"
|
||||||
|
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
|
||||||
|
<< e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
|
||||||
|
<< e3 << " evaluates to " << ::testing::PrintToString(v3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
|
||||||
|
// Don't use this in your code.
|
||||||
|
#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure) \
|
||||||
|
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), on_failure)
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use
|
||||||
|
// this in your code.
|
||||||
|
#define GTEST_PRED3_(pred, v1, v2, v3, on_failure) \
|
||||||
|
GTEST_ASSERT_( \
|
||||||
|
::testing::AssertPred3Helper(#pred, #v1, #v2, #v3, pred, v1, v2, v3), \
|
||||||
|
on_failure)
|
||||||
|
|
||||||
|
// Ternary predicate assertion macros.
|
||||||
|
#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
|
||||||
|
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define EXPECT_PRED3(pred, v1, v2, v3) \
|
||||||
|
GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
|
||||||
|
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED3(pred, v1, v2, v3) \
|
||||||
|
GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
|
||||||
|
|
||||||
|
// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use
|
||||||
|
// this in your code.
|
||||||
|
template <typename Pred, typename T1, typename T2, typename T3, typename T4>
|
||||||
|
AssertionResult AssertPred4Helper(const char* pred_text, const char* e1,
|
||||||
|
const char* e2, const char* e3,
|
||||||
|
const char* e4, Pred pred, const T1& v1,
|
||||||
|
const T2& v2, const T3& v3, const T4& v4) {
|
||||||
|
if (pred(v1, v2, v3, v4)) return AssertionSuccess();
|
||||||
|
|
||||||
|
return AssertionFailure()
|
||||||
|
<< pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
|
||||||
|
<< ") evaluates to false, where"
|
||||||
|
<< "\n"
|
||||||
|
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
|
||||||
|
<< e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
|
||||||
|
<< e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
|
||||||
|
<< e4 << " evaluates to " << ::testing::PrintToString(v4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
|
||||||
|
// Don't use this in your code.
|
||||||
|
#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure) \
|
||||||
|
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), on_failure)
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use
|
||||||
|
// this in your code.
|
||||||
|
#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure) \
|
||||||
|
GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, #v1, #v2, #v3, #v4, pred, \
|
||||||
|
v1, v2, v3, v4), \
|
||||||
|
on_failure)
|
||||||
|
|
||||||
|
// 4-ary predicate assertion macros.
|
||||||
|
#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
|
||||||
|
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
|
||||||
|
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
|
||||||
|
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
|
||||||
|
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
|
||||||
|
|
||||||
|
// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use
|
||||||
|
// this in your code.
|
||||||
|
template <typename Pred, typename T1, typename T2, typename T3, typename T4,
|
||||||
|
typename T5>
|
||||||
|
AssertionResult AssertPred5Helper(const char* pred_text, const char* e1,
|
||||||
|
const char* e2, const char* e3,
|
||||||
|
const char* e4, const char* e5, Pred pred,
|
||||||
|
const T1& v1, const T2& v2, const T3& v3,
|
||||||
|
const T4& v4, const T5& v5) {
|
||||||
|
if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
|
||||||
|
|
||||||
|
return AssertionFailure()
|
||||||
|
<< pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
|
||||||
|
<< ", " << e5 << ") evaluates to false, where"
|
||||||
|
<< "\n"
|
||||||
|
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
|
||||||
|
<< e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
|
||||||
|
<< e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
|
||||||
|
<< e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n"
|
||||||
|
<< e5 << " evaluates to " << ::testing::PrintToString(v5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
|
||||||
|
// Don't use this in your code.
|
||||||
|
#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure) \
|
||||||
|
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
|
||||||
|
on_failure)
|
||||||
|
|
||||||
|
// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use
|
||||||
|
// this in your code.
|
||||||
|
#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure) \
|
||||||
|
GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, #v1, #v2, #v3, #v4, #v5, \
|
||||||
|
pred, v1, v2, v3, v4, v5), \
|
||||||
|
on_failure)
|
||||||
|
|
||||||
|
// 5-ary predicate assertion macros.
|
||||||
|
#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
|
||||||
|
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
|
||||||
|
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
|
||||||
|
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
|
||||||
|
#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
|
||||||
|
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
|
60
test/gtest/include/gtest/gtest_prod.h
Normal file
60
test/gtest/include/gtest/gtest_prod.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2006, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Google C++ Testing and Mocking Framework definitions useful in production
|
||||||
|
// code.
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
|
||||||
|
|
||||||
|
// When you need to test the private or protected members of a class,
|
||||||
|
// use the FRIEND_TEST macro to declare your tests as friends of the
|
||||||
|
// class. For example:
|
||||||
|
//
|
||||||
|
// class MyClass {
|
||||||
|
// private:
|
||||||
|
// void PrivateMethod();
|
||||||
|
// FRIEND_TEST(MyClassTest, PrivateMethodWorks);
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class MyClassTest : public testing::Test {
|
||||||
|
// // ...
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// TEST_F(MyClassTest, PrivateMethodWorks) {
|
||||||
|
// // Can call MyClass::PrivateMethod() here.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Note: The test class must be in the same namespace as the class being tested.
|
||||||
|
// For example, putting MyClassTest in an anonymous namespace will not work.
|
||||||
|
|
||||||
|
#define FRIEND_TEST(test_case_name, test_name) \
|
||||||
|
friend class test_case_name##_##test_name##_Test
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
|
44
test/gtest/include/gtest/internal/custom/README.md
Normal file
44
test/gtest/include/gtest/internal/custom/README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Customization Points
|
||||||
|
|
||||||
|
The custom directory is an injection point for custom user configurations.
|
||||||
|
|
||||||
|
## Header `gtest.h`
|
||||||
|
|
||||||
|
### The following macros can be defined:
|
||||||
|
|
||||||
|
* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of
|
||||||
|
`OsStackTraceGetterInterface`.
|
||||||
|
* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See
|
||||||
|
`testing::TempDir` for semantics and signature.
|
||||||
|
|
||||||
|
## Header `gtest-port.h`
|
||||||
|
|
||||||
|
The following macros can be defined:
|
||||||
|
|
||||||
|
### Logging:
|
||||||
|
|
||||||
|
* `GTEST_LOG_(severity)`
|
||||||
|
* `GTEST_CHECK_(condition)`
|
||||||
|
* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too.
|
||||||
|
|
||||||
|
### Threading:
|
||||||
|
|
||||||
|
* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided.
|
||||||
|
* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal`
|
||||||
|
are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)`
|
||||||
|
and `GTEST_DEFINE_STATIC_MUTEX_(mutex)`
|
||||||
|
* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)`
|
||||||
|
* `GTEST_LOCK_EXCLUDED_(locks)`
|
||||||
|
|
||||||
|
### Underlying library support features
|
||||||
|
|
||||||
|
* `GTEST_HAS_CXXABI_H_`
|
||||||
|
|
||||||
|
### Exporting API symbols:
|
||||||
|
|
||||||
|
* `GTEST_API_` - Specifier for exported symbols.
|
||||||
|
|
||||||
|
## Header `gtest-printers.h`
|
||||||
|
|
||||||
|
* See documentation at `gtest/gtest-printers.h` for details on how to define a
|
||||||
|
custom printer.
|
37
test/gtest/include/gtest/internal/custom/gtest-port.h
Normal file
37
test/gtest/include/gtest/internal/custom/gtest-port.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2015, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Injection point for custom user configurations. See README for details
|
||||||
|
//
|
||||||
|
// ** Custom implementation starts here **
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
|
42
test/gtest/include/gtest/internal/custom/gtest-printers.h
Normal file
42
test/gtest/include/gtest/internal/custom/gtest-printers.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2015, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// This file provides an injection point for custom printers in a local
|
||||||
|
// installation of gTest.
|
||||||
|
// It will be included from gtest-printers.h and the overrides in this file
|
||||||
|
// will be visible to everyone.
|
||||||
|
//
|
||||||
|
// Injection point for custom user configurations. See README for details
|
||||||
|
//
|
||||||
|
// ** Custom implementation starts here **
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
|
37
test/gtest/include/gtest/internal/custom/gtest.h
Normal file
37
test/gtest/include/gtest/internal/custom/gtest.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2015, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
// Injection point for custom user configurations. See README for details
|
||||||
|
//
|
||||||
|
// ** Custom implementation starts here **
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
|
306
test/gtest/include/gtest/internal/gtest-death-test-internal.h
Normal file
306
test/gtest/include/gtest/internal/gtest-death-test-internal.h
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
// Copyright 2005, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This header file defines internal utilities needed for implementing
|
||||||
|
// death tests. They are subject to change without notice.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "gtest/gtest-matchers.h"
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
|
||||||
|
GTEST_DECLARE_string_(internal_run_death_test);
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Names of the flags (needed for parsing Google Test flags).
|
||||||
|
const char kDeathTestStyleFlag[] = "death_test_style";
|
||||||
|
const char kDeathTestUseFork[] = "death_test_use_fork";
|
||||||
|
const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
|
||||||
|
|
||||||
|
#if GTEST_HAS_DEATH_TEST
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
|
||||||
|
/* class A needs to have dll-interface to be used by clients of class B */)
|
||||||
|
|
||||||
|
// DeathTest is a class that hides much of the complexity of the
|
||||||
|
// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method
|
||||||
|
// returns a concrete class that depends on the prevailing death test
|
||||||
|
// style, as defined by the --gtest_death_test_style and/or
|
||||||
|
// --gtest_internal_run_death_test flags.
|
||||||
|
|
||||||
|
// In describing the results of death tests, these terms are used with
|
||||||
|
// the corresponding definitions:
|
||||||
|
//
|
||||||
|
// exit status: The integer exit information in the format specified
|
||||||
|
// by wait(2)
|
||||||
|
// exit code: The integer code passed to exit(3), _exit(2), or
|
||||||
|
// returned from main()
|
||||||
|
class GTEST_API_ DeathTest {
|
||||||
|
public:
|
||||||
|
// Create returns false if there was an error determining the
|
||||||
|
// appropriate action to take for the current death test; for example,
|
||||||
|
// if the gtest_death_test_style flag is set to an invalid value.
|
||||||
|
// The LastMessage method will return a more detailed message in that
|
||||||
|
// case. Otherwise, the DeathTest pointer pointed to by the "test"
|
||||||
|
// argument is set. If the death test should be skipped, the pointer
|
||||||
|
// is set to NULL; otherwise, it is set to the address of a new concrete
|
||||||
|
// DeathTest object that controls the execution of the current test.
|
||||||
|
static bool Create(const char* statement, Matcher<const std::string&> matcher,
|
||||||
|
const char* file, int line, DeathTest** test);
|
||||||
|
DeathTest();
|
||||||
|
virtual ~DeathTest() {}
|
||||||
|
|
||||||
|
// A helper class that aborts a death test when it's deleted.
|
||||||
|
class ReturnSentinel {
|
||||||
|
public:
|
||||||
|
explicit ReturnSentinel(DeathTest* test) : test_(test) {}
|
||||||
|
~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeathTest* const test_;
|
||||||
|
ReturnSentinel(const ReturnSentinel&) = delete;
|
||||||
|
ReturnSentinel& operator=(const ReturnSentinel&) = delete;
|
||||||
|
} GTEST_ATTRIBUTE_UNUSED_;
|
||||||
|
|
||||||
|
// An enumeration of possible roles that may be taken when a death
|
||||||
|
// test is encountered. EXECUTE means that the death test logic should
|
||||||
|
// be executed immediately. OVERSEE means that the program should prepare
|
||||||
|
// the appropriate environment for a child process to execute the death
|
||||||
|
// test, then wait for it to complete.
|
||||||
|
enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
|
||||||
|
|
||||||
|
// An enumeration of the three reasons that a test might be aborted.
|
||||||
|
enum AbortReason {
|
||||||
|
TEST_ENCOUNTERED_RETURN_STATEMENT,
|
||||||
|
TEST_THREW_EXCEPTION,
|
||||||
|
TEST_DID_NOT_DIE
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assumes one of the above roles.
|
||||||
|
virtual TestRole AssumeRole() = 0;
|
||||||
|
|
||||||
|
// Waits for the death test to finish and returns its status.
|
||||||
|
virtual int Wait() = 0;
|
||||||
|
|
||||||
|
// Returns true if the death test passed; that is, the test process
|
||||||
|
// exited during the test, its exit status matches a user-supplied
|
||||||
|
// predicate, and its stderr output matches a user-supplied regular
|
||||||
|
// expression.
|
||||||
|
// The user-supplied predicate may be a macro expression rather
|
||||||
|
// than a function pointer or functor, or else Wait and Passed could
|
||||||
|
// be combined.
|
||||||
|
virtual bool Passed(bool exit_status_ok) = 0;
|
||||||
|
|
||||||
|
// Signals that the death test did not die as expected.
|
||||||
|
virtual void Abort(AbortReason reason) = 0;
|
||||||
|
|
||||||
|
// Returns a human-readable outcome message regarding the outcome of
|
||||||
|
// the last death test.
|
||||||
|
static const char* LastMessage();
|
||||||
|
|
||||||
|
static void set_last_death_test_message(const std::string& message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// A string containing a description of the outcome of the last death test.
|
||||||
|
static std::string last_death_test_message_;
|
||||||
|
|
||||||
|
DeathTest(const DeathTest&) = delete;
|
||||||
|
DeathTest& operator=(const DeathTest&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||||
|
|
||||||
|
// Factory interface for death tests. May be mocked out for testing.
|
||||||
|
class DeathTestFactory {
|
||||||
|
public:
|
||||||
|
virtual ~DeathTestFactory() {}
|
||||||
|
virtual bool Create(const char* statement,
|
||||||
|
Matcher<const std::string&> matcher, const char* file,
|
||||||
|
int line, DeathTest** test) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A concrete DeathTestFactory implementation for normal use.
|
||||||
|
class DefaultDeathTestFactory : public DeathTestFactory {
|
||||||
|
public:
|
||||||
|
bool Create(const char* statement, Matcher<const std::string&> matcher,
|
||||||
|
const char* file, int line, DeathTest** test) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns true if exit_status describes a process that was terminated
|
||||||
|
// by a signal, or exited normally with a nonzero exit code.
|
||||||
|
GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
|
||||||
|
|
||||||
|
// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads
|
||||||
|
// and interpreted as a regex (rather than an Eq matcher) for legacy
|
||||||
|
// compatibility.
|
||||||
|
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
|
||||||
|
::testing::internal::RE regex) {
|
||||||
|
return ContainsRegex(regex.pattern());
|
||||||
|
}
|
||||||
|
inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) {
|
||||||
|
return ContainsRegex(regex);
|
||||||
|
}
|
||||||
|
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
|
||||||
|
const ::std::string& regex) {
|
||||||
|
return ContainsRegex(regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's
|
||||||
|
// used directly.
|
||||||
|
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
|
||||||
|
Matcher<const ::std::string&> matcher) {
|
||||||
|
return matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traps C++ exceptions escaping statement and reports them as test
|
||||||
|
// failures. Note that trapping SEH exceptions is not implemented here.
|
||||||
|
#if GTEST_HAS_EXCEPTIONS
|
||||||
|
#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
|
||||||
|
try { \
|
||||||
|
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||||
|
} catch (const ::std::exception& gtest_exception) { \
|
||||||
|
fprintf( \
|
||||||
|
stderr, \
|
||||||
|
"\n%s: Caught std::exception-derived exception escaping the " \
|
||||||
|
"death test statement. Exception message: %s\n", \
|
||||||
|
::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
|
||||||
|
gtest_exception.what()); \
|
||||||
|
fflush(stderr); \
|
||||||
|
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
|
||||||
|
} catch (...) { \
|
||||||
|
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
|
||||||
|
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
|
||||||
|
// ASSERT_EXIT*, and EXPECT_EXIT*.
|
||||||
|
#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \
|
||||||
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
|
if (::testing::internal::AlwaysTrue()) { \
|
||||||
|
::testing::internal::DeathTest* gtest_dt; \
|
||||||
|
if (!::testing::internal::DeathTest::Create( \
|
||||||
|
#statement, \
|
||||||
|
::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \
|
||||||
|
__FILE__, __LINE__, >est_dt)) { \
|
||||||
|
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
|
||||||
|
} \
|
||||||
|
if (gtest_dt != nullptr) { \
|
||||||
|
std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \
|
||||||
|
switch (gtest_dt->AssumeRole()) { \
|
||||||
|
case ::testing::internal::DeathTest::OVERSEE_TEST: \
|
||||||
|
if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
|
||||||
|
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
|
||||||
|
} \
|
||||||
|
break; \
|
||||||
|
case ::testing::internal::DeathTest::EXECUTE_TEST: { \
|
||||||
|
::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \
|
||||||
|
gtest_dt); \
|
||||||
|
GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
|
||||||
|
gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} else \
|
||||||
|
GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \
|
||||||
|
: fail(::testing::internal::DeathTest::LastMessage())
|
||||||
|
// The symbol "fail" here expands to something into which a message
|
||||||
|
// can be streamed.
|
||||||
|
|
||||||
|
// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
|
||||||
|
// NDEBUG mode. In this case we need the statements to be executed and the macro
|
||||||
|
// must accept a streamed message even though the message is never printed.
|
||||||
|
// The regex object is not evaluated, but it is used to prevent "unused"
|
||||||
|
// warnings and to avoid an expression that doesn't compile in debug mode.
|
||||||
|
#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \
|
||||||
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
|
if (::testing::internal::AlwaysTrue()) { \
|
||||||
|
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
|
||||||
|
} else if (!::testing::internal::AlwaysTrue()) { \
|
||||||
|
::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \
|
||||||
|
} else \
|
||||||
|
::testing::Message()
|
||||||
|
|
||||||
|
// A class representing the parsed contents of the
|
||||||
|
// --gtest_internal_run_death_test flag, as it existed when
|
||||||
|
// RUN_ALL_TESTS was called.
|
||||||
|
class InternalRunDeathTestFlag {
|
||||||
|
public:
|
||||||
|
InternalRunDeathTestFlag(const std::string& a_file, int a_line, int an_index,
|
||||||
|
int a_write_fd)
|
||||||
|
: file_(a_file), line_(a_line), index_(an_index), write_fd_(a_write_fd) {}
|
||||||
|
|
||||||
|
~InternalRunDeathTestFlag() {
|
||||||
|
if (write_fd_ >= 0) posix::Close(write_fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& file() const { return file_; }
|
||||||
|
int line() const { return line_; }
|
||||||
|
int index() const { return index_; }
|
||||||
|
int write_fd() const { return write_fd_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string file_;
|
||||||
|
int line_;
|
||||||
|
int index_;
|
||||||
|
int write_fd_;
|
||||||
|
|
||||||
|
InternalRunDeathTestFlag(const InternalRunDeathTestFlag&) = delete;
|
||||||
|
InternalRunDeathTestFlag& operator=(const InternalRunDeathTestFlag&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns a newly created InternalRunDeathTestFlag object with fields
|
||||||
|
// initialized from the GTEST_FLAG(internal_run_death_test) flag if
|
||||||
|
// the flag is specified; otherwise returns NULL.
|
||||||
|
InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
|
||||||
|
|
||||||
|
#endif // GTEST_HAS_DEATH_TEST
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
|
210
test/gtest/include/gtest/internal/gtest-filepath.h
Normal file
210
test/gtest/include/gtest/internal/gtest-filepath.h
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
// Copyright 2008, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Google Test filepath utilities
|
||||||
|
//
|
||||||
|
// This header file declares classes and functions used internally by
|
||||||
|
// Google Test. They are subject to change without notice.
|
||||||
|
//
|
||||||
|
// This file is #included in gtest/internal/gtest-internal.h.
|
||||||
|
// Do not include this header file separately!
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-string.h"
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
|
||||||
|
/* class A needs to have dll-interface to be used by clients of class B */)
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// FilePath - a class for file and directory pathname manipulation which
|
||||||
|
// handles platform-specific conventions (like the pathname separator).
|
||||||
|
// Used for helper functions for naming files in a directory for xml output.
|
||||||
|
// Except for Set methods, all methods are const or static, which provides an
|
||||||
|
// "immutable value object" -- useful for peace of mind.
|
||||||
|
// A FilePath with a value ending in a path separator ("like/this/") represents
|
||||||
|
// a directory, otherwise it is assumed to represent a file. In either case,
|
||||||
|
// it may or may not represent an actual file or directory in the file system.
|
||||||
|
// Names are NOT checked for syntax correctness -- no checking for illegal
|
||||||
|
// characters, malformed paths, etc.
|
||||||
|
|
||||||
|
class GTEST_API_ FilePath {
|
||||||
|
public:
|
||||||
|
FilePath() : pathname_("") {}
|
||||||
|
FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) {}
|
||||||
|
|
||||||
|
explicit FilePath(const std::string& pathname) : pathname_(pathname) {
|
||||||
|
Normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath& operator=(const FilePath& rhs) {
|
||||||
|
Set(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const FilePath& rhs) { pathname_ = rhs.pathname_; }
|
||||||
|
|
||||||
|
const std::string& string() const { return pathname_; }
|
||||||
|
const char* c_str() const { return pathname_.c_str(); }
|
||||||
|
|
||||||
|
// Returns the current working directory, or "" if unsuccessful.
|
||||||
|
static FilePath GetCurrentDir();
|
||||||
|
|
||||||
|
// Given directory = "dir", base_name = "test", number = 0,
|
||||||
|
// extension = "xml", returns "dir/test.xml". If number is greater
|
||||||
|
// than zero (e.g., 12), returns "dir/test_12.xml".
|
||||||
|
// On Windows platform, uses \ as the separator rather than /.
|
||||||
|
static FilePath MakeFileName(const FilePath& directory,
|
||||||
|
const FilePath& base_name, int number,
|
||||||
|
const char* extension);
|
||||||
|
|
||||||
|
// Given directory = "dir", relative_path = "test.xml",
|
||||||
|
// returns "dir/test.xml".
|
||||||
|
// On Windows, uses \ as the separator rather than /.
|
||||||
|
static FilePath ConcatPaths(const FilePath& directory,
|
||||||
|
const FilePath& relative_path);
|
||||||
|
|
||||||
|
// Returns a pathname for a file that does not currently exist. The pathname
|
||||||
|
// will be directory/base_name.extension or
|
||||||
|
// directory/base_name_<number>.extension if directory/base_name.extension
|
||||||
|
// already exists. The number will be incremented until a pathname is found
|
||||||
|
// that does not already exist.
|
||||||
|
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
|
||||||
|
// There could be a race condition if two or more processes are calling this
|
||||||
|
// function at the same time -- they could both pick the same filename.
|
||||||
|
static FilePath GenerateUniqueFileName(const FilePath& directory,
|
||||||
|
const FilePath& base_name,
|
||||||
|
const char* extension);
|
||||||
|
|
||||||
|
// Returns true if and only if the path is "".
|
||||||
|
bool IsEmpty() const { return pathname_.empty(); }
|
||||||
|
|
||||||
|
// If input name has a trailing separator character, removes it and returns
|
||||||
|
// the name, otherwise return the name string unmodified.
|
||||||
|
// On Windows platform, uses \ as the separator, other platforms use /.
|
||||||
|
FilePath RemoveTrailingPathSeparator() const;
|
||||||
|
|
||||||
|
// Returns a copy of the FilePath with the directory part removed.
|
||||||
|
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
|
||||||
|
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
|
||||||
|
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
|
||||||
|
// returns an empty FilePath ("").
|
||||||
|
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||||
|
FilePath RemoveDirectoryName() const;
|
||||||
|
|
||||||
|
// RemoveFileName returns the directory path with the filename removed.
|
||||||
|
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
|
||||||
|
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
|
||||||
|
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
|
||||||
|
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
|
||||||
|
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||||
|
FilePath RemoveFileName() const;
|
||||||
|
|
||||||
|
// Returns a copy of the FilePath with the case-insensitive extension removed.
|
||||||
|
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
|
||||||
|
// FilePath("dir/file"). If a case-insensitive extension is not
|
||||||
|
// found, returns a copy of the original FilePath.
|
||||||
|
FilePath RemoveExtension(const char* extension) const;
|
||||||
|
|
||||||
|
// Creates directories so that path exists. Returns true if successful or if
|
||||||
|
// the directories already exist; returns false if unable to create
|
||||||
|
// directories for any reason. Will also return false if the FilePath does
|
||||||
|
// not represent a directory (that is, it doesn't end with a path separator).
|
||||||
|
bool CreateDirectoriesRecursively() const;
|
||||||
|
|
||||||
|
// Create the directory so that path exists. Returns true if successful or
|
||||||
|
// if the directory already exists; returns false if unable to create the
|
||||||
|
// directory for any reason, including if the parent directory does not
|
||||||
|
// exist. Not named "CreateDirectory" because that's a macro on Windows.
|
||||||
|
bool CreateFolder() const;
|
||||||
|
|
||||||
|
// Returns true if FilePath describes something in the file-system,
|
||||||
|
// either a file, directory, or whatever, and that something exists.
|
||||||
|
bool FileOrDirectoryExists() const;
|
||||||
|
|
||||||
|
// Returns true if pathname describes a directory in the file-system
|
||||||
|
// that exists.
|
||||||
|
bool DirectoryExists() const;
|
||||||
|
|
||||||
|
// Returns true if FilePath ends with a path separator, which indicates that
|
||||||
|
// it is intended to represent a directory. Returns false otherwise.
|
||||||
|
// This does NOT check that a directory (or file) actually exists.
|
||||||
|
bool IsDirectory() const;
|
||||||
|
|
||||||
|
// Returns true if pathname describes a root directory. (Windows has one
|
||||||
|
// root directory per disk drive.)
|
||||||
|
bool IsRootDirectory() const;
|
||||||
|
|
||||||
|
// Returns true if pathname describes an absolute path.
|
||||||
|
bool IsAbsolutePath() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Replaces multiple consecutive separators with a single separator.
|
||||||
|
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
|
||||||
|
// redundancies that might be in a pathname involving "." or "..".
|
||||||
|
//
|
||||||
|
// A pathname with multiple consecutive separators may occur either through
|
||||||
|
// user error or as a result of some scripts or APIs that generate a pathname
|
||||||
|
// with a trailing separator. On other platforms the same API or script
|
||||||
|
// may NOT generate a pathname with a trailing "/". Then elsewhere that
|
||||||
|
// pathname may have another "/" and pathname components added to it,
|
||||||
|
// without checking for the separator already being there.
|
||||||
|
// The script language and operating system may allow paths like "foo//bar"
|
||||||
|
// but some of the functions in FilePath will not handle that correctly. In
|
||||||
|
// particular, RemoveTrailingPathSeparator() only removes one separator, and
|
||||||
|
// it is called in CreateDirectoriesRecursively() assuming that it will change
|
||||||
|
// a pathname from directory syntax (trailing separator) to filename syntax.
|
||||||
|
//
|
||||||
|
// On Windows this method also replaces the alternate path separator '/' with
|
||||||
|
// the primary path separator '\\', so that for example "bar\\/\\foo" becomes
|
||||||
|
// "bar\\foo".
|
||||||
|
|
||||||
|
void Normalize();
|
||||||
|
|
||||||
|
// Returns a pointer to the last occurrence of a valid path separator in
|
||||||
|
// the FilePath. On Windows, for example, both '/' and '\' are valid path
|
||||||
|
// separators. Returns NULL if no path separator was found.
|
||||||
|
const char* FindLastPathSeparator() const;
|
||||||
|
|
||||||
|
std::string pathname_;
|
||||||
|
}; // class FilePath
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
|
1570
test/gtest/include/gtest/internal/gtest-internal.h
Normal file
1570
test/gtest/include/gtest/internal/gtest-internal.h
Normal file
File diff suppressed because it is too large
Load Diff
956
test/gtest/include/gtest/internal/gtest-param-util.h
Normal file
956
test/gtest/include/gtest/internal/gtest-param-util.h
Normal file
@ -0,0 +1,956 @@
|
|||||||
|
// Copyright 2008 Google Inc.
|
||||||
|
// All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Type and function utilities for implementing parameterized tests.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gtest/gtest-printers.h"
|
||||||
|
#include "gtest/gtest-test-part.h"
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
// Input to a parameterized test name generator, describing a test parameter.
|
||||||
|
// Consists of the parameter value and the integer parameter index.
|
||||||
|
template <class ParamType>
|
||||||
|
struct TestParamInfo {
|
||||||
|
TestParamInfo(const ParamType& a_param, size_t an_index)
|
||||||
|
: param(a_param), index(an_index) {}
|
||||||
|
ParamType param;
|
||||||
|
size_t index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A builtin parameterized test name generator which returns the result of
|
||||||
|
// testing::PrintToString.
|
||||||
|
struct PrintToStringParamName {
|
||||||
|
template <class ParamType>
|
||||||
|
std::string operator()(const TestParamInfo<ParamType>& info) const {
|
||||||
|
return PrintToString(info.param);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
// Utility Functions
|
||||||
|
|
||||||
|
// Outputs a message explaining invalid registration of different
|
||||||
|
// fixture class for the same test suite. This may happen when
|
||||||
|
// TEST_P macro is used to define two tests with the same name
|
||||||
|
// but in different namespaces.
|
||||||
|
GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name,
|
||||||
|
CodeLocation code_location);
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
class ParamGeneratorInterface;
|
||||||
|
template <typename>
|
||||||
|
class ParamGenerator;
|
||||||
|
|
||||||
|
// Interface for iterating over elements provided by an implementation
|
||||||
|
// of ParamGeneratorInterface<T>.
|
||||||
|
template <typename T>
|
||||||
|
class ParamIteratorInterface {
|
||||||
|
public:
|
||||||
|
virtual ~ParamIteratorInterface() {}
|
||||||
|
// A pointer to the base generator instance.
|
||||||
|
// Used only for the purposes of iterator comparison
|
||||||
|
// to make sure that two iterators belong to the same generator.
|
||||||
|
virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0;
|
||||||
|
// Advances iterator to point to the next element
|
||||||
|
// provided by the generator. The caller is responsible
|
||||||
|
// for not calling Advance() on an iterator equal to
|
||||||
|
// BaseGenerator()->End().
|
||||||
|
virtual void Advance() = 0;
|
||||||
|
// Clones the iterator object. Used for implementing copy semantics
|
||||||
|
// of ParamIterator<T>.
|
||||||
|
virtual ParamIteratorInterface* Clone() const = 0;
|
||||||
|
// Dereferences the current iterator and provides (read-only) access
|
||||||
|
// to the pointed value. It is the caller's responsibility not to call
|
||||||
|
// Current() on an iterator equal to BaseGenerator()->End().
|
||||||
|
// Used for implementing ParamGenerator<T>::operator*().
|
||||||
|
virtual const T* Current() const = 0;
|
||||||
|
// Determines whether the given iterator and other point to the same
|
||||||
|
// element in the sequence generated by the generator.
|
||||||
|
// Used for implementing ParamGenerator<T>::operator==().
|
||||||
|
virtual bool Equals(const ParamIteratorInterface& other) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class iterating over elements provided by an implementation of
|
||||||
|
// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
|
||||||
|
// and implements the const forward iterator concept.
|
||||||
|
template <typename T>
|
||||||
|
class ParamIterator {
|
||||||
|
public:
|
||||||
|
typedef T value_type;
|
||||||
|
typedef const T& reference;
|
||||||
|
typedef ptrdiff_t difference_type;
|
||||||
|
|
||||||
|
// ParamIterator assumes ownership of the impl_ pointer.
|
||||||
|
ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {}
|
||||||
|
ParamIterator& operator=(const ParamIterator& other) {
|
||||||
|
if (this != &other) impl_.reset(other.impl_->Clone());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& operator*() const { return *impl_->Current(); }
|
||||||
|
const T* operator->() const { return impl_->Current(); }
|
||||||
|
// Prefix version of operator++.
|
||||||
|
ParamIterator& operator++() {
|
||||||
|
impl_->Advance();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
// Postfix version of operator++.
|
||||||
|
ParamIterator operator++(int /*unused*/) {
|
||||||
|
ParamIteratorInterface<T>* clone = impl_->Clone();
|
||||||
|
impl_->Advance();
|
||||||
|
return ParamIterator(clone);
|
||||||
|
}
|
||||||
|
bool operator==(const ParamIterator& other) const {
|
||||||
|
return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
|
||||||
|
}
|
||||||
|
bool operator!=(const ParamIterator& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ParamGenerator<T>;
|
||||||
|
explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
|
||||||
|
std::unique_ptr<ParamIteratorInterface<T>> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ParamGeneratorInterface<T> is the binary interface to access generators
|
||||||
|
// defined in other translation units.
|
||||||
|
template <typename T>
|
||||||
|
class ParamGeneratorInterface {
|
||||||
|
public:
|
||||||
|
typedef T ParamType;
|
||||||
|
|
||||||
|
virtual ~ParamGeneratorInterface() {}
|
||||||
|
|
||||||
|
// Generator interface definition
|
||||||
|
virtual ParamIteratorInterface<T>* Begin() const = 0;
|
||||||
|
virtual ParamIteratorInterface<T>* End() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wraps ParamGeneratorInterface<T> and provides general generator syntax
|
||||||
|
// compatible with the STL Container concept.
|
||||||
|
// This class implements copy initialization semantics and the contained
|
||||||
|
// ParamGeneratorInterface<T> instance is shared among all copies
|
||||||
|
// of the original object. This is possible because that instance is immutable.
|
||||||
|
template <typename T>
|
||||||
|
class ParamGenerator {
|
||||||
|
public:
|
||||||
|
typedef ParamIterator<T> iterator;
|
||||||
|
|
||||||
|
explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {}
|
||||||
|
ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {}
|
||||||
|
|
||||||
|
ParamGenerator& operator=(const ParamGenerator& other) {
|
||||||
|
impl_ = other.impl_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin() const { return iterator(impl_->Begin()); }
|
||||||
|
iterator end() const { return iterator(impl_->End()); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<const ParamGeneratorInterface<T>> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates values from a range of two comparable values. Can be used to
|
||||||
|
// generate sequences of user-defined types that implement operator+() and
|
||||||
|
// operator<().
|
||||||
|
// This class is used in the Range() function.
|
||||||
|
template <typename T, typename IncrementT>
|
||||||
|
class RangeGenerator : public ParamGeneratorInterface<T> {
|
||||||
|
public:
|
||||||
|
RangeGenerator(T begin, T end, IncrementT step)
|
||||||
|
: begin_(begin),
|
||||||
|
end_(end),
|
||||||
|
step_(step),
|
||||||
|
end_index_(CalculateEndIndex(begin, end, step)) {}
|
||||||
|
~RangeGenerator() override {}
|
||||||
|
|
||||||
|
ParamIteratorInterface<T>* Begin() const override {
|
||||||
|
return new Iterator(this, begin_, 0, step_);
|
||||||
|
}
|
||||||
|
ParamIteratorInterface<T>* End() const override {
|
||||||
|
return new Iterator(this, end_, end_index_, step_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Iterator : public ParamIteratorInterface<T> {
|
||||||
|
public:
|
||||||
|
Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
|
||||||
|
IncrementT step)
|
||||||
|
: base_(base), value_(value), index_(index), step_(step) {}
|
||||||
|
~Iterator() override {}
|
||||||
|
|
||||||
|
const ParamGeneratorInterface<T>* BaseGenerator() const override {
|
||||||
|
return base_;
|
||||||
|
}
|
||||||
|
void Advance() override {
|
||||||
|
value_ = static_cast<T>(value_ + step_);
|
||||||
|
index_++;
|
||||||
|
}
|
||||||
|
ParamIteratorInterface<T>* Clone() const override {
|
||||||
|
return new Iterator(*this);
|
||||||
|
}
|
||||||
|
const T* Current() const override { return &value_; }
|
||||||
|
bool Equals(const ParamIteratorInterface<T>& other) const override {
|
||||||
|
// Having the same base generator guarantees that the other
|
||||||
|
// iterator is of the same type and we can downcast.
|
||||||
|
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
|
||||||
|
<< "The program attempted to compare iterators "
|
||||||
|
<< "from different generators." << std::endl;
|
||||||
|
const int other_index =
|
||||||
|
CheckedDowncastToActualType<const Iterator>(&other)->index_;
|
||||||
|
return index_ == other_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Iterator(const Iterator& other)
|
||||||
|
: ParamIteratorInterface<T>(),
|
||||||
|
base_(other.base_),
|
||||||
|
value_(other.value_),
|
||||||
|
index_(other.index_),
|
||||||
|
step_(other.step_) {}
|
||||||
|
|
||||||
|
// No implementation - assignment is unsupported.
|
||||||
|
void operator=(const Iterator& other);
|
||||||
|
|
||||||
|
const ParamGeneratorInterface<T>* const base_;
|
||||||
|
T value_;
|
||||||
|
int index_;
|
||||||
|
const IncrementT step_;
|
||||||
|
}; // class RangeGenerator::Iterator
|
||||||
|
|
||||||
|
static int CalculateEndIndex(const T& begin, const T& end,
|
||||||
|
const IncrementT& step) {
|
||||||
|
int end_index = 0;
|
||||||
|
for (T i = begin; i < end; i = static_cast<T>(i + step)) end_index++;
|
||||||
|
return end_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No implementation - assignment is unsupported.
|
||||||
|
void operator=(const RangeGenerator& other);
|
||||||
|
|
||||||
|
const T begin_;
|
||||||
|
const T end_;
|
||||||
|
const IncrementT step_;
|
||||||
|
// The index for the end() iterator. All the elements in the generated
|
||||||
|
// sequence are indexed (0-based) to aid iterator comparison.
|
||||||
|
const int end_index_;
|
||||||
|
}; // class RangeGenerator
|
||||||
|
|
||||||
|
// Generates values from a pair of STL-style iterators. Used in the
|
||||||
|
// ValuesIn() function. The elements are copied from the source range
|
||||||
|
// since the source can be located on the stack, and the generator
|
||||||
|
// is likely to persist beyond that stack frame.
|
||||||
|
template <typename T>
|
||||||
|
class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
|
||||||
|
public:
|
||||||
|
template <typename ForwardIterator>
|
||||||
|
ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
|
||||||
|
: container_(begin, end) {}
|
||||||
|
~ValuesInIteratorRangeGenerator() override {}
|
||||||
|
|
||||||
|
ParamIteratorInterface<T>* Begin() const override {
|
||||||
|
return new Iterator(this, container_.begin());
|
||||||
|
}
|
||||||
|
ParamIteratorInterface<T>* End() const override {
|
||||||
|
return new Iterator(this, container_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef typename ::std::vector<T> ContainerType;
|
||||||
|
|
||||||
|
class Iterator : public ParamIteratorInterface<T> {
|
||||||
|
public:
|
||||||
|
Iterator(const ParamGeneratorInterface<T>* base,
|
||||||
|
typename ContainerType::const_iterator iterator)
|
||||||
|
: base_(base), iterator_(iterator) {}
|
||||||
|
~Iterator() override {}
|
||||||
|
|
||||||
|
const ParamGeneratorInterface<T>* BaseGenerator() const override {
|
||||||
|
return base_;
|
||||||
|
}
|
||||||
|
void Advance() override {
|
||||||
|
++iterator_;
|
||||||
|
value_.reset();
|
||||||
|
}
|
||||||
|
ParamIteratorInterface<T>* Clone() const override {
|
||||||
|
return new Iterator(*this);
|
||||||
|
}
|
||||||
|
// We need to use cached value referenced by iterator_ because *iterator_
|
||||||
|
// can return a temporary object (and of type other then T), so just
|
||||||
|
// having "return &*iterator_;" doesn't work.
|
||||||
|
// value_ is updated here and not in Advance() because Advance()
|
||||||
|
// can advance iterator_ beyond the end of the range, and we cannot
|
||||||
|
// detect that fact. The client code, on the other hand, is
|
||||||
|
// responsible for not calling Current() on an out-of-range iterator.
|
||||||
|
const T* Current() const override {
|
||||||
|
if (value_.get() == nullptr) value_.reset(new T(*iterator_));
|
||||||
|
return value_.get();
|
||||||
|
}
|
||||||
|
bool Equals(const ParamIteratorInterface<T>& other) const override {
|
||||||
|
// Having the same base generator guarantees that the other
|
||||||
|
// iterator is of the same type and we can downcast.
|
||||||
|
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
|
||||||
|
<< "The program attempted to compare iterators "
|
||||||
|
<< "from different generators." << std::endl;
|
||||||
|
return iterator_ ==
|
||||||
|
CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Iterator(const Iterator& other)
|
||||||
|
// The explicit constructor call suppresses a false warning
|
||||||
|
// emitted by gcc when supplied with the -Wextra option.
|
||||||
|
: ParamIteratorInterface<T>(),
|
||||||
|
base_(other.base_),
|
||||||
|
iterator_(other.iterator_) {}
|
||||||
|
|
||||||
|
const ParamGeneratorInterface<T>* const base_;
|
||||||
|
typename ContainerType::const_iterator iterator_;
|
||||||
|
// A cached value of *iterator_. We keep it here to allow access by
|
||||||
|
// pointer in the wrapping iterator's operator->().
|
||||||
|
// value_ needs to be mutable to be accessed in Current().
|
||||||
|
// Use of std::unique_ptr helps manage cached value's lifetime,
|
||||||
|
// which is bound by the lifespan of the iterator itself.
|
||||||
|
mutable std::unique_ptr<const T> value_;
|
||||||
|
}; // class ValuesInIteratorRangeGenerator::Iterator
|
||||||
|
|
||||||
|
// No implementation - assignment is unsupported.
|
||||||
|
void operator=(const ValuesInIteratorRangeGenerator& other);
|
||||||
|
|
||||||
|
const ContainerType container_;
|
||||||
|
}; // class ValuesInIteratorRangeGenerator
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// Default parameterized test name generator, returns a string containing the
|
||||||
|
// integer test parameter index.
|
||||||
|
template <class ParamType>
|
||||||
|
std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
|
||||||
|
Message name_stream;
|
||||||
|
name_stream << info.index;
|
||||||
|
return name_stream.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = int>
|
||||||
|
void TestNotEmpty() {
|
||||||
|
static_assert(sizeof(T) == 0, "Empty arguments are not allowed.");
|
||||||
|
}
|
||||||
|
template <typename T = int>
|
||||||
|
void TestNotEmpty(const T&) {}
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// Stores a parameter value and later creates tests parameterized with that
|
||||||
|
// value.
|
||||||
|
template <class TestClass>
|
||||||
|
class ParameterizedTestFactory : public TestFactoryBase {
|
||||||
|
public:
|
||||||
|
typedef typename TestClass::ParamType ParamType;
|
||||||
|
explicit ParameterizedTestFactory(ParamType parameter)
|
||||||
|
: parameter_(parameter) {}
|
||||||
|
Test* CreateTest() override {
|
||||||
|
TestClass::SetParam(¶meter_);
|
||||||
|
return new TestClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ParamType parameter_;
|
||||||
|
|
||||||
|
ParameterizedTestFactory(const ParameterizedTestFactory&) = delete;
|
||||||
|
ParameterizedTestFactory& operator=(const ParameterizedTestFactory&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// TestMetaFactoryBase is a base class for meta-factories that create
|
||||||
|
// test factories for passing into MakeAndRegisterTestInfo function.
|
||||||
|
template <class ParamType>
|
||||||
|
class TestMetaFactoryBase {
|
||||||
|
public:
|
||||||
|
virtual ~TestMetaFactoryBase() {}
|
||||||
|
|
||||||
|
virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// TestMetaFactory creates test factories for passing into
|
||||||
|
// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
|
||||||
|
// ownership of test factory pointer, same factory object cannot be passed
|
||||||
|
// into that method twice. But ParameterizedTestSuiteInfo is going to call
|
||||||
|
// it for each Test/Parameter value combination. Thus it needs meta factory
|
||||||
|
// creator class.
|
||||||
|
template <class TestSuite>
|
||||||
|
class TestMetaFactory
|
||||||
|
: public TestMetaFactoryBase<typename TestSuite::ParamType> {
|
||||||
|
public:
|
||||||
|
using ParamType = typename TestSuite::ParamType;
|
||||||
|
|
||||||
|
TestMetaFactory() {}
|
||||||
|
|
||||||
|
TestFactoryBase* CreateTestFactory(ParamType parameter) override {
|
||||||
|
return new ParameterizedTestFactory<TestSuite>(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TestMetaFactory(const TestMetaFactory&) = delete;
|
||||||
|
TestMetaFactory& operator=(const TestMetaFactory&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// ParameterizedTestSuiteInfoBase is a generic interface
|
||||||
|
// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase
|
||||||
|
// accumulates test information provided by TEST_P macro invocations
|
||||||
|
// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations
|
||||||
|
// and uses that information to register all resulting test instances
|
||||||
|
// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds
|
||||||
|
// a collection of pointers to the ParameterizedTestSuiteInfo objects
|
||||||
|
// and calls RegisterTests() on each of them when asked.
|
||||||
|
class ParameterizedTestSuiteInfoBase {
|
||||||
|
public:
|
||||||
|
virtual ~ParameterizedTestSuiteInfoBase() {}
|
||||||
|
|
||||||
|
// Base part of test suite name for display purposes.
|
||||||
|
virtual const std::string& GetTestSuiteName() const = 0;
|
||||||
|
// Test suite id to verify identity.
|
||||||
|
virtual TypeId GetTestSuiteTypeId() const = 0;
|
||||||
|
// UnitTest class invokes this method to register tests in this
|
||||||
|
// test suite right before running them in RUN_ALL_TESTS macro.
|
||||||
|
// This method should not be called more than once on any single
|
||||||
|
// instance of a ParameterizedTestSuiteInfoBase derived class.
|
||||||
|
virtual void RegisterTests() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ParameterizedTestSuiteInfoBase() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ParameterizedTestSuiteInfoBase(const ParameterizedTestSuiteInfoBase&) =
|
||||||
|
delete;
|
||||||
|
ParameterizedTestSuiteInfoBase& operator=(
|
||||||
|
const ParameterizedTestSuiteInfoBase&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// Report a the name of a test_suit as safe to ignore
|
||||||
|
// as the side effect of construction of this type.
|
||||||
|
struct GTEST_API_ MarkAsIgnored {
|
||||||
|
explicit MarkAsIgnored(const char* test_suite);
|
||||||
|
};
|
||||||
|
|
||||||
|
GTEST_API_ void InsertSyntheticTestCase(const std::string& name,
|
||||||
|
CodeLocation location, bool has_test_p);
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P
|
||||||
|
// macro invocations for a particular test suite and generators
|
||||||
|
// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that
|
||||||
|
// test suite. It registers tests with all values generated by all
|
||||||
|
// generators when asked.
|
||||||
|
template <class TestSuite>
|
||||||
|
class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
|
||||||
|
public:
|
||||||
|
// ParamType and GeneratorCreationFunc are private types but are required
|
||||||
|
// for declarations of public methods AddTestPattern() and
|
||||||
|
// AddTestSuiteInstantiation().
|
||||||
|
using ParamType = typename TestSuite::ParamType;
|
||||||
|
// A function that returns an instance of appropriate generator type.
|
||||||
|
typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
|
||||||
|
using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&);
|
||||||
|
|
||||||
|
explicit ParameterizedTestSuiteInfo(const char* name,
|
||||||
|
CodeLocation code_location)
|
||||||
|
: test_suite_name_(name), code_location_(code_location) {}
|
||||||
|
|
||||||
|
// Test suite base name for display purposes.
|
||||||
|
const std::string& GetTestSuiteName() const override {
|
||||||
|
return test_suite_name_;
|
||||||
|
}
|
||||||
|
// Test suite id to verify identity.
|
||||||
|
TypeId GetTestSuiteTypeId() const override { return GetTypeId<TestSuite>(); }
|
||||||
|
// TEST_P macro uses AddTestPattern() to record information
|
||||||
|
// about a single test in a LocalTestInfo structure.
|
||||||
|
// test_suite_name is the base name of the test suite (without invocation
|
||||||
|
// prefix). test_base_name is the name of an individual test without
|
||||||
|
// parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
|
||||||
|
// test suite base name and DoBar is test base name.
|
||||||
|
void AddTestPattern(const char* test_suite_name, const char* test_base_name,
|
||||||
|
TestMetaFactoryBase<ParamType>* meta_factory,
|
||||||
|
CodeLocation code_location) {
|
||||||
|
tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo(
|
||||||
|
test_suite_name, test_base_name, meta_factory, code_location)));
|
||||||
|
}
|
||||||
|
// INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information
|
||||||
|
// about a generator.
|
||||||
|
int AddTestSuiteInstantiation(const std::string& instantiation_name,
|
||||||
|
GeneratorCreationFunc* func,
|
||||||
|
ParamNameGeneratorFunc* name_func,
|
||||||
|
const char* file, int line) {
|
||||||
|
instantiations_.push_back(
|
||||||
|
InstantiationInfo(instantiation_name, func, name_func, file, line));
|
||||||
|
return 0; // Return value used only to run this method in namespace scope.
|
||||||
|
}
|
||||||
|
// UnitTest class invokes this method to register tests in this test suite
|
||||||
|
// right before running tests in RUN_ALL_TESTS macro.
|
||||||
|
// This method should not be called more than once on any single
|
||||||
|
// instance of a ParameterizedTestSuiteInfoBase derived class.
|
||||||
|
// UnitTest has a guard to prevent from calling this method more than once.
|
||||||
|
void RegisterTests() override {
|
||||||
|
bool generated_instantiations = false;
|
||||||
|
|
||||||
|
for (typename TestInfoContainer::iterator test_it = tests_.begin();
|
||||||
|
test_it != tests_.end(); ++test_it) {
|
||||||
|
std::shared_ptr<TestInfo> test_info = *test_it;
|
||||||
|
for (typename InstantiationContainer::iterator gen_it =
|
||||||
|
instantiations_.begin();
|
||||||
|
gen_it != instantiations_.end(); ++gen_it) {
|
||||||
|
const std::string& instantiation_name = gen_it->name;
|
||||||
|
ParamGenerator<ParamType> generator((*gen_it->generator)());
|
||||||
|
ParamNameGeneratorFunc* name_func = gen_it->name_func;
|
||||||
|
const char* file = gen_it->file;
|
||||||
|
int line = gen_it->line;
|
||||||
|
|
||||||
|
std::string test_suite_name;
|
||||||
|
if (!instantiation_name.empty())
|
||||||
|
test_suite_name = instantiation_name + "/";
|
||||||
|
test_suite_name += test_info->test_suite_base_name;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
std::set<std::string> test_param_names;
|
||||||
|
for (typename ParamGenerator<ParamType>::iterator param_it =
|
||||||
|
generator.begin();
|
||||||
|
param_it != generator.end(); ++param_it, ++i) {
|
||||||
|
generated_instantiations = true;
|
||||||
|
|
||||||
|
Message test_name_stream;
|
||||||
|
|
||||||
|
std::string param_name =
|
||||||
|
name_func(TestParamInfo<ParamType>(*param_it, i));
|
||||||
|
|
||||||
|
GTEST_CHECK_(IsValidParamName(param_name))
|
||||||
|
<< "Parameterized test name '" << param_name
|
||||||
|
<< "' is invalid, in " << file << " line " << line << std::endl;
|
||||||
|
|
||||||
|
GTEST_CHECK_(test_param_names.count(param_name) == 0)
|
||||||
|
<< "Duplicate parameterized test name '" << param_name << "', in "
|
||||||
|
<< file << " line " << line << std::endl;
|
||||||
|
|
||||||
|
test_param_names.insert(param_name);
|
||||||
|
|
||||||
|
if (!test_info->test_base_name.empty()) {
|
||||||
|
test_name_stream << test_info->test_base_name << "/";
|
||||||
|
}
|
||||||
|
test_name_stream << param_name;
|
||||||
|
MakeAndRegisterTestInfo(
|
||||||
|
test_suite_name.c_str(), test_name_stream.GetString().c_str(),
|
||||||
|
nullptr, // No type parameter.
|
||||||
|
PrintToString(*param_it).c_str(), test_info->code_location,
|
||||||
|
GetTestSuiteTypeId(),
|
||||||
|
SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line),
|
||||||
|
SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line),
|
||||||
|
test_info->test_meta_factory->CreateTestFactory(*param_it));
|
||||||
|
} // for param_it
|
||||||
|
} // for gen_it
|
||||||
|
} // for test_it
|
||||||
|
|
||||||
|
if (!generated_instantiations) {
|
||||||
|
// There are no generaotrs, or they all generate nothing ...
|
||||||
|
InsertSyntheticTestCase(GetTestSuiteName(), code_location_,
|
||||||
|
!tests_.empty());
|
||||||
|
}
|
||||||
|
} // RegisterTests
|
||||||
|
|
||||||
|
private:
|
||||||
|
// LocalTestInfo structure keeps information about a single test registered
|
||||||
|
// with TEST_P macro.
|
||||||
|
struct TestInfo {
|
||||||
|
TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name,
|
||||||
|
TestMetaFactoryBase<ParamType>* a_test_meta_factory,
|
||||||
|
CodeLocation a_code_location)
|
||||||
|
: test_suite_base_name(a_test_suite_base_name),
|
||||||
|
test_base_name(a_test_base_name),
|
||||||
|
test_meta_factory(a_test_meta_factory),
|
||||||
|
code_location(a_code_location) {}
|
||||||
|
|
||||||
|
const std::string test_suite_base_name;
|
||||||
|
const std::string test_base_name;
|
||||||
|
const std::unique_ptr<TestMetaFactoryBase<ParamType>> test_meta_factory;
|
||||||
|
const CodeLocation code_location;
|
||||||
|
};
|
||||||
|
using TestInfoContainer = ::std::vector<std::shared_ptr<TestInfo>>;
|
||||||
|
// Records data received from INSTANTIATE_TEST_SUITE_P macros:
|
||||||
|
// <Instantiation name, Sequence generator creation function,
|
||||||
|
// Name generator function, Source file, Source line>
|
||||||
|
struct InstantiationInfo {
|
||||||
|
InstantiationInfo(const std::string& name_in,
|
||||||
|
GeneratorCreationFunc* generator_in,
|
||||||
|
ParamNameGeneratorFunc* name_func_in, const char* file_in,
|
||||||
|
int line_in)
|
||||||
|
: name(name_in),
|
||||||
|
generator(generator_in),
|
||||||
|
name_func(name_func_in),
|
||||||
|
file(file_in),
|
||||||
|
line(line_in) {}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
GeneratorCreationFunc* generator;
|
||||||
|
ParamNameGeneratorFunc* name_func;
|
||||||
|
const char* file;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
typedef ::std::vector<InstantiationInfo> InstantiationContainer;
|
||||||
|
|
||||||
|
static bool IsValidParamName(const std::string& name) {
|
||||||
|
// Check for empty string
|
||||||
|
if (name.empty()) return false;
|
||||||
|
|
||||||
|
// Check for invalid characters
|
||||||
|
for (std::string::size_type index = 0; index < name.size(); ++index) {
|
||||||
|
if (!IsAlNum(name[index]) && name[index] != '_') return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string test_suite_name_;
|
||||||
|
CodeLocation code_location_;
|
||||||
|
TestInfoContainer tests_;
|
||||||
|
InstantiationContainer instantiations_;
|
||||||
|
|
||||||
|
ParameterizedTestSuiteInfo(const ParameterizedTestSuiteInfo&) = delete;
|
||||||
|
ParameterizedTestSuiteInfo& operator=(const ParameterizedTestSuiteInfo&) =
|
||||||
|
delete;
|
||||||
|
}; // class ParameterizedTestSuiteInfo
|
||||||
|
|
||||||
|
// Legacy API is deprecated but still available
|
||||||
|
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
template <class TestCase>
|
||||||
|
using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo<TestCase>;
|
||||||
|
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
|
||||||
|
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
|
||||||
|
//
|
||||||
|
// ParameterizedTestSuiteRegistry contains a map of
|
||||||
|
// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P
|
||||||
|
// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding
|
||||||
|
// ParameterizedTestSuiteInfo descriptors.
|
||||||
|
class ParameterizedTestSuiteRegistry {
|
||||||
|
public:
|
||||||
|
ParameterizedTestSuiteRegistry() {}
|
||||||
|
~ParameterizedTestSuiteRegistry() {
|
||||||
|
for (auto& test_suite_info : test_suite_infos_) {
|
||||||
|
delete test_suite_info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up or creates and returns a structure containing information about
|
||||||
|
// tests and instantiations of a particular test suite.
|
||||||
|
template <class TestSuite>
|
||||||
|
ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder(
|
||||||
|
const char* test_suite_name, CodeLocation code_location) {
|
||||||
|
ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr;
|
||||||
|
for (auto& test_suite_info : test_suite_infos_) {
|
||||||
|
if (test_suite_info->GetTestSuiteName() == test_suite_name) {
|
||||||
|
if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) {
|
||||||
|
// Complain about incorrect usage of Google Test facilities
|
||||||
|
// and terminate the program since we cannot guaranty correct
|
||||||
|
// test suite setup and tear-down in this case.
|
||||||
|
ReportInvalidTestSuiteType(test_suite_name, code_location);
|
||||||
|
posix::Abort();
|
||||||
|
} else {
|
||||||
|
// At this point we are sure that the object we found is of the same
|
||||||
|
// type we are looking for, so we downcast it to that type
|
||||||
|
// without further checks.
|
||||||
|
typed_test_info = CheckedDowncastToActualType<
|
||||||
|
ParameterizedTestSuiteInfo<TestSuite>>(test_suite_info);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typed_test_info == nullptr) {
|
||||||
|
typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>(
|
||||||
|
test_suite_name, code_location);
|
||||||
|
test_suite_infos_.push_back(typed_test_info);
|
||||||
|
}
|
||||||
|
return typed_test_info;
|
||||||
|
}
|
||||||
|
void RegisterTests() {
|
||||||
|
for (auto& test_suite_info : test_suite_infos_) {
|
||||||
|
test_suite_info->RegisterTests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Legacy API is deprecated but still available
|
||||||
|
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
template <class TestCase>
|
||||||
|
ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
|
||||||
|
const char* test_case_name, CodeLocation code_location) {
|
||||||
|
return GetTestSuitePatternHolder<TestCase>(test_case_name, code_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
|
||||||
|
|
||||||
|
private:
|
||||||
|
using TestSuiteInfoContainer = ::std::vector<ParameterizedTestSuiteInfoBase*>;
|
||||||
|
|
||||||
|
TestSuiteInfoContainer test_suite_infos_;
|
||||||
|
|
||||||
|
ParameterizedTestSuiteRegistry(const ParameterizedTestSuiteRegistry&) =
|
||||||
|
delete;
|
||||||
|
ParameterizedTestSuiteRegistry& operator=(
|
||||||
|
const ParameterizedTestSuiteRegistry&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Keep track of what type-parameterized test suite are defined and
|
||||||
|
// where as well as which are intatiated. This allows susequently
|
||||||
|
// identifying suits that are defined but never used.
|
||||||
|
class TypeParameterizedTestSuiteRegistry {
|
||||||
|
public:
|
||||||
|
// Add a suite definition
|
||||||
|
void RegisterTestSuite(const char* test_suite_name,
|
||||||
|
CodeLocation code_location);
|
||||||
|
|
||||||
|
// Add an instantiation of a suit.
|
||||||
|
void RegisterInstantiation(const char* test_suite_name);
|
||||||
|
|
||||||
|
// For each suit repored as defined but not reported as instantiation,
|
||||||
|
// emit a test that reports that fact (configurably, as an error).
|
||||||
|
void CheckForInstantiations();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct TypeParameterizedTestSuiteInfo {
|
||||||
|
explicit TypeParameterizedTestSuiteInfo(CodeLocation c)
|
||||||
|
: code_location(c), instantiated(false) {}
|
||||||
|
|
||||||
|
CodeLocation code_location;
|
||||||
|
bool instantiated;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, TypeParameterizedTestSuiteInfo> suites_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Forward declarations of ValuesIn(), which is implemented in
|
||||||
|
// include/gtest/gtest-param-test.h.
|
||||||
|
template <class Container>
|
||||||
|
internal::ParamGenerator<typename Container::value_type> ValuesIn(
|
||||||
|
const Container& container);
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
// Used in the Values() function to provide polymorphic capabilities.
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4100)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
class ValueArray {
|
||||||
|
public:
|
||||||
|
explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
operator ParamGenerator<T>() const { // NOLINT
|
||||||
|
return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T, size_t... I>
|
||||||
|
std::vector<T> MakeVector(IndexSequence<I...>) const {
|
||||||
|
return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
|
||||||
|
}
|
||||||
|
|
||||||
|
FlatTuple<Ts...> v_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
class CartesianProductGenerator
|
||||||
|
: public ParamGeneratorInterface<::std::tuple<T...>> {
|
||||||
|
public:
|
||||||
|
typedef ::std::tuple<T...> ParamType;
|
||||||
|
|
||||||
|
CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g)
|
||||||
|
: generators_(g) {}
|
||||||
|
~CartesianProductGenerator() override {}
|
||||||
|
|
||||||
|
ParamIteratorInterface<ParamType>* Begin() const override {
|
||||||
|
return new Iterator(this, generators_, false);
|
||||||
|
}
|
||||||
|
ParamIteratorInterface<ParamType>* End() const override {
|
||||||
|
return new Iterator(this, generators_, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class I>
|
||||||
|
class IteratorImpl;
|
||||||
|
template <size_t... I>
|
||||||
|
class IteratorImpl<IndexSequence<I...>>
|
||||||
|
: public ParamIteratorInterface<ParamType> {
|
||||||
|
public:
|
||||||
|
IteratorImpl(const ParamGeneratorInterface<ParamType>* base,
|
||||||
|
const std::tuple<ParamGenerator<T>...>& generators,
|
||||||
|
bool is_end)
|
||||||
|
: base_(base),
|
||||||
|
begin_(std::get<I>(generators).begin()...),
|
||||||
|
end_(std::get<I>(generators).end()...),
|
||||||
|
current_(is_end ? end_ : begin_) {
|
||||||
|
ComputeCurrentValue();
|
||||||
|
}
|
||||||
|
~IteratorImpl() override {}
|
||||||
|
|
||||||
|
const ParamGeneratorInterface<ParamType>* BaseGenerator() const override {
|
||||||
|
return base_;
|
||||||
|
}
|
||||||
|
// Advance should not be called on beyond-of-range iterators
|
||||||
|
// so no component iterators must be beyond end of range, either.
|
||||||
|
void Advance() override {
|
||||||
|
assert(!AtEnd());
|
||||||
|
// Advance the last iterator.
|
||||||
|
++std::get<sizeof...(T) - 1>(current_);
|
||||||
|
// if that reaches end, propagate that up.
|
||||||
|
AdvanceIfEnd<sizeof...(T) - 1>();
|
||||||
|
ComputeCurrentValue();
|
||||||
|
}
|
||||||
|
ParamIteratorInterface<ParamType>* Clone() const override {
|
||||||
|
return new IteratorImpl(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ParamType* Current() const override { return current_value_.get(); }
|
||||||
|
|
||||||
|
bool Equals(const ParamIteratorInterface<ParamType>& other) const override {
|
||||||
|
// Having the same base generator guarantees that the other
|
||||||
|
// iterator is of the same type and we can downcast.
|
||||||
|
GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
|
||||||
|
<< "The program attempted to compare iterators "
|
||||||
|
<< "from different generators." << std::endl;
|
||||||
|
const IteratorImpl* typed_other =
|
||||||
|
CheckedDowncastToActualType<const IteratorImpl>(&other);
|
||||||
|
|
||||||
|
// We must report iterators equal if they both point beyond their
|
||||||
|
// respective ranges. That can happen in a variety of fashions,
|
||||||
|
// so we have to consult AtEnd().
|
||||||
|
if (AtEnd() && typed_other->AtEnd()) return true;
|
||||||
|
|
||||||
|
bool same = true;
|
||||||
|
bool dummy[] = {
|
||||||
|
(same = same && std::get<I>(current_) ==
|
||||||
|
std::get<I>(typed_other->current_))...};
|
||||||
|
(void)dummy;
|
||||||
|
return same;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <size_t ThisI>
|
||||||
|
void AdvanceIfEnd() {
|
||||||
|
if (std::get<ThisI>(current_) != std::get<ThisI>(end_)) return;
|
||||||
|
|
||||||
|
bool last = ThisI == 0;
|
||||||
|
if (last) {
|
||||||
|
// We are done. Nothing else to propagate.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t NextI = ThisI - (ThisI != 0);
|
||||||
|
std::get<ThisI>(current_) = std::get<ThisI>(begin_);
|
||||||
|
++std::get<NextI>(current_);
|
||||||
|
AdvanceIfEnd<NextI>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComputeCurrentValue() {
|
||||||
|
if (!AtEnd())
|
||||||
|
current_value_ = std::make_shared<ParamType>(*std::get<I>(current_)...);
|
||||||
|
}
|
||||||
|
bool AtEnd() const {
|
||||||
|
bool at_end = false;
|
||||||
|
bool dummy[] = {
|
||||||
|
(at_end = at_end || std::get<I>(current_) == std::get<I>(end_))...};
|
||||||
|
(void)dummy;
|
||||||
|
return at_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ParamGeneratorInterface<ParamType>* const base_;
|
||||||
|
std::tuple<typename ParamGenerator<T>::iterator...> begin_;
|
||||||
|
std::tuple<typename ParamGenerator<T>::iterator...> end_;
|
||||||
|
std::tuple<typename ParamGenerator<T>::iterator...> current_;
|
||||||
|
std::shared_ptr<ParamType> current_value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Iterator = IteratorImpl<typename MakeIndexSequence<sizeof...(T)>::type>;
|
||||||
|
|
||||||
|
std::tuple<ParamGenerator<T>...> generators_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class... Gen>
|
||||||
|
class CartesianProductHolder {
|
||||||
|
public:
|
||||||
|
CartesianProductHolder(const Gen&... g) : generators_(g...) {}
|
||||||
|
template <typename... T>
|
||||||
|
operator ParamGenerator<::std::tuple<T...>>() const {
|
||||||
|
return ParamGenerator<::std::tuple<T...>>(
|
||||||
|
new CartesianProductGenerator<T...>(generators_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::tuple<Gen...> generators_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
|
116
test/gtest/include/gtest/internal/gtest-port-arch.h
Normal file
116
test/gtest/include/gtest/internal/gtest-port-arch.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright 2015, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This header file defines the GTEST_OS_* macro.
|
||||||
|
// It is separate from gtest-port.h so that custom/gtest-port.h can include it.
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
|
||||||
|
|
||||||
|
// Determines the platform on which Google Test is compiled.
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
#define GTEST_OS_CYGWIN 1
|
||||||
|
#elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||||
|
#define GTEST_OS_WINDOWS_MINGW 1
|
||||||
|
#define GTEST_OS_WINDOWS 1
|
||||||
|
#elif defined _WIN32
|
||||||
|
#define GTEST_OS_WINDOWS 1
|
||||||
|
#ifdef _WIN32_WCE
|
||||||
|
#define GTEST_OS_WINDOWS_MOBILE 1
|
||||||
|
#elif defined(WINAPI_FAMILY)
|
||||||
|
#include <winapifamily.h>
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
#define GTEST_OS_WINDOWS_DESKTOP 1
|
||||||
|
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
|
||||||
|
#define GTEST_OS_WINDOWS_PHONE 1
|
||||||
|
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
|
||||||
|
#define GTEST_OS_WINDOWS_RT 1
|
||||||
|
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)
|
||||||
|
#define GTEST_OS_WINDOWS_PHONE 1
|
||||||
|
#define GTEST_OS_WINDOWS_TV_TITLE 1
|
||||||
|
#else
|
||||||
|
// WINAPI_FAMILY defined but no known partition matched.
|
||||||
|
// Default to desktop.
|
||||||
|
#define GTEST_OS_WINDOWS_DESKTOP 1
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define GTEST_OS_WINDOWS_DESKTOP 1
|
||||||
|
#endif // _WIN32_WCE
|
||||||
|
#elif defined __OS2__
|
||||||
|
#define GTEST_OS_OS2 1
|
||||||
|
#elif defined __APPLE__
|
||||||
|
#define GTEST_OS_MAC 1
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
#define GTEST_OS_IOS 1
|
||||||
|
#endif
|
||||||
|
#elif defined __DragonFly__
|
||||||
|
#define GTEST_OS_DRAGONFLY 1
|
||||||
|
#elif defined __FreeBSD__
|
||||||
|
#define GTEST_OS_FREEBSD 1
|
||||||
|
#elif defined __Fuchsia__
|
||||||
|
#define GTEST_OS_FUCHSIA 1
|
||||||
|
#elif defined(__GNU__)
|
||||||
|
#define GTEST_OS_GNU_HURD 1
|
||||||
|
#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__)
|
||||||
|
#define GTEST_OS_GNU_KFREEBSD 1
|
||||||
|
#elif defined __linux__
|
||||||
|
#define GTEST_OS_LINUX 1
|
||||||
|
#if defined __ANDROID__
|
||||||
|
#define GTEST_OS_LINUX_ANDROID 1
|
||||||
|
#endif
|
||||||
|
#elif defined __MVS__
|
||||||
|
#define GTEST_OS_ZOS 1
|
||||||
|
#elif defined(__sun) && defined(__SVR4)
|
||||||
|
#define GTEST_OS_SOLARIS 1
|
||||||
|
#elif defined(_AIX)
|
||||||
|
#define GTEST_OS_AIX 1
|
||||||
|
#elif defined(__hpux)
|
||||||
|
#define GTEST_OS_HPUX 1
|
||||||
|
#elif defined __native_client__
|
||||||
|
#define GTEST_OS_NACL 1
|
||||||
|
#elif defined __NetBSD__
|
||||||
|
#define GTEST_OS_NETBSD 1
|
||||||
|
#elif defined __OpenBSD__
|
||||||
|
#define GTEST_OS_OPENBSD 1
|
||||||
|
#elif defined __QNX__
|
||||||
|
#define GTEST_OS_QNX 1
|
||||||
|
#elif defined(__HAIKU__)
|
||||||
|
#define GTEST_OS_HAIKU 1
|
||||||
|
#elif defined ESP8266
|
||||||
|
#define GTEST_OS_ESP8266 1
|
||||||
|
#elif defined ESP32
|
||||||
|
#define GTEST_OS_ESP32 1
|
||||||
|
#elif defined(__XTENSA__)
|
||||||
|
#define GTEST_OS_XTENSA 1
|
||||||
|
#endif // __CYGWIN__
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
|
2413
test/gtest/include/gtest/internal/gtest-port.h
Normal file
2413
test/gtest/include/gtest/internal/gtest-port.h
Normal file
File diff suppressed because it is too large
Load Diff
177
test/gtest/include/gtest/internal/gtest-string.h
Normal file
177
test/gtest/include/gtest/internal/gtest-string.h
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Copyright 2005, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This header file declares the String class and functions used internally by
|
||||||
|
// Google Test. They are subject to change without notice. They should not used
|
||||||
|
// by code external to Google Test.
|
||||||
|
//
|
||||||
|
// This header file is #included by gtest-internal.h.
|
||||||
|
// It should not be #included by other files.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
|
||||||
|
|
||||||
|
#ifdef __BORLANDC__
|
||||||
|
// string.h is not guaranteed to provide strcpy on C++ Builder.
|
||||||
|
#include <mem.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// String - an abstract class holding static string utilities.
|
||||||
|
class GTEST_API_ String {
|
||||||
|
public:
|
||||||
|
// Static utility methods
|
||||||
|
|
||||||
|
// Clones a 0-terminated C string, allocating memory using new. The
|
||||||
|
// caller is responsible for deleting the return value using
|
||||||
|
// delete[]. Returns the cloned string, or NULL if the input is
|
||||||
|
// NULL.
|
||||||
|
//
|
||||||
|
// This is different from strdup() in string.h, which allocates
|
||||||
|
// memory using malloc().
|
||||||
|
static const char* CloneCString(const char* c_str);
|
||||||
|
|
||||||
|
#if GTEST_OS_WINDOWS_MOBILE
|
||||||
|
// Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
|
||||||
|
// able to pass strings to Win32 APIs on CE we need to convert them
|
||||||
|
// to 'Unicode', UTF-16.
|
||||||
|
|
||||||
|
// Creates a UTF-16 wide string from the given ANSI string, allocating
|
||||||
|
// memory using new. The caller is responsible for deleting the return
|
||||||
|
// value using delete[]. Returns the wide string, or NULL if the
|
||||||
|
// input is NULL.
|
||||||
|
//
|
||||||
|
// The wide string is created using the ANSI codepage (CP_ACP) to
|
||||||
|
// match the behaviour of the ANSI versions of Win32 calls and the
|
||||||
|
// C runtime.
|
||||||
|
static LPCWSTR AnsiToUtf16(const char* c_str);
|
||||||
|
|
||||||
|
// Creates an ANSI string from the given wide string, allocating
|
||||||
|
// memory using new. The caller is responsible for deleting the return
|
||||||
|
// value using delete[]. Returns the ANSI string, or NULL if the
|
||||||
|
// input is NULL.
|
||||||
|
//
|
||||||
|
// The returned string is created using the ANSI codepage (CP_ACP) to
|
||||||
|
// match the behaviour of the ANSI versions of Win32 calls and the
|
||||||
|
// C runtime.
|
||||||
|
static const char* Utf16ToAnsi(LPCWSTR utf16_str);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Compares two C strings. Returns true if and only if they have the same
|
||||||
|
// content.
|
||||||
|
//
|
||||||
|
// Unlike strcmp(), this function can handle NULL argument(s). A
|
||||||
|
// NULL C string is considered different to any non-NULL C string,
|
||||||
|
// including the empty string.
|
||||||
|
static bool CStringEquals(const char* lhs, const char* rhs);
|
||||||
|
|
||||||
|
// Converts a wide C string to a String using the UTF-8 encoding.
|
||||||
|
// NULL will be converted to "(null)". If an error occurred during
|
||||||
|
// the conversion, "(failed to convert from wide string)" is
|
||||||
|
// returned.
|
||||||
|
static std::string ShowWideCString(const wchar_t* wide_c_str);
|
||||||
|
|
||||||
|
// Compares two wide C strings. Returns true if and only if they have the
|
||||||
|
// same content.
|
||||||
|
//
|
||||||
|
// Unlike wcscmp(), this function can handle NULL argument(s). A
|
||||||
|
// NULL C string is considered different to any non-NULL C string,
|
||||||
|
// including the empty string.
|
||||||
|
static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
|
||||||
|
|
||||||
|
// Compares two C strings, ignoring case. Returns true if and only if
|
||||||
|
// they have the same content.
|
||||||
|
//
|
||||||
|
// Unlike strcasecmp(), this function can handle NULL argument(s).
|
||||||
|
// A NULL C string is considered different to any non-NULL C string,
|
||||||
|
// including the empty string.
|
||||||
|
static bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs);
|
||||||
|
|
||||||
|
// Compares two wide C strings, ignoring case. Returns true if and only if
|
||||||
|
// they have the same content.
|
||||||
|
//
|
||||||
|
// Unlike wcscasecmp(), this function can handle NULL argument(s).
|
||||||
|
// A NULL C string is considered different to any non-NULL wide C string,
|
||||||
|
// including the empty string.
|
||||||
|
// NB: The implementations on different platforms slightly differ.
|
||||||
|
// On windows, this method uses _wcsicmp which compares according to LC_CTYPE
|
||||||
|
// environment variable. On GNU platform this method uses wcscasecmp
|
||||||
|
// which compares according to LC_CTYPE category of the current locale.
|
||||||
|
// On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
|
||||||
|
// current locale.
|
||||||
|
static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
|
||||||
|
const wchar_t* rhs);
|
||||||
|
|
||||||
|
// Returns true if and only if the given string ends with the given suffix,
|
||||||
|
// ignoring case. Any string is considered to end with an empty suffix.
|
||||||
|
static bool EndsWithCaseInsensitive(const std::string& str,
|
||||||
|
const std::string& suffix);
|
||||||
|
|
||||||
|
// Formats an int value as "%02d".
|
||||||
|
static std::string FormatIntWidth2(int value); // "%02d" for width == 2
|
||||||
|
|
||||||
|
// Formats an int value to given width with leading zeros.
|
||||||
|
static std::string FormatIntWidthN(int value, int width);
|
||||||
|
|
||||||
|
// Formats an int value as "%X".
|
||||||
|
static std::string FormatHexInt(int value);
|
||||||
|
|
||||||
|
// Formats an int value as "%X".
|
||||||
|
static std::string FormatHexUInt32(uint32_t value);
|
||||||
|
|
||||||
|
// Formats a byte as "%02X".
|
||||||
|
static std::string FormatByte(unsigned char value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
String(); // Not meant to be instantiated.
|
||||||
|
}; // class String
|
||||||
|
|
||||||
|
// Gets the content of the stringstream's buffer as an std::string. Each '\0'
|
||||||
|
// character in the buffer is replaced with "\\0".
|
||||||
|
GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
|
186
test/gtest/include/gtest/internal/gtest-type-util.h
Normal file
186
test/gtest/include/gtest/internal/gtest-type-util.h
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// Copyright 2008 Google Inc.
|
||||||
|
// All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Type utilities needed for implementing typed and type-parameterized
|
||||||
|
// tests.
|
||||||
|
|
||||||
|
// IWYU pragma: private, include "gtest/gtest.h"
|
||||||
|
// IWYU pragma: friend gtest/.*
|
||||||
|
// IWYU pragma: friend gmock/.*
|
||||||
|
|
||||||
|
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
|
||||||
|
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
// #ifdef __GNUC__ is too general here. It is possible to use gcc without using
|
||||||
|
// libstdc++ (which is where cxxabi.h comes from).
|
||||||
|
#if GTEST_HAS_CXXABI_H_
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#elif defined(__HP_aCC)
|
||||||
|
#include <acxx_demangle.h>
|
||||||
|
#endif // GTEST_HASH_CXXABI_H_
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Canonicalizes a given name with respect to the Standard C++ Library.
|
||||||
|
// This handles removing the inline namespace within `std` that is
|
||||||
|
// used by various standard libraries (e.g., `std::__1`). Names outside
|
||||||
|
// of namespace std are returned unmodified.
|
||||||
|
inline std::string CanonicalizeForStdLibVersioning(std::string s) {
|
||||||
|
static const char prefix[] = "std::__";
|
||||||
|
if (s.compare(0, strlen(prefix), prefix) == 0) {
|
||||||
|
std::string::size_type end = s.find("::", strlen(prefix));
|
||||||
|
if (end != s.npos) {
|
||||||
|
// Erase everything between the initial `std` and the second `::`.
|
||||||
|
s.erase(strlen("std"), end - strlen("std"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if GTEST_HAS_RTTI
|
||||||
|
// GetTypeName(const std::type_info&) returns a human-readable name of type T.
|
||||||
|
inline std::string GetTypeName(const std::type_info& type) {
|
||||||
|
const char* const name = type.name();
|
||||||
|
#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
|
||||||
|
int status = 0;
|
||||||
|
// gcc's implementation of typeid(T).name() mangles the type name,
|
||||||
|
// so we have to demangle it.
|
||||||
|
#if GTEST_HAS_CXXABI_H_
|
||||||
|
using abi::__cxa_demangle;
|
||||||
|
#endif // GTEST_HAS_CXXABI_H_
|
||||||
|
char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status);
|
||||||
|
const std::string name_str(status == 0 ? readable_name : name);
|
||||||
|
free(readable_name);
|
||||||
|
return CanonicalizeForStdLibVersioning(name_str);
|
||||||
|
#else
|
||||||
|
return name;
|
||||||
|
#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
|
||||||
|
}
|
||||||
|
#endif // GTEST_HAS_RTTI
|
||||||
|
|
||||||
|
// GetTypeName<T>() returns a human-readable name of type T if and only if
|
||||||
|
// RTTI is enabled, otherwise it returns a dummy type name.
|
||||||
|
// NB: This function is also used in Google Mock, so don't move it inside of
|
||||||
|
// the typed-test-only section below.
|
||||||
|
template <typename T>
|
||||||
|
std::string GetTypeName() {
|
||||||
|
#if GTEST_HAS_RTTI
|
||||||
|
return GetTypeName(typeid(T));
|
||||||
|
#else
|
||||||
|
return "<type>";
|
||||||
|
#endif // GTEST_HAS_RTTI
|
||||||
|
}
|
||||||
|
|
||||||
|
// A unique type indicating an empty node
|
||||||
|
struct None {};
|
||||||
|
|
||||||
|
#define GTEST_TEMPLATE_ \
|
||||||
|
template <typename T> \
|
||||||
|
class
|
||||||
|
|
||||||
|
// The template "selector" struct TemplateSel<Tmpl> is used to
|
||||||
|
// represent Tmpl, which must be a class template with one type
|
||||||
|
// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined
|
||||||
|
// as the type Tmpl<T>. This allows us to actually instantiate the
|
||||||
|
// template "selected" by TemplateSel<Tmpl>.
|
||||||
|
//
|
||||||
|
// This trick is necessary for simulating typedef for class templates,
|
||||||
|
// which C++ doesn't support directly.
|
||||||
|
template <GTEST_TEMPLATE_ Tmpl>
|
||||||
|
struct TemplateSel {
|
||||||
|
template <typename T>
|
||||||
|
struct Bind {
|
||||||
|
typedef Tmpl<T> type;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GTEST_BIND_(TmplSel, T) TmplSel::template Bind<T>::type
|
||||||
|
|
||||||
|
template <GTEST_TEMPLATE_ Head_, GTEST_TEMPLATE_... Tail_>
|
||||||
|
struct Templates {
|
||||||
|
using Head = TemplateSel<Head_>;
|
||||||
|
using Tail = Templates<Tail_...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <GTEST_TEMPLATE_ Head_>
|
||||||
|
struct Templates<Head_> {
|
||||||
|
using Head = TemplateSel<Head_>;
|
||||||
|
using Tail = None;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tuple-like type lists
|
||||||
|
template <typename Head_, typename... Tail_>
|
||||||
|
struct Types {
|
||||||
|
using Head = Head_;
|
||||||
|
using Tail = Types<Tail_...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Head_>
|
||||||
|
struct Types<Head_> {
|
||||||
|
using Head = Head_;
|
||||||
|
using Tail = None;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper metafunctions to tell apart a single type from types
|
||||||
|
// generated by ::testing::Types
|
||||||
|
template <typename... Ts>
|
||||||
|
struct ProxyTypeList {
|
||||||
|
using type = Types<Ts...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
struct is_proxy_type_list : std::false_type {};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
struct is_proxy_type_list<ProxyTypeList<Ts...>> : std::true_type {};
|
||||||
|
|
||||||
|
// Generator which conditionally creates type lists.
|
||||||
|
// It recognizes if a requested type list should be created
|
||||||
|
// and prevents creating a new type list nested within another one.
|
||||||
|
template <typename T>
|
||||||
|
struct GenerateTypeList {
|
||||||
|
private:
|
||||||
|
using proxy = typename std::conditional<is_proxy_type_list<T>::value, T,
|
||||||
|
ProxyTypeList<T>>::type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using type = typename proxy::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
using Types = internal::ProxyTypeList<Ts...>;
|
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
|
||||||
|
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
|
49
test/gtest/src/gtest-all.cc
Normal file
49
test/gtest/src/gtest-all.cc
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2008, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
//
|
||||||
|
// Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// Sometimes it's desirable to build Google Test by compiling a single file.
|
||||||
|
// This file serves this purpose.
|
||||||
|
|
||||||
|
// This line ensures that gtest.h can be compiled on its own, even
|
||||||
|
// when it's fused.
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
// The following lines pull in the real gtest *.cc files.
|
||||||
|
#include "src/gtest-assertion-result.cc"
|
||||||
|
#include "src/gtest-death-test.cc"
|
||||||
|
#include "src/gtest-filepath.cc"
|
||||||
|
#include "src/gtest-matchers.cc"
|
||||||
|
#include "src/gtest-port.cc"
|
||||||
|
#include "src/gtest-printers.cc"
|
||||||
|
#include "src/gtest-test-part.cc"
|
||||||
|
#include "src/gtest-typed-test.cc"
|
||||||
|
#include "src/gtest.cc"
|
77
test/gtest/src/gtest-assertion-result.cc
Normal file
77
test/gtest/src/gtest-assertion-result.cc
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2005, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This file defines the AssertionResult type.
|
||||||
|
|
||||||
|
#include "gtest/gtest-assertion-result.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "gtest/gtest-message.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// AssertionResult constructors.
|
||||||
|
// Used in EXPECT_TRUE/FALSE(assertion_result).
|
||||||
|
AssertionResult::AssertionResult(const AssertionResult& other)
|
||||||
|
: success_(other.success_),
|
||||||
|
message_(other.message_.get() != nullptr
|
||||||
|
? new ::std::string(*other.message_)
|
||||||
|
: static_cast< ::std::string*>(nullptr)) {}
|
||||||
|
|
||||||
|
// Swaps two AssertionResults.
|
||||||
|
void AssertionResult::swap(AssertionResult& other) {
|
||||||
|
using std::swap;
|
||||||
|
swap(success_, other.success_);
|
||||||
|
swap(message_, other.message_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
|
||||||
|
AssertionResult AssertionResult::operator!() const {
|
||||||
|
AssertionResult negation(!success_);
|
||||||
|
if (message_.get() != nullptr) negation << *message_;
|
||||||
|
return negation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a successful assertion result.
|
||||||
|
AssertionResult AssertionSuccess() { return AssertionResult(true); }
|
||||||
|
|
||||||
|
// Makes a failed assertion result.
|
||||||
|
AssertionResult AssertionFailure() { return AssertionResult(false); }
|
||||||
|
|
||||||
|
// Makes a failed assertion result with the given failure message.
|
||||||
|
// Deprecated; use AssertionFailure() << message.
|
||||||
|
AssertionResult AssertionFailure(const Message& message) {
|
||||||
|
return AssertionFailure() << message;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace testing
|
1620
test/gtest/src/gtest-death-test.cc
Normal file
1620
test/gtest/src/gtest-death-test.cc
Normal file
File diff suppressed because it is too large
Load Diff
367
test/gtest/src/gtest-filepath.cc
Normal file
367
test/gtest/src/gtest-filepath.cc
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
// Copyright 2008, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-filepath.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "gtest/gtest-message.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
#if GTEST_OS_WINDOWS_MOBILE
|
||||||
|
#include <windows.h>
|
||||||
|
#elif GTEST_OS_WINDOWS
|
||||||
|
#include <direct.h>
|
||||||
|
#include <io.h>
|
||||||
|
#else
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <climits> // Some Linux distributions define PATH_MAX here.
|
||||||
|
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-string.h"
|
||||||
|
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
#define GTEST_PATH_MAX_ _MAX_PATH
|
||||||
|
#elif defined(PATH_MAX)
|
||||||
|
#define GTEST_PATH_MAX_ PATH_MAX
|
||||||
|
#elif defined(_XOPEN_PATH_MAX)
|
||||||
|
#define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
|
||||||
|
#else
|
||||||
|
#define GTEST_PATH_MAX_ _POSIX_PATH_MAX
|
||||||
|
#endif // GTEST_OS_WINDOWS
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
// On Windows, '\\' is the standard path separator, but many tools and the
|
||||||
|
// Windows API also accept '/' as an alternate path separator. Unless otherwise
|
||||||
|
// noted, a file path can contain either kind of path separators, or a mixture
|
||||||
|
// of them.
|
||||||
|
const char kPathSeparator = '\\';
|
||||||
|
const char kAlternatePathSeparator = '/';
|
||||||
|
const char kAlternatePathSeparatorString[] = "/";
|
||||||
|
#if GTEST_OS_WINDOWS_MOBILE
|
||||||
|
// Windows CE doesn't have a current directory. You should not use
|
||||||
|
// the current directory in tests on Windows CE, but this at least
|
||||||
|
// provides a reasonable fallback.
|
||||||
|
const char kCurrentDirectoryString[] = "\\";
|
||||||
|
// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
|
||||||
|
const DWORD kInvalidFileAttributes = 0xffffffff;
|
||||||
|
#else
|
||||||
|
const char kCurrentDirectoryString[] = ".\\";
|
||||||
|
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||||
|
#else
|
||||||
|
const char kPathSeparator = '/';
|
||||||
|
const char kCurrentDirectoryString[] = "./";
|
||||||
|
#endif // GTEST_OS_WINDOWS
|
||||||
|
|
||||||
|
// Returns whether the given character is a valid path separator.
|
||||||
|
static bool IsPathSeparator(char c) {
|
||||||
|
#if GTEST_HAS_ALT_PATH_SEP_
|
||||||
|
return (c == kPathSeparator) || (c == kAlternatePathSeparator);
|
||||||
|
#else
|
||||||
|
return c == kPathSeparator;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the current working directory, or "" if unsuccessful.
|
||||||
|
FilePath FilePath::GetCurrentDir() {
|
||||||
|
#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
|
||||||
|
GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_ESP32 || \
|
||||||
|
GTEST_OS_XTENSA
|
||||||
|
// These platforms do not have a current directory, so we just return
|
||||||
|
// something reasonable.
|
||||||
|
return FilePath(kCurrentDirectoryString);
|
||||||
|
#elif GTEST_OS_WINDOWS
|
||||||
|
char cwd[GTEST_PATH_MAX_ + 1] = {'\0'};
|
||||||
|
return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd);
|
||||||
|
#else
|
||||||
|
char cwd[GTEST_PATH_MAX_ + 1] = {'\0'};
|
||||||
|
char* result = getcwd(cwd, sizeof(cwd));
|
||||||
|
#if GTEST_OS_NACL
|
||||||
|
// getcwd will likely fail in NaCl due to the sandbox, so return something
|
||||||
|
// reasonable. The user may have provided a shim implementation for getcwd,
|
||||||
|
// however, so fallback only when failure is detected.
|
||||||
|
return FilePath(result == nullptr ? kCurrentDirectoryString : cwd);
|
||||||
|
#endif // GTEST_OS_NACL
|
||||||
|
return FilePath(result == nullptr ? "" : cwd);
|
||||||
|
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a copy of the FilePath with the case-insensitive extension removed.
|
||||||
|
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
|
||||||
|
// FilePath("dir/file"). If a case-insensitive extension is not
|
||||||
|
// found, returns a copy of the original FilePath.
|
||||||
|
FilePath FilePath::RemoveExtension(const char* extension) const {
|
||||||
|
const std::string dot_extension = std::string(".") + extension;
|
||||||
|
if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
|
||||||
|
return FilePath(
|
||||||
|
pathname_.substr(0, pathname_.length() - dot_extension.length()));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a pointer to the last occurrence of a valid path separator in
|
||||||
|
// the FilePath. On Windows, for example, both '/' and '\' are valid path
|
||||||
|
// separators. Returns NULL if no path separator was found.
|
||||||
|
const char* FilePath::FindLastPathSeparator() const {
|
||||||
|
const char* const last_sep = strrchr(c_str(), kPathSeparator);
|
||||||
|
#if GTEST_HAS_ALT_PATH_SEP_
|
||||||
|
const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
|
||||||
|
// Comparing two pointers of which only one is NULL is undefined.
|
||||||
|
if (last_alt_sep != nullptr &&
|
||||||
|
(last_sep == nullptr || last_alt_sep > last_sep)) {
|
||||||
|
return last_alt_sep;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return last_sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a copy of the FilePath with the directory part removed.
|
||||||
|
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
|
||||||
|
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
|
||||||
|
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
|
||||||
|
// returns an empty FilePath ("").
|
||||||
|
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||||
|
FilePath FilePath::RemoveDirectoryName() const {
|
||||||
|
const char* const last_sep = FindLastPathSeparator();
|
||||||
|
return last_sep ? FilePath(last_sep + 1) : *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveFileName returns the directory path with the filename removed.
|
||||||
|
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
|
||||||
|
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
|
||||||
|
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
|
||||||
|
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
|
||||||
|
// On Windows platform, '\' is the path separator, otherwise it is '/'.
|
||||||
|
FilePath FilePath::RemoveFileName() const {
|
||||||
|
const char* const last_sep = FindLastPathSeparator();
|
||||||
|
std::string dir;
|
||||||
|
if (last_sep) {
|
||||||
|
dir = std::string(c_str(), static_cast<size_t>(last_sep + 1 - c_str()));
|
||||||
|
} else {
|
||||||
|
dir = kCurrentDirectoryString;
|
||||||
|
}
|
||||||
|
return FilePath(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for naming files in a directory for xml output.
|
||||||
|
|
||||||
|
// Given directory = "dir", base_name = "test", number = 0,
|
||||||
|
// extension = "xml", returns "dir/test.xml". If number is greater
|
||||||
|
// than zero (e.g., 12), returns "dir/test_12.xml".
|
||||||
|
// On Windows platform, uses \ as the separator rather than /.
|
||||||
|
FilePath FilePath::MakeFileName(const FilePath& directory,
|
||||||
|
const FilePath& base_name, int number,
|
||||||
|
const char* extension) {
|
||||||
|
std::string file;
|
||||||
|
if (number == 0) {
|
||||||
|
file = base_name.string() + "." + extension;
|
||||||
|
} else {
|
||||||
|
file =
|
||||||
|
base_name.string() + "_" + StreamableToString(number) + "." + extension;
|
||||||
|
}
|
||||||
|
return ConcatPaths(directory, FilePath(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
|
||||||
|
// On Windows, uses \ as the separator rather than /.
|
||||||
|
FilePath FilePath::ConcatPaths(const FilePath& directory,
|
||||||
|
const FilePath& relative_path) {
|
||||||
|
if (directory.IsEmpty()) return relative_path;
|
||||||
|
const FilePath dir(directory.RemoveTrailingPathSeparator());
|
||||||
|
return FilePath(dir.string() + kPathSeparator + relative_path.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if pathname describes something findable in the file-system,
|
||||||
|
// either a file, directory, or whatever.
|
||||||
|
bool FilePath::FileOrDirectoryExists() const {
|
||||||
|
#if GTEST_OS_WINDOWS_MOBILE
|
||||||
|
LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
|
||||||
|
const DWORD attributes = GetFileAttributes(unicode);
|
||||||
|
delete[] unicode;
|
||||||
|
return attributes != kInvalidFileAttributes;
|
||||||
|
#else
|
||||||
|
posix::StatStruct file_stat{};
|
||||||
|
return posix::Stat(pathname_.c_str(), &file_stat) == 0;
|
||||||
|
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if pathname describes a directory in the file-system
|
||||||
|
// that exists.
|
||||||
|
bool FilePath::DirectoryExists() const {
|
||||||
|
bool result = false;
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
// Don't strip off trailing separator if path is a root directory on
|
||||||
|
// Windows (like "C:\\").
|
||||||
|
const FilePath& path(IsRootDirectory() ? *this
|
||||||
|
: RemoveTrailingPathSeparator());
|
||||||
|
#else
|
||||||
|
const FilePath& path(*this);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if GTEST_OS_WINDOWS_MOBILE
|
||||||
|
LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
|
||||||
|
const DWORD attributes = GetFileAttributes(unicode);
|
||||||
|
delete[] unicode;
|
||||||
|
if ((attributes != kInvalidFileAttributes) &&
|
||||||
|
(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
posix::StatStruct file_stat{};
|
||||||
|
result =
|
||||||
|
posix::Stat(path.c_str(), &file_stat) == 0 && posix::IsDir(file_stat);
|
||||||
|
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if pathname describes a root directory. (Windows has one
|
||||||
|
// root directory per disk drive.)
|
||||||
|
bool FilePath::IsRootDirectory() const {
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
return pathname_.length() == 3 && IsAbsolutePath();
|
||||||
|
#else
|
||||||
|
return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if pathname describes an absolute path.
|
||||||
|
bool FilePath::IsAbsolutePath() const {
|
||||||
|
const char* const name = pathname_.c_str();
|
||||||
|
#if GTEST_OS_WINDOWS
|
||||||
|
return pathname_.length() >= 3 &&
|
||||||
|
((name[0] >= 'a' && name[0] <= 'z') ||
|
||||||
|
(name[0] >= 'A' && name[0] <= 'Z')) &&
|
||||||
|
name[1] == ':' && IsPathSeparator(name[2]);
|
||||||
|
#else
|
||||||
|
return IsPathSeparator(name[0]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a pathname for a file that does not currently exist. The pathname
|
||||||
|
// will be directory/base_name.extension or
|
||||||
|
// directory/base_name_<number>.extension if directory/base_name.extension
|
||||||
|
// already exists. The number will be incremented until a pathname is found
|
||||||
|
// that does not already exist.
|
||||||
|
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
|
||||||
|
// There could be a race condition if two or more processes are calling this
|
||||||
|
// function at the same time -- they could both pick the same filename.
|
||||||
|
FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
|
||||||
|
const FilePath& base_name,
|
||||||
|
const char* extension) {
|
||||||
|
FilePath full_pathname;
|
||||||
|
int number = 0;
|
||||||
|
do {
|
||||||
|
full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
|
||||||
|
} while (full_pathname.FileOrDirectoryExists());
|
||||||
|
return full_pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if FilePath ends with a path separator, which indicates that
|
||||||
|
// it is intended to represent a directory. Returns false otherwise.
|
||||||
|
// This does NOT check that a directory (or file) actually exists.
|
||||||
|
bool FilePath::IsDirectory() const {
|
||||||
|
return !pathname_.empty() &&
|
||||||
|
IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create directories so that path exists. Returns true if successful or if
|
||||||
|
// the directories already exist; returns false if unable to create directories
|
||||||
|
// for any reason.
|
||||||
|
bool FilePath::CreateDirectoriesRecursively() const {
|
||||||
|
if (!this->IsDirectory()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathname_.length() == 0 || this->DirectoryExists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
|
||||||
|
return parent.CreateDirectoriesRecursively() && this->CreateFolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the directory so that path exists. Returns true if successful or
|
||||||
|
// if the directory already exists; returns false if unable to create the
|
||||||
|
// directory for any reason, including if the parent directory does not
|
||||||
|
// exist. Not named "CreateDirectory" because that's a macro on Windows.
|
||||||
|
bool FilePath::CreateFolder() const {
|
||||||
|
#if GTEST_OS_WINDOWS_MOBILE
|
||||||
|
FilePath removed_sep(this->RemoveTrailingPathSeparator());
|
||||||
|
LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
|
||||||
|
int result = CreateDirectory(unicode, nullptr) ? 0 : -1;
|
||||||
|
delete[] unicode;
|
||||||
|
#elif GTEST_OS_WINDOWS
|
||||||
|
int result = _mkdir(pathname_.c_str());
|
||||||
|
#elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA
|
||||||
|
// do nothing
|
||||||
|
int result = 0;
|
||||||
|
#else
|
||||||
|
int result = mkdir(pathname_.c_str(), 0777);
|
||||||
|
#endif // GTEST_OS_WINDOWS_MOBILE
|
||||||
|
|
||||||
|
if (result == -1) {
|
||||||
|
return this->DirectoryExists(); // An error is OK if the directory exists.
|
||||||
|
}
|
||||||
|
return true; // No error.
|
||||||
|
}
|
||||||
|
|
||||||
|
// If input name has a trailing separator character, remove it and return the
|
||||||
|
// name, otherwise return the name string unmodified.
|
||||||
|
// On Windows platform, uses \ as the separator, other platforms use /.
|
||||||
|
FilePath FilePath::RemoveTrailingPathSeparator() const {
|
||||||
|
return IsDirectory() ? FilePath(pathname_.substr(0, pathname_.length() - 1))
|
||||||
|
: *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes any redundant separators that might be in the pathname.
|
||||||
|
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
|
||||||
|
// redundancies that might be in a pathname involving "." or "..".
|
||||||
|
void FilePath::Normalize() {
|
||||||
|
auto out = pathname_.begin();
|
||||||
|
|
||||||
|
for (const char character : pathname_) {
|
||||||
|
if (!IsPathSeparator(character)) {
|
||||||
|
*(out++) = character;
|
||||||
|
} else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
|
||||||
|
*(out++) = kPathSeparator;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pathname_.erase(out, pathname_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace testing
|
1212
test/gtest/src/gtest-internal-inl.h
Normal file
1212
test/gtest/src/gtest-internal-inl.h
Normal file
File diff suppressed because it is too large
Load Diff
98
test/gtest/src/gtest-matchers.cc
Normal file
98
test/gtest/src/gtest-matchers.cc
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2007, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
//
|
||||||
|
// This file implements just enough of the matcher interface to allow
|
||||||
|
// EXPECT_DEATH and friends to accept a matcher argument.
|
||||||
|
|
||||||
|
#include "gtest/gtest-matchers.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-internal.h"
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a const std::string& whose value is
|
||||||
|
// equal to s.
|
||||||
|
Matcher<const std::string&>::Matcher(const std::string& s) { *this = Eq(s); }
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a const std::string& whose value is
|
||||||
|
// equal to s.
|
||||||
|
Matcher<const std::string&>::Matcher(const char* s) {
|
||||||
|
*this = Eq(std::string(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a std::string whose value is equal to
|
||||||
|
// s.
|
||||||
|
Matcher<std::string>::Matcher(const std::string& s) { *this = Eq(s); }
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a std::string whose value is equal to
|
||||||
|
// s.
|
||||||
|
Matcher<std::string>::Matcher(const char* s) { *this = Eq(std::string(s)); }
|
||||||
|
|
||||||
|
#if GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
// Constructs a matcher that matches a const StringView& whose value is
|
||||||
|
// equal to s.
|
||||||
|
Matcher<const internal::StringView&>::Matcher(const std::string& s) {
|
||||||
|
*this = Eq(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a const StringView& whose value is
|
||||||
|
// equal to s.
|
||||||
|
Matcher<const internal::StringView&>::Matcher(const char* s) {
|
||||||
|
*this = Eq(std::string(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a const StringView& whose value is
|
||||||
|
// equal to s.
|
||||||
|
Matcher<const internal::StringView&>::Matcher(internal::StringView s) {
|
||||||
|
*this = Eq(std::string(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a StringView whose value is equal to
|
||||||
|
// s.
|
||||||
|
Matcher<internal::StringView>::Matcher(const std::string& s) { *this = Eq(s); }
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a StringView whose value is equal to
|
||||||
|
// s.
|
||||||
|
Matcher<internal::StringView>::Matcher(const char* s) {
|
||||||
|
*this = Eq(std::string(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a matcher that matches a StringView whose value is equal to
|
||||||
|
// s.
|
||||||
|
Matcher<internal::StringView>::Matcher(internal::StringView s) {
|
||||||
|
*this = Eq(std::string(s));
|
||||||
|
}
|
||||||
|
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
|
||||||
|
} // namespace testing
|
1394
test/gtest/src/gtest-port.cc
Normal file
1394
test/gtest/src/gtest-port.cc
Normal file
File diff suppressed because it is too large
Load Diff
553
test/gtest/src/gtest-printers.cc
Normal file
553
test/gtest/src/gtest-printers.cc
Normal file
@ -0,0 +1,553 @@
|
|||||||
|
// Copyright 2007, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Google Test - The Google C++ Testing and Mocking Framework
|
||||||
|
//
|
||||||
|
// This file implements a universal value printer that can print a
|
||||||
|
// value of any type T:
|
||||||
|
//
|
||||||
|
// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
|
||||||
|
//
|
||||||
|
// It uses the << operator when possible, and prints the bytes in the
|
||||||
|
// object otherwise. A user can override its behavior for a class
|
||||||
|
// type Foo by defining either operator<<(::std::ostream&, const Foo&)
|
||||||
|
// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
|
||||||
|
// defines Foo.
|
||||||
|
|
||||||
|
#include "gtest/gtest-printers.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cwchar>
|
||||||
|
#include <ostream> // NOLINT
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
#include "src/gtest-internal-inl.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::std::ostream;
|
||||||
|
|
||||||
|
// Prints a segment of bytes in the given object.
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
|
||||||
|
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
|
||||||
|
size_t count, ostream* os) {
|
||||||
|
char text[5] = "";
|
||||||
|
for (size_t i = 0; i != count; i++) {
|
||||||
|
const size_t j = start + i;
|
||||||
|
if (i != 0) {
|
||||||
|
// Organizes the bytes into groups of 2 for easy parsing by
|
||||||
|
// human.
|
||||||
|
if ((j % 2) == 0)
|
||||||
|
*os << ' ';
|
||||||
|
else
|
||||||
|
*os << '-';
|
||||||
|
}
|
||||||
|
GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
|
||||||
|
*os << text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints the bytes in the given value to the given ostream.
|
||||||
|
void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
|
||||||
|
ostream* os) {
|
||||||
|
// Tells the user how big the object is.
|
||||||
|
*os << count << "-byte object <";
|
||||||
|
|
||||||
|
const size_t kThreshold = 132;
|
||||||
|
const size_t kChunkSize = 64;
|
||||||
|
// If the object size is bigger than kThreshold, we'll have to omit
|
||||||
|
// some details by printing only the first and the last kChunkSize
|
||||||
|
// bytes.
|
||||||
|
if (count < kThreshold) {
|
||||||
|
PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
|
||||||
|
} else {
|
||||||
|
PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
|
||||||
|
*os << " ... ";
|
||||||
|
// Rounds up to 2-byte boundary.
|
||||||
|
const size_t resume_pos = (count - kChunkSize + 1) / 2 * 2;
|
||||||
|
PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
|
||||||
|
}
|
||||||
|
*os << ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers for widening a character to char32_t. Since the standard does not
|
||||||
|
// specify if char / wchar_t is signed or unsigned, it is important to first
|
||||||
|
// convert it to the unsigned type of the same width before widening it to
|
||||||
|
// char32_t.
|
||||||
|
template <typename CharType>
|
||||||
|
char32_t ToChar32(CharType in) {
|
||||||
|
return static_cast<char32_t>(
|
||||||
|
static_cast<typename std::make_unsigned<CharType>::type>(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
|
||||||
|
// given object. The delegation simplifies the implementation, which
|
||||||
|
// uses the << operator and thus is easier done outside of the
|
||||||
|
// ::testing::internal namespace, which contains a << operator that
|
||||||
|
// sometimes conflicts with the one in STL.
|
||||||
|
void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
|
||||||
|
ostream* os) {
|
||||||
|
PrintBytesInObjectToImpl(obj_bytes, count, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depending on the value of a char (or wchar_t), we print it in one
|
||||||
|
// of three formats:
|
||||||
|
// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
|
||||||
|
// - as a hexadecimal escape sequence (e.g. '\x7F'), or
|
||||||
|
// - as a special escape sequence (e.g. '\r', '\n').
|
||||||
|
enum CharFormat { kAsIs, kHexEscape, kSpecialEscape };
|
||||||
|
|
||||||
|
// Returns true if c is a printable ASCII character. We test the
|
||||||
|
// value of c directly instead of calling isprint(), which is buggy on
|
||||||
|
// Windows Mobile.
|
||||||
|
inline bool IsPrintableAscii(char32_t c) { return 0x20 <= c && c <= 0x7E; }
|
||||||
|
|
||||||
|
// Prints c (of type char, char8_t, char16_t, char32_t, or wchar_t) as a
|
||||||
|
// character literal without the quotes, escaping it when necessary; returns how
|
||||||
|
// c was formatted.
|
||||||
|
template <typename Char>
|
||||||
|
static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
|
||||||
|
const char32_t u_c = ToChar32(c);
|
||||||
|
switch (u_c) {
|
||||||
|
case L'\0':
|
||||||
|
*os << "\\0";
|
||||||
|
break;
|
||||||
|
case L'\'':
|
||||||
|
*os << "\\'";
|
||||||
|
break;
|
||||||
|
case L'\\':
|
||||||
|
*os << "\\\\";
|
||||||
|
break;
|
||||||
|
case L'\a':
|
||||||
|
*os << "\\a";
|
||||||
|
break;
|
||||||
|
case L'\b':
|
||||||
|
*os << "\\b";
|
||||||
|
break;
|
||||||
|
case L'\f':
|
||||||
|
*os << "\\f";
|
||||||
|
break;
|
||||||
|
case L'\n':
|
||||||
|
*os << "\\n";
|
||||||
|
break;
|
||||||
|
case L'\r':
|
||||||
|
*os << "\\r";
|
||||||
|
break;
|
||||||
|
case L'\t':
|
||||||
|
*os << "\\t";
|
||||||
|
break;
|
||||||
|
case L'\v':
|
||||||
|
*os << "\\v";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (IsPrintableAscii(u_c)) {
|
||||||
|
*os << static_cast<char>(c);
|
||||||
|
return kAsIs;
|
||||||
|
} else {
|
||||||
|
ostream::fmtflags flags = os->flags();
|
||||||
|
*os << "\\x" << std::hex << std::uppercase << static_cast<int>(u_c);
|
||||||
|
os->flags(flags);
|
||||||
|
return kHexEscape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kSpecialEscape;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a char32_t c as if it's part of a string literal, escaping it when
|
||||||
|
// necessary; returns how c was formatted.
|
||||||
|
static CharFormat PrintAsStringLiteralTo(char32_t c, ostream* os) {
|
||||||
|
switch (c) {
|
||||||
|
case L'\'':
|
||||||
|
*os << "'";
|
||||||
|
return kAsIs;
|
||||||
|
case L'"':
|
||||||
|
*os << "\\\"";
|
||||||
|
return kSpecialEscape;
|
||||||
|
default:
|
||||||
|
return PrintAsCharLiteralTo(c, os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* GetCharWidthPrefix(char) { return ""; }
|
||||||
|
|
||||||
|
static const char* GetCharWidthPrefix(signed char) { return ""; }
|
||||||
|
|
||||||
|
static const char* GetCharWidthPrefix(unsigned char) { return ""; }
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
static const char* GetCharWidthPrefix(char8_t) { return "u8"; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char* GetCharWidthPrefix(char16_t) { return "u"; }
|
||||||
|
|
||||||
|
static const char* GetCharWidthPrefix(char32_t) { return "U"; }
|
||||||
|
|
||||||
|
static const char* GetCharWidthPrefix(wchar_t) { return "L"; }
|
||||||
|
|
||||||
|
// Prints a char c as if it's part of a string literal, escaping it when
|
||||||
|
// necessary; returns how c was formatted.
|
||||||
|
static CharFormat PrintAsStringLiteralTo(char c, ostream* os) {
|
||||||
|
return PrintAsStringLiteralTo(ToChar32(c), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
static CharFormat PrintAsStringLiteralTo(char8_t c, ostream* os) {
|
||||||
|
return PrintAsStringLiteralTo(ToChar32(c), os);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static CharFormat PrintAsStringLiteralTo(char16_t c, ostream* os) {
|
||||||
|
return PrintAsStringLiteralTo(ToChar32(c), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
|
||||||
|
return PrintAsStringLiteralTo(ToChar32(c), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a character c (of type char, char8_t, char16_t, char32_t, or wchar_t)
|
||||||
|
// and its code. '\0' is printed as "'\\0'", other unprintable characters are
|
||||||
|
// also properly escaped using the standard C++ escape sequence.
|
||||||
|
template <typename Char>
|
||||||
|
void PrintCharAndCodeTo(Char c, ostream* os) {
|
||||||
|
// First, print c as a literal in the most readable form we can find.
|
||||||
|
*os << GetCharWidthPrefix(c) << "'";
|
||||||
|
const CharFormat format = PrintAsCharLiteralTo(c, os);
|
||||||
|
*os << "'";
|
||||||
|
|
||||||
|
// To aid user debugging, we also print c's code in decimal, unless
|
||||||
|
// it's 0 (in which case c was printed as '\\0', making the code
|
||||||
|
// obvious).
|
||||||
|
if (c == 0) return;
|
||||||
|
*os << " (" << static_cast<int>(c);
|
||||||
|
|
||||||
|
// For more convenience, we print c's code again in hexadecimal,
|
||||||
|
// unless c was already printed in the form '\x##' or the code is in
|
||||||
|
// [1, 9].
|
||||||
|
if (format == kHexEscape || (1 <= c && c <= 9)) {
|
||||||
|
// Do nothing.
|
||||||
|
} else {
|
||||||
|
*os << ", 0x" << String::FormatHexInt(static_cast<int>(c));
|
||||||
|
}
|
||||||
|
*os << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintTo(unsigned char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); }
|
||||||
|
void PrintTo(signed char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); }
|
||||||
|
|
||||||
|
// Prints a wchar_t as a symbol if it is printable or as its internal
|
||||||
|
// code otherwise and also as its code. L'\0' is printed as "L'\\0'".
|
||||||
|
void PrintTo(wchar_t wc, ostream* os) { PrintCharAndCodeTo(wc, os); }
|
||||||
|
|
||||||
|
// TODO(dcheng): Consider making this delegate to PrintCharAndCodeTo() as well.
|
||||||
|
void PrintTo(char32_t c, ::std::ostream* os) {
|
||||||
|
*os << std::hex << "U+" << std::uppercase << std::setfill('0') << std::setw(4)
|
||||||
|
<< static_cast<uint32_t>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcc/clang __{u,}int128_t
|
||||||
|
#if defined(__SIZEOF_INT128__)
|
||||||
|
void PrintTo(__uint128_t v, ::std::ostream* os) {
|
||||||
|
if (v == 0) {
|
||||||
|
*os << "0";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer large enough for ceil(log10(2^128))==39 and the null terminator
|
||||||
|
char buf[40];
|
||||||
|
char* p = buf + sizeof(buf);
|
||||||
|
|
||||||
|
// Some configurations have a __uint128_t, but no support for built in
|
||||||
|
// division. Do manual long division instead.
|
||||||
|
|
||||||
|
uint64_t high = static_cast<uint64_t>(v >> 64);
|
||||||
|
uint64_t low = static_cast<uint64_t>(v);
|
||||||
|
|
||||||
|
*--p = 0;
|
||||||
|
while (high != 0 || low != 0) {
|
||||||
|
uint64_t high_mod = high % 10;
|
||||||
|
high = high / 10;
|
||||||
|
// This is the long division algorithm specialized for a divisor of 10 and
|
||||||
|
// only two elements.
|
||||||
|
// Notable values:
|
||||||
|
// 2^64 / 10 == 1844674407370955161
|
||||||
|
// 2^64 % 10 == 6
|
||||||
|
const uint64_t carry = 6 * high_mod + low % 10;
|
||||||
|
low = low / 10 + high_mod * 1844674407370955161 + carry / 10;
|
||||||
|
|
||||||
|
char digit = static_cast<char>(carry % 10);
|
||||||
|
*--p = '0' + digit;
|
||||||
|
}
|
||||||
|
*os << p;
|
||||||
|
}
|
||||||
|
void PrintTo(__int128_t v, ::std::ostream* os) {
|
||||||
|
__uint128_t uv = static_cast<__uint128_t>(v);
|
||||||
|
if (v < 0) {
|
||||||
|
*os << "-";
|
||||||
|
uv = -uv;
|
||||||
|
}
|
||||||
|
PrintTo(uv, os);
|
||||||
|
}
|
||||||
|
#endif // __SIZEOF_INT128__
|
||||||
|
|
||||||
|
// Prints the given array of characters to the ostream. CharType must be either
|
||||||
|
// char, char8_t, char16_t, char32_t, or wchar_t.
|
||||||
|
// The array starts at begin, the length is len, it may include '\0' characters
|
||||||
|
// and may not be NUL-terminated.
|
||||||
|
template <typename CharType>
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ static CharFormat
|
||||||
|
PrintCharsAsStringTo(const CharType* begin, size_t len, ostream* os) {
|
||||||
|
const char* const quote_prefix = GetCharWidthPrefix(*begin);
|
||||||
|
*os << quote_prefix << "\"";
|
||||||
|
bool is_previous_hex = false;
|
||||||
|
CharFormat print_format = kAsIs;
|
||||||
|
for (size_t index = 0; index < len; ++index) {
|
||||||
|
const CharType cur = begin[index];
|
||||||
|
if (is_previous_hex && IsXDigit(cur)) {
|
||||||
|
// Previous character is of '\x..' form and this character can be
|
||||||
|
// interpreted as another hexadecimal digit in its number. Break string to
|
||||||
|
// disambiguate.
|
||||||
|
*os << "\" " << quote_prefix << "\"";
|
||||||
|
}
|
||||||
|
is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
|
||||||
|
// Remember if any characters required hex escaping.
|
||||||
|
if (is_previous_hex) {
|
||||||
|
print_format = kHexEscape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*os << "\"";
|
||||||
|
return print_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a (const) char/wchar_t array of 'len' elements, starting at address
|
||||||
|
// 'begin'. CharType must be either char or wchar_t.
|
||||||
|
template <typename CharType>
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
|
||||||
|
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ static void
|
||||||
|
UniversalPrintCharArray(const CharType* begin, size_t len,
|
||||||
|
ostream* os) {
|
||||||
|
// The code
|
||||||
|
// const char kFoo[] = "foo";
|
||||||
|
// generates an array of 4, not 3, elements, with the last one being '\0'.
|
||||||
|
//
|
||||||
|
// Therefore when printing a char array, we don't print the last element if
|
||||||
|
// it's '\0', such that the output matches the string literal as it's
|
||||||
|
// written in the source code.
|
||||||
|
if (len > 0 && begin[len - 1] == '\0') {
|
||||||
|
PrintCharsAsStringTo(begin, len - 1, os);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If, however, the last element in the array is not '\0', e.g.
|
||||||
|
// const char kFoo[] = { 'f', 'o', 'o' };
|
||||||
|
// we must print the entire array. We also print a message to indicate
|
||||||
|
// that the array is not NUL-terminated.
|
||||||
|
PrintCharsAsStringTo(begin, len, os);
|
||||||
|
*os << " (no terminating NUL)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a (const) char array of 'len' elements, starting at address 'begin'.
|
||||||
|
void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
|
||||||
|
UniversalPrintCharArray(begin, len, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
// Prints a (const) char8_t array of 'len' elements, starting at address
|
||||||
|
// 'begin'.
|
||||||
|
void UniversalPrintArray(const char8_t* begin, size_t len, ostream* os) {
|
||||||
|
UniversalPrintCharArray(begin, len, os);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Prints a (const) char16_t array of 'len' elements, starting at address
|
||||||
|
// 'begin'.
|
||||||
|
void UniversalPrintArray(const char16_t* begin, size_t len, ostream* os) {
|
||||||
|
UniversalPrintCharArray(begin, len, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a (const) char32_t array of 'len' elements, starting at address
|
||||||
|
// 'begin'.
|
||||||
|
void UniversalPrintArray(const char32_t* begin, size_t len, ostream* os) {
|
||||||
|
UniversalPrintCharArray(begin, len, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a (const) wchar_t array of 'len' elements, starting at address
|
||||||
|
// 'begin'.
|
||||||
|
void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
|
||||||
|
UniversalPrintCharArray(begin, len, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Prints a null-terminated C-style string to the ostream.
|
||||||
|
template <typename Char>
|
||||||
|
void PrintCStringTo(const Char* s, ostream* os) {
|
||||||
|
if (s == nullptr) {
|
||||||
|
*os << "NULL";
|
||||||
|
} else {
|
||||||
|
*os << ImplicitCast_<const void*>(s) << " pointing to ";
|
||||||
|
PrintCharsAsStringTo(s, std::char_traits<Char>::length(s), os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
void PrintTo(const char* s, ostream* os) { PrintCStringTo(s, os); }
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
void PrintTo(const char8_t* s, ostream* os) { PrintCStringTo(s, os); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void PrintTo(const char16_t* s, ostream* os) { PrintCStringTo(s, os); }
|
||||||
|
|
||||||
|
void PrintTo(const char32_t* s, ostream* os) { PrintCStringTo(s, os); }
|
||||||
|
|
||||||
|
// MSVC compiler can be configured to define whar_t as a typedef
|
||||||
|
// of unsigned short. Defining an overload for const wchar_t* in that case
|
||||||
|
// would cause pointers to unsigned shorts be printed as wide strings,
|
||||||
|
// possibly accessing more memory than intended and causing invalid
|
||||||
|
// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
|
||||||
|
// wchar_t is implemented as a native type.
|
||||||
|
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
|
||||||
|
// Prints the given wide C string to the ostream.
|
||||||
|
void PrintTo(const wchar_t* s, ostream* os) { PrintCStringTo(s, os); }
|
||||||
|
#endif // wchar_t is native
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool ContainsUnprintableControlCodes(const char* str, size_t length) {
|
||||||
|
const unsigned char* s = reinterpret_cast<const unsigned char*>(str);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
unsigned char ch = *s++;
|
||||||
|
if (std::iscntrl(ch)) {
|
||||||
|
switch (ch) {
|
||||||
|
case '\t':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t <= 0xbf; }
|
||||||
|
|
||||||
|
bool IsValidUTF8(const char* str, size_t length) {
|
||||||
|
const unsigned char* s = reinterpret_cast<const unsigned char*>(str);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length;) {
|
||||||
|
unsigned char lead = s[i++];
|
||||||
|
|
||||||
|
if (lead <= 0x7f) {
|
||||||
|
continue; // single-byte character (ASCII) 0..7F
|
||||||
|
}
|
||||||
|
if (lead < 0xc2) {
|
||||||
|
return false; // trail byte or non-shortest form
|
||||||
|
} else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) {
|
||||||
|
++i; // 2-byte character
|
||||||
|
} else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length &&
|
||||||
|
IsUTF8TrailByte(s[i]) && IsUTF8TrailByte(s[i + 1]) &&
|
||||||
|
// check for non-shortest form and surrogate
|
||||||
|
(lead != 0xe0 || s[i] >= 0xa0) &&
|
||||||
|
(lead != 0xed || s[i] < 0xa0)) {
|
||||||
|
i += 2; // 3-byte character
|
||||||
|
} else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length &&
|
||||||
|
IsUTF8TrailByte(s[i]) && IsUTF8TrailByte(s[i + 1]) &&
|
||||||
|
IsUTF8TrailByte(s[i + 2]) &&
|
||||||
|
// check for non-shortest form
|
||||||
|
(lead != 0xf0 || s[i] >= 0x90) &&
|
||||||
|
(lead != 0xf4 || s[i] < 0x90)) {
|
||||||
|
i += 3; // 4-byte character
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionalPrintAsText(const char* str, size_t length, ostream* os) {
|
||||||
|
if (!ContainsUnprintableControlCodes(str, length) &&
|
||||||
|
IsValidUTF8(str, length)) {
|
||||||
|
*os << "\n As Text: \"" << str << "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
void PrintStringTo(const ::std::string& s, ostream* os) {
|
||||||
|
if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) {
|
||||||
|
if (GTEST_FLAG_GET(print_utf8)) {
|
||||||
|
ConditionalPrintAsText(s.data(), s.size(), os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
void PrintU8StringTo(const ::std::u8string& s, ostream* os) {
|
||||||
|
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void PrintU16StringTo(const ::std::u16string& s, ostream* os) {
|
||||||
|
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintU32StringTo(const ::std::u32string& s, ostream* os) {
|
||||||
|
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if GTEST_HAS_STD_WSTRING
|
||||||
|
void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
|
||||||
|
PrintCharsAsStringTo(s.data(), s.size(), os);
|
||||||
|
}
|
||||||
|
#endif // GTEST_HAS_STD_WSTRING
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
} // namespace testing
|
105
test/gtest/src/gtest-test-part.cc
Normal file
105
test/gtest/src/gtest-test-part.cc
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2008, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
//
|
||||||
|
// The Google C++ Testing and Mocking Framework (Google Test)
|
||||||
|
|
||||||
|
#include "gtest/gtest-test-part.h"
|
||||||
|
|
||||||
|
#include "gtest/internal/gtest-port.h"
|
||||||
|
#include "src/gtest-internal-inl.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
|
||||||
|
using internal::GetUnitTestImpl;
|
||||||
|
|
||||||
|
// Gets the summary of the failure message by omitting the stack trace
|
||||||
|
// in it.
|
||||||
|
std::string TestPartResult::ExtractSummary(const char* message) {
|
||||||
|
const char* const stack_trace = strstr(message, internal::kStackTraceMarker);
|
||||||
|
return stack_trace == nullptr ? message : std::string(message, stack_trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prints a TestPartResult object.
|
||||||
|
std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
|
||||||
|
return os << internal::FormatFileLocation(result.file_name(),
|
||||||
|
result.line_number())
|
||||||
|
<< " "
|
||||||
|
<< (result.type() == TestPartResult::kSuccess ? "Success"
|
||||||
|
: result.type() == TestPartResult::kSkip ? "Skipped"
|
||||||
|
: result.type() == TestPartResult::kFatalFailure
|
||||||
|
? "Fatal failure"
|
||||||
|
: "Non-fatal failure")
|
||||||
|
<< ":\n"
|
||||||
|
<< result.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appends a TestPartResult to the array.
|
||||||
|
void TestPartResultArray::Append(const TestPartResult& result) {
|
||||||
|
array_.push_back(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the TestPartResult at the given index (0-based).
|
||||||
|
const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {
|
||||||
|
if (index < 0 || index >= size()) {
|
||||||
|
printf("\nInvalid index (%d) into TestPartResultArray.\n", index);
|
||||||
|
internal::posix::Abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_[static_cast<size_t>(index)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of TestPartResult objects in the array.
|
||||||
|
int TestPartResultArray::size() const {
|
||||||
|
return static_cast<int>(array_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
HasNewFatalFailureHelper::HasNewFatalFailureHelper()
|
||||||
|
: has_new_fatal_failure_(false),
|
||||||
|
original_reporter_(
|
||||||
|
GetUnitTestImpl()->GetTestPartResultReporterForCurrentThread()) {
|
||||||
|
GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HasNewFatalFailureHelper::~HasNewFatalFailureHelper() {
|
||||||
|
GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(
|
||||||
|
original_reporter_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HasNewFatalFailureHelper::ReportTestPartResult(
|
||||||
|
const TestPartResult& result) {
|
||||||
|
if (result.fatally_failed()) has_new_fatal_failure_ = true;
|
||||||
|
original_reporter_->ReportTestPartResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
} // namespace testing
|
104
test/gtest/src/gtest-typed-test.cc
Normal file
104
test/gtest/src/gtest-typed-test.cc
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Copyright 2008 Google Inc.
|
||||||
|
// All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "gtest/gtest-typed-test.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace testing {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Skips to the first non-space char in str. Returns an empty string if str
|
||||||
|
// contains only whitespace characters.
|
||||||
|
static const char* SkipSpaces(const char* str) {
|
||||||
|
while (IsSpace(*str)) str++;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> SplitIntoTestNames(const char* src) {
|
||||||
|
std::vector<std::string> name_vec;
|
||||||
|
src = SkipSpaces(src);
|
||||||
|
for (; src != nullptr; src = SkipComma(src)) {
|
||||||
|
name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src)));
|
||||||
|
}
|
||||||
|
return name_vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifies that registered_tests match the test names in
|
||||||
|
// registered_tests_; returns registered_tests if successful, or
|
||||||
|
// aborts the program otherwise.
|
||||||
|
const char* TypedTestSuitePState::VerifyRegisteredTestNames(
|
||||||
|
const char* test_suite_name, const char* file, int line,
|
||||||
|
const char* registered_tests) {
|
||||||
|
RegisterTypeParameterizedTestSuite(test_suite_name, CodeLocation(file, line));
|
||||||
|
|
||||||
|
typedef RegisteredTestsMap::const_iterator RegisteredTestIter;
|
||||||
|
registered_ = true;
|
||||||
|
|
||||||
|
std::vector<std::string> name_vec = SplitIntoTestNames(registered_tests);
|
||||||
|
|
||||||
|
Message errors;
|
||||||
|
|
||||||
|
std::set<std::string> tests;
|
||||||
|
for (std::vector<std::string>::const_iterator name_it = name_vec.begin();
|
||||||
|
name_it != name_vec.end(); ++name_it) {
|
||||||
|
const std::string& name = *name_it;
|
||||||
|
if (tests.count(name) != 0) {
|
||||||
|
errors << "Test " << name << " is listed more than once.\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registered_tests_.count(name) != 0) {
|
||||||
|
tests.insert(name);
|
||||||
|
} else {
|
||||||
|
errors << "No test named " << name
|
||||||
|
<< " can be found in this test suite.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RegisteredTestIter it = registered_tests_.begin();
|
||||||
|
it != registered_tests_.end(); ++it) {
|
||||||
|
if (tests.count(it->first) == 0) {
|
||||||
|
errors << "You forgot to list test " << it->first << ".\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& errors_str = errors.GetString();
|
||||||
|
if (errors_str != "") {
|
||||||
|
fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
|
||||||
|
errors_str.c_str());
|
||||||
|
fflush(stderr);
|
||||||
|
posix::Abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return registered_tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace testing
|
6795
test/gtest/src/gtest.cc
Normal file
6795
test/gtest/src/gtest.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -27,13 +27,27 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <iostream>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
GTEST_API_ int main(int argc, char **argv) {
|
#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
|
||||||
std::cout << "Running main() from gtest_main.cc\n";
|
#if GTEST_OS_ESP8266
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
void setup() { testing::InitGoogleTest(); }
|
||||||
|
|
||||||
|
void loop() { RUN_ALL_TESTS(); }
|
||||||
|
|
||||||
|
#if GTEST_OS_ESP8266
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
GTEST_API_ int main(int argc, char **argv) {
|
||||||
|
printf("Running main() from %s\n", __FILE__);
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
#endif
|
5
test/include_httplib.cc
Normal file
5
test/include_httplib.cc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// The sole purpose of this file is to include httplib.h in a separate
|
||||||
|
// compilation unit, thus verifying that inline keywords have not been forgotten
|
||||||
|
// when linked together with test.cc.
|
||||||
|
|
||||||
|
#include <httplib.h>
|
6
test/include_windows_h.cc
Normal file
6
test/include_windows_h.cc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Test if including windows.h conflicts with httplib.h
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <httplib.h>
|
28
test/make-shared-library.sh
Executable file
28
test/make-shared-library.sh
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
if [ "$#" -ne 1 ]; then
|
||||||
|
echo "Usage: $0 build_dir"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUILD_DIR=$1
|
||||||
|
|
||||||
|
# Make the build directory
|
||||||
|
rm -rf $BUILD_DIR
|
||||||
|
mkdir -p $BUILD_DIR/out
|
||||||
|
|
||||||
|
cd $BUILD_DIR
|
||||||
|
|
||||||
|
# Build the version
|
||||||
|
git checkout $BUILD_DIR -q
|
||||||
|
|
||||||
|
cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug \
|
||||||
|
-DCMAKE_CXX_FLAGS="-g -Og" \
|
||||||
|
-DBUILD_SHARED_LIBS=ON \
|
||||||
|
-DHTTPLIB_COMPILE=ON \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=./out \
|
||||||
|
../..
|
||||||
|
|
||||||
|
cmake --build . --target install
|
||||||
|
cmake --build . --target clean
|
||||||
|
|
144
test/meson.build
Normal file
144
test/meson.build
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2021 Andrea Pappacoda
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
gtest_dep = dependency('gtest', main: true)
|
||||||
|
libcurl_dep = dependency('libcurl')
|
||||||
|
openssl = find_program('openssl')
|
||||||
|
test_conf = files('test.conf')
|
||||||
|
req_x509_flag = openssl.version().version_compare('>=3.2.0') ? '-x509v1' : '-x509'
|
||||||
|
|
||||||
|
key_pem = custom_target(
|
||||||
|
'key_pem',
|
||||||
|
output: 'key.pem',
|
||||||
|
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||||
|
)
|
||||||
|
|
||||||
|
temp_req = custom_target(
|
||||||
|
'temp_req',
|
||||||
|
input: key_pem,
|
||||||
|
output: 'temp_req',
|
||||||
|
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
cert_pem = custom_target(
|
||||||
|
'cert_pem',
|
||||||
|
input: [temp_req, key_pem],
|
||||||
|
output: 'cert.pem',
|
||||||
|
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '3650', '-req', '-signkey', '@INPUT1@', '-out', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
cert2_pem = custom_target(
|
||||||
|
'cert2_pem',
|
||||||
|
input: key_pem,
|
||||||
|
output: 'cert2.pem',
|
||||||
|
command: [openssl, 'req', req_x509_flag, '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
|
||||||
|
)
|
||||||
|
|
||||||
|
key_encrypted_pem = custom_target(
|
||||||
|
'key_encrypted_pem',
|
||||||
|
output: 'key_encrypted.pem',
|
||||||
|
command: [openssl, 'genrsa', '-passout', 'pass:test123!', '-out', '@OUTPUT@', '2048']
|
||||||
|
)
|
||||||
|
|
||||||
|
cert_encrypted_pem = custom_target(
|
||||||
|
'cert_encrypted_pem',
|
||||||
|
input: key_encrypted_pem,
|
||||||
|
output: 'cert_encrypted.pem',
|
||||||
|
command: [openssl, 'req', req_x509_flag, '-config', test_conf, '-key', '@INPUT@', '-sha256', '-days', '3650', '-nodes', '-out', '@OUTPUT@', '-extensions', 'SAN']
|
||||||
|
)
|
||||||
|
|
||||||
|
rootca_key_pem = custom_target(
|
||||||
|
'rootca_key_pem',
|
||||||
|
output: 'rootCA.key.pem',
|
||||||
|
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||||
|
)
|
||||||
|
|
||||||
|
rootca_cert_pem = custom_target(
|
||||||
|
'rootca_cert_pem',
|
||||||
|
input: rootca_key_pem,
|
||||||
|
output: 'rootCA.cert.pem',
|
||||||
|
command: [openssl, 'req', req_x509_flag, '-new', '-batch', '-config', files('test.rootCA.conf'), '-key', '@INPUT@', '-days', '1024', '-out', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
client_key_pem = custom_target(
|
||||||
|
'client_key_pem',
|
||||||
|
output: 'client.key.pem',
|
||||||
|
command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048']
|
||||||
|
)
|
||||||
|
|
||||||
|
client_temp_req = custom_target(
|
||||||
|
'client_temp_req',
|
||||||
|
input: client_key_pem,
|
||||||
|
output: 'client_temp_req',
|
||||||
|
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
client_cert_pem = custom_target(
|
||||||
|
'client_cert_pem',
|
||||||
|
input: [client_temp_req, rootca_cert_pem, rootca_key_pem],
|
||||||
|
output: 'client.cert.pem',
|
||||||
|
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
client_encrypted_key_pem = custom_target(
|
||||||
|
'client_encrypted_key_pem',
|
||||||
|
output: 'client_encrypted.key.pem',
|
||||||
|
command: [openssl, 'genrsa', '-aes256', '-passout', 'pass:test012!', '-out', '@OUTPUT@', '2048']
|
||||||
|
)
|
||||||
|
|
||||||
|
client_encrypted_temp_req = custom_target(
|
||||||
|
'client_encrypted_temp_req',
|
||||||
|
input: client_encrypted_key_pem,
|
||||||
|
output: 'client_encrypted_temp_req',
|
||||||
|
command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-passin', 'pass:test012!', '-out', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
client_encrypted_cert_pem = custom_target(
|
||||||
|
'client_encrypted_cert_pem',
|
||||||
|
input: [client_encrypted_temp_req, rootca_cert_pem, rootca_key_pem],
|
||||||
|
output: 'client_encrypted.cert.pem',
|
||||||
|
command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Copy test files to the build directory
|
||||||
|
configure_file(input: 'ca-bundle.crt', output: 'ca-bundle.crt', copy: true)
|
||||||
|
configure_file(input: 'image.jpg', output: 'image.jpg', copy: true)
|
||||||
|
subdir('www')
|
||||||
|
subdir('www2'/'dir')
|
||||||
|
subdir('www3'/'dir')
|
||||||
|
|
||||||
|
# GoogleTest 1.13.0 requires C++14
|
||||||
|
test_options = []
|
||||||
|
if gtest_dep.version().version_compare('>=1.13.0')
|
||||||
|
test_options += 'cpp_std=c++14'
|
||||||
|
endif
|
||||||
|
|
||||||
|
test(
|
||||||
|
'main',
|
||||||
|
executable(
|
||||||
|
'main',
|
||||||
|
'test.cc',
|
||||||
|
dependencies: [
|
||||||
|
cpp_httplib_dep,
|
||||||
|
gtest_dep,
|
||||||
|
libcurl_dep
|
||||||
|
],
|
||||||
|
override_options: test_options
|
||||||
|
),
|
||||||
|
depends: [
|
||||||
|
key_pem,
|
||||||
|
cert_pem,
|
||||||
|
cert2_pem,
|
||||||
|
key_encrypted_pem,
|
||||||
|
cert_encrypted_pem,
|
||||||
|
rootca_key_pem,
|
||||||
|
rootca_cert_pem,
|
||||||
|
client_key_pem,
|
||||||
|
client_cert_pem,
|
||||||
|
client_encrypted_key_pem,
|
||||||
|
client_encrypted_cert_pem
|
||||||
|
],
|
||||||
|
workdir: meson.current_build_dir(),
|
||||||
|
timeout: 300
|
||||||
|
)
|
@ -1 +0,0 @@
|
|||||||
docker-compose down --rmi all
|
|
@ -1 +0,0 @@
|
|||||||
docker-compose up -d
|
|
6340
test/test.cc
6340
test/test.cc
File diff suppressed because it is too large
Load Diff
@ -16,3 +16,6 @@ emailAddress = test@email.address
|
|||||||
|
|
||||||
[req_attributes]
|
[req_attributes]
|
||||||
challengePassword = 1234
|
challengePassword = 1234
|
||||||
|
|
||||||
|
[SAN]
|
||||||
|
subjectAltName=IP:127.0.0.1
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user