Compare commits
1112 Commits
preserves@
...
main
Author | SHA1 | Date |
---|---|---|
Tony Garnock-Jones | 73c6593f84 | |
Tony Garnock-Jones | a9e226f759 | |
Tony Garnock-Jones | 33db0b8718 | |
Tony Garnock-Jones | e923d87fa5 | |
Tony Garnock-Jones | 83697b0e56 | |
Tony Garnock-Jones | 1798e64615 | |
Tony Garnock-Jones | be32f9b7c8 | |
Tony Garnock-Jones | dc1b0ac54d | |
Tony Garnock-Jones | d579a0d607 | |
Tony Garnock-Jones | 7178fb0d9b | |
Tony Garnock-Jones | 4c0bd3b9d7 | |
Tony Garnock-Jones | b98f434ac9 | |
Tony Garnock-Jones | 61cec52d46 | |
Tony Garnock-Jones | f6ddf0ca3b | |
Tony Garnock-Jones | 9c7770a54f | |
Tony Garnock-Jones | cd29602761 | |
Tony Garnock-Jones | c411e47d7f | |
Tony Garnock-Jones | 897fc13054 | |
Tony Garnock-Jones | 9420cc7236 | |
Tony Garnock-Jones | e0ef236001 | |
Tony Garnock-Jones | 634b263ed2 | |
Tony Garnock-Jones | 2a6d0912b6 | |
Tony Garnock-Jones | 4a656dc929 | |
Tony Garnock-Jones | 2532b42959 | |
Tony Garnock-Jones | b12d49739c | |
Tony Garnock-Jones | aea735bb4e | |
Tony Garnock-Jones | 9b71388817 | |
Tony Garnock-Jones | b6ac046ba7 | |
Tony Garnock-Jones | 7b3731a5e4 | |
Tony Garnock-Jones | 185c233b2f | |
Tony Garnock-Jones | 22b2f162bc | |
Tony Garnock-Jones | f664399a8c | |
Tony Garnock-Jones | cd504becf7 | |
Tony Garnock-Jones | 48b2f06f8e | |
Tony Garnock-Jones | 284614eecb | |
Tony Garnock-Jones | 114875b52f | |
Tony Garnock-Jones | 12a690b4b5 | |
Tony Garnock-Jones | f01cbf7443 | |
Tony Garnock-Jones | 375cf291e0 | |
Tony Garnock-Jones | ab34971eef | |
Tony Garnock-Jones | 441941fb19 | |
Tony Garnock-Jones | 586385c716 | |
Tony Garnock-Jones | b192313c94 | |
Tony Garnock-Jones | 3153dc7c62 | |
Tony Garnock-Jones | 5edcca1e7f | |
Tony Garnock-Jones | 401e3973ee | |
Tony Garnock-Jones | b0001e44cb | |
Tony Garnock-Jones | 831f15099d | |
Tony Garnock-Jones | 782cbd73b2 | |
Tony Garnock-Jones | 6e3950cbc5 | |
Tony Garnock-Jones | cd4f8e410f | |
Tony Garnock-Jones | d540ee6faf | |
Tony Garnock-Jones | 0e43df1f9b | |
Tony Garnock-Jones | 694d4e7ae7 | |
Tony Garnock-Jones | c5bec4ea76 | |
Tony Garnock-Jones | 2e84614b3b | |
Tony Garnock-Jones | 071566b1e1 | |
Tony Garnock-Jones | 8a8facc080 | |
Tony Garnock-Jones | d4d1919957 | |
Tony Garnock-Jones | d0619cb164 | |
Tony Garnock-Jones | 85b3e513f9 | |
Tony Garnock-Jones | d638555239 | |
Tony Garnock-Jones | ebd8b3f05b | |
Tony Garnock-Jones | 39bfeedb54 | |
Tony Garnock-Jones | 2bfd5f4b03 | |
Tony Garnock-Jones | 3313674f15 | |
Tony Garnock-Jones | d11ec61714 | |
Tony Garnock-Jones | c89147dd6a | |
Tony Garnock-Jones | e0736e03c5 | |
Tony Garnock-Jones | d7b983e140 | |
Tony Garnock-Jones | 1a0772d39f | |
Tony Garnock-Jones | 9ae16b4561 | |
Tony Garnock-Jones | ae7555f6f3 | |
Tony Garnock-Jones | 63c124307b | |
Tony Garnock-Jones | 1784024952 | |
Tony Garnock-Jones | 9ed9296fc0 | |
Tony Garnock-Jones | 03cb5ab02f | |
Tony Garnock-Jones | 5820755277 | |
Tony Garnock-Jones | e269acebee | |
Tony Garnock-Jones | 334d9df81c | |
Tony Garnock-Jones | ac34d3fa8e | |
Tony Garnock-Jones | 000c0ff2be | |
Tony Garnock-Jones | e4fdd26c71 | |
Tony Garnock-Jones | 7fe4030dd5 | |
Tony Garnock-Jones | 61f298503e | |
Tony Garnock-Jones | 2eb12a30d6 | |
Tony Garnock-Jones | fb63ac24b0 | |
Tony Garnock-Jones | ec03bdb45f | |
Tony Garnock-Jones | 23e0e59daf | |
Tony Garnock-Jones | c18e9dd1fe | |
Tony Garnock-Jones | a69444f085 | |
Tony Garnock-Jones | 982d916b61 | |
Tony Garnock-Jones | 47b4c07268 | |
Tony Garnock-Jones | 8276a50552 | |
Tony Garnock-Jones | cf50e00f80 | |
Tony Garnock-Jones | c053102d07 | |
Tony Garnock-Jones | 1a2657fe33 | |
Tony Garnock-Jones | cdc5295a72 | |
Tony Garnock-Jones | 7c0fc8f358 | |
Tony Garnock-Jones | 1aedfe46b7 | |
Tony Garnock-Jones | e2e81a67c3 | |
Tony Garnock-Jones | 9d1db2d71f | |
Tony Garnock-Jones | eaa8963ead | |
Tony Garnock-Jones | 7958b9a6f2 | |
Tony Garnock-Jones | 8c9f3b13ee | |
Tony Garnock-Jones | b8fb7abab1 | |
Tony Garnock-Jones | 9595872177 | |
Tony Garnock-Jones | e8c0a2565e | |
Tony Garnock-Jones | 755a8bc73b | |
Tony Garnock-Jones | c30073c3f9 | |
Tony Garnock-Jones | a14b2d49b7 | |
Tony Garnock-Jones | 4869507b09 | |
Tony Garnock-Jones | c0df6c83a8 | |
Tony Garnock-Jones | 2445ab4a5a | |
Tony Garnock-Jones | 5b8c07cb3f | |
Tony Garnock-Jones | 55deeea343 | |
Tony Garnock-Jones | ee0f9a79fd | |
Tony Garnock-Jones | 49242bb4dc | |
Tony Garnock-Jones | 3893b0e581 | |
Tony Garnock-Jones | ed40b2198b | |
Tony Garnock-Jones | 9c90efe36b | |
Tony Garnock-Jones | e31f834d72 | |
Tony Garnock-Jones | af5de5b836 | |
Tony Garnock-Jones | 8db860648b | |
Tony Garnock-Jones | f009920dd7 | |
Tony Garnock-Jones | a2cb071f01 | |
Tony Garnock-Jones | 130e58a3e1 | |
Tony Garnock-Jones | 4b40bf174d | |
Tony Garnock-Jones | 01d3659216 | |
Tony Garnock-Jones | ccf277cddb | |
Tony Garnock-Jones | 4e471ed896 | |
Tony Garnock-Jones | 0eabbd4257 | |
Tony Garnock-Jones | 19a4edc36a | |
Tony Garnock-Jones | 975fc05d8f | |
Tony Garnock-Jones | 9cc537abf8 | |
Tony Garnock-Jones | 6a56dad886 | |
Tony Garnock-Jones | 7b4724f795 | |
Tony Garnock-Jones | 5b2db04a05 | |
Tony Garnock-Jones | 3253c665d6 | |
Tony Garnock-Jones | a123630bb5 | |
Tony Garnock-Jones | 04e1c1b0b1 | |
Tony Garnock-Jones | 71ead5abb7 | |
Tony Garnock-Jones | 9f0217b22a | |
Tony Garnock-Jones | 0f08461ccc | |
Tony Garnock-Jones | 53e15bc46c | |
Tony Garnock-Jones | 3360e013a4 | |
Tony Garnock-Jones | 01a766c974 | |
Tony Garnock-Jones | 5aee3af65a | |
Tony Garnock-Jones | 82f8bf1e0f | |
Tony Garnock-Jones | bc1f4f0ae5 | |
Tony Garnock-Jones | 82903af6c0 | |
Tony Garnock-Jones | 8ba216ff96 | |
Tony Garnock-Jones | e3d67af17f | |
Tony Garnock-Jones | 22e4d0b272 | |
Tony Garnock-Jones | c649339ab5 | |
Tony Garnock-Jones | 683ef11587 | |
Tony Garnock-Jones | 486528416d | |
Tony Garnock-Jones | 202d92d237 | |
Tony Garnock-Jones | 5d0bf91d30 | |
Tony Garnock-Jones | f47786871c | |
Tony Garnock-Jones | 3b89cbe880 | |
Tony Garnock-Jones | b7a2acf65b | |
Tony Garnock-Jones | 6cbfefc76d | |
Tony Garnock-Jones | 0cdcc1d8e7 | |
Tony Garnock-Jones | 8edb657603 | |
Tony Garnock-Jones | ad3da3896b | |
Tony Garnock-Jones | c395265c3b | |
Tony Garnock-Jones | 930964ca05 | |
Tony Garnock-Jones | 32ee76f7d8 | |
Tony Garnock-Jones | 08e62fd681 | |
Bruce Mitchener | a618987545 | |
Bruce Mitchener | acfacd636b | |
Tony Garnock-Jones | 6f085a7192 | |
Tony Garnock-Jones | 775741f944 | |
Tony Garnock-Jones | 5562add7ba | |
Tony Garnock-Jones | 829ff5c3c5 | |
Tony Garnock-Jones | 9f556fd9e6 | |
Tony Garnock-Jones | 54b9bb6f25 | |
Tony Garnock-Jones | ae02cff81d | |
Tony Garnock-Jones | 3c76819687 | |
Tony Garnock-Jones | 9c5368b69b | |
Tony Garnock-Jones | 60f23286e2 | |
Tony Garnock-Jones | 8788a72a8d | |
Tony Garnock-Jones | 3c8d9dcf98 | |
Tony Garnock-Jones | 1f06ff9422 | |
Tony Garnock-Jones | ac6a138a00 | |
Tony Garnock-Jones | 69010737b9 | |
Tony Garnock-Jones | fe12a8d775 | |
Tony Garnock-Jones | b41c0125b0 | |
Tony Garnock-Jones | 0081e290ca | |
Tony Garnock-Jones | 134de6989b | |
Tony Garnock-Jones | a167e97c00 | |
Tony Garnock-Jones | 7e088ef8b3 | |
Tony Garnock-Jones | 10768bd83e | |
Tony Garnock-Jones | 741b664970 | |
Tony Garnock-Jones | b382b61bf6 | |
Tony Garnock-Jones | fe9d836942 | |
Tony Garnock-Jones | 18dd532df2 | |
Tony Garnock-Jones | 8c28dfa3ae | |
Tony Garnock-Jones | d53ca13cd7 | |
Tony Garnock-Jones | 047ef7c521 | |
Tony Garnock-Jones | 316c2dd407 | |
Tony Garnock-Jones | 108119a351 | |
Tony Garnock-Jones | 89b96f623d | |
Tony Garnock-Jones | f6032d899f | |
Tony Garnock-Jones | fac24309aa | |
Tony Garnock-Jones | b74f9a708b | |
Tony Garnock-Jones | 1fc45a5365 | |
Tony Garnock-Jones | d89896a6a7 | |
Tony Garnock-Jones | a86ff376c0 | |
Tony Garnock-Jones | 8c033fa111 | |
Tony Garnock-Jones | 04cc30d77f | |
Tony Garnock-Jones | 0838d39187 | |
Tony Garnock-Jones | 86000bfbea | |
Tony Garnock-Jones | 2a7eaa5ae3 | |
Tony Garnock-Jones | f69906650e | |
Tony Garnock-Jones | 5314958086 | |
Tony Garnock-Jones | e96faede83 | |
Tony Garnock-Jones | 9645c11051 | |
Tony Garnock-Jones | 019cd35932 | |
Tony Garnock-Jones | a4403cf6f3 | |
Tony Garnock-Jones | c7687772b0 | |
Tony Garnock-Jones | 514dc04920 | |
Tony Garnock-Jones | a6c98159ce | |
Tony Garnock-Jones | 6bc3601e95 | |
Tony Garnock-Jones | 47e579068a | |
Tony Garnock-Jones | 6b2b266a8f | |
Tony Garnock-Jones | 2bad9a6bca | |
Tony Garnock-Jones | 2ab814333c | |
Tony Garnock-Jones | e3e1dd4944 | |
Tony Garnock-Jones | 7cbd1a2813 | |
Tony Garnock-Jones | 99258be1ef | |
Tony Garnock-Jones | 031575ad82 | |
Tony Garnock-Jones | 98697e049d | |
Tony Garnock-Jones | 07b28ca042 | |
Tony Garnock-Jones | d11f008705 | |
Tony Garnock-Jones | 34f92c3870 | |
Tony Garnock-Jones | 313b2c4271 | |
Andrew Elgert | b1f9eb45d2 | |
Tony Garnock-Jones | 8dc51d70e7 | |
Tony Garnock-Jones | d027fc4ed6 | |
Tony Garnock-Jones | 55d55d9ccf | |
Tony Garnock-Jones | cd996bcbc7 | |
Tony Garnock-Jones | 26d65a4dd9 | |
Tony Garnock-Jones | eef675cc0c | |
Tony Garnock-Jones | 156777cfa6 | |
Tony Garnock-Jones | 25f9f2c70c | |
Tony Garnock-Jones | eb28206b3e | |
Tony Garnock-Jones | d73118fc77 | |
Tony Garnock-Jones | f5d7fde6f8 | |
Tony Garnock-Jones | 43312f5ead | |
Emery Hemingway | 661e35a757 | |
Tony Garnock-Jones | 23dbf3cb43 | |
Tony Garnock-Jones | 333503c772 | |
Tony Garnock-Jones | 9559558831 | |
Tony Garnock-Jones | c5974e0b00 | |
Tony Garnock-Jones | b413d458f0 | |
Tony Garnock-Jones | a51fc34700 | |
Tony Garnock-Jones | 7f387d6d71 | |
Tony Garnock-Jones | acbc1c5fe5 | |
Tony Garnock-Jones | 04d8201e08 | |
Tony Garnock-Jones | 5bee5fb247 | |
Tony Garnock-Jones | 560267d0d4 | |
Tony Garnock-Jones | fcf58f0dc0 | |
Tony Garnock-Jones | 502e83c26d | |
Tony Garnock-Jones | 340f809c55 | |
Tony Garnock-Jones | a8d7fda89e | |
Tony Garnock-Jones | 269ed2391a | |
Tony Garnock-Jones | 351feba8d2 | |
Tony Garnock-Jones | fd676e8f53 | |
Tony Garnock-Jones | 0b94af3a52 | |
Tony Garnock-Jones | 52de025c21 | |
Tony Garnock-Jones | a1043f406c | |
Tony Garnock-Jones | b5e2eeae46 | |
Tony Garnock-Jones | 1804fa3918 | |
Tony Garnock-Jones | ad1aec3d89 | |
Tony Garnock-Jones | fd121cfb4c | |
Tony Garnock-Jones | f88e3ca85c | |
Tony Garnock-Jones | be7b8e6477 | |
Tony Garnock-Jones | 703d5ac602 | |
Emery Hemingway | a15cb4fb53 | |
Tony Garnock-Jones | fbda60c96b | |
Tony Garnock-Jones | 4b98acf8ba | |
Tony Garnock-Jones | 3544bf18fa | |
Tony Garnock-Jones | 4528100248 | |
Tony Garnock-Jones | 0feef9cc9e | |
Tony Garnock-Jones | 7d3789e371 | |
Tony Garnock-Jones | 1f495eef1e | |
Tony Garnock-Jones | 73ae8ddba3 | |
Tony Garnock-Jones | bb878bab46 | |
Tony Garnock-Jones | b332f2668e | |
Tony Garnock-Jones | 5b01beb2f6 | |
Tony Garnock-Jones | cd635477c6 | |
Tony Garnock-Jones | 9ba8617952 | |
Tony Garnock-Jones | c49b8673f7 | |
Tony Garnock-Jones | 0bc742d7a2 | |
Tony Garnock-Jones | aff94bbb2f | |
Tony Garnock-Jones | 8b8da80f61 | |
Tony Garnock-Jones | d5a8440a9e | |
Tony Garnock-Jones | 7fdf50b963 | |
Tony Garnock-Jones | 2e8d53c779 | |
Tony Garnock-Jones | 7ebe538c42 | |
Tony Garnock-Jones | 34183a9519 | |
Tony Garnock-Jones | e019148065 | |
Tony Garnock-Jones | c9c973ce9c | |
Tony Garnock-Jones | 00eb9e97b6 | |
Tony Garnock-Jones | 78fe5a2a85 | |
Tony Garnock-Jones | 4ef1341883 | |
Tony Garnock-Jones | 2e50591946 | |
Tony Garnock-Jones | c0d3f37905 | |
Tony Garnock-Jones | 597e993c05 | |
Tony Garnock-Jones | 1a090ce5ff | |
Tony Garnock-Jones | 80e6e4aa3f | |
Tony Garnock-Jones | 46a1228aa7 | |
Tony Garnock-Jones | 0b21fc4a3f | |
Tony Garnock-Jones | 8595034190 | |
Tony Garnock-Jones | b45306db03 | |
Tony Garnock-Jones | 859a88adc2 | |
Tony Garnock-Jones | e3a1be80e9 | |
Tony Garnock-Jones | 5d24902de1 | |
Tony Garnock-Jones | 800e9ccf80 | |
Tony Garnock-Jones | 197359ff22 | |
Tony Garnock-Jones | 22a7cffd12 | |
Tony Garnock-Jones | 9c71df9abb | |
Tony Garnock-Jones | 4a41fac6ec | |
Tony Garnock-Jones | daa6b8f931 | |
Tony Garnock-Jones | fad9111241 | |
Tony Garnock-Jones | 7f65f9ecb1 | |
Tony Garnock-Jones | 32544c544a | |
Tony Garnock-Jones | 0f49b8b1b4 | |
Tony Garnock-Jones | a1f5a6ee13 | |
Tony Garnock-Jones | 356ff74058 | |
Tony Garnock-Jones | 21de8b799a | |
Tony Garnock-Jones | 0596d877f8 | |
Tony Garnock-Jones | d9ee80093b | |
Tony Garnock-Jones | a461657cde | |
Tony Garnock-Jones | 574209966b | |
Tony Garnock-Jones | c5094f8b8f | |
Tony Garnock-Jones | 0129901dab | |
Tony Garnock-Jones | 6cd8cb2c37 | |
Tony Garnock-Jones | bdbffad5d3 | |
Tony Garnock-Jones | 2c58066380 | |
Tony Garnock-Jones | f6999938f8 | |
Tony Garnock-Jones | 382bf8d956 | |
Tony Garnock-Jones | e4792ddccd | |
Tony Garnock-Jones | ae6eaa663e | |
Tony Garnock-Jones | d8615175a5 | |
Tony Garnock-Jones | a98aa357d4 | |
Tony Garnock-Jones | 3d1b151462 | |
Tony Garnock-Jones | 261395beaf | |
Tony Garnock-Jones | 76ca8fe12b | |
Tony Garnock-Jones | 35e5d54683 | |
Tony Garnock-Jones | 8f7ef3ff69 | |
Tony Garnock-Jones | c1077e2f35 | |
Tony Garnock-Jones | 087b8d6130 | |
Tony Garnock-Jones | 9a718548c0 | |
Tony Garnock-Jones | 15765126e2 | |
Tony Garnock-Jones | 2721ce81c4 | |
Tony Garnock-Jones | 850678a80a | |
Tony Garnock-Jones | c3af4656c1 | |
Tony Garnock-Jones | 61ef91a958 | |
Tony Garnock-Jones | 8f99f14b92 | |
Tony Garnock-Jones | f348701169 | |
Tony Garnock-Jones | ed518a9c71 | |
Tony Garnock-Jones | b70352bc85 | |
Tony Garnock-Jones | ddfd2e24a6 | |
Tony Garnock-Jones | 3cc875bce3 | |
Tony Garnock-Jones | fb6a5caf38 | |
Tony Garnock-Jones | 50de00eac5 | |
Tony Garnock-Jones | 8c4fedac0a | |
Tony Garnock-Jones | 602dea1ac6 | |
Tony Garnock-Jones | 275a5aa45b | |
Tony Garnock-Jones | 39f9a2e5ef | |
Tony Garnock-Jones | 21cfa3a1e6 | |
Tony Garnock-Jones | 6c0ef47e03 | |
Tony Garnock-Jones | 550a524ca1 | |
Tony Garnock-Jones | 944ac54414 | |
Tony Garnock-Jones | bfa0b0bcb4 | |
Tony Garnock-Jones | 3b1669389c | |
Tony Garnock-Jones | 9442abe9bd | |
Tony Garnock-Jones | fc84813704 | |
Tony Garnock-Jones | 972085a59b | |
Tony Garnock-Jones | 8b7ea7c589 | |
Tony Garnock-Jones | 80dd3cd6b4 | |
Tony Garnock-Jones | ef6db841dd | |
Tony Garnock-Jones | 282d3f798f | |
Tony Garnock-Jones | 874303186d | |
Tony Garnock-Jones | 2ff4e88822 | |
Tony Garnock-Jones | 57c66f81b1 | |
Tony Garnock-Jones | 1e4bf0dc51 | |
Tony Garnock-Jones | 5a0fbeb1b5 | |
Tony Garnock-Jones | f2d9b68329 | |
Tony Garnock-Jones | 43f31b9a78 | |
Tony Garnock-Jones | 6576f30639 | |
Tony Garnock-Jones | e12d978915 | |
Tony Garnock-Jones | 4673de5db4 | |
Tony Garnock-Jones | bb0d825cdf | |
Tony Garnock-Jones | aadd83d715 | |
Tony Garnock-Jones | ed63d46348 | |
Tony Garnock-Jones | b38d180956 | |
Tony Garnock-Jones | b7c9efc3a1 | |
Emery Hemingway | 4ae8c341c9 | |
Emery Hemingway | 2ff489d975 | |
Tony Garnock-Jones | 1668fdc6dd | |
Tony Garnock-Jones | 3a605e75d6 | |
Tony Garnock-Jones | b2c3032e7a | |
Tony Garnock-Jones | 668ac9f680 | |
Tony Garnock-Jones | ea83031a28 | |
Tony Garnock-Jones | 77c305a4cf | |
Tony Garnock-Jones | 602cfb8800 | |
Tony Garnock-Jones | 5260f85952 | |
Tony Garnock-Jones | 55b02b9cff | |
Tony Garnock-Jones | 386c07628c | |
Tony Garnock-Jones | 5fded03fa4 | |
Tony Garnock-Jones | 0507ab2f38 | |
Tony Garnock-Jones | b73e0c7025 | |
Tony Garnock-Jones | e2a4e3d6cb | |
Tony Garnock-Jones | e31cf739df | |
Tony Garnock-Jones | 486a631e73 | |
Tony Garnock-Jones | 29a882f953 | |
Tony Garnock-Jones | d1d52c2a30 | |
Tony Garnock-Jones | 62d9236045 | |
Tony Garnock-Jones | 1b4064b17c | |
Tony Garnock-Jones | a72810c416 | |
Tony Garnock-Jones | b1ed29657e | |
Tony Garnock-Jones | 9936ddb29d | |
Tony Garnock-Jones | 94e9fabc70 | |
Tony Garnock-Jones | f778325748 | |
Tony Garnock-Jones | b68b485af4 | |
Tony Garnock-Jones | 72a38cea7e | |
Tony Garnock-Jones | ccf01f5f24 | |
Tony Garnock-Jones | 6fdda6636b | |
Tony Garnock-Jones | 843c0c894f | |
Tony Garnock-Jones | e5a5130b56 | |
Tony Garnock-Jones | a91ee3977f | |
Tony Garnock-Jones | ac8567731d | |
Tony Garnock-Jones | 6869a89291 | |
Tony Garnock-Jones | e78196c942 | |
Tony Garnock-Jones | 15a27b4865 | |
Tony Garnock-Jones | df4059ee7a | |
Tony Garnock-Jones | 8d587c0aaa | |
Tony Garnock-Jones | d872f7cf8a | |
Tony Garnock-Jones | 7bf5403353 | |
Tony Garnock-Jones | 9f98e1ef3b | |
Tony Garnock-Jones | 06fc9aa017 | |
Tony Garnock-Jones | 997bea2836 | |
Tony Garnock-Jones | 28249b19f7 | |
Tony Garnock-Jones | e56b62cfbb | |
Tony Garnock-Jones | 985a0b6795 | |
Tony Garnock-Jones | 352d8ba1b3 | |
Tony Garnock-Jones | 8127033407 | |
Tony Garnock-Jones | 6348524542 | |
Tony Garnock-Jones | 87227b5623 | |
Tony Garnock-Jones | c4afc49646 | |
Tony Garnock-Jones | ef67347b8d | |
Tony Garnock-Jones | aabe7b2623 | |
Tony Garnock-Jones | e43e85ce8e | |
Tony Garnock-Jones | 5f2a3e3eb8 | |
Tony Garnock-Jones | eda9979041 | |
Tony Garnock-Jones | 4afc6d4c94 | |
Tony Garnock-Jones | d26e38ded0 | |
Tony Garnock-Jones | 66e7af491f | |
Tony Garnock-Jones | 18ac916899 | |
Tony Garnock-Jones | 00e31c0e29 | |
Tony Garnock-Jones | a9125874bf | |
Tony Garnock-Jones | 8550be0ba2 | |
Tony Garnock-Jones | 3cdf1f662e | |
Tony Garnock-Jones | 027966fb3b | |
Tony Garnock-Jones | f387f5e8c9 | |
Tony Garnock-Jones | 0aded61071 | |
Tony Garnock-Jones | dc451ea7b4 | |
Tony Garnock-Jones | 3e56cf3d7e | |
Tony Garnock-Jones | 94fe6ad946 | |
Tony Garnock-Jones | 446b2ee5f7 | |
Tony Garnock-Jones | 62cd9ac78f | |
Tony Garnock-Jones | 8b5aa372b5 | |
Tony Garnock-Jones | d408070fde | |
Tony Garnock-Jones | 0f30522f19 | |
Tony Garnock-Jones | e0bc1b31b8 | |
Tony Garnock-Jones | 6bb99b45c3 | |
Tony Garnock-Jones | 5669f2aff1 | |
Tony Garnock-Jones | 0c693d8ece | |
Tony Garnock-Jones | 82c66ec1c4 | |
Tony Garnock-Jones | a5065955ca | |
Tony Garnock-Jones | 8afc8f1eae | |
Tony Garnock-Jones | 123b6222ca | |
Tony Garnock-Jones | cf192b634c | |
Tony Garnock-Jones | fff34b8d45 | |
Tony Garnock-Jones | e2b27b619f | |
Tony Garnock-Jones | a2ca133983 | |
Tony Garnock-Jones | e056394ca6 | |
Tony Garnock-Jones | fc1d6afc28 | |
Tony Garnock-Jones | 59bcced776 | |
Tony Garnock-Jones | e45ff6b020 | |
Tony Garnock-Jones | abe60b3506 | |
Tony Garnock-Jones | 5c8bacd759 | |
Tony Garnock-Jones | 1b466aade7 | |
Tony Garnock-Jones | 23329cd8f3 | |
Tony Garnock-Jones | 06a08631aa | |
Tony Garnock-Jones | eafc22fb1c | |
Tony Garnock-Jones | ea75dc8f59 | |
Tony Garnock-Jones | 5161e54e0d | |
Tony Garnock-Jones | 7f8db08039 | |
Tony Garnock-Jones | cf94a95266 | |
Tony Garnock-Jones | 3128f6da82 | |
Tony Garnock-Jones | f1b4a4568b | |
Tony Garnock-Jones | d67bafcb30 | |
Tony Garnock-Jones | ebc609dfec | |
Tony Garnock-Jones | 4f60845dc0 | |
Tony Garnock-Jones | 37ca3fd493 | |
Tony Garnock-Jones | e6d7e9c1b5 | |
Tony Garnock-Jones | b11316e40b | |
Tony Garnock-Jones | c2167f1ee8 | |
Tony Garnock-Jones | 0821f6e3da | |
Tony Garnock-Jones | e02ee00894 | |
Tony Garnock-Jones | 32ebebec34 | |
Tony Garnock-Jones | a0bf6ebf41 | |
Tony Garnock-Jones | 02420543f1 | |
Tony Garnock-Jones | 7abd4a3d3a | |
Tony Garnock-Jones | 2ee1c48fcd | |
Tony Garnock-Jones | 965bda9f9e | |
Tony Garnock-Jones | 61af114d5f | |
Tony Garnock-Jones | 137cc63a97 | |
Tony Garnock-Jones | be10924118 | |
Tony Garnock-Jones | 96707352e6 | |
Tony Garnock-Jones | cfd9898b4d | |
Tony Garnock-Jones | 661d96780d | |
Tony Garnock-Jones | 87946abb63 | |
Tony Garnock-Jones | 96f5c9f434 | |
Tony Garnock-Jones | b24aca8f0f | |
Tony Garnock-Jones | 3078396487 | |
Tony Garnock-Jones | d28901446d | |
Tony Garnock-Jones | e913951b91 | |
Tony Garnock-Jones | e80d849f9a | |
Tony Garnock-Jones | dcdfdb8dd9 | |
Tony Garnock-Jones | 3176e5f8d0 | |
Tony Garnock-Jones | af1405e87a | |
Tony Garnock-Jones | 9d4e6998f2 | |
Tony Garnock-Jones | 9b100ab9aa | |
Tony Garnock-Jones | 5fa8c32ba0 | |
Tony Garnock-Jones | 61c6dfbc3e | |
Tony Garnock-Jones | 48412ae7ea | |
Tony Garnock-Jones | 9b88db6790 | |
Tony Garnock-Jones | cae254ef21 | |
Tony Garnock-Jones | 6d9ed94065 | |
Tony Garnock-Jones | 7712c6e0a9 | |
Tony Garnock-Jones | 423c9d0bba | |
Tony Garnock-Jones | 6ffc34065f | |
Tony Garnock-Jones | 932375fa49 | |
Tony Garnock-Jones | 7e3bf2ade5 | |
Tony Garnock-Jones | 790782fc87 | |
Tony Garnock-Jones | c527160e9d | |
Tony Garnock-Jones | 64593436a8 | |
Tony Garnock-Jones | 7c8a5c61ca | |
Tony Garnock-Jones | e9b5b3549c | |
Tony Garnock-Jones | e30ade6ed3 | |
Tony Garnock-Jones | 7e76503779 | |
Tony Garnock-Jones | da08189dd4 | |
Tony Garnock-Jones | 8f1a83e548 | |
Tony Garnock-Jones | 41fe3c3440 | |
Tony Garnock-Jones | 64ff818cd1 | |
Tony Garnock-Jones | 83d15a838e | |
Tony Garnock-Jones | 0fb1ef4efd | |
Tony Garnock-Jones | c04447d62a | |
Tony Garnock-Jones | c7b252ca9d | |
Tony Garnock-Jones | 6143ddc93d | |
Tony Garnock-Jones | c70035b044 | |
Tony Garnock-Jones | 297d1d39eb | |
Tony Garnock-Jones | 8b7baec26b | |
Tony Garnock-Jones | 23943f8b14 | |
Tony Garnock-Jones | 8cafcbcaf1 | |
Tony Garnock-Jones | 70990d2371 | |
Tony Garnock-Jones | 7c4cf38110 | |
Tony Garnock-Jones | 569563a564 | |
Tony Garnock-Jones | aea230b056 | |
Tony Garnock-Jones | 00759673ce | |
Tony Garnock-Jones | 5b9c4d29f6 | |
Tony Garnock-Jones | 17d8d076ec | |
Tony Garnock-Jones | e882d5a4df | |
Tony Garnock-Jones | fc8709706c | |
Tony Garnock-Jones | 3156180601 | |
Tony Garnock-Jones | f1d403a6a7 | |
Tony Garnock-Jones | d69787e5ee | |
Tony Garnock-Jones | c3bc678a46 | |
Tony Garnock-Jones | dd9e190bed | |
Tony Garnock-Jones | 01e8e2c279 | |
Tony Garnock-Jones | e4392ea2d5 | |
Tony Garnock-Jones | aa1c983acc | |
Tony Garnock-Jones | 5c2d12971d | |
Tony Garnock-Jones | 9c4be54be1 | |
Tony Garnock-Jones | 7546ba29ad | |
Tony Garnock-Jones | c7dbbdc178 | |
Tony Garnock-Jones | 4144a90b9d | |
Tony Garnock-Jones | 60d1be41a3 | |
Tony Garnock-Jones | fdb43f6292 | |
Tony Garnock-Jones | da513a249e | |
Tony Garnock-Jones | f808e37e89 | |
Tony Garnock-Jones | f12343e723 | |
Tony Garnock-Jones | 0d4d1e738c | |
Tony Garnock-Jones | feb6361029 | |
Tony Garnock-Jones | 27002dfe7f | |
Tony Garnock-Jones | e5b6c46169 | |
Tony Garnock-Jones | 6cecf64df5 | |
Tony Garnock-Jones | 7acf7c5b40 | |
Tony Garnock-Jones | 0bcb4e64ec | |
Tony Garnock-Jones | 30bcc1a50b | |
Tony Garnock-Jones | a4d61017d8 | |
Tony Garnock-Jones | ade9b0a0f1 | |
Tony Garnock-Jones | f93d329f48 | |
Tony Garnock-Jones | c05180c492 | |
Tony Garnock-Jones | f14b902f24 | |
Tony Garnock-Jones | 139f4ff08b | |
Tony Garnock-Jones | 12e38ddd8f | |
Tony Garnock-Jones | 2c5ee2066b | |
Tony Garnock-Jones | 53bd5a1a7e | |
Tony Garnock-Jones | b968f77ff6 | |
Tony Garnock-Jones | bd68786f1c | |
Tony Garnock-Jones | 64696ac184 | |
Tony Garnock-Jones | 7683a64a5b | |
Tony Garnock-Jones | 498c63ef67 | |
Tony Garnock-Jones | 9014a0ffb8 | |
Tony Garnock-Jones | 8d96743d53 | |
Tony Garnock-Jones | b23acdaf5a | |
Tony Garnock-Jones | a44884d9f5 | |
Tony Garnock-Jones | 947b816a57 | |
Tony Garnock-Jones | b69c3a0894 | |
Tony Garnock-Jones | d7bf235813 | |
Tony Garnock-Jones | ab12c6535f | |
Tony Garnock-Jones | 534018e3a4 | |
Tony Garnock-Jones | ef7cea09bf | |
Tony Garnock-Jones | 264c4b9d2e | |
Tony Garnock-Jones | 87e816306d | |
Tony Garnock-Jones | 5470497aa2 | |
Tony Garnock-Jones | f90544d807 | |
Tony Garnock-Jones | cb88c587b6 | |
Tony Garnock-Jones | e6efd03be7 | |
Tony Garnock-Jones | 351cafddb4 | |
Tony Garnock-Jones | 90ce0a544d | |
Tony Garnock-Jones | 7ab12108e4 | |
Tony Garnock-Jones | ed3cd8de26 | |
Tony Garnock-Jones | e4f7219dc6 | |
Tony Garnock-Jones | 98e2511fe1 | |
Tony Garnock-Jones | 3559cc679e | |
Tony Garnock-Jones | 460529e1c7 | |
Tony Garnock-Jones | badb059440 | |
Tony Garnock-Jones | 668d4e6271 | |
Tony Garnock-Jones | 394d10e6da | |
Tony Garnock-Jones | de7ac63b96 | |
Tony Garnock-Jones | 52bc77c9d7 | |
Tony Garnock-Jones | 49efc76580 | |
Tony Garnock-Jones | a24a5b19f5 | |
Tony Garnock-Jones | eeb84ad669 | |
Tony Garnock-Jones | 6c9071fd88 | |
Tony Garnock-Jones | 8b2aa0fb87 | |
Tony Garnock-Jones | 5c644624c4 | |
Tony Garnock-Jones | 163e338ce5 | |
Tony Garnock-Jones | 4ed8fd2c92 | |
Tony Garnock-Jones | 669d1b480d | |
Tony Garnock-Jones | 7d06c7dce0 | |
Tony Garnock-Jones | bdd699ae9f | |
Tony Garnock-Jones | 33a80533fa | |
Tony Garnock-Jones | 1ca796e6aa | |
Tony Garnock-Jones | 4914c8cd68 | |
Tony Garnock-Jones | 46d76dfca7 | |
Tony Garnock-Jones | 0db223ede8 | |
Tony Garnock-Jones | 52be118dc7 | |
Tony Garnock-Jones | a1fdddcf7b | |
Tony Garnock-Jones | e594d22d09 | |
Tony Garnock-Jones | aaee62044c | |
Tony Garnock-Jones | 9bdfc4c3ab | |
Tony Garnock-Jones | c4bfc0eefc | |
Tony Garnock-Jones | 2559a4713f | |
Tony Garnock-Jones | 1d6956fa55 | |
Tony Garnock-Jones | 9e6743abdc | |
Tony Garnock-Jones | 43b776eb7f | |
Tony Garnock-Jones | ebab3fafc5 | |
Tony Garnock-Jones | 986e7fa30d | |
Tony Garnock-Jones | b5405d80ec | |
Tony Garnock-Jones | 49cba14b4f | |
Tony Garnock-Jones | 1654ad4c80 | |
Tony Garnock-Jones | 2ddedc7673 | |
Tony Garnock-Jones | dbd6c3cf53 | |
Tony Garnock-Jones | 2bde06f509 | |
Tony Garnock-Jones | 0f1ea4aa20 | |
Tony Garnock-Jones | 46ab6d90ec | |
Tony Garnock-Jones | b5b4effeac | |
Tony Garnock-Jones | 20b676df27 | |
Tony Garnock-Jones | ecdb314366 | |
Tony Garnock-Jones | 7253d1507e | |
Tony Garnock-Jones | 10380e451a | |
Tony Garnock-Jones | 436b14e2fe | |
Tony Garnock-Jones | cefc029f70 | |
Tony Garnock-Jones | e4a2503899 | |
Tony Garnock-Jones | 6fc41ead6f | |
Tony Garnock-Jones | 8e068fbdbf | |
Tony Garnock-Jones | 8442718f96 | |
Tony Garnock-Jones | 854a2bc41c | |
Tony Garnock-Jones | 638f8e026e | |
Tony Garnock-Jones | 2d0e6255bd | |
Tony Garnock-Jones | 58d2bf6f3a | |
Tony Garnock-Jones | 70ce961dd2 | |
Tony Garnock-Jones | e5965fde83 | |
Tony Garnock-Jones | 8c783dbc7d | |
Tony Garnock-Jones | aef970dc2d | |
Tony Garnock-Jones | 8895d2b6a4 | |
Tony Garnock-Jones | be6537f6d4 | |
Tony Garnock-Jones | c54a17162d | |
Tony Garnock-Jones | 63cf5d1cf2 | |
Tony Garnock-Jones | 9f9514a7e6 | |
Tony Garnock-Jones | 825d208198 | |
Tony Garnock-Jones | b4d7af4322 | |
Tony Garnock-Jones | e763174846 | |
Tony Garnock-Jones | e1e7904a87 | |
Tony Garnock-Jones | 545e1bb6de | |
Tony Garnock-Jones | 3ad56a5275 | |
Tony Garnock-Jones | 55fab35073 | |
Tony Garnock-Jones | 4d8618ce63 | |
Tony Garnock-Jones | 121bcc7a53 | |
Tony Garnock-Jones | 94f6959ac8 | |
Tony Garnock-Jones | d64bb82c22 | |
Tony Garnock-Jones | c23cbcc60c | |
Tony Garnock-Jones | e187fb83b4 | |
Tony Garnock-Jones | 4434f712b6 | |
Tony Garnock-Jones | d91924c72b | |
Tony Garnock-Jones | 7d8453a806 | |
Tony Garnock-Jones | 1d73289345 | |
Tony Garnock-Jones | 4ded3a484c | |
Tony Garnock-Jones | 87dda48083 | |
Tony Garnock-Jones | 4814790d8e | |
Tony Garnock-Jones | 0304c2631b | |
Tony Garnock-Jones | 889d38bbb8 | |
Tony Garnock-Jones | c2fe82e71d | |
Tony Garnock-Jones | 0970898065 | |
Tony Garnock-Jones | 1c07573178 | |
Tony Garnock-Jones | c8b752a73b | |
Tony Garnock-Jones | d372977023 | |
Tony Garnock-Jones | 084f54f869 | |
Tony Garnock-Jones | 98558b81f0 | |
Tony Garnock-Jones | 5d2ee85b36 | |
Tony Garnock-Jones | 3463cd4a65 | |
Tony Garnock-Jones | 5f71239130 | |
Tony Garnock-Jones | 376e83acd0 | |
Tony Garnock-Jones | 306c7c2cae | |
Tony Garnock-Jones | bacf310648 | |
Tony Garnock-Jones | 98346c61d5 | |
Tony Garnock-Jones | ca42ffe832 | |
Tony Garnock-Jones | 05c7343983 | |
Tony Garnock-Jones | e6f99ae2e1 | |
Tony Garnock-Jones | b9019d03f1 | |
Tony Garnock-Jones | 178f528bf0 | |
Tony Garnock-Jones | 8f2da8f8db | |
Tony Garnock-Jones | 12121128a6 | |
Tony Garnock-Jones | 425e7dd5cb | |
Tony Garnock-Jones | 434279ab66 | |
Tony Garnock-Jones | 7072f19407 | |
Tony Garnock-Jones | adfb1822ac | |
Tony Garnock-Jones | 14be044092 | |
Tony Garnock-Jones | 5393308be4 | |
Tony Garnock-Jones | 5afb1469f3 | |
Tony Garnock-Jones | e078a71b30 | |
Tony Garnock-Jones | 47da2e5308 | |
Tony Garnock-Jones | 5e3ae0c18b | |
Tony Garnock-Jones | d6d4e830a3 | |
Tony Garnock-Jones | 2311dbd245 | |
Tony Garnock-Jones | d502249c53 | |
Tony Garnock-Jones | 1cc0325007 | |
Tony Garnock-Jones | 4ee9f99529 | |
Tony Garnock-Jones | 942fb79a2e | |
Tony Garnock-Jones | ce947c109a | |
Tony Garnock-Jones | 2b9eddfc79 | |
Tony Garnock-Jones | d811032ac7 | |
Tony Garnock-Jones | bddb111f87 | |
Tony Garnock-Jones | a1db64422c | |
Tony Garnock-Jones | cfa0a9caa3 | |
Tony Garnock-Jones | fc88612414 | |
Tony Garnock-Jones | c75aaf4b18 | |
Tony Garnock-Jones | 55e4222d68 | |
Tony Garnock-Jones | 9a46a22fb6 | |
Tony Garnock-Jones | 86fc0acc92 | |
Tony Garnock-Jones | f531eb347d | |
Tony Garnock-Jones | 8ec5946696 | |
Tony Garnock-Jones | d9726a6878 | |
Tony Garnock-Jones | 8d7e7c6d95 | |
Tony Garnock-Jones | c09032f609 | |
Tony Garnock-Jones | c8f564aea4 | |
Tony Garnock-Jones | 94f6f9af9d | |
Tony Garnock-Jones | d932431d83 | |
Tony Garnock-Jones | ba2c7e9978 | |
Tony Garnock-Jones | a0d51fab4c | |
Tony Garnock-Jones | 87bb930020 | |
Tony Garnock-Jones | 929f916d1d | |
Tony Garnock-Jones | 932818145b | |
Tony Garnock-Jones | 7c587f03d3 | |
Tony Garnock-Jones | 036d42a73a | |
Tony Garnock-Jones | 754306aca3 | |
Tony Garnock-Jones | 8187337187 | |
Tony Garnock-Jones | 447380218e | |
Tony Garnock-Jones | 62bab41bed | |
Tony Garnock-Jones | c27aa7579e | |
Tony Garnock-Jones | e894d0dbbc | |
Tony Garnock-Jones | dc96f74075 | |
Tony Garnock-Jones | a05bf0cb7a | |
Tony Garnock-Jones | 4022b76650 | |
Tony Garnock-Jones | 5412f8b9d0 | |
Tony Garnock-Jones | 550224e0b1 | |
Tony Garnock-Jones | eaff7b86d8 | |
Tony Garnock-Jones | 6d2120989b | |
Tony Garnock-Jones | c8c027f762 | |
Tony Garnock-Jones | 75790f237b | |
Tony Garnock-Jones | 1268c4f9bd | |
Tony Garnock-Jones | 481f866ada | |
Tony Garnock-Jones | 993689356b | |
Tony Garnock-Jones | 074fc5db98 | |
Tony Garnock-Jones | c46566e5a0 | |
Tony Garnock-Jones | 055b367764 | |
Tony Garnock-Jones | a19a9d50c6 | |
Tony Garnock-Jones | 4353d5280e | |
Tony Garnock-Jones | a69297c3ba | |
Tony Garnock-Jones | 407e8778a1 | |
Tony Garnock-Jones | 83b09d9406 | |
Tony Garnock-Jones | 95c04bd5d5 | |
Tony Garnock-Jones | 1743756097 | |
Tony Garnock-Jones | 013c5f4dae | |
Tony Garnock-Jones | 6fd06cec98 | |
Tony Garnock-Jones | 532e811894 | |
Tony Garnock-Jones | 6bf49874b7 | |
Tony Garnock-Jones | abca13e260 | |
Tony Garnock-Jones | 0bb61d260f | |
Tony Garnock-Jones | a5d4098e29 | |
Tony Garnock-Jones | d8a041a647 | |
Tony Garnock-Jones | 320215dca0 | |
Tony Garnock-Jones | b4d5334a1a | |
Tony Garnock-Jones | 329cee7bd6 | |
Tony Garnock-Jones | 10351b5369 | |
Tony Garnock-Jones | 3f62d68bab | |
Tony Garnock-Jones | b0ed7e914b | |
Tony Garnock-Jones | 55db55b42b | |
Tony Garnock-Jones | f7b7f29a3b | |
Tony Garnock-Jones | 98e981dccf | |
Tony Garnock-Jones | 66cac324e0 | |
Tony Garnock-Jones | 44f142d86b | |
Tony Garnock-Jones | 2c5ed693f5 | |
Tony Garnock-Jones | a1a604aee8 | |
Tony Garnock-Jones | ca2276d268 | |
Tony Garnock-Jones | 8459521db5 | |
Tony Garnock-Jones | 749747ca05 | |
Tony Garnock-Jones | 77fd8e86bf | |
Tony Garnock-Jones | 85fe7b3b07 | |
Tony Garnock-Jones | 5d719c2c6f | |
Tony Garnock-Jones | ccf4f97ed8 | |
Tony Garnock-Jones | 2391722a25 | |
Tony Garnock-Jones | 3c6bff6646 | |
Tony Garnock-Jones | 137aa308e3 | |
Tony Garnock-Jones | 3c059a573c | |
Tony Garnock-Jones | 8d3146cf30 | |
Tony Garnock-Jones | 7cc7bd783d | |
Tony Garnock-Jones | 9f83756931 | |
Tony Garnock-Jones | 28101cc7d1 | |
Tony Garnock-Jones | 3f76049f13 | |
Tony Garnock-Jones | 95cdd84db4 | |
Tony Garnock-Jones | 70a305458a | |
Tony Garnock-Jones | 24afca5d32 | |
Tony Garnock-Jones | b1c7fe8c04 | |
Tony Garnock-Jones | 046a2ad999 | |
Tony Garnock-Jones | 6cfb474a6f | |
Tony Garnock-Jones | 89c3171758 | |
Tony Garnock-Jones | fefa2af730 | |
Tony Garnock-Jones | f2910eb8d0 | |
Tony Garnock-Jones | 078e8dd4e8 | |
Tony Garnock-Jones | e4e6622074 | |
Tony Garnock-Jones | 21cc7595ae | |
Tony Garnock-Jones | e01f960ddc | |
Tony Garnock-Jones | 275d9e73b1 | |
Tony Garnock-Jones | bffbeb2f6e | |
Tony Garnock-Jones | 1f4adc5ba6 | |
Tony Garnock-Jones | 83b7513fae | |
Tony Garnock-Jones | b9aba35d39 | |
Tony Garnock-Jones | eb088aa491 | |
Tony Garnock-Jones | 22b76cb9b6 | |
Tony Garnock-Jones | 65b5399dbe | |
Tony Garnock-Jones | d1a5389060 | |
Tony Garnock-Jones | 0788edaaed | |
Tony Garnock-Jones | c4f90ef86b | |
Tony Garnock-Jones | 83dce41092 | |
Tony Garnock-Jones | 0b0709b615 | |
Tony Garnock-Jones | b5f4c3a498 | |
Tony Garnock-Jones | e7ef060695 | |
Tony Garnock-Jones | dfc50fcaee | |
Tony Garnock-Jones | c30154e0c8 | |
Tony Garnock-Jones | b122d6e2e0 | |
Tony Garnock-Jones | 2d57d0001b | |
Tony Garnock-Jones | 450b73a4d2 | |
Tony Garnock-Jones | 3f5e431717 | |
Tony Garnock-Jones | 1011818be6 | |
Tony Garnock-Jones | 94058b5ec2 | |
Tony Garnock-Jones | f07d2e6a5d | |
Tony Garnock-Jones | 6ad1707870 | |
Tony Garnock-Jones | 92463c37d4 | |
Tony Garnock-Jones | fdae09b44c | |
Tony Garnock-Jones | 67bf47a5c9 | |
Tony Garnock-Jones | bddb4331aa | |
Tony Garnock-Jones | 0feff28d82 | |
Tony Garnock-Jones | d29ec9ffc4 | |
Tony Garnock-Jones | 316a772fec | |
Tony Garnock-Jones | 2a85e53acc | |
Tony Garnock-Jones | a099fc9a3b | |
Tony Garnock-Jones | 3eeaab375a | |
Tony Garnock-Jones | af7c523513 | |
Tony Garnock-Jones | 8e0ab95d82 | |
Tony Garnock-Jones | ebbd268166 | |
Tony Garnock-Jones | 4272238048 | |
Tony Garnock-Jones | d883ad9a75 | |
Tony Garnock-Jones | 340f77cc3c | |
Tony Garnock-Jones | 46b220e042 | |
Tony Garnock-Jones | f6bbe191ab | |
Tony Garnock-Jones | 1054cd4219 | |
Tony Garnock-Jones | 0832b94ce9 | |
Tony Garnock-Jones | 02c02b641f | |
Tony Garnock-Jones | ab06cd465d | |
Tony Garnock-Jones | b1ca95c835 | |
Tony Garnock-Jones | 27fb961653 | |
Tony Garnock-Jones | 826fce6203 | |
Tony Garnock-Jones | 91f48d08f9 | |
Tony Garnock-Jones | 488bfdc59c | |
Tony Garnock-Jones | ccc3b5780b | |
Tony Garnock-Jones | 6f03538956 | |
Tony Garnock-Jones | 84f99fc471 | |
Tony Garnock-Jones | 4b957d8785 | |
Tony Garnock-Jones | a00314d108 | |
Tony Garnock-Jones | d2a57c839c | |
Tony Garnock-Jones | 96679e8bf5 | |
Tony Garnock-Jones | c95b000b7b | |
Tony Garnock-Jones | 9611c1f977 | |
Tony Garnock-Jones | 623bbd0a71 | |
Tony Garnock-Jones | 54d1e349b3 | |
Tony Garnock-Jones | f1b37ac9af | |
Tony Garnock-Jones | cea42db3dc | |
Tony Garnock-Jones | 2ff6cf400b | |
Tony Garnock-Jones | 383824c5e0 | |
Tony Garnock-Jones | 0a537b73ed | |
Tony Garnock-Jones | ab1ef92334 | |
Tony Garnock-Jones | 5b0ad234fd | |
Tony Garnock-Jones | ebcdbe9ee2 | |
Tony Garnock-Jones | ba8875e1d7 | |
Tony Garnock-Jones | 8aaec11635 | |
Tony Garnock-Jones | fe8c766d1e | |
Tony Garnock-Jones | da2a6d44d1 | |
Tony Garnock-Jones | f081470b1e | |
Tony Garnock-Jones | 72edd1327a | |
Tony Garnock-Jones | 8cdd67f5ba | |
Tony Garnock-Jones | 898a5d680d | |
Tony Garnock-Jones | 63cd853ba1 | |
Tony Garnock-Jones | 116b37a24f | |
Tony Garnock-Jones | 9af8ca8e97 | |
Tony Garnock-Jones | ef3b88147e | |
Tony Garnock-Jones | 199ce1fdba | |
Tony Garnock-Jones | 6775868272 | |
Tony Garnock-Jones | a548485b2e | |
Tony Garnock-Jones | 2349e80b30 | |
Tony Garnock-Jones | bec3a10d64 | |
Tony Garnock-Jones | 365e41b798 | |
Tony Garnock-Jones | 2fc1d42cd7 | |
Tony Garnock-Jones | 1be54a2b8f | |
Tony Garnock-Jones | 02e0eae396 | |
Tony Garnock-Jones | d5cbe90235 | |
Tony Garnock-Jones | 2dd2c329ed | |
Tony Garnock-Jones | 5fe529825f | |
Tony Garnock-Jones | 9c6deecb55 | |
Tony Garnock-Jones | 07312954df | |
Tony Garnock-Jones | 15d14cdafe | |
Tony Garnock-Jones | b2203b6f5b | |
Tony Garnock-Jones | 85804a9e6d | |
Tony Garnock-Jones | 7efce309f6 | |
Tony Garnock-Jones | e35c237c34 | |
Tony Garnock-Jones | 942fa30d9d | |
Tony Garnock-Jones | 64c7de832e | |
Tony Garnock-Jones | 04ecbe03e3 | |
Tony Garnock-Jones | 102cb93f26 | |
Tony Garnock-Jones | 738a47ce90 | |
Tony Garnock-Jones | 2b74100b2a | |
Tony Garnock-Jones | aa59846ee9 | |
Tony Garnock-Jones | a34a4cd20e | |
Tony Garnock-Jones | a817fed40d | |
Tony Garnock-Jones | f09067d719 | |
Tony Garnock-Jones | 6f4e06f6b9 | |
Tony Garnock-Jones | a7147fa123 | |
Tony Garnock-Jones | 54b33f5f13 | |
Tony Garnock-Jones | 4d73ab6d69 | |
Tony Garnock-Jones | aac73efd3a | |
Tony Garnock-Jones | 36c5d92b73 | |
Tony Garnock-Jones | adda505f45 | |
Tony Garnock-Jones | e90a790963 | |
Tony Garnock-Jones | 27ac21bed1 | |
Tony Garnock-Jones | 79af429b58 | |
Tony Garnock-Jones | 6783daa20d | |
Tony Garnock-Jones | e57fe62a48 | |
Tony Garnock-Jones | fa5eaa6e39 | |
Tony Garnock-Jones | 3f0ec34d49 | |
Tony Garnock-Jones | e84b4c5780 | |
Tony Garnock-Jones | 9de37d5df0 | |
Tony Garnock-Jones | fd87f07ec0 | |
Tony Garnock-Jones | e7a528fc72 | |
Tony Garnock-Jones | 3609b121d5 | |
Tony Garnock-Jones | f4beba8b7a | |
Tony Garnock-Jones | 389b74bf87 | |
Tony Garnock-Jones | 8f205ea0ca | |
Tony Garnock-Jones | d7daa7dd02 | |
Tony Garnock-Jones | 9f61059449 | |
Tony Garnock-Jones | fc0ee42a98 | |
Tony Garnock-Jones | 9064258dbc | |
Tony Garnock-Jones | 1bb7e1862e | |
Tony Garnock-Jones | 467da29c56 | |
Tony Garnock-Jones | 6221bdf5c7 | |
Tony Garnock-Jones | 3c676cb3de | |
Tony Garnock-Jones | 7d2ffe640d | |
Christopher Lemmer Webber | 1a84c3f609 | |
Tony Garnock-Jones | 40d474d456 | |
Tony Garnock-Jones | 872ea562a2 | |
Tony Garnock-Jones | 37a12b3595 | |
Tony Garnock-Jones | 4074699766 | |
Tony Garnock-Jones | 8d022a332a | |
Tony Garnock-Jones | e38f56c1c3 | |
Tony Garnock-Jones | 2db315d140 | |
Tony Garnock-Jones | 95e9167d78 | |
Tony Garnock-Jones | 3556c98346 | |
Tony Garnock-Jones | 5e89bfb2ca | |
Tony Garnock-Jones | c7bae6eff8 | |
Christopher Lemmer Webber | 17368c8961 | |
Christopher Lemmer Webber | 90ed5bc6d7 | |
Christopher Lemmer Webber | bcd7dcba79 | |
Christopher Lemmer Webber | ba62d998ca | |
Christopher Lemmer Webber | dca049ce46 | |
Tony Garnock-Jones | 0365fd8c36 | |
Tony Garnock-Jones | 0aa7218523 | |
Tony Garnock-Jones | 8ffa9ce915 | |
Tony Garnock-Jones | 9974002cad | |
Christopher Lemmer Webber | c9a624839f | |
Tony Garnock-Jones | efd976ed90 | |
Tony Garnock-Jones | 0f5f0630d2 | |
Tony Garnock-Jones | 74f9093c5e | |
Tony Garnock-Jones | 05789f77cd | |
Tony Garnock-Jones | c058a4ca71 | |
Tony Garnock-Jones | 7520067318 | |
Tony Garnock-Jones | 78681265ee | |
Tony Garnock-Jones | 423a3531f6 | |
Tony Garnock-Jones | a737a0cf18 | |
Tony Garnock-Jones | 1cf1ab1707 | |
Tony Garnock-Jones | bd6517e154 | |
Tony Garnock-Jones | 672ee83be0 | |
Tony Garnock-Jones | 892df1634a | |
Tony Garnock-Jones | a9bed81556 | |
Tony Garnock-Jones | f08e223cb0 | |
Tony Garnock-Jones | e2b859e55d | |
Tony Garnock-Jones | d349e89ea4 | |
Tony Garnock-Jones | 098cbe58ac | |
Tony Garnock-Jones | 2b1e2f2fba | |
Tony Garnock-Jones | 7933e34766 | |
Tony Garnock-Jones | c6700c2f2b | |
Tony Garnock-Jones | 04868a2309 | |
Tony Garnock-Jones | 681bb9705f | |
Tony Garnock-Jones | df94b7a8c6 | |
Tony Garnock-Jones | 7861341951 | |
Tony Garnock-Jones | 4a70364eda | |
Tony Garnock-Jones | 2c20de2730 | |
Tony Garnock-Jones | 97cd007bab | |
Tony Garnock-Jones | 0d0124f004 | |
Tony Garnock-Jones | e2e4d23e23 | |
Tony Garnock-Jones | eadd83d603 | |
Tony Garnock-Jones | 5e8e462091 | |
Tony Garnock-Jones | e6e3057de3 | |
Tony Garnock-Jones | 52cd074767 | |
Tony Garnock-Jones | 6960046263 | |
Tony Garnock-Jones | 4d4cd6f417 | |
Tony Garnock-Jones | 48f8024056 | |
Tony Garnock-Jones | 2bf10030c1 | |
Tony Garnock-Jones | 0e835bc6f9 | |
Tony Garnock-Jones | a2755a6c9b | |
Tony Garnock-Jones | 48b800a7ce | |
Tony Garnock-Jones | 5ab78e8281 | |
Tony Garnock-Jones | c1898ef73f | |
Tony Garnock-Jones | 5d9b03171d | |
Tony Garnock-Jones | 65866b9041 | |
Tony Garnock-Jones | 320cb894f1 | |
Tony Garnock-Jones | 921fe7dfd2 | |
Tony Garnock-Jones | 70888543fa | |
Tony Garnock-Jones | 6db903bd37 | |
Tony Garnock-Jones | 328f82acbc | |
Tony Garnock-Jones | 5de9e31bdb | |
Tony Garnock-Jones | 0799fd3293 | |
Tony Garnock-Jones | 1a2ad3201f | |
Tony Garnock-Jones | d826aa9116 | |
Tony Garnock-Jones | cbbd6ffd0c | |
Tony Garnock-Jones | 8f20ae7a48 | |
Tony Garnock-Jones | fdd7eb6e94 | |
Tony Garnock-Jones | b2a9b53b6c | |
Tony Garnock-Jones | 870da87350 | |
Tony Garnock-Jones | 1fd763cf56 | |
Tony Garnock-Jones | 87deddc879 | |
Tony Garnock-Jones | 594b379a77 | |
Tony Garnock-Jones | 28cf1d37a8 | |
Tony Garnock-Jones | ecdf123358 | |
Tony Garnock-Jones | 10d8eb1b0a | |
Tony Garnock-Jones | f654280b88 | |
Tony Garnock-Jones | 639241c5a6 | |
Tony Garnock-Jones | c9484ac9ac | |
Tony Garnock-Jones | 80c55e4f30 | |
Tony Garnock-Jones | ab8acf2154 | |
Tony Garnock-Jones | f0a63fbb4c | |
Tony Garnock-Jones | bd08ede47a | |
Tony Garnock-Jones | a8519f6ae3 | |
Tony Garnock-Jones | 9b4b548896 | |
Tony Garnock-Jones | da10d47d64 |
|
@ -1,2 +1,8 @@
|
|||
_site/
|
||||
cheatsheet.pdf
|
||||
preserves-expressions.pdf
|
||||
preserves-binary.pdf
|
||||
preserves-schema.pdf
|
||||
preserves-text.pdf
|
||||
preserves.pdf
|
||||
scratch/
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
image: jekyll/jekyll:3.8
|
||||
|
||||
variables:
|
||||
JEKYLL_ENV: production
|
||||
|
||||
pages:
|
||||
script:
|
||||
- jekyll build -d public
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
only:
|
||||
- main
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg height="86" viewBox="0 0 76 86" width="76" xmlns="http://www.w3.org/2000/svg"><path d="m76 82v4h-76l.00080851-4zm-3-6v5h-70v-5zm-62.6696277-54 .8344146.4217275.4176066 6.7436084.4176065 10.9576581v10.5383496l-.4176065 13.1364492-.0694681 8.8498268-1.1825531.3523804h-4.17367003l-1.25202116-.3523804-.48627608-8.8498268-.41840503-13.0662957v-10.5375432l.41840503-11.028618.38167482-6.7798947.87034634-.3854412zm60.0004653 0 .8353798.4217275.4168913 6.7436084.4168913 10.9576581v10.5383496l-.4168913 13.1364492-.0686832 8.8498268-1.1835879.3523804h-4.1737047l-1.2522712-.3523804-.4879704-8.8498268-.4168913-13.0662957v-10.5375432l.4168913-11.028618.3833483-6.7798947.8697215-.3854412zm-42.000632 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2529447-.3523804-.4863246-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8688361-.3854412zm23 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2521462-.3523804-.4871231-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8696347-.3854412zm21.6697944-9v7h-70v-7zm-35.7200748-13 36.7200748 8.4088317-1.4720205 2.5911683h-70.32799254l-2.19998696-2.10140371z" fill="#2c2c2c" fill-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
26
Makefile
26
Makefile
|
@ -1,2 +1,24 @@
|
|||
preserves.pdf: preserves.md preserves.css
|
||||
google-chrome --headless --disable-gpu --print-to-pdf=$@ http://localhost:4000/preserves.html
|
||||
__ignored__ := $(shell ./setup.sh)
|
||||
|
||||
PDFS=\
|
||||
preserves.pdf \
|
||||
preserves-text.pdf \
|
||||
preserves-binary.pdf \
|
||||
preserves-schema.pdf \
|
||||
preserves-expressions.pdf \
|
||||
cheatsheet.pdf
|
||||
|
||||
all: $(PDFS)
|
||||
|
||||
clean:
|
||||
rm -f $(PDFS)
|
||||
|
||||
%.pdf: %.md preserves.css
|
||||
google-chrome --headless --disable-gpu --print-to-pdf=$@ \
|
||||
http://localhost:4000/$*.html
|
||||
|
||||
test-all:
|
||||
make -C tests
|
||||
(cd implementations/javascript; npm test)
|
||||
(cd implementations/python; make test)
|
||||
(cd implementations/racket/preserves; make testonly)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Preserves: an Expressive Data Language
|
||||
Copyright 2018-2022 Tony Garnock-Jones
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
projectpages: "https://gitlab.com/preserves/preserves"
|
||||
projecttree: "https://gitlab.com/preserves/preserves/tree/main"
|
||||
title: "Preserves: an Expressive Data Language"
|
||||
no_site_title: true
|
||||
---
|
||||
|
||||
This [repository]({{page.projectpages}}) contains a
|
||||
[definition](preserves.html) and various implementations of *Preserves*, a
|
||||
data model with associated serialization formats in many ways
|
||||
comparable to JSON, XML, S-expressions, CBOR, ASN.1 BER, and so on.
|
||||
|
||||
## Core documents
|
||||
|
||||
### Preserves data model and serialization formats
|
||||
|
||||
Preserves is defined in terms of a syntax-neutral
|
||||
[data model and semantics](preserves.html#semantics)
|
||||
which all transfer syntaxes share. This allows trivial, completely
|
||||
automatic, perfect-fidelity conversion between syntaxes.
|
||||
|
||||
- [Preserves specification](preserves.html):
|
||||
- [Preserves semantics and data model](preserves.html#semantics),
|
||||
- [Preserves textual syntax](preserves-text.html), and
|
||||
- [Preserves machine-oriented binary syntax](preserves-binary.html)
|
||||
- [Preserves tutorial](TUTORIAL.html)
|
||||
- [Quick Reference for Preserves syntax](cheatsheet.html)
|
||||
- [Canonical Form for Binary Syntax](canonical-binary.html)
|
||||
- [Syrup](https://github.com/ocapn/syrup#pseudo-specification), a
|
||||
hybrid binary/human-readable syntax for the Preserves data model
|
||||
|
||||
### Preserves schema and queries
|
||||
|
||||
- [Preserves Schema specification](preserves-schema.html)
|
||||
- [Preserves Path specification](preserves-path.html)
|
||||
|
||||
## Implementations
|
||||
|
||||
#### Implementations of the data model, plus Preserves textual and binary transfer syntax
|
||||
|
||||
| Language[^pre-alpha-implementations] | Code | Package | Docs |
|
||||
|--------------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------|
|
||||
| Nim | [git.syndicate-lang.org](https://git.syndicate-lang.org/ehmry/preserves-nim) | | |
|
||||
| Python | [preserves.dev]({{page.projecttree}}/implementations/python/) | [`pip install preserves`](https://pypi.org/project/preserves/) | [docs](python/latest/) |
|
||||
| Racket | [preserves.dev]({{page.projecttree}}/implementations/racket/preserves/) | [`raco pkg install preserves`](https://pkgs.racket-lang.org/package/preserves) | |
|
||||
| Rust | [preserves.dev](https://gitlab.com/preserves/preserves-rs/) | [`cargo add preserves`](https://crates.io/crates/preserves) | [docs](https://docs.rs/preserves/latest/) |
|
||||
| Squeak Smalltalk | [SqueakSource](https://squeaksource.com/Preserves.html) | `Installer ss project: 'Preserves';`<br>` install: 'Preserves'` | |
|
||||
| TypeScript/JavaScript | [preserves.dev]({{page.projecttree}}/implementations/javascript/) | [`yarn add @preserves/core`](https://www.npmjs.com/package/@preserves/core) | |
|
||||
|
||||
[^pre-alpha-implementations]: Pre-alpha implementations also exist for
|
||||
[C]({{page.projecttree}}/implementations/c/) and
|
||||
[C++]({{page.projecttree}}/implementations/cpp/).
|
||||
|
||||
#### Implementations of the data model, plus Syrup transfer syntax
|
||||
|
||||
| Language | Code |
|
||||
|------------|----------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Guile | [github.com/ocapn/syrup](https://github.com/ocapn/syrup/blob/master/impls/guile/syrup.scm) |
|
||||
| Haskell | [github.com/zenhack/haskell-preserves](https://github.com/zenhack/haskell-preserves) |
|
||||
| JavaScript | [github.com/zarutian/agoric-sdk](https://github.com/zarutian/agoric-sdk/blob/zarutian/captp_variant/packages/captp/lib/syrup.js) |
|
||||
| Python | [github.com/ocapn/syrup](https://github.com/ocapn/syrup/blob/master/impls/python/syrup.py) |
|
||||
| Racket | [github.com/ocapn/syrup](https://github.com/ocapn/syrup/blob/master/impls/racket/syrup/syrup.rkt) |
|
||||
|
||||
## Tools
|
||||
|
||||
### Preserves documents
|
||||
|
||||
- [preserves-tool](doc/preserves-tool.html), generic syntax translation and pretty-printing
|
||||
|
||||
### Preserves Schema documents and codegen
|
||||
|
||||
- [Tools for working with Preserves Schema](doc/schema-tools.html)
|
||||
|
||||
## Additional resources
|
||||
|
||||
- [Preserves Expressions (P-expressions, pexprs)](preserves-expressions.html)
|
||||
- Some [conventions for common data types](conventions.html)
|
||||
- [Open questions](questions.html); see also the
|
||||
[issues list]({{page.projectpages}}/issues)
|
||||
- [Why not Just Use JSON?](why-not-json.html)
|
||||
|
||||
## Contact
|
||||
|
||||
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||
|
||||
## Licensing
|
||||
|
||||
The contents of this repository are made available to you under the
|
||||
[Apache License, version 2.0](LICENSE)
|
||||
(<http://www.apache.org/licenses/LICENSE-2.0>), and are Copyright
|
||||
2018-2022 Tony Garnock-Jones.
|
||||
|
||||
## Notes
|
|
@ -0,0 +1,365 @@
|
|||
---
|
||||
---
|
||||
|
||||
TODO:
|
||||
|
||||
- consider a lead byte value used to wrap an encoded `Value` in a
|
||||
size-counted wrapper? That way parsers can quickly skip nested
|
||||
structure they're not interested in...
|
||||
|
||||
- https://github.com/uwiger/sext
|
||||
|
||||
- http://erlang.org/doc/reference_manual/expressions.html#term-comparisons;
|
||||
in particular, see the non-lexicographic ordering on tuples (vs
|
||||
lists).
|
||||
|
||||
- should there be a built-in (i.e. recommended) reference type for external data??
|
||||
- if there were, it'd give IPLD-like characteristics to the thing from the get-go
|
||||
- IRIs and mime-typed things are already in there so why not content-based addressing
|
||||
|
||||
- Check out https://hitchdev.com/strictyaml/, in particular the "Why
|
||||
StrictYAML?" and "Design justifications" sections; perhaps borrow
|
||||
elements of that structure for writing a comparison of Preserves
|
||||
with other things
|
||||
|
||||
It is becoming VERY CLEAR that on-the-wire efficiency is... a
|
||||
secondary concern. Perhaps revise the binary syntax to be less terse
|
||||
and better for simple encoding and for term ordering,
|
||||
canonicalization, quick indexing, etc.
|
||||
|
||||
- the indexing thing clashes with the term ordering thing
|
||||
- maybe put the indexes at the end?? they could be optional
|
||||
|
||||
It might be nice to define some kind of jsonpath/xpath-like means of
|
||||
naming a subterm within a Preserve. Record labels would be a kind of
|
||||
assertion on the current node. Indexes and keys would be steps. It'd
|
||||
be a lot like xpath I think; see also my `racket-xe` package.
|
||||
|
||||
- `<child>` - moves into direct children
|
||||
- `<descendant-or-self>` - moves into direct and indirect children, including this node
|
||||
- `<descendant>` - moves into direct and indirect children, excluding this node
|
||||
- `<where[P*]>` - "where" clause, applies nested path, keeping nodes with submatches
|
||||
- `<or[P*]>` - result of first non-empty `P` match
|
||||
- `<at K>` - moves into direct children whose keys are `K` from
|
||||
dictionaries, sequences or records; `K` should be a number for the
|
||||
latter two
|
||||
- `<label>` - moves into labels of records
|
||||
- `<equals V>` - filters to only nodes that equal `V`
|
||||
- `<isa T>` - filters to only nodes that are `T ∈
|
||||
{boolean float double signed-integer string byte-string symbol record sequence set dictionary}`
|
||||
|
||||
Abbreviations:
|
||||
|
||||
/ = <child>
|
||||
// = <descendant-or-self>
|
||||
[P*] = <where[P*]>
|
||||
Symbol = [<label> <equals Symbol>]
|
||||
NonSymbolAtom = <at NonSymbolAtom>
|
||||
|
||||
# TODO
|
||||
|
||||
- explain why total order / comparison of values is important and/or useful
|
||||
- what does having a total order unlock?
|
||||
- explain why records are good (see below on yaml tags etc)
|
||||
- hashability: comes from equivalence
|
||||
- more examples
|
||||
- over-8000er mountains
|
||||
- yaml example from the top of https://camel.readthedocs.io/en/latest/yamlref.html
|
||||
- having records with ANONYMOUS but ordered fields is good for easy
|
||||
parsing in languages like C where you don't want to explicitly
|
||||
search dictionaries of key/value mappings
|
||||
- labels vs. yaml tags vs. annotations
|
||||
- yaml tags are complex. they're relative uris, for the most part
|
||||
anyway, except the local ones; they force interpretation rather
|
||||
than being data, e.g. `!` forces a node to be interpreted as a
|
||||
string, sequence, or map and `?` forces "tag resolution" aka
|
||||
dwimming of scalar syntax. Labels here don't change how their
|
||||
fields resolve at all.
|
||||
- they're also used to specify particular host-language classes
|
||||
and other objects.
|
||||
|
||||
!!python/none
|
||||
!!python/bool
|
||||
!!python/bytes
|
||||
!!python/str
|
||||
!!python/unicode
|
||||
!!python/int
|
||||
!!python/long
|
||||
!!python/float
|
||||
!!python/complex
|
||||
!!python/list
|
||||
!!python/tuple
|
||||
!!python/dict
|
||||
!!python/name:module.name
|
||||
!!python/module:package.module
|
||||
!!python/object:module.Cls
|
||||
!!python/object/new:module.Cls
|
||||
!!python/object/apply:module.f
|
||||
|
||||
!ruby/symbol
|
||||
!ruby/sym (alias of the previous!)
|
||||
!ruby/range
|
||||
!ruby/regexp
|
||||
!ruby/struct:StructTypeName
|
||||
!ruby/object:Module::ClassName
|
||||
!ruby/array:Module::ClassName (subtyping arrays! objects, not data)
|
||||
!ruby/hash:Module::ClassName (subtyping hashes! objects, not data)
|
||||
|
||||
!perl/regexp
|
||||
|
||||
- yaml tag meanings are per-document or global. Labels aren't
|
||||
really specified. Is this good or bad? Once there's a type
|
||||
system, labels will become meaningful in a per-type context.
|
||||
|
||||
- yaml tags basically are meant to mean the type of the object
|
||||
following. Labels are not: they are for distinguishing among
|
||||
variants *within* a type. (In a unityped setting, this boils
|
||||
down to the same thing at a different level; object-level vs
|
||||
meta-level variants.)
|
||||
|
||||
- in some cases (ruby) a tag indicates a subclass: a
|
||||
behavioural refinement of some *object* rather than a
|
||||
structural extension of some *data*.
|
||||
|
||||
- yaml tags don't have intrinsic meaning: implementations are
|
||||
allowed to complain if they don't recognise a tag. They also
|
||||
affect how and whether an object can be used as a dict key;
|
||||
labels, otoh, have intrinsic (trivial) meaning, and *any*
|
||||
preserves value is allowed to be used as a dict key. YAML
|
||||
documents then have implementation-specific meaning, but
|
||||
Preserves have intrinsic meaning.
|
||||
|
||||
- yaml has schemas, holy shit, and there the tags really do
|
||||
direct interpretation of values to a significant extent.
|
||||
Preserves forces the application to do such interpretations:
|
||||
the parser/reader won't do them for you.
|
||||
- TODO: be clearer in the bit on "validity"
|
||||
|
||||
- yaml tags are URIs, and cannot be structured data
|
||||
|
||||
- annotations
|
||||
- in brief: out-of-domain METADATA; implementation/metalevel, not domain/objectlevel
|
||||
- comments are a good example: out-of-domain description about the
|
||||
value, not part of the value itself
|
||||
- uses:
|
||||
- roundtripping config cf the approach taken by http://augeas.net/
|
||||
- embedding trace information in messages
|
||||
- provenance information
|
||||
- stack information / distributed trace/continuation record
|
||||
|
||||
- remove comments once annotations are in!
|
||||
|
||||
- binary syntax: length-prefixing is good for pattern-matching,
|
||||
because it allows you to reject terms based on arity without having
|
||||
to scan the contents.
|
||||
|
||||
- hey so what about protobufs? the optional fields /
|
||||
forward-and-backwards-compatibility thing is interesting.
|
||||
|
||||
- what about skipping e.g. lists? would need byte-length prefix
|
||||
|
||||
- When thinking about extensibility and forward/backward
|
||||
compatibility, consider this:
|
||||
<https://eighty-twenty.org/2016/09/18/gnome-flashback-patch>
|
||||
|
||||
- types, type-directed whitespace-sensitive parsing (oh hey it might
|
||||
also lead to optimized binary parsers based on type?)
|
||||
|
||||
- Zephyr (here `*` is postfix Kleene star and `?` marks zero-or-one):
|
||||
|
||||
asdl_ty = Sum(identifier, field*, ctor, ctor*) ;; typename, common fields, at least one ctor, more ctors
|
||||
| Product(identifier, field, field*) ;; ?? i guess a degenerate kind of sum??
|
||||
ctor = Con(identifier, field*) ;; most like Preserves' record
|
||||
field = Id(identifier, identifier?) ;; basic typename reference (?)
|
||||
| Option(identifier, identifier?) ;; postfix `?`
|
||||
| Sequence(identifier, identifier?) ;; postfix `*`
|
||||
|
||||
value = SumVal(identifier, value*, value*) ;; there are common fields
|
||||
| ProductVal(value, value*)
|
||||
| SequenceVal(value*)
|
||||
| NoneVal
|
||||
| SomeVal(value)
|
||||
| PrimVal(prim)
|
||||
prim = IntVal(int)
|
||||
| IdentifierVal(identifier)
|
||||
| StringVal(string)
|
||||
|
||||
- So then for us, where we have kind of union types more than
|
||||
labelled sums:
|
||||
- `<equals Value>`, `<lessthan Value>`, `<greaterthan Value>`
|
||||
- must be equal to / less than / greater than this value
|
||||
- maybe take `<range lo hi>` as primitive?
|
||||
- no, because of infinitesimals
|
||||
- `<regexp string>` ... etc? (Perhaps `<pattern regexpstring>`
|
||||
is better) (Be sure to specify ECMA-262 dialect, with
|
||||
restrictions a la JSON-schema
|
||||
https://json-schema.org/latest/json-schema-validation.html#rfc.section.4.3)
|
||||
- identifier naming a type definition
|
||||
- some type definitions are builtin: `Boolean = <union <equals #true> <equals #false>>`
|
||||
- some have to be primitive rather than builtin, like
|
||||
`SignedInteger` or `Double`, because they have unboundedly
|
||||
(or awkwardly) many inhabitants and the class above or
|
||||
below them doesn't have a limit ordinal in the right place
|
||||
- parameters/`forall`?
|
||||
- `<record Type Type ...>` - first one is the label type
|
||||
- `<list Type ...>` - heterogeneous list of specific types
|
||||
- `<listof Type>` - homogeneous list
|
||||
- `<setof Type>`
|
||||
- `{ keyType: valueType, ... }` - heterogeneous dict
|
||||
- wait, `{ keyLiteral: valueType, ... }` might be better - sugar for
|
||||
`<dict [<equals keyLiteral> valueType] ...>`
|
||||
- `<dict+ ...>` for when extra members are allowed
|
||||
- what about optional members?
|
||||
- `<dictof keyType valueType>` - homogeneous dict
|
||||
- `<union Type ...>`
|
||||
- empty union is uninhabited type(!)
|
||||
- a kind of or
|
||||
- `<and Type ...>`
|
||||
- simultaneous constraints on type, for range, or for range-and-type
|
||||
- a kind of intersection; parallel reduction
|
||||
- `<interleave Type ...>` ?? maybe, if sequences are a thing?
|
||||
Could be good for organizing key-value mappings in
|
||||
dictionary-brackets, because unordered... and sets...
|
||||
|
||||
Sketching it out:
|
||||
|
||||
preserves_ty =
|
||||
|
||||
- Oh dear, actually this is very close to being just a pattern
|
||||
language without the captures.
|
||||
|
||||
a1.a & b1.b = a1.(a & b1.b) + b1.(a1.a & b)
|
||||
|
||||
- Take two.
|
||||
- `<== Value >`, `<|<| value>`, `<|>| value>`, `|<=|`, `|>=|`, `*eq` `*lt` `*gt` `*le` `*ge`
|
||||
- `_` for discard, `<*discard>`
|
||||
- scalar values not symbols beginning with `*` match themselves as if they were `==`-wrapped
|
||||
- all the special things are records, possibly 0-ary, with labels symbols starting with `*`
|
||||
except for `==` etc and `_` and `...`
|
||||
- if you have to match a label like `*foo` it might clash, so match `<== *foo>` instead:
|
||||
`<*foo 1 2 3>` ==> `<<== *foo> 1 2 3>`
|
||||
- `<*int>` for `SignedInteger`, `<*string>`, `<*symbol>`,
|
||||
`<*bytestring>`/`<*binary>`, `<*float>`, `<*double>`,
|
||||
`<*bool>`
|
||||
- `<*and Pattern ⋯>`
|
||||
- `<*or Pattern ⋯>`
|
||||
- `<*not Pattern>` ?
|
||||
- `<Pattern Pattern ⋯>` - match record
|
||||
- `[Pattern ⋯]` - match sequence
|
||||
- `#set{Pattern}` - match set
|
||||
- don't know how to match dictionaries yet
|
||||
- view it as an interleave of its keyvalues
|
||||
- `<*interleave Pattern ⋯>`?
|
||||
- somehow allow specification of a keyvalue that is repeating, that is optional, etc
|
||||
- `{Keypat:Valpat ⋯ <... Keypat>:<... Valpat>}` ??? eww?
|
||||
- `<*group Pattern ⋯>` - sequence of values spliced into wider sequence?
|
||||
- use literal `...` symbol (!) to mark repetition in a sequence:
|
||||
`[<*string> ...]`
|
||||
- could use literal `?` to mark optionality; or better perhaps `<*optional Pattern>`,
|
||||
equivalent to `<*biased-choice Pattern <*group>>`; hmm, biased choice!
|
||||
- could use `<*repeat lo hi>` or similar for counted repetition
|
||||
- don't know how to write refs to other types yet! def labels starting with `*`?
|
||||
|
||||
<*def <*foo> <*or <*int> <*string>>>
|
||||
<*foo>
|
||||
|
||||
<*def <*maybe a> <*or <nothing> <just a>>>
|
||||
<*maybe <*int>>
|
||||
|
||||
- should those be relative URLs, or jsonpointer or something,
|
||||
so can drag in types from the web?
|
||||
- NOTE: No schema for indicating attachment of annotations?!?!?!
|
||||
|
||||
The YAML example:
|
||||
|
||||
database:
|
||||
username: admin
|
||||
password: foobar # TODO get prod passwords out of config
|
||||
socket: /var/tmp/database.sock
|
||||
options: {use_utf8: true}
|
||||
memcached:
|
||||
host: 10.0.0.99
|
||||
workers:
|
||||
- host: 10.0.0.101
|
||||
port: 2301
|
||||
- host: 10.0.0.102
|
||||
port: 2302
|
||||
|
||||
Could be:
|
||||
|
||||
[ <Database [<Username "admin">
|
||||
@<TODO "get prod passwords out of config"> <Password "foobar">
|
||||
<Socket "/var/tmp/database.sock">
|
||||
<Options [<UseUTF8>]>]>
|
||||
<Memcached [<Host "10.0.0.99">]>
|
||||
<Workers [<Worker "10.0.0.101" 2301>
|
||||
<Worker "10.0.0.102" 2302>]> ]
|
||||
|
||||
Or
|
||||
|
||||
{
|
||||
database: {
|
||||
username: "admin",
|
||||
@<TODO "get prod passwords out of config">
|
||||
password: "foobar",
|
||||
socket: "/var/tmp/database.sock",
|
||||
options: #set{use_utf8}
|
||||
},
|
||||
memcached: {
|
||||
host: "10.0.0.99"
|
||||
},
|
||||
workers: [ <Worker "10.0.0.101" 2301>
|
||||
<Worker "10.0.0.102" 2302> ]
|
||||
}
|
||||
|
||||
Its schema-sketch could be
|
||||
|
||||
[ <*interleave <Database [ <*interleave <Username <*string>>
|
||||
<Password <*string>>
|
||||
<*optional <Socket <*string>>>
|
||||
<*optional <Options [<*option> ...]>>> ]>
|
||||
<Memcached [ <Host <*ipv4>> ... ]>
|
||||
<Workers [ <Worker <*ipv4> <*u16>> ... ]>> ]
|
||||
|
||||
(for the first variant) or
|
||||
|
||||
{
|
||||
database: {
|
||||
username: <*string>,
|
||||
password: <*string>,
|
||||
<*optional socket>: <*string>,
|
||||
<*optional options>: #set{<*option>}
|
||||
},
|
||||
memcached: {
|
||||
host: <*ipv4>
|
||||
},
|
||||
workers: [ <Worker <*ipv4> <*u16>> ... ]
|
||||
}
|
||||
|
||||
Annotations will be allowed on any value; but also perhaps on a
|
||||
key-value mapping pair?
|
||||
|
||||
{
|
||||
@"I label the key" key: value
|
||||
key @"I label the mapping": value
|
||||
key: @"I label the value" value
|
||||
}
|
||||
|
||||
??
|
||||
|
||||
Perhaps not.
|
||||
|
||||
The schema for the second YAML config sketch would allow the instance
|
||||
to be written:
|
||||
|
||||
database:
|
||||
username: admin
|
||||
@<TODO "get prod passwords out of config">
|
||||
password: foobar
|
||||
socket: /var/tmp/database.sock
|
||||
options: use_utf8
|
||||
memcached:
|
||||
host: 10.0.0.99
|
||||
workers:
|
||||
<Worker 10.0.0.101 2301>
|
||||
<Worker 10.0.0.102 2302>
|
|
@ -0,0 +1,390 @@
|
|||
---
|
||||
no_site_title: true
|
||||
title: "Preserves: a tutorial"
|
||||
---
|
||||
|
||||
By Christine Lemmer Webber and Tony Garnock-Jones
|
||||
August 2019.
|
||||
|
||||
*This document, like Preserves itself, is released under*
|
||||
*[version 2.0 of the Apache license](./LICENSE).*
|
||||
|
||||
|
||||
<a id="org74bd8ba"></a>
|
||||
|
||||
# Overview
|
||||
|
||||
Preserves is a serialization system which supplies both a
|
||||
human-readable textual and efficient binary syntax; converting between
|
||||
the two is straightforward.
|
||||
Preserves' human readable syntax is easy to read and should be mostly
|
||||
familiar if you already know systems like JSON.
|
||||
However, Preserves is more precisely specified than JSON, and also
|
||||
has a clean extension mechanism.
|
||||
|
||||
This document is a tutorial; it does not get into all the details of
|
||||
Preserves.
|
||||
For that, see the [Preserves specification](preserves.html).
|
||||
|
||||
|
||||
<a id="org0a73f91"></a>
|
||||
|
||||
# Preserves basics
|
||||
|
||||
|
||||
<a id="orge8d9054"></a>
|
||||
|
||||
## Starting with the familiar
|
||||
|
||||
If you're familiar with JSON, Preserves looks fairly similar:
|
||||
|
||||
```
|
||||
{"name": "Missy Rose",
|
||||
"species": "Felis Catus",
|
||||
"age": 13,
|
||||
"foods": ["kibble", "cat treats", "tinned meat"]}
|
||||
```
|
||||
|
||||
Preserves also has something we can use for debugging/development
|
||||
information called "annotations"; they aren't actually read in as data
|
||||
but we can use them for comments.
|
||||
(They can also be used for other development tools and are not
|
||||
restricted to strings; more on this later, but for now, we will stick
|
||||
to the special comment annotation syntax.)
|
||||
|
||||
```
|
||||
# I'm an annotation... basically a comment. Ignore me!
|
||||
"I'm data! Don't ignore me!"
|
||||
```
|
||||
|
||||
Preserves supports some data types you're probably already familiar
|
||||
with from JSON, and which look fairly similar in the textual format:
|
||||
|
||||
```
|
||||
# booleans
|
||||
#t
|
||||
#f
|
||||
|
||||
# various kinds of numbers:
|
||||
42
|
||||
123556789012345678901234567890
|
||||
-10
|
||||
13.5
|
||||
|
||||
# strings
|
||||
"I'm feeling stringy!"
|
||||
|
||||
# sequences (lists)
|
||||
["cat", "dog", "mouse", "goldfish"]
|
||||
|
||||
# dictionaries (hashmaps)
|
||||
{"cat": "meow",
|
||||
"dog": "woof",
|
||||
"goldfish": "glub glub",
|
||||
"mouse": "squeak"}
|
||||
```
|
||||
|
||||
|
||||
<a id="org270f7f4"></a>
|
||||
|
||||
## Going beyond JSON
|
||||
|
||||
We can observe a few differences from JSON already; it's possible to
|
||||
*reliably* express integers of arbitrary length in Preserves, and booleans look a little
|
||||
bit different.
|
||||
A few more interesting differences:
|
||||
|
||||
```
|
||||
# Preserves treats commas as whitespace, so these are the same
|
||||
["cat", "dog", "mouse", "goldfish"]
|
||||
["cat" "dog" "mouse" "goldfish"]
|
||||
|
||||
# We can use anything as keys in dictionaries, not just strings
|
||||
{1: "the loneliest number",
|
||||
["why", "was", 6, "afraid", "of", 7]: "because 7 8 9",
|
||||
{"dictionaries": "as keys???"}: "well, why not?"}
|
||||
```
|
||||
|
||||
Preserves technically provides various types of numbers:
|
||||
|
||||
```
|
||||
# Signed Integers
|
||||
42
|
||||
-42
|
||||
5907212309572059846509324862304968273468909473609826340
|
||||
-5907212309572059846509324862304968273468909473609826340
|
||||
|
||||
# Doubles (Double-precision IEEE floats)
|
||||
3.141592653589793
|
||||
```
|
||||
|
||||
Preserves also provides some types that don't come in JSON.
|
||||
`Symbols` are fairly interesting; they look a lot like strings but
|
||||
really aren't meant to represent text as much as they are, well… a
|
||||
symbolic name.
|
||||
Often they're meant to be used for something that has symbolic importance
|
||||
to the program, but not textual importance (other than to guide the
|
||||
programmer… not unlike variable names).
|
||||
|
||||
```
|
||||
# A symbol (NOT a string!)
|
||||
JustASymbol
|
||||
|
||||
# You can do mixedCase or CamelCase too of course, pick your poison
|
||||
# (but be consistent, for the sake of your collaborators!)
|
||||
iAmASymbol
|
||||
i-am-a-symbol
|
||||
|
||||
# A list of symbols
|
||||
[GET, PUT, POST, DELETE]
|
||||
|
||||
# A symbol with spaces in it
|
||||
|this is just one symbol believe it or not|
|
||||
```
|
||||
|
||||
We can also add binary data, aka ByteStrings:
|
||||
|
||||
```
|
||||
# Some binary data, base64 encoded
|
||||
#[cGljdHVyZSBvZiBhIGNhdA==]
|
||||
|
||||
# Some other binary data, hexadecimal encoded
|
||||
#x"616263"
|
||||
|
||||
# Same binary data as above, base64 encoded
|
||||
#[YWJj]
|
||||
```
|
||||
|
||||
What's neat about this is that we don't have to "pay the cost" of
|
||||
base64 or hexadecimal encoding when we serialize this data to binary;
|
||||
the length of the binary data is the length of the binary data.
|
||||
|
||||
Conveniently, Preserves also includes Sets, which are collections of
|
||||
unique elements where ordering of items is unimportant.
|
||||
|
||||
```
|
||||
#{flour, salt, water}
|
||||
```
|
||||
|
||||
<a id="orgefafe56"></a>
|
||||
|
||||
## Canonicalization
|
||||
|
||||
This is a good time to mention that even though from a semantic
|
||||
perspective sets and dictionaries do not carry information about the
|
||||
ordering of their elements (and Preserves doesn't care what order we
|
||||
enter them in for our hand-written-as-text Preserves documents),
|
||||
[Preserves provides support for canonical ordering](canonical-binary.html)
|
||||
when serializing.
|
||||
|
||||
In canonicalizing output mode, Preserves will always write out a given
|
||||
value using exactly the same bytes, every time. This is important and
|
||||
useful for many contexts, but especially for cryptographic signatures
|
||||
and hashing.
|
||||
|
||||
```
|
||||
# This hand-typed Preserves document...
|
||||
{monkey: {"noise": "ooh-ooh",
|
||||
"eats": #{"bananas", "berries"}}
|
||||
cat: {"noise": "meow",
|
||||
"eats": #{"kibble", "cat treats", "tinned meat"}}}
|
||||
|
||||
# Will always, always be written out in this order (except in
|
||||
# binary, of course) when canonicalized:
|
||||
{cat: {"eats": #{"cat treats", "kibble", "tinned meat"},
|
||||
"noise": "meow"}
|
||||
monkey: {"eats": #{"bananas", "berries"},
|
||||
"noise": "ooh-ooh"}}
|
||||
```
|
||||
|
||||
<a id="org0366627"></a>
|
||||
|
||||
## Defining our own types using Records
|
||||
|
||||
Finally, there is one more type that Preserves provides… but in a
|
||||
sense, it's a meta-type.
|
||||
`Record` objects have a label and a series of arguments (or "fields").
|
||||
For example, we can make a `Date` record:
|
||||
|
||||
```
|
||||
<Date 2019 8 15>
|
||||
```
|
||||
|
||||
In this example, the `Date` label is a symbol; 2019, 8, and 15 are the
|
||||
year, month, and day fields respectively.
|
||||
|
||||
Why do we care about this?
|
||||
We could instead just decide to encode our date data in a string,
|
||||
like "2019-08-15".
|
||||
A document using such a date structure might look like so:
|
||||
|
||||
```
|
||||
{"name": "Gregor Samsa",
|
||||
"description": "humanoid trapped in an insect body",
|
||||
"born": "1915-10-04"}
|
||||
```
|
||||
|
||||
Unfortunately, say our boss comes along and tells us that the people
|
||||
doing data entry have complained that it isn't always possible to get
|
||||
an exact date.
|
||||
They would like to be able to type in what they know if they don't
|
||||
know the date exactly.
|
||||
|
||||
This causes a problem.
|
||||
Now we might have two kinds of entries:
|
||||
|
||||
```
|
||||
# Exact date known
|
||||
{"name": "Gregor Samsa",
|
||||
"description": "humanoid trapped in an insect body",
|
||||
"born": "1915-10-04"}
|
||||
|
||||
# Not sure about exact date...
|
||||
{"name": "Gregor Samsa",
|
||||
"description": "humanoid trapped in an insect body",
|
||||
"born": "Sometime in October 1915? Or was that when he became an insect?"}
|
||||
```
|
||||
|
||||
This is a mess.
|
||||
We *could* just try parsing a regular expression to see if it "looks
|
||||
like a date", but doing this kind of thing is prone to errors and weird
|
||||
edge cases.
|
||||
No, it's better to be able to have a separate type:
|
||||
|
||||
```
|
||||
# Exact date known
|
||||
{"name": "Gregor Samsa",
|
||||
"description": "humanoid trapped in an insect body",
|
||||
"born": <Date 1915 10 04>}
|
||||
|
||||
# Not sure about exact date...
|
||||
{"name": "Gregor Samsa",
|
||||
"description": "humanoid trapped in an insect body",
|
||||
"born": <Unknown "Sometime in October 1915? Or was that when he became an insect?">}
|
||||
```
|
||||
|
||||
Now we can distinguish the two.
|
||||
|
||||
We can make as many Record types as our program needs, though it is up
|
||||
to our program to make sense of what these mean.
|
||||
Since Preserves does not specify the `Date` itself, both the program
|
||||
(or person) writing the Preserves document and the program reading it
|
||||
need to have a mutual understanding of how many fields it has and what
|
||||
the meaning the label signifies for it to be of use.
|
||||
|
||||
Still, there are plenty of interesting labels we can define.
|
||||
Here is one for an "iri", a hyperlink:
|
||||
|
||||
```
|
||||
<iri "https://dustycloud.org/blog/">
|
||||
```
|
||||
|
||||
That's nice enough, but here's another interesting detail… labels on
|
||||
Records are usually symbols but aren't necessarily so.
|
||||
They can also be strings or numbers or even dictionaries.
|
||||
And very interestingly, they can also be other records:
|
||||
|
||||
```
|
||||
< <iri "https://www.w3.org/ns/activitystreams#Note">
|
||||
{"to": [<iri "https://chatty.example/ben/">],
|
||||
"attributedTo": <iri "https://social.example/alyssa/">,
|
||||
"content": "Say, did you finish reading that book I lent you?"} >
|
||||
```
|
||||
|
||||
Do you see it? This Record's label is… an `iri` Record!
|
||||
The link here points to a more precise term saying that "this is a
|
||||
note meant to be sent around in social networks".
|
||||
It is considerably more precise than just using the string or symbol
|
||||
"Note", which could be ambiguous.
|
||||
(A social networking note? A footnote? A music note?)
|
||||
While not all systems need this, this (partial) example hints at how
|
||||
Preserves can also be used to coordinate meaning in larger, more
|
||||
decentralized systems.
|
||||
|
||||
Likewise, it is also possible to annotate records with integers.
|
||||
Languages like OCaml use integers instead of symbolic record labels
|
||||
because their type systems ensure that it is never ambiguous what,
|
||||
say, the label `23` means in any given context.
|
||||
Allowing integer record labels lets Preserves directly express OCaml
|
||||
data.
|
||||
|
||||
|
||||
<a id="org1b72b96"></a>
|
||||
|
||||
## Annotations
|
||||
|
||||
Annotations are not strictly a necessary feature, but they are useful
|
||||
in some circumstances.
|
||||
We have previously shown them used as comments:
|
||||
|
||||
```
|
||||
# I'm a comment!
|
||||
"I am not a comment, I am data!"
|
||||
```
|
||||
|
||||
Annotations annotate the values they precede.
|
||||
It is possible to have multiple annotations on a value.
|
||||
The hash-space (or hash-tab) comment syntax is syntactic sugar for the general
|
||||
`@`-prefixed string annotation syntax.
|
||||
|
||||
```
|
||||
# I am annotating this number
|
||||
@"And so am I!"
|
||||
42
|
||||
```
|
||||
|
||||
As said, annotations are not really data.
|
||||
They are merely meant for development tooling or debugging.
|
||||
You have to explicitly ask for them when reading, and they wrap all
|
||||
the values.
|
||||
Many implementations will, in the same mode, also supply line number
|
||||
and column information attached to each read value.
|
||||
|
||||
So what's the point of them then?
|
||||
If annotations were just for comments, there would be indeed hardly any
|
||||
point at all… it would be simpler to just provide a comment syntax.
|
||||
|
||||
However, annotations can be used for more than just comments.
|
||||
They can also be used for debugging or other development-tool-oriented
|
||||
data.
|
||||
|
||||
For instance, here's a reply from an HTTP API service running in
|
||||
"debug" mode annotated with the time it took to produce the reply and
|
||||
the internal name of the server that produced the response:
|
||||
|
||||
```
|
||||
@<ResponseTime <Milliseconds 64.4>>
|
||||
@<BackendServer "humpty-dumpty.example.com">
|
||||
<Success
|
||||
<Employees [
|
||||
<Employee "Alyssa P. Hacker"
|
||||
#{<Role Programmer>, <Role Manager>}
|
||||
<Date 2018, 1, 24>>
|
||||
<Employee "Ben Bitdiddle"
|
||||
#{<Role Programmer>}
|
||||
<Date 2019, 2, 13>> ]>>
|
||||
```
|
||||
|
||||
The annotations aren't related to the data requested, which is all
|
||||
about "employees"; instead, they're about the systems that produced
|
||||
the response.
|
||||
You could say they're in the domain of "debugging" instead of the
|
||||
domain of "employees".
|
||||
|
||||
|
||||
<a id="org1924a0a"></a>
|
||||
|
||||
# Conclusions
|
||||
|
||||
We've covered the broad strokes of Preserves, but not everything that
|
||||
is possible with it.
|
||||
We leave it as an exercise to the reader to try reading these examples
|
||||
into their languages (several libraries exist already) and writing them
|
||||
out as binary objects.
|
||||
|
||||
But as we've seen, Preserves is a flexible system which comes with
|
||||
well-defined, carefully specified built-in types, as well as a
|
||||
meta-type which can be used as an extension point.
|
||||
|
||||
Happy preserving!
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
exclude: [implementations, scratch]
|
||||
|
||||
markdown: kramdown
|
||||
highlighter: rouge
|
||||
|
||||
baseurl: "https://preserves.dev"
|
||||
|
||||
defaults:
|
||||
-
|
||||
scope:
|
||||
path: ""
|
||||
values:
|
||||
layout: page
|
||||
|
||||
title: "Preserves"
|
||||
version_date: "February 2024"
|
||||
version: "0.994.0"
|
|
@ -0,0 +1,12 @@
|
|||
[
|
||||
{"version":"0.994.0","title":"0.994.0","aliases":["latest"]},
|
||||
{"version":"0.993.0","title":"0.993.0","aliases":[]},
|
||||
{"version":"0.992.2","title":"0.992.2","aliases":[]},
|
||||
{"version":"0.992.1","title":"0.992.1","aliases":[]},
|
||||
{"version":"0.992.0","title":"0.992.0","aliases":[]},
|
||||
{"version":"0.991.0","title":"0.991.0","aliases":[]},
|
||||
{"version":"0.990.1","title":"0.990.1","aliases":[]},
|
||||
{"version":"0.990.0","title":"0.990.0","aliases":[]},
|
||||
{"version":"0.18.1","title":"0.18.1","aliases":[]},
|
||||
{"version":"0.18.0","title":"0.18.0","aliases":[]}
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
For a value `V`, we write `«V»` for the binary encoding of `V`.
|
||||
|
||||
```text
|
||||
«#f» = [0x80]
|
||||
«#t» = [0x81]
|
||||
|
||||
«@W V» = [0x85] ++ «W» ++ «V»
|
||||
«#:V» = [0x86] ++ «V»
|
||||
|
||||
«V» if V ∈ Double = [0x87, 0x08] ++ binary64(V)
|
||||
|
||||
«V» if V ∈ SignedInteger = [0xB0] ++ varint(|intbytes(V)|) ++ intbytes(V)
|
||||
«V» if V ∈ String = [0xB1] ++ varint(|utf8(V)|) ++ utf8(V)
|
||||
«V» if V ∈ ByteString = [0xB2] ++ varint(|V|) ++ V
|
||||
«V» if V ∈ Symbol = [0xB3] ++ varint(|utf8(V)|) ++ utf8(V)
|
||||
|
||||
«<L F_1...F_m>» = [0xB4] ++ «L» ++ «F_1» ++...++ «F_m» ++ [0x84]
|
||||
«[X_1...X_m]» = [0xB5] ++ «X_1» ++...++ «X_m» ++ [0x84]
|
||||
«#{E_1...E_m}» = [0xB6] ++ «E_1» ++...++ «E_m» ++ [0x84]
|
||||
«{K_1:V_1...K_m:V_m}» = [0xB7] ++ «K_1» ++ «V_1» ++...++ «K_m» ++ «V_m» ++ [0x84]
|
||||
|
||||
varint(n) = [n] if n < 128
|
||||
[(n & 127) | 128] ++ varint(n >> 7) if n ≥ 128
|
||||
|
||||
intbytes(n) = the empty sequence if n = 0, otherwise signedBigEndian(n)
|
||||
|
||||
signedBigEndian(n) = [n & 255] if -128 ≤ n ≤ 127
|
||||
signedBigEndian(n >> 8) ++ [n & 255] otherwise
|
||||
```
|
||||
|
||||
The function `binary64(D)` yields the big-endian 8-byte IEEE 754 binary representation of `D`.
|
|
@ -0,0 +1,51 @@
|
|||
For a value <span class="postcard-grammar binarysyntax">*V*</span>, we write <span
|
||||
class="postcard-grammar binarysyntax">«*V*»</span> for the binary encoding of <span
|
||||
class="postcard-grammar binarysyntax">*V*</span>.
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
«`#f`» | = | `80`
|
||||
«`#t`» | = | `81`
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
«`@`*W* *V*» | = | `85` «*W*» «*V*»
|
||||
«`#:`*V*» | = | `86` «*V*»
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
«*V*» | = | `87``08` **binary64**(*V*) | if *V* ∈ Double
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
«*V*» | = | `B0` **varint**(|**intbytes**(*V*)|) **intbytes**(*V*) | if *V* ∈ SignedInteger
|
||||
«*V*» | = | `B1` **varint**(|**utf8**(*V*)|) **utf8**(*V*) | if *V* ∈ String
|
||||
«*V*» | = | `B2` **varint**(|*V*|) *V* | if *V* ∈ ByteString
|
||||
«*V*» | = | `B3` **varint**(|**utf8**(*V*)|) **utf8**(*V*) | if *V* ∈ Symbol
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
«`<`*L* *F*<sub>1</sub> ... *F*<sub>m</sub>`>`» | = | `B4` «*L*» «*F*<sub>1</sub>» ... «*F*<sub>m</sub>» `84`
|
||||
«`[`*X*<sub>1</sub> ... *X*<sub>m</sub>`]`» | = | `B5` «*X*<sub>1</sub>» ... «*X*<sub>m</sub>» `84`
|
||||
«`#{`*E*<sub>1</sub> ... *E*<sub>m</sub>`}`» | = | `B6` «*E*<sub>1</sub>» ... «*E*<sub>m</sub>» `84`
|
||||
«`{`*K*<sub>1</sub>`:`*V*<sub>1</sub> ... *K*<sub>m</sub>`:`*V*<sub>m</sub>`}`» | = | `B7` «*K*<sub>1</sub>» «*V*<sub>1</sub>» ... «*K*<sub>m</sub>» «*V*<sub>m</sub>» `84`
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
**varint**(*n*) | = | <span class="outputish">*n*</span> | if *n* < 128
|
||||
| | <span class="outputish">(*n* & 127) | 128</span> **varint**(*n* >> 7) | if *n* ≥ 128
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
**intbytes**(*n*) | = | <span class="roman">the empty sequence if</span> *n* = 0<span class="roman">, otherwise</span> **signedBigEndian**(*n*)
|
||||
|
||||
{:.postcard-grammar.binarysyntax}
|
||||
**signedBigEndian**(*n*) | = | <span class="outputish">*n* & 255</span> | if −128 ≤ *n* ≤ 127
|
||||
| | **signedBigEndian**(*n* >> 8) <span class="outputish">*n* & 255</span> | otherwise
|
||||
|
||||
The function <span class="postcard-grammar binarysyntax">**binary64**(*D*)</span> yields the
|
||||
big-endian 8-byte IEEE 754 binary representation of <span class="postcard-grammar
|
||||
binarysyntax">*D*</span>.
|
||||
|
||||
<!--
|
||||
Together, <span class="postcard-grammar binarysyntax">**div**</span> and <span
|
||||
class="postcard-grammar binarysyntax">**mod**</span> give [Euclidean
|
||||
division](https://en.wikipedia.org/wiki/Euclidean_division); that is, if
|
||||
<span class="postcard-grammar binarysyntax">*n* **div** *d* = *q*</span> and
|
||||
<span class="postcard-grammar binarysyntax">*n* **mod** *d* = *r*</span>, then
|
||||
<span class="postcard-grammar binarysyntax">*n* = *dq* + *r*</span> and
|
||||
<span class="postcard-grammar binarysyntax">0 ≤ *r* < |d|</span>.
|
||||
-->
|
|
@ -0,0 +1,21 @@
|
|||
The definitions of `Atom`, `ws`, and `linecomment` are as given in the Preserves text syntax.
|
||||
|
||||
```text
|
||||
Document := Expr* Trailer ws
|
||||
Expr := ws (SimpleExpr | Punct)
|
||||
SimpleExpr := Compound | Embedded | Annotated | Atom
|
||||
Compound := Sequence | Record | Block | Group | Set
|
||||
Punct := `,` | `;` | `:`+
|
||||
|
||||
Sequence := `[` Expr* Trailer ws `]`
|
||||
Record := `<` Expr* Trailer ws `>`
|
||||
Block := `{` Expr* Trailer ws `}`
|
||||
Group := `(` Expr* Trailer ws `)`
|
||||
Set := `#{` Expr* Trailer ws `}`
|
||||
|
||||
Trailer := (ws Annotation)*
|
||||
|
||||
Embedded := `#:` SimpleExpr
|
||||
Annotated := Annotation SimpleExpr
|
||||
Annotation := `@` SimpleExpr | `#` ((space | tab) linecomment) (cr | lf)
|
||||
```
|
|
@ -0,0 +1,23 @@
|
|||
The definitions of `Atom`, `ws`, and `linecomment` are as given in the Preserves text syntax.
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Document* | := | *Expr*<sup>⋆</sup> *Trailer* **ws**
|
||||
| *Expr* | := | **ws** (*SimpleExpr* | *Punct*)
|
||||
| *SimpleExpr* | := | *Compound* | *Embedded* | *Annotated* | *Atom*
|
||||
| *Compound* | := | *Sequence* | *Record* | *Block* | *Group* | *Set*
|
||||
| *Punct* | := | `,` | `;` | `:`<sup>+</sup>
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Sequence* | := | `[` *Expr*<sup>⋆</sup> *Trailer* **ws** `]`
|
||||
| *Record* | := | `<` *Expr*<sup>⋆</sup> *Trailer* **ws** `>`
|
||||
| *Block* | := | `{` *Expr*<sup>⋆</sup> *Trailer* **ws** `}`
|
||||
| *Group* | := | `(` *Expr*<sup>⋆</sup> *Trailer* **ws** `)`
|
||||
| *Set* | := | `#{` *Expr*<sup>⋆</sup> *Trailer* **ws** `}`
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Trailer* | := | (**ws** *Annotation*)<sup>⋆</sup>
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Embedded* | := | `#:` *SimpleExpr*
|
||||
| *Annotated* | := | *Annotation* *SimpleExpr*
|
||||
| *Annotation* | := | `@` *SimpleExpr* | `#` ((**space** | **tab**) *linecomment*) (**cr** | **lf**)
|
|
@ -0,0 +1,48 @@
|
|||
```text
|
||||
Document := Value ws
|
||||
Value := ws (Record | Collection | Embedded | Annotated | Atom)
|
||||
Collection := Sequence | Dictionary | Set
|
||||
|
||||
Record := `<` Value+ ws `>`
|
||||
Sequence := `[` (commas Value)* commas `]`
|
||||
Set := `#{` (commas Value)* commas `}`
|
||||
Dictionary := `{` (commas Value ws `:` Value)* commas `}`
|
||||
commas := (ws `,`)* ws
|
||||
|
||||
Embedded := `#:` Value
|
||||
Annotated := Annotation Value
|
||||
Annotation := `@` Value | `#` ((space | tab) linecomment) (cr | lf)
|
||||
|
||||
Atom := Boolean | ByteString | String | QuotedSymbol | Symbol | Number
|
||||
Boolean := `#t` | `#f`
|
||||
ByteString := `#"` binchar* `"`
|
||||
| `#x"` (ws hex hex)* ws `"`
|
||||
| `#[` (ws base64char)* ws `]`
|
||||
String := `"` («any unicode scalar except `\` or `"`» | escaped | `\"`)* `"`
|
||||
QuotedSymbol := `|` («any unicode scalar except `\` or `|`» | escaped | `\|`)* `|`
|
||||
Symbol := (`A`..`Z` | `a`..`z` | `0`..`9` | sympunct | symuchar)+
|
||||
Number := Double | SignedInteger
|
||||
Double := flt | `#xd"` (ws hex hex)8 ws `"`
|
||||
SignedInteger := int
|
||||
|
||||
escaped := `\\` | `\/` | `\b` | `\f` | `\n` | `\r` | `\t` | `\u` hex hex hex hex
|
||||
binescaped := `\\` | `\/` | `\b` | `\f` | `\n` | `\r` | `\t` | `\x` hex hex
|
||||
binchar := «any scalar ≥32 and ≤126, except `\` or `"`» | binescaped | `\"`
|
||||
base64char := `A`..`Z` | `a`..`z` | `0`..`9` | `+` | `/` | `-` | `_` | `=`
|
||||
sympunct := `~` | `!` | `$` | `%` | `^` | `&` | `*` | `?`
|
||||
| `_` | `=` | `+` | `-` | `/` | `.`
|
||||
symuchar := «any scalar value ≥128 whose Unicode category is
|
||||
Lu, Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd, Nl, No, Pc,
|
||||
Pd, Po, Sc, Sm, Sk, So, or Co»
|
||||
|
||||
flt := int ( frac exp | frac | exp )
|
||||
int := (`-`|`+`) (`0`..`9`)+
|
||||
frac := `.` (`0`..`9`)+
|
||||
exp := (`e`|`E`) (`-`|`+`) (`0`..`9`)+
|
||||
hex := `A`..`F` | `a`..`f` | `0`..`9`
|
||||
|
||||
ws := (space | tab | cr | lf)*
|
||||
delimiter := ws | `<` | `>` | `[` | `]` | `{` | `}`
|
||||
| `#` | `:` | `"` | `|` | `@` | `;` | `,`
|
||||
linecomment := «any unicode scalar except cr or lf»*
|
||||
```
|
|
@ -0,0 +1,47 @@
|
|||
{:.postcard-grammar.textsyntax}
|
||||
| *Document* | := | *Value* **ws** |
|
||||
| *Value* | := | **ws** (*Record* | *Collection* | *Embedded* | *Annotated* | *Atom*) |
|
||||
| *Collection* | := | *Sequence* | *Dictionary* | *Set* |
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Record* | := | `<`*Value*<sup>+</sup> **ws**`>` |
|
||||
| *Sequence* | := | `[`(**commas** *Value*)<sup>⋆</sup> **commas**`]` |
|
||||
| *Set* | := | `#{`(**commas** *Value*)<sup>⋆</sup> **commas**`}` |
|
||||
| *Dictionary* | := | `{` (**commas** *Value* **ws**`:`*Value*)<sup>⋆</sup> **commas**`}` |
|
||||
| **commas** | := | (**ws** `,`)<sup>⋆</sup> **ws** |
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Embedded* | := | `#:`*Value* |
|
||||
| *Annotated* | := | *Annotation* *Value* |
|
||||
| *Annotation* | := | `@`*Value* |`#` ((**space** | **tab**) *linecomment*) (**cr** | **lf**) |
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *Atom* | := | *Boolean* | *ByteString* | *String* | *QuotedSymbol* | *Symbol* | *Number* |
|
||||
| *Boolean* | := | `#t`|`#f` |
|
||||
| *ByteString* | := | `#"`*binchar*<sup>⋆</sup> `"`|`#x"` (**ws** *hex* *hex*)<sup>⋆</sup> **ws**`"`|`#[` (**ws** *base64char*)<sup>⋆</sup> **ws**`]` |
|
||||
| *String* | := | `"` (« any unicode scalar value except `\` or `"` » | *escaped* |`\"`)<sup>⋆</sup> `"` |
|
||||
| *QuotedSymbol* | := | `|` (« any unicode scalar value except `\` or `|` » | *escaped* |`\|`)<sup>⋆</sup> `|` |
|
||||
| *Symbol* | := | (`A`..`Z`|`a`..`z`|`0`..`9`| *sympunct* | *symuchar*)<sup>+</sup> |
|
||||
| *Number* | := | *Double* | *SignedInteger* |
|
||||
| *Double* | := | *flt* |`#xd"` (**ws** *hex* *hex*)<sup>8</sup> **ws**`"` |
|
||||
| *SignedInteger* | := | *int* |
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *escaped* | := | `\\`|`\/`|`\b`|`\f`|`\n`|`\r`|`\t`|`\u`*hex* *hex* *hex* *hex* |
|
||||
| *binescaped* | := | `\\`|`\/`|`\b`|`\f`|`\n`|`\r`|`\t`|`\x`*hex* *hex* |
|
||||
| *binchar* | := | « any unicode scalar value ≥32 and ≤126, except `\` or `"` » | *binescaped* |`\"` |
|
||||
| *base64char* | := | `A`..`Z`|`a`..`z`|`0`..`9`|`+`|`/`|`-`|`_`|`=` |
|
||||
| *sympunct* | := | `~`|`!`|`$`|`%`|`^`|`&`|`*`|`?`|`_`|`=`|`+`|`-`|`/`|`.` |
|
||||
| *symuchar* | := | « any scalar value ≥128 whose Unicode category is Lu, Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd, Nl, No, Pc, Pd, Po, Sc, Sm, Sk, So, or Co » |
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| *flt* | := | *int* ( *frac* *exp* | *frac* | *exp* ) |
|
||||
| *int* | := | (`-`|`+`) (`0`..`9`)<sup>+</sup> |
|
||||
| *frac* | := | `.` (`0`..`9`)<sup>+</sup> |
|
||||
| *exp* | := | (`e`|`E`) (`-`|`+`) (`0`..`9`)<sup>+</sup> |
|
||||
| *hex* | := | `A`..`F`|`a`..`f`|`0`..`9` |
|
||||
|
||||
{:.postcard-grammar.textsyntax}
|
||||
| **ws** | := | (**space** | **tab** | **cr** | **lf**)<sup>⋆</sup> |
|
||||
| **delimiter** | := | **ws** | `<` | `>` | `[` | `]` | `{` | `}` | `#` | `:` | `"` | `|` | `@` | `;` | `,` |
|
||||
| *linecomment* | := | « any unicode scalar value except **cr** or **lf** »<sup>⋆</sup> |
|
|
@ -0,0 +1 @@
|
|||
<svg height="86" viewBox="0 0 76 86" height="12" xmlns="http://www.w3.org/2000/svg"><path d="m76 82v4h-76l.00080851-4zm-3-6v5h-70v-5zm-62.6696277-54 .8344146.4217275.4176066 6.7436084.4176065 10.9576581v10.5383496l-.4176065 13.1364492-.0694681 8.8498268-1.1825531.3523804h-4.17367003l-1.25202116-.3523804-.48627608-8.8498268-.41840503-13.0662957v-10.5375432l.41840503-11.028618.38167482-6.7798947.87034634-.3854412zm60.0004653 0 .8353798.4217275.4168913 6.7436084.4168913 10.9576581v10.5383496l-.4168913 13.1364492-.0686832 8.8498268-1.1835879.3523804h-4.1737047l-1.2522712-.3523804-.4879704-8.8498268-.4168913-13.0662957v-10.5375432l.4168913-11.028618.3833483-6.7798947.8697215-.3854412zm-42.000632 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2529447-.3523804-.4863246-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8688361-.3854412zm23 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2521462-.3523804-.4871231-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8696347-.3854412zm21.6697944-9v7h-70v-7zm-35.7200748-13 36.7200748 8.4088317-1.4720205 2.5911683h-70.32799254l-2.19998696-2.10140371z" fill="currentColor" fill-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,129 @@
|
|||
#lang racket
|
||||
|
||||
(define (base-bytes v)
|
||||
(define byte-count (cond [(zero? v) 0]
|
||||
[else (define raw-bit-count (+ (integer-length v) 1))
|
||||
(quotient (+ raw-bit-count 7) 8)]))
|
||||
(for/list [(shift (in-range (* byte-count 8) 0 -8))]
|
||||
(bitwise-bit-field v (- shift 8) shift)))
|
||||
|
||||
(define (mod n d) (modulo n d)) ;; sign equal to sign of d
|
||||
(define (div n d) (/ (- n (mod n d)) d)) ;; sign equal to sign of n, or zero
|
||||
|
||||
;;---------------------------------------------------------------------------
|
||||
;; One
|
||||
;;
|
||||
;; (define (intbytes* v)
|
||||
;; (cond [(zero? v) '()]
|
||||
;; [(= v -1) '()]
|
||||
;; [else (append (intbytes* (div v 256)) (list (mod v 256)))]))
|
||||
;;
|
||||
;; (define (intbytes v)
|
||||
;; (define bs (intbytes* v))
|
||||
;; (define looks-negative (and (pair? bs) (>= (car bs) 128)))
|
||||
;; (if (xor (negative? v) looks-negative)
|
||||
;; (cons (if (negative? v) 255 0) bs)
|
||||
;; bs))
|
||||
|
||||
;;---------------------------------------------------------------------------
|
||||
;; Two
|
||||
;;
|
||||
;; (define (intbytes** v)
|
||||
;; (cond [(zero? v) '()]
|
||||
;; [else (append (intbytes** (quotient v 256)) (list (remainder v 256)))]))
|
||||
;;
|
||||
;; (define (intbytes* v)
|
||||
;; (define bs (intbytes** v))
|
||||
;; (if (and (pair? bs) (>= (car bs) 128))
|
||||
;; (cons 0 bs)
|
||||
;; bs))
|
||||
;;
|
||||
;; (define (intbytes v)
|
||||
;; (if (negative? v)
|
||||
;; (map (lambda (n) (- 255 n)) (intbytes* (- (+ v 1))))
|
||||
;; (intbytes* v)))
|
||||
|
||||
;;---------------------------------------------------------------------------
|
||||
;; Three
|
||||
;;
|
||||
;; (define (intbytes+ v)
|
||||
;; (cond [(>= v 128) (append (intbytes+ (div v 256)) (list (mod v 256)))]
|
||||
;; [else (list (mod v 256))]))
|
||||
;;
|
||||
;; (define (intbytes- v)
|
||||
;; (cond [(< v -128) (append (intbytes- (div v 256)) (list (mod v 256)))]
|
||||
;; [else (list (mod v 256))]))
|
||||
;;
|
||||
;; (define (intbytes v)
|
||||
;; (cond [(negative? v) (intbytes- v)]
|
||||
;; [(zero? v) '()]
|
||||
;; [(positive? v) (intbytes+ v)]))
|
||||
|
||||
;;---------------------------------------------------------------------------
|
||||
;; Four
|
||||
;;
|
||||
;; (define (intbytes* v)
|
||||
;; (append (if (<= -128 v 127)
|
||||
;; '()
|
||||
;; (intbytes* (div v 256)))
|
||||
;; (list (mod v 256))))
|
||||
;;
|
||||
;; (define (intbytes v)
|
||||
;; (if (zero? v)
|
||||
;; '()
|
||||
;; (intbytes* v)))
|
||||
|
||||
;;---------------------------------------------------------------------------
|
||||
;; Five
|
||||
;;
|
||||
;; (define (intbytes* v)
|
||||
;; (if (<= -128 v 127)
|
||||
;; (list (mod v 256))
|
||||
;; (append (intbytes* (div v 256)) (list (mod v 256)))))
|
||||
;;
|
||||
;; (define (intbytes v)
|
||||
;; (if (zero? v)
|
||||
;; '()
|
||||
;; (intbytes* v)))
|
||||
|
||||
;;---------------------------------------------------------------------------
|
||||
;; Six
|
||||
|
||||
(define (intbytes* v)
|
||||
(if (<= -128 v 127)
|
||||
(list (bitwise-and v 255))
|
||||
(append (intbytes* (arithmetic-shift v -8)) (list (bitwise-and v 255)))))
|
||||
|
||||
(define (intbytes v)
|
||||
(if (zero? v)
|
||||
'()
|
||||
(intbytes* v)))
|
||||
|
||||
(define cases `(
|
||||
(-257 (#xfe #xff))
|
||||
(-256 (#xff #x00))
|
||||
(-255 (#xff #x01))
|
||||
(-129 (#xff #x7f))
|
||||
(-128 (#x80))
|
||||
(-127 (#x81))
|
||||
(-2 (#xfe))
|
||||
(-1 (#xff))
|
||||
(0 ())
|
||||
(1 (#x01))
|
||||
(127 (#x7f))
|
||||
(128 (#x00 #x80))
|
||||
(255 (#x00 #xff))
|
||||
(256 (#x01 #x00))
|
||||
(32767 (#x7f #xff))
|
||||
(32768 (#x00 #x80 #x00))
|
||||
(65535 (#x00 #xff #xff))
|
||||
(65536 (#x01 #x00 #x00))
|
||||
))
|
||||
|
||||
(module+ test
|
||||
(require rackunit)
|
||||
(for [(c (in-list cases))]
|
||||
(match-define (list input output) c)
|
||||
(writeln (list input output (base-bytes input) (intbytes input)))
|
||||
(check-equal? output (base-bytes input))
|
||||
(check-equal? output (intbytes input))))
|
|
@ -0,0 +1,8 @@
|
|||
Python's strings, byte strings, integers, booleans, and double-precision floats stand directly
|
||||
for their Preserves counterparts. Wrapper objects for
|
||||
[Symbol][preserves.values.Symbol] complete the suite of atomic types.
|
||||
|
||||
Python's lists and tuples correspond to Preserves `Sequence`s, and dicts and sets to
|
||||
`Dictionary` and `Set` values, respectively. Preserves `Record`s are represented by
|
||||
[Record][preserves.values.Record] objects. Finally, embedded values are represented by
|
||||
[Embedded][preserves.values.Embedded] objects.
|
|
@ -0,0 +1,16 @@
|
|||
Here are a few example values, written using the [text
|
||||
syntax](https://preserves.dev/preserves-text.html):
|
||||
|
||||
Boolean : #t #f
|
||||
Double : 1.0 10.4e3 -100.6
|
||||
Integer : 1 0 -100
|
||||
String : "Hello, world!\n"
|
||||
ByteString : #"bin\x00str\x00" #[YmluAHN0cgA] #x"62696e0073747200"
|
||||
Symbol : hello-world |hello world| = ! hello? || ...
|
||||
Record : <label field1 field2 ...>
|
||||
Sequence : [value1 value2 ...]
|
||||
Set : #{value1 value2 ...}
|
||||
Dictionary : {key1: value1 key2: value2 ...: ...}
|
||||
Embedded : #:value
|
||||
|
||||
Commas are optional in sequences, sets, and dictionaries.
|
|
@ -0,0 +1,17 @@
|
|||
```text
|
||||
Value = Atom
|
||||
| Compound
|
||||
| Embedded
|
||||
|
||||
Atom = Boolean
|
||||
| Double
|
||||
| SignedInteger
|
||||
| String
|
||||
| ByteString
|
||||
| Symbol
|
||||
|
||||
Compound = Record
|
||||
| Sequence
|
||||
| Set
|
||||
| Dictionary
|
||||
```
|
|
@ -0,0 +1,16 @@
|
|||
A Preserves schema connects Preserves `Value`s to host-language data
|
||||
structures. Each definition within a schema can be processed by a
|
||||
compiler to produce
|
||||
|
||||
- a simple host-language *type definition*;
|
||||
|
||||
- a partial *parsing* function from `Value`s to instances of the
|
||||
produced type; and
|
||||
|
||||
- a total *serialization* function from instances of the type to
|
||||
`Value`s.
|
||||
|
||||
Every parsed `Value` retains enough information to always be able to
|
||||
be serialized again, and every instance of a host-language data
|
||||
structure contains, by construction, enough information to be
|
||||
successfully serialized.
|
|
@ -0,0 +1,12 @@
|
|||
*Preserves* is a data model, with associated serialization formats.
|
||||
|
||||
It supports *records* with user-defined *labels*, embedded
|
||||
*references*, and the usual suite of atomic and compound data types,
|
||||
including *binary* data as a distinct type from text strings. Its
|
||||
*annotations* allow separation of data from metadata such as comments,
|
||||
trace information, and provenance information.
|
||||
|
||||
Preserves departs from many other data languages in defining how to
|
||||
*compare* two values. Comparison is based on the data model, not on
|
||||
syntax or on data structures of any particular implementation
|
||||
language.
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
layout: skeleton
|
||||
extra_html_headers: >
|
||||
<link rel="stylesheet" href="{{ site.baseurl }}/normalize.css">
|
||||
<link rel="stylesheet" href="{{ site.baseurl }}/preserves.css">
|
||||
---
|
||||
<nav>
|
||||
<div class="left">
|
||||
<h1>
|
||||
<a href="{{ site.baseurl }}/">
|
||||
<span class="icon">
|
||||
<img src="{{ site.baseurl }}/logo-64x64.png">
|
||||
</span>
|
||||
</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="middle">
|
||||
<ul>
|
||||
<li><a href="{{site.baseurl}}/preserves.html">Core</a>
|
||||
<li><a href="{{site.baseurl}}/cheatsheet.html">QuickRef</a>
|
||||
<li><a href="{{site.baseurl}}/preserves-schema.html">Schema</a>
|
||||
<li><a href="{{site.baseurl}}/preserves-path.html">Path</a>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a href="https://gitlab.com/preserves/preserves">
|
||||
<span class="icon">
|
||||
<img src="{{ site.baseurl }}/gitlab-logo-500.svg" height="64" alt="Gitlab logo">
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>{{ page.title }}</h1>
|
||||
{{ content }}
|
||||
</main>
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
layout: skeleton
|
||||
extra_html_headers: |
|
||||
<link rel="canonical" href="{{ site.baseurl }}{{ page.redirect_target }}">
|
||||
<meta http-equiv="Refresh" content="0; URL={{ site.baseurl }}{{ page.redirect_target }}">
|
||||
---
|
||||
<main>
|
||||
<h1>Redirecting</h1>
|
||||
|
||||
<p>Redirecting you to <a href="{{ site.baseurl }}{{ page.redirect_target }}">{{ page.redirect_target }}</a>.</p>
|
||||
</main>
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>{% unless page.no_site_title %}{{ site.title }}: {% endunless %}{{ page.title }}</title>
|
||||
|
||||
<link href="{{ site.baseurl }}/logo-64x64.png" rel="icon" />
|
||||
<link href="{{ site.baseurl }}/logo-256x256.png" rel="shortcut icon" />
|
||||
|
||||
<meta name="author" content="Tony Garnock-Jones">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">{{
|
||||
page.extra_html_headers | liquify }}{{
|
||||
layout.extra_html_headers | liquify }}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{ content }}
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
# http://stackoverflow.com/questions/14487110/include-jekyll-liquid-template-data-in-a-yaml-variable
|
||||
|
||||
module Jekyll
|
||||
module LiquifyFilter
|
||||
def liquify(input)
|
||||
Liquid::Template.parse(input).render(@context)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_filter(Jekyll::LiquifyFilter)
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
title: "Canonical Form for Binary Syntax"
|
||||
---
|
||||
|
||||
[spec]: preserves.html
|
||||
|
||||
When two `Value`s are written down in *canonical form*, comparing
|
||||
their *syntax* for equivalence gives the same result as comparing them
|
||||
*semantically* according to the equivalence defined in the
|
||||
[Preserves specification][spec].[^equivalence-not-ordering]
|
||||
|
||||
[^equivalence-not-ordering]: However, canonical form does *not*
|
||||
induce a match between lexicographic ordering on syntax and
|
||||
semantic ordering [as specified][spec]. It *only* induces a
|
||||
connection between equivalences.
|
||||
|
||||
That is, canonical forms are equal if and only if the encoded `Value`s
|
||||
are equal.
|
||||
|
||||
This document specifies canonical form for the Preserves [machine-oriented
|
||||
binary syntax](preserves-binary.html).
|
||||
|
||||
**Annotations.**
|
||||
Annotations *MUST NOT* be present.
|
||||
|
||||
**Sets.**
|
||||
The elements of a `Set` *MUST* be serialized sorted in ascending order
|
||||
by comparing their canonical encoded binary representations.
|
||||
|
||||
**Dictionaries.**
|
||||
The key-value pairs in a `Dictionary` *MUST* be serialized sorted in
|
||||
ascending order by comparing the canonical encoded binary
|
||||
representations of their keys.[^no-need-for-by-value]
|
||||
|
||||
[^no-need-for-by-value]: There is no need to order by (key, value)
|
||||
pair, since a `Dictionary` has no duplicate keys.
|
||||
|
||||
**Other kinds of `Value`.**
|
||||
There are no special canonicalization restrictions on
|
||||
`SignedInteger`s, `String`s, `ByteString`s, `Symbol`s, `Boolean`s,
|
||||
`Double`s, `Record`s, `Sequence`s, or `Embedded`s. The
|
||||
constraints given for these `Value`s in the [specification][spec]
|
||||
suffice to ensure canonicity.
|
||||
|
||||
<!-- Heading to visually offset the footnotes from the main document: -->
|
||||
## Notes
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
no_site_title: true
|
||||
title: "Preserves Quick Reference (Plaintext)"
|
||||
---
|
||||
|
||||
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||
{{ site.version_date }}. Version {{ site.version }}.
|
||||
|
||||
These are non-normative "reference card" definitions. See also the normative [semantics]({{
|
||||
site.baseurl }}/preserves.html), [text syntax specification]({{ site.baseurl
|
||||
}}/preserves-text.html), and [machine-oriented syntax specification]({{ site.baseurl
|
||||
}}/preserves-binary.html), and the experimental [P-expressions definition]({{ site.baseurl
|
||||
}}/preserves-expressions.html).
|
||||
|
||||
## <a id="binary"></a>Machine-Oriented Binary Syntax
|
||||
|
||||
{% include cheatsheet-binary-plaintext.md %}
|
||||
|
||||
## <a id="text"></a>Human-Oriented Text Syntax
|
||||
|
||||
{% include cheatsheet-text-plaintext.md %}
|
||||
|
||||
## <a id="pexprs"></a>P-expression Syntax
|
||||
|
||||
{% include cheatsheet-pexprs-plaintext.md %}
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
no_site_title: true
|
||||
title: "Preserves Quick Reference"
|
||||
---
|
||||
|
||||
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||
{{ site.version_date }}. Version {{ site.version }}.
|
||||
|
||||
These are non-normative "reference card" definitions. See also the normative [semantics]({{
|
||||
site.baseurl }}/preserves.html), [text syntax specification]({{ site.baseurl
|
||||
}}/preserves-text.html), and [machine-oriented syntax specification]({{ site.baseurl
|
||||
}}/preserves-binary.html), and the experimental [P-expressions definition]({{ site.baseurl
|
||||
}}/preserves-expressions.html).
|
||||
|
||||
## <a id="binary"></a>Machine-Oriented Binary Syntax
|
||||
|
||||
{% include cheatsheet-binary.md %}
|
||||
|
||||
## <a id="text"></a>Human-Oriented Text Syntax
|
||||
|
||||
{% include cheatsheet-text.md %}
|
||||
|
||||
## <a id="pexprs"></a>P-expression Syntax
|
||||
|
||||
{% include cheatsheet-pexprs.md %}
|
|
@ -0,0 +1,219 @@
|
|||
---
|
||||
title: "Conventions for Common Data Types"
|
||||
---
|
||||
|
||||
The `Value` data type is essentially an S-Expression, able to
|
||||
represent semi-structured data over `ByteString`, `String`,
|
||||
`SignedInteger` atoms and so on.[^why-not-spki-sexps]
|
||||
|
||||
[^why-not-spki-sexps]: Rivest's S-Expressions are in many ways
|
||||
similar to Preserves. However, while they include binary data and
|
||||
sequences, and an obvious equivalence for them exists, they lack
|
||||
numbers *per se* as well as any kind of unordered structure such
|
||||
as sets or maps. In addition, while “display hints” allow
|
||||
labelling of binary data with an intended interpretation, they
|
||||
cannot be attached to any other kind of structure, and the “hint”
|
||||
itself can only be a binary blob.
|
||||
|
||||
However, users need a wide variety of data types for representing
|
||||
domain-specific values such as various kinds of encoded and normalized
|
||||
text, calendrical values, machine words, and so on.
|
||||
|
||||
Appropriately-labelled `Record`s denote these domain-specific data
|
||||
types.[^why-dictionaries]
|
||||
|
||||
[^why-dictionaries]: Given `Record`'s existence, it may seem odd
|
||||
that `Dictionary`, `Set`, `Double`, etc. are given special
|
||||
treatment. Preserves aims to offer a useful basic equivalence
|
||||
predicate to programmers, and so if a data type demands a special
|
||||
equivalence predicate, as `Dictionary`, `Set` and `Double` all do,
|
||||
then the type should be included in the base language. Otherwise,
|
||||
it can be represented as a `Record` and treated separately.
|
||||
`Boolean`, `String` and `Symbol` are seeming exceptions. The first
|
||||
two merit inclusion because of their cultural importance, while
|
||||
`Symbol`s are included to allow their use as `Record` labels.
|
||||
Primitive `Symbol` support avoids a bootstrapping issue.
|
||||
|
||||
All of these conventions are optional. They form a layer atop the core
|
||||
`Value` structure. Non-domain-specific tools do not in general need to
|
||||
treat them specially.
|
||||
|
||||
**Validity.** Many of the labels we will describe in this section come
|
||||
with side-conditions on the contents of labelled `Record`s. It is
|
||||
possible to construct an instance of `Value` that violates these
|
||||
side-conditions without ceasing to be a `Value` or becoming
|
||||
unrepresentable. However, we say that such a `Value` is *invalid*
|
||||
because it fails to honour the necessary side-conditions.
|
||||
Implementations *SHOULD* allow two modes of working: one which
|
||||
treats all `Value`s identically, without regard for side-conditions,
|
||||
and one which enforces validity (i.e. side-conditions) when reading,
|
||||
writing, or constructing `Value`s.
|
||||
|
||||
## Metaconventions.
|
||||
|
||||
By and large `Capitalized` and `CamelCase` identifiers refer to *types* or *schema definition
|
||||
names* describing families of `Value`s, while `kebab-case`, `lisp-style` identifiers are used
|
||||
for concrete symbols appearing in e.g. `Record` labels.
|
||||
|
||||
## IOLists.
|
||||
|
||||
Inspired by Erlang's notions of
|
||||
[`iolist()` and `iodata()`](http://erlang.org/doc/reference_manual/typespec.html),
|
||||
an `IOList` is any tree constructed from `ByteString`s and
|
||||
`Sequence`s. Formally, an `IOList` is either a `ByteString` or a
|
||||
`Sequence` of `IOList`s.
|
||||
|
||||
`IOList`s can be useful for
|
||||
[vectored I/O](https://en.wikipedia.org/wiki/Vectored_I/O).
|
||||
Additionally, the flexibility of `IOList` trees allows annotation of
|
||||
interior portions of a tree.
|
||||
|
||||
## Comments.
|
||||
|
||||
`String` values used as annotations are conventionally interpreted as
|
||||
comments. Special syntax exists for such string annotations, though
|
||||
the usual `@`-prefixed annotation notation can also be used.
|
||||
|
||||
# I am a comment for the Dictionary
|
||||
{
|
||||
# I am a comment for the key
|
||||
key: # I am a comment for the value
|
||||
value
|
||||
}
|
||||
|
||||
# I am a comment for this entire IOList, as are the next three lines.
|
||||
#
|
||||
# The previous line (containing only hash-newline) adds an empty
|
||||
# string to the annotations attached to the entire IOList.
|
||||
[
|
||||
#x"00010203"
|
||||
# I am a comment for the middle half of the IOList
|
||||
# A second comment for the same portion of the IOList
|
||||
@ # I am the first and only comment for the following comment
|
||||
"A third (itself commented!) comment for the same part of the IOList"
|
||||
[
|
||||
# I am a comment for the following ByteString
|
||||
#x"04050607"
|
||||
#x"08090A0B"
|
||||
]
|
||||
#x"0C0D0E0F"
|
||||
]
|
||||
|
||||
## MIME-type tagged binary data.
|
||||
|
||||
Many internet protocols use
|
||||
[media types](https://tools.ietf.org/html/rfc6838) (a.k.a MIME types)
|
||||
to indicate the format of some associated binary data. For this
|
||||
purpose, we define `MIMEData` to be a record labelled `mime` with two
|
||||
fields, the first being a `Symbol`, the media type, and the second
|
||||
being a `ByteString`, the binary data.
|
||||
|
||||
While each media type may define its own rules for comparing
|
||||
documents, we define ordering among `MIMEData` *representations* of
|
||||
such media types following the general rules for ordering of
|
||||
`Record`s.
|
||||
|
||||
**Examples.**
|
||||
|
||||
<mime application/octet-stream #"abcde">
|
||||
<mime text/plain #"ABC">
|
||||
<mime application/xml #"<xhtml/>">
|
||||
<mime text/csv #"123,234,345">
|
||||
|
||||
## Unicode normalization forms.
|
||||
|
||||
Unicode defines multiple
|
||||
[normalization forms](http://unicode.org/reports/tr15/) for text.
|
||||
While no particular normalization form is required for `String`s,
|
||||
users may need to unambiguously signal or require a particular
|
||||
normalization form. A `NormalizedString` is a `Record` labelled with
|
||||
`unicode-normalization` and having two fields, the first of which is a
|
||||
`Symbol` specifying the normalization form used (e.g. `nfc`, `nfd`,
|
||||
`nfkc`, `nfkd`), and the second of which is a `String` whose
|
||||
underlying Unicode scalar value sequence *MUST* be normalized according to
|
||||
the named normalization form.
|
||||
|
||||
## IRIs (URIs, URLs, URNs, etc.).
|
||||
|
||||
An `IRI` is a `Record` labelled with `iri` and having one field, a
|
||||
`String` which is the IRI itself and which *MUST* be a valid absolute
|
||||
or relative IRI.
|
||||
|
||||
## Machine words.
|
||||
|
||||
The definition of `SignedInteger` captures all integers. However, in
|
||||
certain circumstances it can be valuable to assert that a number
|
||||
inhabits a particular range, such as a fixed-width machine word.
|
||||
|
||||
A family of labels `i`*n* and `u`*n* for *n* ∈ {8,16,32,64,128} denote
|
||||
*n*-bit-wide signed and unsigned range restrictions, respectively.
|
||||
Records with these labels *MUST* have one field, a `SignedInteger`,
|
||||
which *MUST* fall within the appropriate range. That is, to be valid,
|
||||
- in `<i8 `*x*`>`, -128 <= *x* <= 127.
|
||||
- in `<u8 `*x*`>`, 0 <= *x* <= 255.
|
||||
- in `<i16 `*x*`>`, -32768 <= *x* <= 32767.
|
||||
- etc.
|
||||
|
||||
## Anonymous Tuples and Unit.
|
||||
|
||||
A `Tuple` is a `Record` with label `tuple` and zero or more fields,
|
||||
denoting an anonymous tuple of values.
|
||||
|
||||
The 0-ary tuple, `<tuple>`, denotes the empty tuple, sometimes called
|
||||
“unit” or “void” (but *not* e.g. JavaScript's “undefined” value).
|
||||
|
||||
## Null and Undefined.
|
||||
|
||||
Tony Hoare's
|
||||
“[billion-dollar mistake](https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions)”
|
||||
can be represented with the 0-ary `Record` `<null>`. An “undefined”
|
||||
value can be represented as `<undefined>`.
|
||||
|
||||
## Dates and Times.
|
||||
|
||||
Dates, times, moments, and timestamps can be represented with a
|
||||
`Record` with label `rfc3339` having a single field, a `String`, which
|
||||
*MUST* conform to one of the `full-date`, `partial-time`, `full-time`,
|
||||
or `date-time` productions of [section 5.6 of RFC
|
||||
3339](https://tools.ietf.org/html/rfc3339#section-5.6). (In
|
||||
`date-time`, "T" and "Z" *MUST* be upper-case and "T" *MUST* be used;
|
||||
a space separating the `full-date` and `full-time` *MUST NOT* be
|
||||
used.)
|
||||
|
||||
## XML Infoset
|
||||
|
||||
[XML Infoset](https://www.w3.org/TR/2004/REC-xml-infoset-20040204/)
|
||||
describes the semantics of XML - that is, the underlying information
|
||||
contained in a document, independent of surface syntax.
|
||||
|
||||
A useful subset of XML Infoset, namely its Element Information Items
|
||||
(omitting processing instructions, entities, entity references,
|
||||
comments, namespaces, name prefixes, and base URIs), can be captured
|
||||
with the [schema](preserves-schema.html)
|
||||
|
||||
Node = Text / Element .
|
||||
Text = string .
|
||||
Element =
|
||||
/ @withAttributes
|
||||
<<rec> @localName symbol [@attributes Attributes @children Node ...]>
|
||||
/ @withoutAttributes
|
||||
<<rec> @localName symbol @children [Node ...]> .
|
||||
Attributes = { symbol: string ...:... } .
|
||||
|
||||
**Examples.**
|
||||
|
||||
<html
|
||||
<h1 {class: "title"} "Hello World!">
|
||||
<p
|
||||
"I could swear I've seen markup like this somewhere before. "
|
||||
"Perhaps it was "
|
||||
<a {href: "https://docs.racket-lang.org/search/index.html?q=xexpr%3F"} "here">
|
||||
"?"
|
||||
>
|
||||
<table
|
||||
<tr <th> <th "Column 1"> <th "Column 2">>
|
||||
<tr <th "Row 1"> <td 123> <td 234>>>
|
||||
>
|
||||
|
||||
<!-- Heading to visually offset the footnotes from the main document: -->
|
||||
## Notes
|
Binary file not shown.
After Width: | Height: | Size: 506 KiB |
|
@ -0,0 +1,10 @@
|
|||
version 1 .
|
||||
JSON =
|
||||
/ @string string
|
||||
/ @integer int
|
||||
/ @double double
|
||||
/ @boolean JSONBoolean
|
||||
/ @null =null
|
||||
/ @array [JSON ...]
|
||||
/ @object { string: JSON ...:... } .
|
||||
JSONBoolean = =true / =false .
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
title: preserves-schema-rkt
|
||||
---
|
||||
|
||||
The `preserves-schema-rkt` program reads
|
||||
[Preserves Schema](../preserves-schema.html) DSL input files. For each
|
||||
input file, it produces a Racket source file of the same name but
|
||||
with `.rkt` in place of `.prs`.
|
||||
|
||||
Instead of using this tool, you may prefer to use `#lang
|
||||
preserves-schema` to use Schema DSL syntax in an ordinary Racket
|
||||
module source file.
|
||||
|
||||
## Installation
|
||||
|
||||
Install Racket. Then, `raco pkg install preserves`.
|
||||
|
||||
## Usage
|
||||
|
||||
usage: preserves-schema-rkt [ <option> ... ] [<input-glob>] ...
|
||||
|
||||
<option> is one of
|
||||
|
||||
--output <directory>
|
||||
Output directory for modules (default: next to sources)
|
||||
--stdout
|
||||
Prints each module to stdout one after the other instead of writing them to files in the `--output` directory
|
||||
--no-write-files
|
||||
Disables generation of output to the filesystem
|
||||
--base <directory>
|
||||
Base directory for sources (default: common prefix)
|
||||
* --module <namespace=path>
|
||||
Additional Namespace=path import
|
||||
* --plugin-lib <lib-path>, -l <lib-path>
|
||||
Load compiler plugin library
|
||||
* --plugin-file <rkt-file-path>, -f <rkt-file-path>
|
||||
Load compiler plugin source file
|
||||
--help, -h
|
||||
Show this help
|
||||
--
|
||||
Do not treat any remaining argument as a switch (at this level)
|
||||
|
||||
* Asterisks indicate options allowed multiple times.
|
||||
|
||||
Multiple single-letter switches can be combined after
|
||||
one `-`. For example, `-h-` is the same as `-h --`.
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
title: preserves-schema-rs
|
||||
---
|
||||
|
||||
The `preserves-schema-rs` program reads
|
||||
[Preserves Schema](../preserves-schema.html) AST input files (such as
|
||||
are produced by [`preserves-schemac`]({% link doc/preserves-schemac.md
|
||||
%})). It produces a collection of Rust source files providing parsers,
|
||||
unparsers, and Rust data structures reflecting the definitions in the
|
||||
inputs.
|
||||
|
||||
## Using the compiler from `build.rs` instead
|
||||
|
||||
You will usually not need to use the `preserves-schema-rs`
|
||||
command-line program. Instead, access the preserves-schema compiler
|
||||
API from your `build.rs`. The following example is taken from
|
||||
[`build.rs` for the `preserves-path` crate](https://gitlab.com/preserves/preserves/-/blob/af5de5b836ffc51999db93797d1995ff677cf6f8/implementations/rust/preserves-path/build.rs):
|
||||
|
||||
use preserves_schema::compiler::*;
|
||||
|
||||
use std::io::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let buildroot = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
let mut gen_dir = buildroot.clone();
|
||||
gen_dir.push("src/schemas");
|
||||
|
||||
let mut c = CompilerConfig::new(gen_dir, "crate::schemas".to_owned());
|
||||
|
||||
let inputs = expand_inputs(&vec!["path.bin".to_owned()])?;
|
||||
c.load_schemas_and_bundles(&inputs, &vec![])?;
|
||||
|
||||
compile(&c)
|
||||
}
|
||||
|
||||
This approach also requires an `include!` from your main, hand-written
|
||||
source tree. The following is a snippet from
|
||||
[`preserves-path/src/lib.rs`](https://gitlab.com/preserves/preserves/-/blob/af5de5b836ffc51999db93797d1995ff677cf6f8/implementations/rust/preserves-path/src/lib.rs):
|
||||
|
||||
pub mod schemas {
|
||||
include!(concat!(env!("OUT_DIR"), "/src/schemas/mod.rs"));
|
||||
}
|
||||
|
||||
## Installation
|
||||
|
||||
The tool is
|
||||
[written in Rust](https://crates.io/crates/preserves-schema).
|
||||
[Install `cargo`.](https://doc.rust-lang.org/cargo/getting-started/installation.html)
|
||||
Then, `cargo install preserves-schema`.
|
||||
|
||||
## Usage
|
||||
|
||||
preserves-schema 3.990.2
|
||||
|
||||
USAGE:
|
||||
preserves-schema-rs [FLAGS] [OPTIONS] --output-dir <output-dir> --prefix <prefix>
|
||||
[--] [input-glob]...
|
||||
|
||||
FLAGS:
|
||||
-h, --help Prints help information
|
||||
--rustfmt-skip
|
||||
-V, --version Prints version information
|
||||
|
||||
OPTIONS:
|
||||
--module <module>...
|
||||
-o, --output-dir <output-dir>
|
||||
-p, --prefix <prefix>
|
||||
--support-crate <support-crate>
|
||||
--xref <xref>...
|
||||
|
||||
ARGS:
|
||||
<input-glob>...
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: preserves-schema-ts
|
||||
---
|
||||
|
||||
The `preserves-schema-ts` program reads
|
||||
[Preserves Schema](../preserves-schema.html) DSL input files. For each
|
||||
input file, it produces a TypeScript source file of the same name but
|
||||
with `.ts` in place of `.prs`.
|
||||
|
||||
## Installation
|
||||
|
||||
Install node.js v12 or newer. Then, `yarn global add @preserves/schema`.
|
||||
|
||||
## Usage
|
||||
|
||||
Usage: preserves-schema-ts [options] [input...]
|
||||
|
||||
Compile Preserves schema definitions to TypeScript
|
||||
|
||||
Arguments:
|
||||
input Input directory, optionally with :<glob> on the end
|
||||
|
||||
Options:
|
||||
--xref <glob> Cross-reference other textual Preserves schema definitions
|
||||
(default: [])
|
||||
--output <directory> Output directory for modules
|
||||
--stdout Prints each module to stdout one after the other instead
|
||||
of writing them to files in the `--output` directory
|
||||
--core <path> Import path for @preserves/core
|
||||
(default: "@preserves/core")
|
||||
--watch Watch base directory for changes
|
||||
--traceback Include stack traces in compiler errors
|
||||
--module <namespace=path> Additional Namespace=path import (default: [])
|
||||
-h, --help display help for command
|
|
@ -0,0 +1,135 @@
|
|||
---
|
||||
title: preserves-schemac
|
||||
---
|
||||
|
||||
The `preserves-schemac` program reads
|
||||
[Preserves Schema](../preserves-schema.html) DSL input files and
|
||||
outputs a binary-syntax Preserves document conforming to the
|
||||
[metaschema](https://gitlab.com/preserves/preserves/-/blob/main/schema/schema.prs).
|
||||
|
||||
It can either output single `Schema` records (corresponding to a
|
||||
single input file), or a `Bundle` of `Schema`s (corresponding to a
|
||||
directory tree of files).
|
||||
|
||||
## Installation
|
||||
|
||||
Install node.js v12 or newer. Then, `yarn global add @preserves/schema`.
|
||||
|
||||
## Usage
|
||||
|
||||
Usage: preserves-schemac [options] [input...]
|
||||
|
||||
Compile textual Preserves schema definitions to binary format
|
||||
|
||||
Arguments:
|
||||
input Input directory, with optional ":glob" appended (defaults to ":**/*.prs")
|
||||
|
||||
Options:
|
||||
--no-bundle Emit a single Schema instead of a schema Bundle
|
||||
-h, --help display help for command
|
||||
|
||||
## Examples
|
||||
|
||||
### Single file (non-bundle)
|
||||
|
||||
Given a file [`demo.prs`](demo.prs) containing:
|
||||
|
||||
version 1 .
|
||||
JSON =
|
||||
/ @string string
|
||||
/ @integer int
|
||||
/ @double double
|
||||
/ @boolean JSONBoolean
|
||||
/ @null =null
|
||||
/ @array [JSON ...]
|
||||
/ @object { string: JSON ...:... } .
|
||||
JSONBoolean = =true / =false .
|
||||
|
||||
running the following:
|
||||
|
||||
preserves-schemac --no-bundle .:demo.prs
|
||||
|
||||
will produce the following binary file on `stdout`:
|
||||
|
||||
00000000: b4b3 0673 6368 656d 61b7 b307 7665 7273 ...schema...vers
|
||||
00000010: 696f 6e91 b30b 6465 6669 6e69 7469 6f6e ion...definition
|
||||
00000020: 73b7 b304 4a53 4f4e b4b3 026f 72b5 b5b1 s...JSON...or...
|
||||
00000030: 0673 7472 696e 67b4 b304 6174 6f6d b306 .string...atom..
|
||||
00000040: 5374 7269 6e67 8484 b5b1 0769 6e74 6567 String.....integ
|
||||
00000050: 6572 b4b3 0461 746f 6db3 0d53 6967 6e65 er...atom..Signe
|
||||
00000060: 6449 6e74 6567 6572 8484 b5b1 0664 6f75 dInteger.....dou
|
||||
00000070: 626c 65b4 b304 6174 6f6d b306 446f 7562 ble...atom..Doub
|
||||
00000080: 6c65 8484 b5b1 0762 6f6f 6c65 616e b4b3 le.....boolean..
|
||||
00000090: 0372 6566 b584 b30b 4a53 4f4e 426f 6f6c .ref....JSONBool
|
||||
000000a0: 6561 6e84 84b5 b104 6e75 6c6c b4b3 036c ean.....null...l
|
||||
000000b0: 6974 b304 6e75 6c6c 8484 b5b1 0561 7272 it..null.....arr
|
||||
000000c0: 6179 b4b3 0573 6571 6f66 b4b3 0372 6566 ay...seqof...ref
|
||||
000000d0: b584 b304 4a53 4f4e 8484 84b5 b106 6f62 ....JSON......ob
|
||||
000000e0: 6a65 6374 b4b3 0664 6963 746f 66b4 b304 ject...dictof...
|
||||
000000f0: 6174 6f6d b306 5374 7269 6e67 84b4 b303 atom..String....
|
||||
00000100: 7265 66b5 84b3 044a 534f 4e84 8484 8484 ref....JSON.....
|
||||
00000110: b30b 4a53 4f4e 426f 6f6c 6561 6eb4 b302 ..JSONBoolean...
|
||||
00000120: 6f72 b5b5 b104 7472 7565 b4b3 036c 6974 or....true...lit
|
||||
00000130: b304 7472 7565 8484 b5b1 0566 616c 7365 ..true.....false
|
||||
00000140: b4b3 036c 6974 b305 6661 6c73 6584 8484 ...lit..false...
|
||||
00000150: 8484 b30c 656d 6265 6464 6564 5479 7065 ....embeddedType
|
||||
00000160: 8084 84 ...
|
||||
|
||||
Piping the output to [`preserves-tool`](./preserves-tool.html) to
|
||||
pretty-print it produces:
|
||||
|
||||
<schema {
|
||||
version: 1,
|
||||
embeddedType: #f,
|
||||
definitions: {
|
||||
JSONBoolean: <or [
|
||||
[
|
||||
"true",
|
||||
<lit true>
|
||||
],
|
||||
[
|
||||
"false",
|
||||
<lit false>
|
||||
]
|
||||
]>,
|
||||
JSON: <or [
|
||||
[
|
||||
"string",
|
||||
<atom String>
|
||||
],
|
||||
[
|
||||
"integer",
|
||||
<atom SignedInteger>
|
||||
],
|
||||
[
|
||||
"double",
|
||||
<atom Double>
|
||||
],
|
||||
[
|
||||
"boolean",
|
||||
<ref [] JSONBoolean>
|
||||
],
|
||||
[
|
||||
"null",
|
||||
<lit null>
|
||||
],
|
||||
[
|
||||
"array",
|
||||
<seqof <ref [] JSON>>
|
||||
],
|
||||
[
|
||||
"object",
|
||||
<dictof <atom String> <ref [] JSON>>
|
||||
]
|
||||
]>
|
||||
}
|
||||
}>
|
||||
|
||||
### Multiple file (bundle)
|
||||
|
||||
Given a directory tree containing multiple `*.prs` files, running
|
||||
|
||||
preserves-schemac .
|
||||
|
||||
will produce a binary `Bundle` on `stdout` containing one `Schema` for
|
||||
each input file in the tree.
|
|
@ -0,0 +1,196 @@
|
|||
---
|
||||
title: preserves-tool
|
||||
---
|
||||
|
||||
The `preserves-tool` program is a swiss army knife for working with
|
||||
Preserves documents.
|
||||
|
||||
```
|
||||
preserves-tool 4.992.0
|
||||
Swiss-army knife tool for working with Preserves data.
|
||||
See https://preserves.dev/. If no subcommand is specified, the default
|
||||
subcommand will be `convert`.
|
||||
|
||||
USAGE:
|
||||
preserves-tool [OPTIONS]
|
||||
preserves-tool <SUBCOMMAND>
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
|
||||
OPTIONS FOR DEFAULT SUBCOMMAND convert:
|
||||
[...]
|
||||
|
||||
SUBCOMMANDS:
|
||||
completions
|
||||
convert
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
quote
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
The tool is
|
||||
[written in Rust](https://crates.io/crates/preserves-tools).
|
||||
[Install `cargo`.](https://doc.rust-lang.org/cargo/getting-started/installation.html)
|
||||
Then, `cargo install preserves-tools`.
|
||||
|
||||
## Subcommands
|
||||
|
||||
The tool includes three subcommands.
|
||||
|
||||
### `preserves-tool convert`, `preserves-tool`
|
||||
|
||||
This is the main tool, and is also the default if no subcommand is
|
||||
explicitly specified. It can
|
||||
|
||||
- translate between the various Preserves text and binary document
|
||||
syntaxes;
|
||||
- strip annotations;
|
||||
- pretty-print; and
|
||||
- break down and filter documents using [preserves path](..{% link preserves-path.md %}) selectors.
|
||||
|
||||
#### Usage
|
||||
|
||||
preserves-tool-convert
|
||||
|
||||
USAGE:
|
||||
preserves-tool convert [FLAGS] [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--bundle <filename>
|
||||
|
||||
|
||||
-c, --commas <COMMAS>
|
||||
[default: none] [possible values: none, separating, terminating]
|
||||
|
||||
--collect
|
||||
|
||||
|
||||
--escape-spaces
|
||||
|
||||
|
||||
-h, --help
|
||||
Print help information
|
||||
|
||||
-i, --input-format <INPUT_FORMAT>
|
||||
[default: auto-detect] [possible values: auto-detect, text, binary]
|
||||
|
||||
--indent <on/off>
|
||||
[default: on] [possible values: disabled, enabled]
|
||||
|
||||
--limit <LIMIT>
|
||||
|
||||
|
||||
-o, --output-format <OUTPUT_FORMAT>
|
||||
[default: text] [possible values: text, binary, unquoted]
|
||||
|
||||
--read-annotations <on/off>
|
||||
[default: on] [possible values: disabled, enabled]
|
||||
|
||||
--select <SELECT_EXPR>
|
||||
[default: *]
|
||||
|
||||
--select-output <SELECT_OUTPUT>
|
||||
[default: sequence] [possible values: sequence, set]
|
||||
|
||||
--write-annotations <on/off>
|
||||
[default: on] [possible values: disabled, enabled]
|
||||
|
||||
### `preserves-tool quote`
|
||||
|
||||
This subcommand reads chunks from standard input and outputs each one
|
||||
as a Preserves `String`, `Symbol`, or `ByteString` using either the
|
||||
text or binary Preserves surface syntax.
|
||||
|
||||
This is useful when writing shell scripts that interact with other
|
||||
programs using Preserves as an interchange format.
|
||||
|
||||
It defaults to taking the entirety of standard input as a single large
|
||||
chunk, but it can also work with newline- or `nul`-delimited chunks.
|
||||
|
||||
#### Usage
|
||||
|
||||
```
|
||||
preserves-tool-quote
|
||||
|
||||
USAGE:
|
||||
preserves-tool quote [OPTIONS] <SUBCOMMAND>
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-o, --output-format <OUTPUT_FORMAT> [default: text] [possible values: text,
|
||||
binary, unquoted]
|
||||
|
||||
SUBCOMMANDS:
|
||||
byte-string
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
string
|
||||
symbol
|
||||
```
|
||||
|
||||
```
|
||||
preserves-tool-quote-string
|
||||
|
||||
USAGE:
|
||||
preserves-tool quote string [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--escape-spaces
|
||||
-h, --help Print help information
|
||||
--include-terminator
|
||||
--input-terminator <INPUT_TERMINATOR> [default: eof] [possible values:
|
||||
eof, newline, nul]
|
||||
```
|
||||
|
||||
```
|
||||
preserves-tool-quote-symbol
|
||||
|
||||
USAGE:
|
||||
preserves-tool quote symbol [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--escape-spaces
|
||||
-h, --help Print help information
|
||||
--include-terminator
|
||||
--input-terminator <INPUT_TERMINATOR> [default: eof] [possible values:
|
||||
eof, newline, nul]
|
||||
```
|
||||
|
||||
```
|
||||
preserves-tool-quote-byte-string
|
||||
|
||||
USAGE:
|
||||
preserves-tool quote byte-string
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
```
|
||||
|
||||
### `preserves-tool completions`
|
||||
|
||||
This subcommand outputs Bash completion code to stdout, for sourcing
|
||||
at shell startup time.
|
||||
|
||||
#### Usage
|
||||
|
||||
Add the following to your `.profile` or similar:
|
||||
|
||||
eval "$(preserves-tool completions bash 2>/dev/null)"
|
||||
|
||||
Multiple shell dialects are supported (courtesy of
|
||||
[`clap`](https://crates.io/crates/clap)):
|
||||
|
||||
```
|
||||
preserves-tool-completions
|
||||
|
||||
USAGE:
|
||||
preserves-tool completions <SHELL>
|
||||
|
||||
ARGS:
|
||||
<SHELL> [possible values: bash, elvish, fish, powershell, zsh]
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
```
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: Tools for working with Preserves Schema
|
||||
---
|
||||
|
||||
A number of tools for working with [Preserves Schema](..{% link preserves-schema.md %}) exist:
|
||||
|
||||
- [preserves-schemac](preserves-schemac.html), generic Schema reader and linter
|
||||
- [preserves-schema-rkt](preserves-schema-rkt.html), Racket code generator
|
||||
- [preserves-schema-rs](preserves-schema-rs.html), Rust code generator
|
||||
- [preserves-schema-ts](preserves-schema-ts.html), TypeScript code generator
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
exec 1>&2
|
||||
|
||||
failed=
|
||||
cmp_and_fail() {
|
||||
if ! cmp "$1" "$2"
|
||||
then
|
||||
failed=failed
|
||||
fi
|
||||
}
|
||||
|
||||
COMMAND=cmp_and_fail
|
||||
if [ "$1" = "--fix" ];
|
||||
then
|
||||
COMMAND=cp
|
||||
fi
|
||||
|
||||
# https://gitlab.com/preserves/preserves/-/issues/30
|
||||
#
|
||||
# So it turns out that Racket's git-checkout mechanism pays attention
|
||||
# to portions of the tree outside the package of interest, which is
|
||||
# totally fair enough!
|
||||
#
|
||||
# But it means we can't use updir-containing symlinks anywhere in the
|
||||
# repository if we want to have a Racket-installable package as well,
|
||||
# *even if* the non-Racket implementation concerned is OK with
|
||||
# updir-containing symlinks.
|
||||
|
||||
# Ensure that various copies of schema.prs, schema.bin, path.bin,
|
||||
# samples.pr and samples.bin are in fact identical.
|
||||
${COMMAND} path/path.bin implementations/python/preserves/path.prb
|
||||
|
||||
${COMMAND} schema/schema.bin implementations/python/preserves/schema.prb
|
||||
${COMMAND} schema/schema.prs implementations/racket/preserves/preserves-schema/schema.prs
|
||||
|
||||
${COMMAND} tests/samples.bin implementations/python/tests/samples.bin
|
||||
${COMMAND} tests/samples.pr implementations/python/tests/samples.pr
|
||||
${COMMAND} tests/samples.pr implementations/racket/preserves/preserves/tests/samples.pr
|
||||
|
||||
[ -z "$failed" ]
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 990 380"><defs><style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}.cls-4{fill:#fff;}</style></defs><g id="LOGO"><path class="cls-1" d="M302,174.37l-.21-.56-21.2-55.3a5.5,5.5,0,0,0-2.18-2.63,5.6,5.6,0,0,0-8.41,3.2l-14.31,43.81H197.74l-14.31-43.81a5.61,5.61,0,0,0-8.41-3.2,5.5,5.5,0,0,0-2.18,2.63l-21.19,55.31-.22.55a39.36,39.36,0,0,0,13.06,45.49l.08.06.18.14L197,244.23l16,12.09,9.72,7.35a6.57,6.57,0,0,0,7.92,0l9.72-7.35,16-12.09,32.48-24.31.09-.07A39.36,39.36,0,0,0,302,174.37Z"/><path class="cls-2" d="M302,174.37l-.21-.56a71.5,71.5,0,0,0-28.5,12.82l-46.55,35.2,29.64,22.4,32.48-24.31.09-.07A39.36,39.36,0,0,0,302,174.37Z"/><path class="cls-3" d="M197,244.23l16,12.09,9.72,7.35a6.57,6.57,0,0,0,7.92,0l9.72-7.35,16-12.09-29.64-22.4Z"/><path class="cls-2" d="M180.14,186.63a71.44,71.44,0,0,0-28.49-12.81l-.22.55a39.36,39.36,0,0,0,13.06,45.49l.08.06.18.14L197,244.23l29.66-22.4Z"/><path class="cls-4" d="M428.92,171.51H451.7c-3.8-24.22-24.77-41.09-52.06-41.09-32.29,0-56.52,23.74-56.52,63.5,0,39.05,23.14,63.27,57.18,63.27,30.55,0,52.42-19.65,52.42-51.46V190.91H402.65v17.47h28.44c-.36,17.6-12.11,28.74-30.67,28.74-20.66,0-34.82-15.48-34.82-43.44,0-27.78,14.4-43.2,34.34-43.2C414.82,150.48,425,158.43,428.92,171.51Z"/><path class="cls-4" d="M467.78,255.5h21.81V163H467.78Zm11-107.2c6.93,0,12.59-5.31,12.59-11.81s-5.66-11.87-12.59-11.87-12.65,5.3-12.65,11.87S471.75,148.3,478.74,148.3Z"/><path class="cls-4" d="M554.9,163H536.64V140.78H514.83V163H501.7v16.87h13.13v51.46c-.12,17.41,12.54,26,28.92,25.49a44.29,44.29,0,0,0,12.84-2.17l-3.68-17.06a26.57,26.57,0,0,1-6.38.85c-5.49,0-9.89-1.93-9.89-10.73V179.82H554.9Z"/><path class="cls-4" d="M571.78,255.5h76.7V236.76H594.14V132.1H571.78Z"/><path class="cls-4" d="M690.26,257.37c14.52,0,23.19-6.81,27.17-14.58h.72V255.5h21V193.56c0-24.46-19.94-31.81-37.6-31.81-19.46,0-34.4,8.67-39.22,25.54l20.37,2.9c2.16-6.33,8.31-11.75,19-11.75,10.13,0,15.67,5.18,15.67,14.28v.36c0,6.26-6.57,6.57-22.9,8.31-17.95,1.93-35.12,7.29-35.12,28.14C659.29,247.73,672.6,257.37,690.26,257.37Zm7.17-16c-9.1,0-15.61-4.16-15.61-12.17,0-8.38,7.29-11.87,17-13.26,5.73-.78,17.18-2.23,20-4.51v10.9C718.88,232.6,710.56,241.34,697.43,241.34Z"/><path class="cls-4" d="M755.21,255.5h21.45V240.92h1.26c3.44,6.75,10.61,16.21,26.52,16.21,21.81,0,38.14-17.3,38.14-47.78,0-30.85-16.81-47.6-38.2-47.6-16.33,0-23.14,9.82-26.46,16.51H777V132.1H755.21Zm21.39-46.27c0-18,7.71-29.59,21.75-29.59,14.52,0,22,12.35,22,29.59s-7.59,30-22,30C784.43,239.23,776.6,227.18,776.6,209.23Z"/></g></svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-2" d="M282.83,170.73l-.27-.69a88.3,88.3,0,0,0-35.15,15.8L190,229.25c19.55,14.79,36.57,27.64,36.57,27.64l40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-3" d="M153.43,256.89l19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91S209.55,244,190,229.25C170.45,244,153.43,256.89,153.43,256.89Z"/><path class="cls-2" d="M132.58,185.84A88.19,88.19,0,0,0,97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82s17-12.85,36.57-27.64Z"/></g></svg>
|
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#fff;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/></g></svg>
|
After Width: | Height: | Size: 530 B |
|
@ -13,7 +13,7 @@ h2 { border-bottom: solid black 1px; }
|
|||
# SPKI CAT: SPKI S-Expressions with Canonical Atom Tags
|
||||
|
||||
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||
Christopher Lemmer Webber <cwebber@dustycloud.org>
|
||||
Christine Lemmer Webber <cwebber@dustycloud.org>
|
||||
May 2018
|
||||
Version 0.0.1
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Preserves Implementations
|
||||
|
||||
Here you may find:
|
||||
|
||||
- [dhall](dhall/), functions for converting Dhall values to a corresponding
|
||||
subset of Preserves.
|
||||
|
||||
- [javascript](javascript/), an implementation in TypeScript,
|
||||
compiling to JavaScript, for node.js and the Browser.
|
||||
|
||||
- [python](python/), an implementation for Python 2.x and 3.x.
|
||||
|
||||
- [racket](racket/), an implementation for Racket 7.x and newer
|
||||
(though older Rackets may also work with it).
|
||||
|
||||
Other implementations are also available:
|
||||
|
||||
- [Preserves for Rust](https://gitlab.com/preserves/preserves-rs/), an implementation for Rust
|
||||
that interoperates with serde.
|
||||
|
||||
- [Preserves for Squeak Smalltalk](https://squeaksource.com/Preserves.html)
|
|
@ -0,0 +1,2 @@
|
|||
m.output.txt
|
||||
m
|
|
@ -0,0 +1,8 @@
|
|||
m: main.c preserves.h
|
||||
gcc -Wall -Wextra -Werror -g3 -o $@ main.c
|
||||
|
||||
go: m
|
||||
cat ../../tests/samples.bin | ./m | tee m.output.txt
|
||||
|
||||
clean:
|
||||
rm -f m
|
|
@ -0,0 +1,96 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#define PRESERVES_IMPLEMENTATION
|
||||
#include "preserves.h"
|
||||
|
||||
static double now() {
|
||||
struct timeval tv;
|
||||
if (gettimeofday(&tv, NULL) < 0) {
|
||||
perror("gettimeofday");
|
||||
}
|
||||
return (double) tv.tv_sec + ((double) tv.tv_usec) / 1000000.0;
|
||||
}
|
||||
|
||||
int main(__attribute__ ((unused)) int argc,
|
||||
__attribute__ ((unused)) char const * const argv[])
|
||||
{
|
||||
preserves_bytes_t input = preserves_create_bytes();
|
||||
bool silent = getenv("SILENT") != NULL;
|
||||
|
||||
double start = now();
|
||||
|
||||
{
|
||||
preserves_bytes_t chunk = preserves_create_bytes();
|
||||
if (preserves_resize_bytes(&chunk, 131072) == -1) {
|
||||
perror("allocating chunk");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
size_t count = fread(chunk.ptr, 1, chunk.len, stdin);
|
||||
if (count == 0) {
|
||||
if (ferror(stdin)) {
|
||||
perror("reading");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (preserves_extend_bytes(&input, preserves_bytes_subsequence(&chunk, 0, count)) == -1) {
|
||||
perror("appending");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
preserves_free_bytes(&chunk);
|
||||
}
|
||||
|
||||
double mid = now();
|
||||
|
||||
{
|
||||
preserves_reader_t reader = preserves_create_reader();
|
||||
preserves_reader_result_t result = preserves_read_binary(&reader, &input, 1);
|
||||
more_input:
|
||||
if (result.index == NULL) {
|
||||
perror("parsing");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
printf("Size of index: %lu bytes; %lu entries\n",
|
||||
reader.index_pos * sizeof(preserves_index_entry_t),
|
||||
reader.index_pos);
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
for (preserves_index_entry_t *i = result.index; i != result.end_marker; i++) {
|
||||
preserves_dump_index_entry(stdout, &reader.input, i, true);
|
||||
}
|
||||
preserves_dump_index_entry(stdout, &reader.input, result.end_marker, true);
|
||||
}
|
||||
|
||||
if (result.end_marker->data._err == PRESERVES_END_MORE_INPUT_REMAINING) {
|
||||
if (!silent) {
|
||||
printf("\n");
|
||||
}
|
||||
reader.index_pos = 0;
|
||||
result = preserves_read_binary_continue(&reader, 1);
|
||||
goto more_input;
|
||||
}
|
||||
|
||||
preserves_free_reader(&reader);
|
||||
}
|
||||
|
||||
double end = now();
|
||||
|
||||
printf("stage 1: %g s\n", mid - start);
|
||||
printf("stage 2: %g s\n", end - mid);
|
||||
printf("total: %g s\n", end - start);
|
||||
|
||||
preserves_free_bytes(&input);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
m.output.txt
|
||||
m
|
||||
.vscode
|
||||
*.o
|
||||
/googletest.a
|
|
@ -0,0 +1,28 @@
|
|||
CXX=g++ -std=c++14 -Wall -Wextra -Werror -g -O0 -I googletest
|
||||
|
||||
HEADERS=$(wildcard preserves*.hpp)
|
||||
|
||||
test: all
|
||||
./m
|
||||
|
||||
all: m
|
||||
|
||||
m: main.cpp $(HEADERS) googletest.a
|
||||
$(CXX) -o $@ main.cpp googletest.a
|
||||
|
||||
googletest.a: googletest/src/gtest-all.o googletest/src/gtest_main.o
|
||||
ar r $@ $^
|
||||
ranlib $@
|
||||
|
||||
%.o: %.cc
|
||||
$(CXX) -c $< -o $@
|
||||
%.o: %.cpp
|
||||
$(CXX) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f m
|
||||
rm -f *.o
|
||||
|
||||
veryclean: clean
|
||||
rm -f googletest.a
|
||||
rm -f googletest/src/*.o
|
|
@ -0,0 +1,37 @@
|
|||
Include files and library source code from the GoogleTest library,
|
||||
`googletest-1.13.0`, <https://github.com/google/googletest>.
|
||||
|
||||
The GoogleTest library is licensed as follows (from the
|
||||
[LICENSE](https://github.com/google/googletest/blob/main/LICENSE) file
|
||||
in the repository):
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -0,0 +1,220 @@
|
|||
// 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 <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#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_
|
|
@ -0,0 +1,545 @@
|
|||
// 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...);
|
||||
}
|
||||
|
||||
// ConvertGenerator() wraps a parameter generator in order to cast each produced
|
||||
// value through a known type before supplying it to the test suite
|
||||
//
|
||||
// Synopsis:
|
||||
// ConvertGenerator<T>(gen)
|
||||
// - returns a generator producing the same elements as generated by gen, but
|
||||
// each element is static_cast to type T before being returned
|
||||
//
|
||||
// It is useful when using the Combine() function to get the generated
|
||||
// parameters in a custom type instead of std::tuple
|
||||
//
|
||||
// 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 };
|
||||
// struct ParamType {
|
||||
// using TupleT = std::tuple<const char*, Color>;
|
||||
// std::string animal;
|
||||
// Color color;
|
||||
// ParamType(TupleT t) : animal(std::get<0>(t)), color(std::get<1>(t)) {}
|
||||
// };
|
||||
// class AnimalTest
|
||||
// : public testing::TestWithParam<ParamType> {...};
|
||||
//
|
||||
// TEST_P(AnimalTest, AnimalLooksNice) {...}
|
||||
//
|
||||
// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest,
|
||||
// ConvertGenerator<ParamType::TupleT>(
|
||||
// Combine(Values("cat", "dog"),
|
||||
// Values(BLACK, WHITE))));
|
||||
//
|
||||
template <typename T>
|
||||
internal::ParamConverterGenerator<T> ConvertGenerator(
|
||||
internal::ParamGenerator<T> gen) {
|
||||
return internal::ParamConverterGenerator<T>(gen);
|
||||
}
|
||||
|
||||
#define TEST_P(test_suite_name, test_name) \
|
||||
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
|
||||
: public test_suite_name, private ::testing::internal::GTestNonCopyable {\
|
||||
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_; \
|
||||
}; \
|
||||
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_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,250 @@
|
|||
// 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 <string>
|
||||
|
||||
#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_
|
|
@ -0,0 +1,192 @@
|
|||
// 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 <ostream>
|
||||
#include <string>
|
||||
#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_
|
|
@ -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_
|
File diff suppressed because it is too large
Load Diff
|
@ -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_
|
|
@ -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_
|
|
@ -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.
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
|
@ -0,0 +1,307 @@
|
|||
// 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 <string>
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
// 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_
|
|
@ -0,0 +1,227 @@
|
|||
// 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 <string>
|
||||
|
||||
#include "gtest/internal/gtest-port.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 */)
|
||||
|
||||
#if GTEST_HAS_FILE_SYSTEM
|
||||
|
||||
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;
|
||||
|
||||
// Returns the length of the path root, including the directory separator at
|
||||
// the end of the prefix. Returns zero by definition if the path is relative.
|
||||
// Examples:
|
||||
// - [Windows] "..\Sibling" => 0
|
||||
// - [Windows] "\Windows" => 1
|
||||
// - [Windows] "C:/Windows\Notepad.exe" => 3
|
||||
// - [Windows] "\\Host\Share\C$/Windows" => 13
|
||||
// - [UNIX] "/bin" => 1
|
||||
size_t CalculateRootLength() const;
|
||||
|
||||
std::string pathname_;
|
||||
}; // class FilePath
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
|
||||
|
||||
#endif // GTEST_HAS_FILE_SYSTEM
|
||||
|
||||
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,118 @@
|
|||
// 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
|
||||
#elif defined(__hexagon__)
|
||||
#define GTEST_OS_QURT 1
|
||||
#endif // __CYGWIN__
|
||||
|
||||
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,178 @@
|
|||
// 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 <sstream>
|
||||
#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_
|
|
@ -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.
|
||||
|
||||
// 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 <string>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
|
||||
#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_
|
|
@ -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"
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,410 @@
|
|||
// 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
|
||||
|
||||
#if GTEST_HAS_FILE_SYSTEM
|
||||
|
||||
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 || GTEST_OS_QURT
|
||||
// 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;
|
||||
}
|
||||
|
||||
size_t FilePath::CalculateRootLength() const {
|
||||
const auto &path = pathname_;
|
||||
auto s = path.begin();
|
||||
auto end = path.end();
|
||||
#if GTEST_OS_WINDOWS
|
||||
if (end - s >= 2 && s[1] == ':' &&
|
||||
(end - s == 2 || IsPathSeparator(s[2])) &&
|
||||
(('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))) {
|
||||
// A typical absolute path like "C:\Windows" or "D:"
|
||||
s += 2;
|
||||
if (s != end) {
|
||||
++s;
|
||||
}
|
||||
} else if (end - s >= 3 && IsPathSeparator(*s) && IsPathSeparator(*(s + 1))
|
||||
&& !IsPathSeparator(*(s + 2))) {
|
||||
// Move past the "\\" prefix in a UNC path like "\\Server\Share\Folder"
|
||||
s += 2;
|
||||
// Skip 2 components and their following separators ("Server\" and "Share\")
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
while (s != end) {
|
||||
bool stop = IsPathSeparator(*s);
|
||||
++s;
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (s != end && IsPathSeparator(*s)) {
|
||||
// A drive-rooted path like "\Windows"
|
||||
++s;
|
||||
}
|
||||
#else
|
||||
if (s != end && IsPathSeparator(*s)) {
|
||||
++s;
|
||||
}
|
||||
#endif
|
||||
return static_cast<size_t>(s - path.begin());
|
||||
}
|
||||
|
||||
// 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. UNC share roots are also included.)
|
||||
bool FilePath::IsRootDirectory() const {
|
||||
size_t root_length = CalculateRootLength();
|
||||
return root_length > 0 && root_length == pathname_.size() &&
|
||||
IsPathSeparator(pathname_[root_length - 1]);
|
||||
}
|
||||
|
||||
// Returns true if pathname describes an absolute path.
|
||||
bool FilePath::IsAbsolutePath() const {
|
||||
return CalculateRootLength() > 0;
|
||||
}
|
||||
|
||||
// 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 || GTEST_OS_QURT
|
||||
// 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 "..".
|
||||
// Note that "\\Host\Share" does not contain a redundancy on Windows!
|
||||
void FilePath::Normalize() {
|
||||
auto out = pathname_.begin();
|
||||
|
||||
auto i = pathname_.cbegin();
|
||||
#if GTEST_OS_WINDOWS
|
||||
// UNC paths are treated specially
|
||||
if (pathname_.end() - i >= 3 && IsPathSeparator(*i) &&
|
||||
IsPathSeparator(*(i + 1)) && !IsPathSeparator(*(i + 2))) {
|
||||
*(out++) = kPathSeparator;
|
||||
*(out++) = kPathSeparator;
|
||||
}
|
||||
#endif
|
||||
while (i != pathname_.end()) {
|
||||
const char character = *i;
|
||||
if (!IsPathSeparator(character)) {
|
||||
*(out++) = character;
|
||||
} else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
|
||||
*(out++) = kPathSeparator;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
pathname_.erase(out, pathname_.end());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace testing
|
||||
|
||||
#endif // GTEST_HAS_FILE_SYSTEM
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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 = static_cast<char>('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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,65 @@
|
|||
// 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.
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
|
||||
// Arduino-like platforms: program entry points are setup/loop instead of main.
|
||||
|
||||
#if GTEST_OS_ESP8266
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void setup() { testing::InitGoogleTest(); }
|
||||
|
||||
void loop() { RUN_ALL_TESTS(); }
|
||||
|
||||
#if GTEST_OS_ESP8266
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif GTEST_OS_QURT
|
||||
// QuRT: program entry point is main, but argc/argv are unusable.
|
||||
|
||||
GTEST_API_ int main() {
|
||||
printf("Running main() from %s\n", __FILE__);
|
||||
testing::InitGoogleTest();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
#else
|
||||
// Normal platforms: program entry point is main, argc/argv are initialized.
|
||||
|
||||
GTEST_API_ int main(int argc, char **argv) {
|
||||
printf("Running main() from %s\n", __FILE__);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,67 @@
|
|||
#include "preserves.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "googletest/gtest/gtest.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Preserves;
|
||||
|
||||
TEST(Value, Basics) {
|
||||
auto vs = Value<>::from(vector<Value<>>{
|
||||
Value<>::from(1),
|
||||
Value<>::from(2.0),
|
||||
Value<>::from("three"),
|
||||
});
|
||||
ASSERT_EQ(3U, vs.size());
|
||||
ASSERT_EQ(1U, vs[0].to_unsigned());
|
||||
ASSERT_EQ(1.0, vs[0].to_double());
|
||||
ASSERT_EQ(2.0, vs[1].to_double());
|
||||
ASSERT_EQ("three", vs[2].to_string());
|
||||
ASSERT_EQ(ValueKind::Sequence, vs.value_kind());
|
||||
ASSERT_EQ(ValueKind::String, vs[2].value_kind());
|
||||
}
|
||||
|
||||
TEST(BinaryReader, Negative257) {
|
||||
istringstream input("\xB0\x02\xFE\xFF");
|
||||
auto v = BinaryReader<>(input).next();
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_EQ(v->to_signed(), -257);
|
||||
}
|
||||
|
||||
TEST(BinaryReader, Negative127) {
|
||||
istringstream input("\xB0\x01\x81");
|
||||
auto v = BinaryReader<>(input).next();
|
||||
ASSERT_TRUE(v);
|
||||
ASSERT_EQ(v->to_signed(), -127);
|
||||
}
|
||||
|
||||
TEST(BinaryWriter, Negative257) {
|
||||
ostringstream s;
|
||||
BinaryWriter w(s);
|
||||
w << -257;
|
||||
std::string output(s.str());
|
||||
ASSERT_EQ(output[0], char(BinaryTag::SignedInteger));
|
||||
ASSERT_EQ(output[1], char(0x02));
|
||||
ASSERT_EQ(output[2], char(0xFE));
|
||||
ASSERT_EQ(output[3], char(0xFF));
|
||||
}
|
||||
|
||||
TEST(BinaryWriter, Negative127) {
|
||||
ostringstream s;
|
||||
BinaryWriter w(s);
|
||||
w << -127;
|
||||
std::string output(s.str());
|
||||
ASSERT_EQ(output[0], char(BinaryTag::SignedInteger));
|
||||
ASSERT_EQ(output[1], char(0x01));
|
||||
ASSERT_EQ(output[2], char(0x81));
|
||||
}
|
||||
|
||||
TEST(BinaryReader, ReadSamples) {
|
||||
ifstream f("../../tests/samples.bin", ios::binary);
|
||||
BinaryReader<> r(f, &GenericEmbedded::wrap);
|
||||
auto v = r.next();
|
||||
ASSERT_TRUE(v);
|
||||
// BinaryWriter(cerr) << *v;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "preserves_value.hpp"
|
||||
#include "preserves_text.hpp"
|
||||
#include "preserves_binary_writer.hpp"
|
||||
#include "preserves_impl.hpp"
|
||||
#include "preserves_binary_reader.hpp"
|
|
@ -0,0 +1,189 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Preserves {
|
||||
template <typename T = class GenericEmbedded>
|
||||
class BinaryReader {
|
||||
std::istream& i;
|
||||
std::function<std::shared_ptr<T>(Value<>)> decodeEmbedded;
|
||||
|
||||
bool next_chunk(void* p, size_t n) {
|
||||
i.read(static_cast<char *>(p), n);
|
||||
return i.good();
|
||||
}
|
||||
|
||||
public:
|
||||
static boost::optional<uint64_t> varint(std::istream &i) {
|
||||
uint64_t n = 0;
|
||||
// Can read max 9 bytes, each with 7 bits of payload, for 9*7 = 63 bits.
|
||||
for (size_t count = 0; count < 9; count++) {
|
||||
int b = i.get();
|
||||
if (i.eof()) return boost::none;
|
||||
n |= (b & 0x7f) << (count * 7);
|
||||
if ((b & 0x80) == 0) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
BinaryReader(typename std::enable_if<std::is_same<T, GenericEmbedded>::value, std::istream&>::type i) :
|
||||
BinaryReader(i, &GenericEmbedded::wrap)
|
||||
{}
|
||||
|
||||
BinaryReader(std::istream& i, std::function<std::shared_ptr<T>(Value<>)> decodeEmbedded) :
|
||||
i(i),
|
||||
decodeEmbedded(decodeEmbedded)
|
||||
{}
|
||||
|
||||
boost::optional<double> next_double() {
|
||||
uint8_t buf[8];
|
||||
if (!next_chunk(buf, sizeof(buf))) return boost::none;
|
||||
uint32_t n1 = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
|
||||
uint32_t n2 = buf[4] << 24 | buf[5] << 16 | buf[6] << 8 | buf[7];
|
||||
uint64_t n = uint64_t(n1) << 32 | n2;
|
||||
double d;
|
||||
memcpy(&d, &n, sizeof(d));
|
||||
return d;
|
||||
}
|
||||
|
||||
boost::optional<Value<T>> next_machineword(size_t n, bool always_unsigned) {
|
||||
uint8_t buf[n];
|
||||
if (!next_chunk(buf, n)) return boost::none;
|
||||
if ((buf[0] & 0x80) && !always_unsigned) {
|
||||
int64_t v = -1;
|
||||
for (size_t j = 0; j < n; j++) v = (v << 8) | buf[j];
|
||||
return Value<T>::from_int(v);
|
||||
} else {
|
||||
uint64_t v = 0;
|
||||
for (size_t j = 0; j < n; j++) v = (v << 8) | buf[j];
|
||||
return Value<T>::from_int(v);
|
||||
}
|
||||
}
|
||||
|
||||
boost::optional<Value<T>> next_bignum(size_t n) {
|
||||
auto b = std::make_shared<BigNum<T>>(std::vector<uint8_t>());
|
||||
b->_value().resize(n);
|
||||
if (!next_chunk(&b->_value()[0], n)) return boost::none;
|
||||
return Value<T>(b);
|
||||
}
|
||||
|
||||
boost::optional<Value<T>> next() {
|
||||
bool end_sentinel;
|
||||
return _next(end_sentinel);
|
||||
}
|
||||
|
||||
boost::optional<Value<T>> _next(bool& end_sentinel) {
|
||||
end_sentinel = false;
|
||||
auto tag = BinaryTag(i.get());
|
||||
// std::cout << "tag " << std::hex << int(tag) << " pos " << i.tellg() - 1 << std::endl;
|
||||
if (i.eof()) return boost::none;
|
||||
switch (tag) {
|
||||
case BinaryTag::False: return Value<T>::from_bool(false);
|
||||
case BinaryTag::True: return Value<T>::from_bool(true);
|
||||
case BinaryTag::End: end_sentinel = true; return boost::none;
|
||||
case BinaryTag::Annotation: {
|
||||
std::vector<Value<T>> annotations;
|
||||
while (true) {
|
||||
auto ann = next();
|
||||
if (!ann) return boost::none;
|
||||
annotations.push_back(*ann);
|
||||
if (BinaryTag(i.peek()) != BinaryTag::Annotation) break;
|
||||
i.get();
|
||||
}
|
||||
auto underlying = next();
|
||||
if (!underlying) return boost::none;
|
||||
return Value<T>(new AnnotatedValue<T>(std::move(annotations), *underlying));
|
||||
}
|
||||
case BinaryTag::Embedded:
|
||||
return BinaryReader<>(i).next().map(decodeEmbedded).map(Value<T>::from_embedded);
|
||||
case BinaryTag::Ieee754: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
|
||||
switch (len) {
|
||||
case 8: return next_double().map(Value<T>::from_double);
|
||||
default: return boost::none;
|
||||
}
|
||||
});
|
||||
case BinaryTag::SignedInteger: return varint(i).flat_map([&](size_t n)-> boost::optional<Value<T>> {
|
||||
if (n == 0) return Value<T>::from_int(uint64_t(0));
|
||||
if (n < 9) return next_machineword(n, false);
|
||||
if (n == 9) {
|
||||
// We can handle this with uint64_t if it's unsigned and the first byte is 0.
|
||||
if (i.peek() == 0) {
|
||||
i.get();
|
||||
return next_machineword(8, true);
|
||||
}
|
||||
}
|
||||
return next_bignum(n);
|
||||
});
|
||||
case BinaryTag::String: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
|
||||
auto s = std::make_shared<String<T>>(std::string());
|
||||
s->_value().resize(len);
|
||||
if (!next_chunk(&s->_value()[0], len)) return boost::none;
|
||||
return Value<T>(s);
|
||||
});
|
||||
case BinaryTag::ByteString: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
|
||||
auto s = std::make_shared<ByteString<T>>(std::vector<uint8_t>());
|
||||
s->_value().resize(len);
|
||||
if (!next_chunk(&s->_value()[0], len)) return boost::none;
|
||||
return Value<T>(s);
|
||||
});
|
||||
case BinaryTag::Symbol: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
|
||||
auto s = std::make_shared<Symbol<T>>(std::string());
|
||||
s->_value().resize(len);
|
||||
if (!next_chunk(&s->_value()[0], len)) return boost::none;
|
||||
return Value<T>(s);
|
||||
});
|
||||
case BinaryTag::Record: return next().flat_map([&](auto label)-> boost::optional<Value<T>> {
|
||||
auto r = std::make_shared<Record<T>>(label);
|
||||
while (true) {
|
||||
bool end_rec = false;
|
||||
auto v = _next(end_rec);
|
||||
if (end_rec) return Value<T>(r);
|
||||
if (!v) return boost::none;
|
||||
r->fields.push_back(*v);
|
||||
}
|
||||
});
|
||||
case BinaryTag::Sequence: {
|
||||
auto s = std::make_shared<Sequence<T>>();
|
||||
while (true) {
|
||||
bool end_rec = false;
|
||||
auto v = _next(end_rec);
|
||||
if (end_rec) return Value<T>(s);
|
||||
if (!v) return boost::none;
|
||||
s->values.push_back(*v);
|
||||
}
|
||||
}
|
||||
case BinaryTag::Set: {
|
||||
auto s = std::make_shared<Set<T>>();
|
||||
while (true) {
|
||||
bool end_rec = false;
|
||||
auto v = _next(end_rec);
|
||||
if (end_rec) return Value<T>(s);
|
||||
if (!v) return boost::none;
|
||||
s->values.insert(*v);
|
||||
}
|
||||
}
|
||||
case BinaryTag::Dictionary: {
|
||||
auto s = std::make_shared<Dictionary<T>>();
|
||||
while (true) {
|
||||
bool end_rec = false;
|
||||
auto k = _next(end_rec);
|
||||
if (end_rec) return Value<T>(s);
|
||||
if (!k) return boost::none;
|
||||
auto v = next();
|
||||
if (!v) return boost::none;
|
||||
s->values.emplace(*k, *v);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <limits>
|
||||
|
||||
namespace Preserves {
|
||||
enum class BinaryTag {
|
||||
False = 0x80,
|
||||
True = 0x81,
|
||||
End = 0x84,
|
||||
Annotation = 0x85,
|
||||
Embedded = 0x86,
|
||||
Ieee754 = 0x87,
|
||||
SignedInteger = 0xb0,
|
||||
String = 0xb1,
|
||||
ByteString = 0xb2,
|
||||
Symbol = 0xb3,
|
||||
Record = 0xb4,
|
||||
Sequence = 0xb5,
|
||||
Set = 0xb6,
|
||||
Dictionary = 0xb7,
|
||||
};
|
||||
|
||||
template <uint64_t wholeTest, uint64_t bitTest>
|
||||
int _shift_for(uint64_t u) {
|
||||
int shift = 56;
|
||||
while (true) {
|
||||
if (shift == 0) break;
|
||||
if (((u >> shift) & 0xff) != wholeTest) break;
|
||||
shift -= 8;
|
||||
if (((u >> shift) & 0x80) != bitTest) {
|
||||
shift += 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
class BinaryWriter {
|
||||
std::ostream& o;
|
||||
|
||||
template <typename Collection>
|
||||
struct _iterable {
|
||||
Collection const& c;
|
||||
};
|
||||
|
||||
template <typename Collection>
|
||||
_iterable<Collection> iterable(Collection const& c) { return _iterable<Collection>{c}; }
|
||||
|
||||
public:
|
||||
BinaryWriter(std::ostream& o) : o(o) {}
|
||||
|
||||
std::ostream& stream() const { return o; }
|
||||
|
||||
BinaryWriter& put(uint8_t b) {
|
||||
o.put(b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BinaryWriter& operator<<(BinaryTag const& t) {
|
||||
return put(uint8_t(t));
|
||||
}
|
||||
|
||||
BinaryWriter& write(void const* buf, size_t size) {
|
||||
o.write(reinterpret_cast<char const*>(buf), size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BinaryWriter& varint(size_t n) {
|
||||
while (n >= 0x80) {
|
||||
put(uint8_t(0x80 | (n & 0x7f)));
|
||||
n >>= 7;
|
||||
}
|
||||
return put(uint8_t(n & 0x7f));
|
||||
}
|
||||
|
||||
BinaryWriter& write(BinaryTag const& tag, void const* buf, size_t size) {
|
||||
(*this) << tag;
|
||||
varint(size);
|
||||
return write(buf, size);
|
||||
}
|
||||
|
||||
BinaryWriter& bignum(std::vector<uint8_t> const& n) {
|
||||
size_t startOffset = 0;
|
||||
if (n.size() > 0) {
|
||||
if (n[0] & 0x80) {
|
||||
while (true) {
|
||||
if (startOffset == n.size() - 1) break;
|
||||
if (n[startOffset] != 0xff) break;
|
||||
startOffset++;
|
||||
if ((n[startOffset] & 0x80) != 0x80) {
|
||||
startOffset--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (true) {
|
||||
if (startOffset == n.size() - 1) break;
|
||||
if (n[startOffset] != 0x00) break;
|
||||
startOffset++;
|
||||
if ((n[startOffset] & 0x80) != 0x00) {
|
||||
startOffset--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t count = n.size() - startOffset;
|
||||
(*this) << BinaryTag::SignedInteger;
|
||||
varint(count);
|
||||
return write(&n[startOffset], count);
|
||||
}
|
||||
|
||||
BinaryWriter& string(std::string const& s) {
|
||||
return write(BinaryTag::String, &s[0], s.size());
|
||||
}
|
||||
|
||||
BinaryWriter& symbol(std::string const& s) {
|
||||
return write(BinaryTag::Symbol, &s[0], s.size());
|
||||
}
|
||||
|
||||
BinaryWriter& bytes(std::vector<uint8_t> const& v) {
|
||||
return bytes(v.data(), v.size());
|
||||
}
|
||||
|
||||
BinaryWriter& bytes(void const* buf, size_t size) {
|
||||
return write(BinaryTag::ByteString, buf, size);
|
||||
}
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
BinaryWriter& operator<<(Value<T> const& v) {
|
||||
return v->write(*this);
|
||||
}
|
||||
|
||||
template <typename Iter>
|
||||
BinaryWriter& writeseq(Iter const& begin, Iter const& end) {
|
||||
for (Iter i = begin; i != end; ++i) (*this) << (*i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Collection>
|
||||
BinaryWriter& operator<<(_iterable<Collection> const& c) {
|
||||
return writeseq(c.c.begin(), c.c.end());
|
||||
}
|
||||
|
||||
BinaryWriter& operator<<(bool b) {
|
||||
return (*this) << (b ? BinaryTag::True : BinaryTag::False);
|
||||
}
|
||||
|
||||
BinaryWriter& operator<<(double d) {
|
||||
uint64_t n;
|
||||
memcpy(&n, &d, sizeof(d));
|
||||
uint8_t buf[8];
|
||||
buf[0] = (n >> 56) & 0xff;
|
||||
buf[1] = (n >> 48) & 0xff;
|
||||
buf[2] = (n >> 40) & 0xff;
|
||||
buf[3] = (n >> 32) & 0xff;
|
||||
buf[4] = (n >> 24) & 0xff;
|
||||
buf[5] = (n >> 16) & 0xff;
|
||||
buf[6] = (n >> 8) & 0xff;
|
||||
buf[7] = (n) & 0xff;
|
||||
(*this) << BinaryTag::Ieee754;
|
||||
put(uint8_t(sizeof(buf)));
|
||||
return write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
BinaryWriter& _put_medium_int(uint64_t u, int shift, int extra) {
|
||||
(*this) << BinaryTag::SignedInteger;
|
||||
put(uint8_t((shift >> 3) + extra + 1));
|
||||
if (extra) put(0);
|
||||
while (shift >= 0) {
|
||||
put(uint8_t((u >> shift) & 0xff));
|
||||
shift -= 8;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, BinaryWriter&>::type operator<<(T t) {
|
||||
if (t == 0) {
|
||||
(*this) << BinaryTag::SignedInteger;
|
||||
put(0);
|
||||
return *this;
|
||||
} else if (std::numeric_limits<T>::is_signed) {
|
||||
auto i = static_cast<int64_t>(t);
|
||||
uint64_t u;
|
||||
memcpy(&u, &i, sizeof(i));
|
||||
int shift = i < 0 ? _shift_for<0xff, 0x80>(u) : _shift_for<0x00, 0x00>(u);
|
||||
return _put_medium_int(u, shift, 0);
|
||||
} else {
|
||||
auto u = static_cast<uint64_t>(t);
|
||||
if ((u & 0x8000000000000000) != 0) {
|
||||
return _put_medium_int(u, 56, 1);
|
||||
} else {
|
||||
return _put_medium_int(u, _shift_for<0x00, 0x00>(u), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& record(Value<T> const& label, std::vector<Value<T>> const& fields) {
|
||||
return (*this) << BinaryTag::Record << label << iterable(fields) << BinaryTag::End;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& sequence(std::vector<Value<T>> const& vs) {
|
||||
return (*this) << BinaryTag::Sequence << iterable(vs) << BinaryTag::End;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& set(std::set<Value<T>> const& vs) {
|
||||
return (*this) << BinaryTag::Set << iterable(vs) << BinaryTag::End;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& dictionary(std::map<Value<T>, Value<T>> const& vs) {
|
||||
(*this) << BinaryTag::Dictionary;
|
||||
for (auto& i : vs) { (*this) << i.first << i.second; }
|
||||
return (*this) << BinaryTag::End;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
BinaryWriter& annotated(Value<T> const& underlying, std::vector<Value<T>> const& vs) {
|
||||
for (auto& v : vs) { (*this) << BinaryTag::Annotation << v; }
|
||||
return (*this) << underlying;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace Preserves {
|
||||
template <typename T, typename Atom, ValueKind kind>
|
||||
class Atomic: public ValueImpl<T> {
|
||||
protected:
|
||||
Atom value;
|
||||
public:
|
||||
Atomic(Atom const& value) : value(value) {}
|
||||
Atom& _value() { return value; }
|
||||
Atom const& _value() const { return value; }
|
||||
ValueKind value_kind() const { return kind; }
|
||||
};
|
||||
|
||||
#define PRESERVES_ATOMIC_VALUE_CLASS(Name, a_t, r_t, VK, getter, extra) \
|
||||
template <typename T = class GenericEmbedded> \
|
||||
class Name: public Atomic<T, a_t, VK> { \
|
||||
public: \
|
||||
Name(a_t const& value) : Atomic<T, a_t, VK>(value) {} \
|
||||
boost::optional<r_t> getter() const override { return this->value; } \
|
||||
extra \
|
||||
}
|
||||
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(Boolean, bool, bool, ValueKind::Boolean, as_bool,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w << this->_value();
|
||||
});
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(Double, double, double, ValueKind::Double, as_double,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w << this->_value();
|
||||
});
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(Uint64, uint64_t, uint64_t, ValueKind::SignedInteger, as_unsigned,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w << this->_value();
|
||||
}
|
||||
boost::optional<int64_t> as_signed() const override {
|
||||
if (this->value <= uint64_t(std::numeric_limits<int64_t>::max())) {
|
||||
return this->value;
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<double> as_double() const override {
|
||||
if (uint64_t(double(this->value)) == this->value) {
|
||||
return double(this->value);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
});
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(Int64, int64_t, int64_t, ValueKind::SignedInteger, as_signed,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w << this->_value();
|
||||
}
|
||||
boost::optional<uint64_t> as_unsigned() const override {
|
||||
if (this->value >= 0) {
|
||||
return this->value;
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
boost::optional<double> as_double() const override {
|
||||
if (int64_t(double(this->value)) == this->value) {
|
||||
return double(this->value);
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
});
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(BigNum, std::vector<uint8_t>, std::vector<uint8_t> const&, ValueKind::SignedInteger, as_bignum,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.bignum(this->_value());
|
||||
});
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(String, std::string, std::string const&, ValueKind::String, as_string,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.string(this->_value());
|
||||
});
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(ByteString, std::vector<uint8_t>, std::vector<uint8_t> const&, ValueKind::ByteString, as_bytes,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.bytes(this->_value());
|
||||
});
|
||||
PRESERVES_ATOMIC_VALUE_CLASS(Symbol, std::string, std::string const&, ValueKind::Symbol, as_symbol,
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.symbol(this->_value());
|
||||
});
|
||||
|
||||
template <typename T>
|
||||
class Record: public ValueImpl<T> {
|
||||
public:
|
||||
Value<T> labelValue;
|
||||
std::vector<Value<T>> fields;
|
||||
|
||||
Record(Value<T> const& label) : labelValue(label), fields() {}
|
||||
Record(Value<T> const& label, std::vector<Value<T>> const& fields) : labelValue(label), fields(fields) {}
|
||||
ValueKind value_kind() const { return ValueKind::Record; }
|
||||
boost::optional<Value<T>> label() const override { return labelValue; }
|
||||
size_t size() const { return fields.size(); }
|
||||
boost::optional<Value<T>> get(size_t index) const {
|
||||
if (index < size()) {
|
||||
return fields[index];
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
bool add(Value<T> const& value) override {
|
||||
fields.push_back(value);
|
||||
return true;
|
||||
}
|
||||
bool set(size_t index, Value<T> const& value) override {
|
||||
if (index < size()) {
|
||||
fields[index] = value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator<(Record<T> const& other) const {
|
||||
if (labelValue < other.labelValue) return true;
|
||||
if (other.labelValue < labelValue) return false;
|
||||
return fields < other.fields;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.record(labelValue, fields);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Sequence: public ValueImpl<T> {
|
||||
public:
|
||||
std::vector<Value<T>> values;
|
||||
|
||||
Sequence() : values() {}
|
||||
Sequence(std::vector<Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Sequence; }
|
||||
boost::optional<std::vector<Value<T>> const&> as_sequence() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
boost::optional<Value<T>> get(size_t index) const override {
|
||||
if (index < size()) {
|
||||
return values[index];
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
bool add(Value<T> const& value) override {
|
||||
values.push_back(value);
|
||||
return true;
|
||||
}
|
||||
bool set(size_t index, Value<T> const& value) override {
|
||||
if (index < size()) {
|
||||
values[index] = value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.sequence(values);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Set: public ValueImpl<T> {
|
||||
public:
|
||||
std::set<Value<T>> values;
|
||||
|
||||
Set() : values() {}
|
||||
Set(std::set<Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Set; }
|
||||
boost::optional<std::set<Value<T>> const&> as_set() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
|
||||
bool add(Value<T> const& value) override {
|
||||
return values.insert(value).second;
|
||||
}
|
||||
bool erase(Value<T> const& value) override {
|
||||
return values.erase(value) > 0;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.set(values);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Dictionary: public ValueImpl<T> {
|
||||
public:
|
||||
std::map<Value<T>, Value<T>> values;
|
||||
|
||||
Dictionary() : values() {}
|
||||
Dictionary(std::map<Value<T>, Value<T>> const& values) : values(values) {}
|
||||
ValueKind value_kind() const { return ValueKind::Dictionary; }
|
||||
boost::optional<std::map<Value<T>, Value<T>> const&> as_dictionary() const override {
|
||||
return values;
|
||||
}
|
||||
size_t size() const override { return values.size(); }
|
||||
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
|
||||
boost::optional<Value<T>> get(Value<T> const& key) const override {
|
||||
auto i = values.find(key);
|
||||
if (i == values.end()) return boost::none;
|
||||
return i->second;
|
||||
}
|
||||
bool set(Value<T> const& key, Value<T> const& value) override {
|
||||
return values.emplace(key, value).second;
|
||||
}
|
||||
bool erase(Value<T> const& key) override {
|
||||
return values.erase(key) > 0;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
return w.dictionary(values);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Embedded: public ValueImpl<T> {
|
||||
public:
|
||||
std::shared_ptr<T> value;
|
||||
|
||||
Embedded(std::shared_ptr<T> const& value) : value(value) {}
|
||||
ValueKind value_kind() const { return ValueKind::Embedded; }
|
||||
boost::optional<std::shared_ptr<T>> as_embedded() const override {
|
||||
return value;
|
||||
}
|
||||
BinaryWriter& write(BinaryWriter& w) const override {
|
||||
w << BinaryTag::Embedded;
|
||||
return value->write(w);
|
||||
}
|
||||
};
|
||||
|
||||
class GenericEmbedded: public Value<GenericEmbedded> {
|
||||
public:
|
||||
GenericEmbedded(std::shared_ptr<ValueImpl<GenericEmbedded>> p) :
|
||||
Value(p)
|
||||
{}
|
||||
|
||||
static std::shared_ptr<GenericEmbedded> wrap(Value<> v) {
|
||||
return std::make_shared<GenericEmbedded>(v._impl());
|
||||
}
|
||||
|
||||
BinaryWriter& write(BinaryWriter& w) const {
|
||||
return (*this)->write(w);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class AnnotatedValue: public ValueImpl<T> {
|
||||
std::vector<Value<T>> anns;
|
||||
Value<T> underlying;
|
||||
|
||||
friend class ValueImpl<T>;
|
||||
|
||||
protected:
|
||||
Value<T> internal_annotate(std::shared_ptr<ValueImpl<T>> const& self, Value<T> const &ann) {
|
||||
if (self.use_count() == 1) {
|
||||
anns.push_back(ann);
|
||||
return self;
|
||||
} else {
|
||||
auto a = std::make_shared<AnnotatedValue<T>>(std::vector<Value<T>>(anns), underlying);
|
||||
a->anns.push_back(ann);
|
||||
return Value<T>(a);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AnnotatedValue(std::vector<Value<T>> &&anns, Value<T> const& v) : anns(anns), underlying(v) {}
|
||||
|
||||
ValueKind value_kind() const override { return underlying.value_kind(); }
|
||||
bool is_mutable() const override { return underlying.is_mutable(); }
|
||||
|
||||
boost::optional<bool> as_bool() const override { return underlying.as_bool(); }
|
||||
boost::optional<double> as_double() const override { return underlying.as_double(); }
|
||||
boost::optional<uint64_t> as_unsigned() const override { return underlying.as_unsigned(); }
|
||||
boost::optional<int64_t> as_signed() const override { return underlying.as_signed(); }
|
||||
boost::optional<std::vector<uint8_t> const&> as_bignum() const override { return underlying.as_bignum(); }
|
||||
boost::optional<std::string const&> as_string() const override { return underlying.as_string(); }
|
||||
boost::optional<std::vector<uint8_t> const&> as_bytes() const override { return underlying.as_bytes(); }
|
||||
boost::optional<std::string const&> as_symbol() const override { return underlying.as_symbol(); }
|
||||
boost::optional<Record<T> const&> as_record() const override { return underlying.as_record(); }
|
||||
boost::optional<std::vector<Value<T>> const&> as_sequence() const override { return underlying.as_sequence(); }
|
||||
boost::optional<std::set<Value<T>> const&> as_set() const override { return underlying.as_set(); }
|
||||
boost::optional<std::map<Value<T>,Value<T>> const&> as_dictionary() const override { return underlying.as_dictionary(); }
|
||||
boost::optional<std::shared_ptr<T>> as_embedded() const override { return underlying.as_embedded(); }
|
||||
|
||||
boost::optional<Value<T>> label() const override { return underlying.label(); }
|
||||
size_t size() const override { return underlying.size(); }
|
||||
bool contains(Value<T> const& key) const override { return underlying.contains(key); }
|
||||
boost::optional<Value<T>> get(Value<T> const& key) const override { return underlying.get(key); }
|
||||
boost::optional<Value<T>> get(size_t index) const override { return underlying.get(index); }
|
||||
|
||||
bool add(Value<T> const& item) override {
|
||||
return underlying->add(item);
|
||||
}
|
||||
bool set(Value<T> const& key, Value<T> const& value) override {
|
||||
return underlying->set(key, value);
|
||||
}
|
||||
bool set(size_t index, Value<T> const& value) override {
|
||||
return underlying->set(index, value);
|
||||
}
|
||||
bool erase(Value<T> const& key) override {
|
||||
return underlying->erase(key);
|
||||
}
|
||||
|
||||
boost::optional<std::vector<Value<T>> const&> annotations() const override {
|
||||
return anns;
|
||||
}
|
||||
|
||||
BinaryWriter& write(BinaryWriter &w) const override {
|
||||
return w.annotated(underlying, anns);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::annotate(Value<T> const& ann) const {
|
||||
return p->internal_annotate(p, ann);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> ValueImpl<T>::internal_annotate(std::shared_ptr<ValueImpl<T>> const& self, Value<T> const &ann) {
|
||||
auto a = std::make_shared<AnnotatedValue<T>>(std::vector<Value<T>>(), Value<T>(self));
|
||||
a->anns.push_back(ann);
|
||||
return Value<T>(a);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_bool(bool b)
|
||||
{
|
||||
return Value<T>(new Boolean<T>(b));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_double(double d)
|
||||
{
|
||||
return Value<T>(new Double<T>(d));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_int(uint64_t i)
|
||||
{
|
||||
return Value<T>(new Uint64<T>(i));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_int(int64_t i) {
|
||||
return Value<T>(new Int64<T>(i));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_string(std::string const& s) {
|
||||
return Value<T>(new String<T>(s));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::sequence(std::vector<Value<T>> const& values) {
|
||||
return Value<T>(new Sequence<T>(values));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Value<T> Value<T>::from_embedded(std::shared_ptr<T> const& v) {
|
||||
return Value<T>(new Embedded<T>(v));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "preserves.hpp"
|
||||
|
||||
namespace Preserves {
|
||||
template <typename T>
|
||||
class TextReader {
|
||||
public:
|
||||
TextReader(std::istream& i);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace Preserves {
|
||||
|
||||
enum class ValueKind {
|
||||
Boolean,
|
||||
Double,
|
||||
SignedInteger,
|
||||
String,
|
||||
ByteString,
|
||||
Symbol,
|
||||
|
||||
Record,
|
||||
Sequence,
|
||||
Set,
|
||||
Dictionary,
|
||||
|
||||
Embedded,
|
||||
};
|
||||
|
||||
template <typename T = class GenericEmbedded> class Record;
|
||||
template <typename T = class GenericEmbedded> class ValueImpl;
|
||||
|
||||
template <typename T = class GenericEmbedded>
|
||||
class Value {
|
||||
std::shared_ptr<ValueImpl<T>> p;
|
||||
|
||||
public:
|
||||
Value(std::shared_ptr<ValueImpl<T>> const& p) : p(p) {}
|
||||
Value(ValueImpl<T> *p) : p(p) {}
|
||||
|
||||
std::shared_ptr<ValueImpl<T>> _impl() const { return p; }
|
||||
|
||||
static Value from_bool(bool b);
|
||||
static Value from_double(double d);
|
||||
static Value from_int(uint64_t i);
|
||||
static Value from_int(int64_t i);
|
||||
static Value from_string(std::string const& s);
|
||||
static Value from_bytes(std::vector<uint8_t> const& v);
|
||||
static Value from_bytes(std::vector<char> const& v);
|
||||
static Value from_bytes(void *p, size_t len);
|
||||
static Value from_symbol(std::string const& s);
|
||||
|
||||
static Value record(Record<T> const& r);
|
||||
static Value record(Value const& label, std::vector<Value> const& fields);
|
||||
static Value sequence(std::vector<Value> const& items);
|
||||
static Value set(std::set<Value> const& items);
|
||||
static Value dictionary(std::map<Value, Value> const& entries);
|
||||
|
||||
static Value from_embedded(std::shared_ptr<T> const& p);
|
||||
|
||||
static Value from_unsigned(uint64_t i) { return from_int(i); }
|
||||
static Value from_signed(int64_t i) { return from_int(i); }
|
||||
static Value from_bignum(std::vector<uint8_t> const& v);
|
||||
|
||||
static Value from_number(uint64_t i) { return from_int(i); }
|
||||
static Value from_number(int64_t i) { return from_int(i); }
|
||||
static Value from_number(double d) { return from_double(d); }
|
||||
|
||||
static Value from(bool b) { return from_bool(b); }
|
||||
static Value from(double d) { return from_double(d); }
|
||||
static Value from(uint64_t i) { return from_int(i); }
|
||||
static Value from(unsigned i) { return from_int(uint64_t(i)); }
|
||||
static Value from(int64_t i) { return from_int(i); }
|
||||
static Value from(signed i) { return from_int(int64_t(i)); }
|
||||
static Value from(std::string const& s) { return from_string(s); }
|
||||
static Value from(char const* s) { return from_string(s); }
|
||||
static Value from(std::vector<uint8_t> const& v) { return from_bytes(v); }
|
||||
static Value from(std::vector<char> const& v) { return from_bytes(v); }
|
||||
static Value from(void *p, size_t len) { return from_bytes(p, len); }
|
||||
static Value from(Record<T> const& r) { return record(r); }
|
||||
static Value from(Value const& label, std::vector<Value> const& fields) { return record(label, fields); }
|
||||
static Value from(std::vector<Value> const& items) { return sequence(items); }
|
||||
static Value from(std::set<Value> const& items) { return set(items); }
|
||||
static Value from(std::map<Value, Value> const& entries) { return dictionary(entries); }
|
||||
|
||||
ValueImpl<T>& operator*() const { return *p; }
|
||||
ValueImpl<T>* operator->() const { return p.get(); }
|
||||
|
||||
ValueKind value_kind() const;
|
||||
bool is_mutable() const;
|
||||
|
||||
bool is_bool() const { return value_kind() == ValueKind::Boolean; }
|
||||
bool is_double() const { return value_kind() == ValueKind::Double; }
|
||||
bool is_int() const { return value_kind() == ValueKind::SignedInteger; }
|
||||
bool is_string() const { return value_kind() == ValueKind::String; }
|
||||
bool is_bytes() const { return value_kind() == ValueKind::ByteString; }
|
||||
bool is_symbol() const { return value_kind() == ValueKind::Symbol; }
|
||||
bool is_record() const { return value_kind() == ValueKind::Record; }
|
||||
bool is_sequence() const { return value_kind() == ValueKind::Sequence; }
|
||||
bool is_set() const { return value_kind() == ValueKind::Set; }
|
||||
bool is_dictionary() const { return value_kind() == ValueKind::Dictionary; }
|
||||
|
||||
boost::optional<bool> as_bool() const;
|
||||
bool to_bool() const { return as_bool().value(); }
|
||||
|
||||
boost::optional<double> as_double() const;
|
||||
double to_double() const { return as_double().value(); }
|
||||
|
||||
boost::optional<uint64_t> as_unsigned() const;
|
||||
uint64_t to_unsigned() const { return as_unsigned().value(); }
|
||||
|
||||
boost::optional<int64_t> as_signed() const;
|
||||
int64_t to_signed() const { return as_signed().value(); }
|
||||
|
||||
boost::optional<std::vector<uint8_t> const&> as_bignum() const;
|
||||
std::vector<uint8_t> const& to_bignum() const { return as_bignum().value(); }
|
||||
|
||||
boost::optional<std::string const&> as_string() const;
|
||||
std::string const& to_string() const { return as_string().value(); }
|
||||
|
||||
boost::optional<std::vector<uint8_t> const&> as_bytes() const;
|
||||
std::vector<uint8_t> const& to_bytes() const { return as_bytes().value(); }
|
||||
|
||||
boost::optional<std::string const&> as_symbol() const;
|
||||
std::string const& to_symbol() const { return as_symbol().value(); }
|
||||
|
||||
boost::optional<Record<T> const&> as_record() const;
|
||||
Record<T> const& to_record() const { return as_record().value(); };
|
||||
|
||||
boost::optional<std::vector<Value> const&> as_sequence() const;
|
||||
std::vector<Value> const& to_sequence() const { return as_sequence().value(); }
|
||||
|
||||
boost::optional<std::set<Value> const&> as_set() const;
|
||||
std::set<Value> const& to_set() const { return as_set().value(); }
|
||||
|
||||
boost::optional<std::map<Value,Value> const&> as_dictionary() const;
|
||||
std::map<Value,Value> const& to_dictionary() const { return as_dictionary().value(); }
|
||||
|
||||
boost::optional<std::shared_ptr<T>> as_embedded() const;
|
||||
std::shared_ptr<T> to_embedded() const { return as_embedded().value(); }
|
||||
|
||||
Value annotate(Value const& ann) const;
|
||||
boost::optional<std::vector<Value> const&> annotations() const;
|
||||
|
||||
boost::optional<Value> label() const;
|
||||
size_t size() const;
|
||||
bool contains(Value const& key) const;
|
||||
boost::optional<Value> get(Value const& key) const;
|
||||
Value operator[](Value const& key) const { return get(key).value(); }
|
||||
boost::optional<Value> get(size_t index) const;
|
||||
Value operator[](size_t index) const { return get(index).value(); }
|
||||
};
|
||||
|
||||
class BinaryWriter; // forward declaration; see preserves_binary_writer.hpp
|
||||
|
||||
template <typename T>
|
||||
class ValueImpl {
|
||||
protected:
|
||||
virtual Value<T> internal_annotate(std::shared_ptr<ValueImpl<T>> const& self, Value<T> const& ann);
|
||||
|
||||
friend class Value<T>;
|
||||
public:
|
||||
virtual ~ValueImpl() {}
|
||||
|
||||
virtual ValueKind value_kind() const = 0;
|
||||
virtual bool is_mutable() const { return false; }
|
||||
|
||||
virtual boost::optional<bool> as_bool() const { return boost::none; }
|
||||
virtual boost::optional<double> as_double() const { return boost::none; }
|
||||
virtual boost::optional<uint64_t> as_unsigned() const { return boost::none; }
|
||||
virtual boost::optional<int64_t> as_signed() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<uint8_t> const&> as_bignum() const { return boost::none; }
|
||||
virtual boost::optional<std::string const&> as_string() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<uint8_t> const&> as_bytes() const { return boost::none; }
|
||||
virtual boost::optional<std::string const&> as_symbol() const { return boost::none; }
|
||||
virtual boost::optional<Record<T> const&> as_record() const { return boost::none; }
|
||||
virtual boost::optional<std::vector<Value<T>> const&> as_sequence() const { return boost::none; }
|
||||
virtual boost::optional<std::set<Value<T>> const&> as_set() const { return boost::none; }
|
||||
virtual boost::optional<std::map<Value<T>,Value<T>> const&> as_dictionary() const { return boost::none; }
|
||||
virtual boost::optional<std::shared_ptr<T>> as_embedded() const { return boost::none; }
|
||||
|
||||
virtual boost::optional<Value<T>> label() const { return boost::none; }
|
||||
virtual size_t size() const { return 0; }
|
||||
virtual bool contains(Value<T> const& /* key */) const { return false; }
|
||||
virtual boost::optional<Value<T>> get(Value<T> const& /* key */) const { return boost::none; }
|
||||
virtual boost::optional<Value<T>> get(size_t /* index */) const { return boost::none; }
|
||||
|
||||
virtual bool add(Value<T> const& /* item */) {
|
||||
throw std::runtime_error("Cannot add item to Preserves value");
|
||||
}
|
||||
virtual bool set(Value<T> const& /* key */, Value<T> const& /* value */) {
|
||||
throw std::runtime_error("Cannot set item by key in Preserves value");
|
||||
}
|
||||
virtual bool set(size_t /* index */, Value<T> const& /* value */) {
|
||||
throw std::runtime_error("Cannot set item by index in Preserves value");
|
||||
}
|
||||
virtual bool erase(Value<T> const& /* key */) {
|
||||
throw std::runtime_error("Cannot erase item in Preserves value");
|
||||
}
|
||||
|
||||
virtual boost::optional<std::vector<Value<T>> const&> annotations() const { return boost::none; }
|
||||
|
||||
virtual BinaryWriter& write(BinaryWriter& w) const = 0;
|
||||
};
|
||||
|
||||
template <typename T> ValueKind Value<T>::value_kind() const { return p->value_kind(); }
|
||||
template <typename T> bool Value<T>::is_mutable() const { return p->is_mutable(); }
|
||||
|
||||
#define PRESERVES_DELEGATE_CAST(t, name) \
|
||||
template <typename T> boost::optional<t> Value<T>::name() const { return p->name(); }
|
||||
PRESERVES_DELEGATE_CAST(bool, as_bool);
|
||||
PRESERVES_DELEGATE_CAST(double, as_double);
|
||||
PRESERVES_DELEGATE_CAST(uint64_t, as_unsigned);
|
||||
PRESERVES_DELEGATE_CAST(int64_t, as_signed);
|
||||
PRESERVES_DELEGATE_CAST(std::vector<uint8_t> const&, as_bignum);
|
||||
PRESERVES_DELEGATE_CAST(std::string const&, as_string);
|
||||
PRESERVES_DELEGATE_CAST(std::vector<uint8_t> const&, as_bytes);
|
||||
PRESERVES_DELEGATE_CAST(std::string const&, as_symbol);
|
||||
PRESERVES_DELEGATE_CAST(Record<T> const&, as_record);
|
||||
PRESERVES_DELEGATE_CAST(std::vector<Value<T>> const&, as_sequence);
|
||||
PRESERVES_DELEGATE_CAST(std::set<Value<T>> const&, as_set);
|
||||
#define COMMA ,
|
||||
PRESERVES_DELEGATE_CAST(std::map<Value<T> COMMA Value<T>> const&, as_dictionary);
|
||||
#undef COMMA
|
||||
PRESERVES_DELEGATE_CAST(std::shared_ptr<T>, as_embedded);
|
||||
#undef PRESERVES_DELEGATE_CAST
|
||||
|
||||
template <typename T> boost::optional<Value<T>> Value<T>::label() const { return p->label(); }
|
||||
template <typename T> bool Value<T>::contains(Value const& key) const { return p->contains(key); }
|
||||
template <typename T> boost::optional<Value<T>> Value<T>::get(Value<T> const& key) const { return p->get(key); }
|
||||
template <typename T> boost::optional<Value<T>> Value<T>::get(size_t index) const { return p->get(index); }
|
||||
template <typename T> size_t Value<T>::size() const { return p->size(); }
|
||||
|
||||
inline bool bignum_lt(std::vector<uint8_t> const& a, std::vector<uint8_t> const& b) {
|
||||
bool aNegative = (a.size() > 0) && (a[0] & 0x80);
|
||||
bool bNegative = (b.size() > 0) && (b[0] & 0x80);
|
||||
if (aNegative != bNegative) return aNegative;
|
||||
if (aNegative) {
|
||||
if (a.size() > b.size()) return true;
|
||||
if (a.size() < b.size()) return false;
|
||||
return a < b;
|
||||
} else {
|
||||
if (a.size() > b.size()) return false;
|
||||
if (a.size() < b.size()) return true;
|
||||
return a < b;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator<(Value<T> const& a, Value<T> const &b) {
|
||||
auto aKind = a.value_kind();
|
||||
auto bKind = b.value_kind();
|
||||
if (aKind < bKind) return true;
|
||||
if (bKind < aKind) return false;
|
||||
switch (aKind) {
|
||||
case ValueKind::Boolean: return a.to_bool() < b.to_bool();
|
||||
case ValueKind::Double: return a.to_double() < b.to_double();
|
||||
case ValueKind::SignedInteger: {
|
||||
if (auto av = a.as_signed()) {
|
||||
if (auto bv = b.as_signed()) {
|
||||
return *av < *bv;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (auto bv = b.as_signed()) {
|
||||
return false;
|
||||
} else {
|
||||
if (auto av = a.as_unsigned()) {
|
||||
if (auto bv = b.as_unsigned()) {
|
||||
return *av < *bv;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (auto bv = b.as_unsigned()) {
|
||||
return false;
|
||||
} else {
|
||||
return bignum_lt(a.to_bignum(), b.to_bignum());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case ValueKind::String: return a.to_string() < b.to_string();
|
||||
case ValueKind::ByteString: return a.to_bytes() < b.to_bytes();
|
||||
case ValueKind::Symbol: return a.to_symbol() < b.to_symbol();
|
||||
case ValueKind::Record: return a.to_record() < b.to_record();
|
||||
case ValueKind::Sequence: return a.to_sequence() < b.to_sequence();
|
||||
case ValueKind::Set: return a.to_set() < b.to_set();
|
||||
case ValueKind::Dictionary: return a.to_dictionary() < b.to_dictionary();
|
||||
case ValueKind::Embedded: return *a.to_embedded() < *b.to_embedded();
|
||||
default: throw std::runtime_error("Invalid ValueKind");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
env:DHALL_PRELUDE
|
||||
? https://prelude.dhall-lang.org/v20.2.0/package.dhall
|
||||
sha256:a6036bc38d883450598d1de7c98ead113196fe2db02e9733855668b18096f07b
|
|
@ -0,0 +1,48 @@
|
|||
# Dhall
|
||||
|
||||
Not a true implementation of Preserves, but functions for translating Dhall
|
||||
values to Preserves and rendering them.
|
||||
|
||||
For example, to generate configuration for a Syndicate server listener:
|
||||
```dhall
|
||||
let Prelude = ./Prelude.dhall
|
||||
|
||||
let Preserves = ./package.dhall
|
||||
|
||||
let Tcp/Type = { address : Text, port : Natural }
|
||||
|
||||
let RelayListener/Type = { transport : Tcp/Type }
|
||||
|
||||
let RequireService/Type = { relayListener : RelayListener/Type }
|
||||
|
||||
let Tcp/toPreserves =
|
||||
λ(tcp : Tcp/Type) →
|
||||
Preserves.record
|
||||
(Preserves.symbol "tcp")
|
||||
[ Preserves.string tcp.address
|
||||
, Preserves.integer (Prelude.Natural.toInteger tcp.port)
|
||||
]
|
||||
|
||||
let RelayListener/toPreserves =
|
||||
λ(relayListener : RelayListener/Type) →
|
||||
Preserves.record
|
||||
(Preserves.symbol "relay-listener")
|
||||
[ Tcp.toPreserves relayListener.transport ]
|
||||
|
||||
let RequireService/toPreserves =
|
||||
λ(requireService : RequireService/Type) →
|
||||
Preserves.record
|
||||
(Preserves.symbol "require-service")
|
||||
[ RelayListener.toPreserves requireService.relayListener ]
|
||||
|
||||
let example = { relayListener.transport = { address = "127.0.0.1", port = 1 } }
|
||||
|
||||
let rendering = Preserves.render (RequireService.toPreserves example)
|
||||
|
||||
let check =
|
||||
assert
|
||||
: rendering ≡ "<require-service <relay-listener <tcp \"127.0.0.1\" 1>>>"
|
||||
|
||||
in rendering
|
||||
|
||||
```
|
|
@ -0,0 +1,10 @@
|
|||
{-|
|
||||
Dhall encoding of an arbitrary Preserves value
|
||||
-}
|
||||
let Preserves/function = ./function.dhall
|
||||
|
||||
let Preserves/Type
|
||||
: Type
|
||||
= ∀(Preserves : Type) → ∀(value : Preserves/function Preserves) → Preserves
|
||||
|
||||
in Preserves/Type
|
|
@ -0,0 +1,15 @@
|
|||
{-|
|
||||
Create a Preserves boolean map from a `Bool` value
|
||||
-}
|
||||
let Preserves/Type = ./Type.dhall
|
||||
|
||||
let Preserves/function = ./function.dhall
|
||||
|
||||
let bool
|
||||
: Bool → Preserves/Type
|
||||
= λ(x : Bool) →
|
||||
λ(Preserves : Type) →
|
||||
λ(value : Preserves/function Preserves) →
|
||||
value.boolean x
|
||||
|
||||
in bool
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue